@epic-web/workshop-utils 6.32.2 → 6.33.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.
- package/dist/esm/cache.server.d.ts.map +1 -1
- package/dist/esm/cache.server.js +1 -0
- package/dist/esm/cache.server.js.map +1 -1
- package/dist/esm/db.server.d.ts +56 -0
- package/dist/esm/db.server.d.ts.map +1 -1
- package/dist/esm/db.server.js +1 -1
- package/dist/esm/db.server.js.map +1 -1
- package/dist/esm/epic-api.server.d.ts.map +1 -1
- package/dist/esm/epic-api.server.js +20 -19
- package/dist/esm/epic-api.server.js.map +1 -1
- package/dist/esm/logger.d.ts +5 -4
- package/dist/esm/logger.d.ts.map +1 -1
- package/dist/esm/logger.js +13 -5
- package/dist/esm/logger.js.map +1 -1
- package/dist/esm/utils.server.d.ts.map +1 -1
- package/dist/esm/utils.server.js +38 -4
- package/dist/esm/utils.server.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/cache.server.js
CHANGED
|
@@ -519,6 +519,7 @@ export async function cachified({ request, timings, key, timingKey = key.length
|
|
|
519
519
|
if (offlineFallbackValue !== undefined) {
|
|
520
520
|
const isOnline = await checkConnection({ request, timings });
|
|
521
521
|
if (!isOnline) {
|
|
522
|
+
log.warn(`Offline: using cached value for ${key} or offline fallback if no cache is present`);
|
|
522
523
|
const cacheEntry = await options.cache.get(key);
|
|
523
524
|
return cacheEntry?.value ?? offlineFallbackValue;
|
|
524
525
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.server.js","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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","sourcesContent":["// eslint-disable-next-line import/order -- this must be first\nimport { getEnv } from './init-env.js'\n\nimport path from 'path'\nimport * as C from '@epic-web/cachified'\nimport { type CacheEntry, type CreateReporter } from '@epic-web/cachified'\nimport { remember } from '@epic-web/remember'\nimport fsExtra from 'fs-extra'\nimport { LRUCache } from 'lru-cache'\nimport md5 from 'md5-hex'\nimport z from 'zod'\nimport {\n\ttype ExampleApp,\n\ttype PlaygroundApp,\n\ttype ProblemApp,\n\ttype SolutionApp,\n} from './apps.server.js'\nimport { resolveCacheDir } from './data-storage.server.js'\nimport { logger } from './logger.js'\nimport { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnection } from './utils.server.js'\n\nconst MAX_CACHE_FILE_SIZE = 3 * 1024 * 1024 // 3MB in bytes\nconst cacheDir = resolveCacheDir()\nconst log = logger('epic:cache')\n\n// Throttle repeated Sentry reports for corrupted cache files to reduce noise\nconst corruptedReportThrottle = remember(\n\t'epic:cache:corruption-throttle',\n\t() => new LRUCache<string, number>({ max: 2000, ttl: 60_000 }),\n)\n\n// Format cache time helper function (copied from @epic-web/cachified for consistency)\nfunction formatCacheTime(\n\tmetadata: any,\n\tformatDuration: (ms: number) => string,\n): string {\n\tconst ttl = metadata?.ttl\n\tif (ttl === undefined || ttl === Infinity) return 'forever'\n\treturn formatDuration(ttl)\n}\n\n// Default duration formatter (copied from @epic-web/cachified for consistency)\nfunction defaultFormatDuration(ms: number): string {\n\tif (ms < 1000) return `${Math.round(ms)}ms`\n\tif (ms < 60000) return `${Math.round(ms / 1000)}s`\n\tif (ms < 3600000) return `${Math.round(ms / 60000)}m`\n\treturn `${Math.round(ms / 3600000)}h`\n}\n\n/**\n * Creates a cachified reporter that integrates with the Epic Workshop logger system.\n * Uses the pattern `epic:cache:{name-of-cache}` for logger namespaces.\n * Only logs when the specific cache namespace is enabled via NODE_DEBUG.\n */\nexport function epicCacheReporter<Value>({\n\tformatDuration = defaultFormatDuration,\n\tperformance = globalThis.performance || Date,\n}: {\n\tformatDuration?: (ms: number) => string\n\tperformance?: Pick<typeof Date, 'now'>\n} = {}): CreateReporter<Value> {\n\treturn ({ key, fallbackToCache, forceFresh, metadata, cache }) => {\n\t\t// Determine cache name for logger namespace\n\t\tconst cacheName =\n\t\t\tcache.name || cache.toString().replace(/^\\[object (.*?)]$/, '$1')\n\n\t\t// Create logger with epic:cache:{name} pattern\n\t\t// Extract a reasonable cache name from longer descriptions\n\t\tlet loggerSuffix = 'unknown'\n\t\tif (cacheName.includes('(') && cacheName.includes(')')) {\n\t\t\t// Extract name from \"Filesystem cache (CacheName)\" format\n\t\t\tconst match = cacheName.match(/\\(([^)]+)\\)/)\n\t\t\tloggerSuffix = (match?.[1] ?? 'unknown').toLowerCase()\n\t\t} else if (cacheName === 'LRUCache') {\n\t\t\t// For LRU caches, we can't determine the name from the cache object alone\n\t\t\tloggerSuffix = 'lru'\n\t\t} else {\n\t\t\tloggerSuffix = cacheName.toLowerCase()\n\t\t}\n\n\t\tconst cacheLog = log.logger(loggerSuffix)\n\n\t\tlet freshValue: unknown\n\t\tlet getFreshValueStartTs: number\n\t\tlet refreshValueStartTS: number\n\n\t\treturn (event) => {\n\t\t\tswitch (event.name) {\n\t\t\t\tcase 'getCachedValueStart': {\n\t\t\t\t\tcacheLog(`Starting cache lookup for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueEmpty': {\n\t\t\t\t\tcacheLog(`Cache miss for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueSuccess': {\n\t\t\t\t\tcacheLog(`Cache hit for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'done': {\n\t\t\t\t\tcacheLog(`Cache operation done for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueRead': {\n\t\t\t\t\tcacheLog(`Read cached value for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueHookPending': {\n\t\t\t\t\tcacheLog(`Waiting for ongoing fetch for fresh value for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueOutdated': {\n\t\t\t\t\tcacheLog(`Cached value for ${key} is outdated`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkCachedValueErrorObj': {\n\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t`check failed for cached value of ${key}\\nReason: ${event.reason}.\\nDeleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkFreshValueErrorObj': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`check failed for fresh value of ${key}\\nReason: ${event.reason}.`,\n\t\t\t\t\t\tfreshValue,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueCacheFallback': {\n\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t`Falling back to cached value for ${key} due to error getting fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkCachedValueError': {\n\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t`check failed for cached value of ${key}\\nReason: ${event.reason}.\\nDeleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`error with cache at ${key}. Deleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t\tevent.error,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`getting a fresh value for ${key} failed`,\n\t\t\t\t\t\t{ fallbackToCache, forceFresh },\n\t\t\t\t\t\tevent.error,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueStart': {\n\t\t\t\t\tgetFreshValueStartTs = performance.now()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'writeFreshValueSuccess': {\n\t\t\t\t\tconst totalTime = performance.now() - getFreshValueStartTs\n\t\t\t\t\tif (event.written) {\n\t\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t\t`Updated the cache value for ${key}.`,\n\t\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\t\ttotalTime,\n\t\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t\t`Caching for ${formatCacheTime(\n\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t\t)} in ${cacheName}.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t\t`Not updating the cache value for ${key}.`,\n\t\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\t\ttotalTime,\n\t\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t\t`Thereby exceeding caching time of ${formatCacheTime(\n\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'writeFreshValueError': {\n\t\t\t\t\tlog.error(`error setting cache: ${key}`, event.error)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueSuccess': {\n\t\t\t\t\tfreshValue = event.value\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkFreshValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`check failed for fresh value of ${key}\\nReason: ${event.reason}.`,\n\t\t\t\t\t\tfreshValue,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueStart': {\n\t\t\t\t\trefreshValueStartTS = performance.now()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueSuccess': {\n\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t`Background refresh for ${key} successful.`,\n\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\tperformance.now() - refreshValueStartTS,\n\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t`Caching for ${formatCacheTime(\n\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t)} in ${cacheName}.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueError': {\n\t\t\t\t\tlog.error(`Background refresh for ${key} failed.`, event.error)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\t// @ts-expect-error Defensive programming: log unknown events for debugging\n\t\t\t\t\tcacheLog(`Unknown cache event \"${event.name}\" for key ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const solutionAppCache =\n\tmakeSingletonFsCache<SolutionApp>('SolutionAppCache')\nexport const problemAppCache =\n\tmakeSingletonFsCache<ProblemApp>('ProblemAppCache')\nexport const exampleAppCache =\n\tmakeSingletonFsCache<ExampleApp>('ExampleAppCache')\nexport const playgroundAppCache =\n\tmakeSingletonFsCache<PlaygroundApp>('PlaygroundAppCache')\nexport const diffCodeCache = makeSingletonFsCache<string>('DiffCodeCache')\nexport const diffFilesCache = makeSingletonFsCache<string>('DiffFilesCache')\nexport const copyUnignoredFilesCache = makeSingletonCache<string>(\n\t'CopyUnignoredFilesCache',\n)\nexport const compiledMarkdownCache = makeSingletonFsCache<string>(\n\t'CompiledMarkdownCache',\n)\nexport const compiledCodeCache = makeSingletonCache<string>('CompiledCodeCache')\nexport const ogCache = makeSingletonCache<string>('OgCache')\nexport const compiledInstructionMarkdownCache = makeSingletonFsCache<{\n\tcode: string\n\ttitle: string | null\n\tepicVideoEmbeds: Array<string>\n}>('CompiledInstructionMarkdownCache')\nexport const dirModifiedTimeCache = makeSingletonCache<number>(\n\t'DirModifiedTimeCache',\n)\nexport const connectionCache = makeSingletonCache<boolean>('ConnectionCache')\nexport const checkForUpdatesCache = makeSingletonCache<{\n\tupdatesAvailable: boolean\n\tlocalCommit: string\n\tremoteCommit: string\n\tdiffLink: string | null\n}>('CheckForUpdatesCache')\nexport const notificationsCache =\n\tmakeSingletonCache<Array<Notification>>('NotificationsCache')\nexport const directoryEmptyCache = makeSingletonCache<boolean>(\n\t'DirectoryEmptyCache',\n)\n\nexport const discordCache = makeSingletonFsCache('DiscordCache')\nexport const epicApiCache = makeSingletonFsCache('EpicApiCache')\n\nasync function readJsonFilesInDirectory(\n\tdir: string,\n): Promise<Record<string, any>> {\n\tconst files = await fsExtra.readdir(dir)\n\tconst entries = await Promise.all(\n\t\tfiles\n\t\t\t.filter((file) => {\n\t\t\t\t// Filter out system files that should not be parsed as JSON\n\t\t\t\tconst lowercaseFile = file.toLowerCase()\n\t\t\t\treturn (\n\t\t\t\t\t!lowercaseFile.startsWith('.ds_store') &&\n\t\t\t\t\t!lowercaseFile.startsWith('.') &&\n\t\t\t\t\t!lowercaseFile.includes('thumbs.db')\n\t\t\t\t)\n\t\t\t})\n\t\t\t.map(async (file) => {\n\t\t\t\tconst filePath = path.join(dir, file)\n\t\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\tconst subEntries = await readJsonFilesInDirectory(filePath)\n\t\t\t\t\treturn [file, subEntries]\n\t\t\t\t} else {\n\t\t\t\t\t// Check file size before attempting to read JSON\n\t\t\t\t\tif (stats.size > MAX_CACHE_FILE_SIZE) {\n\t\t\t\t\t\tconst sizeInMB = (stats.size / (1024 * 1024)).toFixed(2)\n\t\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t\t`Skipping large cache file ${filePath} (${sizeInMB}MB > 3MB limit). ` +\n\t\t\t\t\t\t\t\t`Consider clearing cache or excluding this file type from the admin interface.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tfile,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\terror: `File too large (${sizeInMB}MB > 3MB limit)`,\n\t\t\t\t\t\t\t\tsize: stats.size,\n\t\t\t\t\t\t\t\tskipped: true,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = await readJSONWithRetries(filePath)\n\t\t\t\t\tif (data) {\n\t\t\t\t\t\treturn [file, { ...data, size: stats.size, filepath: filePath }]\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [file, null]\n\t\t\t\t}\n\t\t\t}),\n\t)\n\treturn Object.fromEntries(entries)\n}\n\n// Helper to read JSON with a couple retries; deletes corrupted files and warns\nasync function readJSONWithRetries(filePath: string): Promise<any | null> {\n\tconst maxRetries = 3\n\tconst baseDelay = 10\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\treturn await fsExtra.readJSON(filePath)\n\t\t} catch (error: unknown) {\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\t'code' in error &&\n\t\t\t\t(error as NodeJS.ErrnoException).code === 'ENOENT'\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst isJsonParseError =\n\t\t\t\terror instanceof SyntaxError ||\n\t\t\t\t(error instanceof Error && error.message.includes('JSON'))\n\n\t\t\tif (isJsonParseError) {\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for ${filePath}, retrying in ${delay}ms...`,\n\t\t\t\t\t)\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, delay))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Final attempt failed: optionally report and delete file\n\t\t\t\tif (getEnv().SENTRY_DSN && getEnv().EPICSHOP_IS_PUBLISHED) {\n\t\t\t\t\tconst throttleKey = `readJSON:${md5(filePath)}`\n\t\t\t\t\tif (!corruptedReportThrottle.has(throttleKey)) {\n\t\t\t\t\t\tcorruptedReportThrottle.set(throttleKey, Date.now())\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\t\t\tSentry.withScope((scope) => {\n\t\t\t\t\t\t\t\tscope.setLevel('warning')\n\t\t\t\t\t\t\t\tscope.setTag('error_type', 'corrupted_cache_file')\n\t\t\t\t\t\t\t\tscope.setExtra('filePath', filePath)\n\t\t\t\t\t\t\t\tscope.setExtra('errorMessage', (error as Error).message)\n\t\t\t\t\t\t\t\tscope.setExtra('retryAttempts', attempt)\n\t\t\t\t\t\t\t\tSentry.captureException(error)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} catch (sentryError) {\n\t\t\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Always delete corrupted files so subsequent reads can refetch\n\t\t\t\ttry {\n\t\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Deleted corrupted cache file after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t)\n\t\t\t\t} catch (deleteError) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Failed to delete corrupted cache file ${filePath}:`,\n\t\t\t\t\t\tdeleteError,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Other errors: do not retry\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn null\n}\n\nconst CacheEntrySchema = z.object({\n\tkey: z.string(),\n\tentry: z.object({\n\t\tvalue: z.unknown(),\n\t\tmetadata: z.object({\n\t\t\tcreatedTime: z.number(),\n\t\t\t// Stored JSON may serialize Infinity as null; allow number | null | undefined\n\t\t\tttl: z.number().nullable().optional(),\n\t\t\t// Some entries may omit swr; allow optional\n\t\t\tswr: z.number().optional(),\n\t\t}),\n\t}),\n\tsize: z.number().optional(), // File size in bytes\n\tfilepath: z.string().optional(), // Full filesystem path to the cache file\n})\n\n// Schema for files that were skipped due to size limits\nconst SkippedFileSchema = z.object({\n\terror: z.string(),\n\tsize: z.number(),\n\tskipped: z.literal(true),\n})\n\n// Combined schema that can handle both cache entries and skipped files\nconst CacheFileSchema = z.union([CacheEntrySchema, SkippedFileSchema])\n\ntype CacheEntryType = z.infer<typeof CacheEntrySchema>\n\nexport const WorkshopCacheSchema = z\n\t.record(z.record(z.record(CacheFileSchema)))\n\t.transform((workshopCaches) => {\n\t\ttype CacheEntryWithFilename = CacheEntryType & { filename: string }\n\t\ttype SkippedFileWithFilename = {\n\t\t\tfilename: string\n\t\t\terror: string\n\t\t\tsize: number\n\t\t\tskipped: true\n\t\t}\n\t\ttype Cache = {\n\t\t\tname: string\n\t\t\tentries: Array<CacheEntryWithFilename>\n\t\t\tskippedFiles?: Array<SkippedFileWithFilename>\n\t\t}\n\n\t\tconst cachesArray: Array<{\n\t\t\tworkshopId: string\n\t\t\tcaches: Array<Cache>\n\t\t}> = []\n\n\t\tfor (const [workshopId, caches] of Object.entries(workshopCaches)) {\n\t\t\tconst cachesInDir: Array<Cache> = []\n\t\t\tfor (const [cacheName, entriesObj] of Object.entries(caches)) {\n\t\t\t\tconst entries: Array<CacheEntryWithFilename> = []\n\t\t\t\tconst skippedFiles: Array<SkippedFileWithFilename> = []\n\n\t\t\t\tfor (const [key, value] of Object.entries(entriesObj)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === 'object' &&\n\t\t\t\t\t\t'skipped' in value &&\n\t\t\t\t\t\tvalue.skipped\n\t\t\t\t\t) {\n\t\t\t\t\t\t// This is a skipped file\n\t\t\t\t\t\tskippedFiles.push({\n\t\t\t\t\t\t\tfilename: key,\n\t\t\t\t\t\t\terror: value.error as string,\n\t\t\t\t\t\t\tsize: value.size as number,\n\t\t\t\t\t\t\tskipped: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This is a regular cache entry\n\t\t\t\t\t\tentries.push({ ...(value as CacheEntryType), filename: key })\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst cache: Cache = { name: cacheName, entries }\n\t\t\t\tif (skippedFiles.length > 0) {\n\t\t\t\t\tcache.skippedFiles = skippedFiles\n\t\t\t\t}\n\t\t\t\tcachesInDir.push(cache)\n\t\t\t}\n\t\t\tcachesArray.push({ workshopId, caches: cachesInDir })\n\t\t}\n\n\t\treturn cachesArray\n\t})\n\nexport async function getAllWorkshopCaches() {\n\tconst files = await readJsonFilesInDirectory(cacheDir)\n\tconst parseResult = WorkshopCacheSchema.safeParse(files)\n\tif (!parseResult.success) {\n\t\tlog.error('Failed to parse workshop caches:', parseResult.error)\n\t\treturn []\n\t}\n\treturn parseResult.data\n}\n\nexport async function getWorkshopFileCaches() {\n\tconst workshopCacheDir = path.join(\n\t\tcacheDir,\n\t\tgetEnv().EPICSHOP_WORKSHOP_INSTANCE_ID,\n\t)\n\tconst caches = readJsonFilesInDirectory(workshopCacheDir)\n\treturn caches\n}\n\nexport async function readEntryByPath(cacheFilePath: string) {\n\tconst filePath = path.join(cacheDir, cacheFilePath)\n\tconst data = await readJSONWithRetries(filePath)\n\treturn data?.entry ?? null\n}\n\nexport async function deleteCache() {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(cacheDir)) {\n\t\t\tawait fsExtra.remove(cacheDir)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the cache in ${cacheDir}`, error)\n\t}\n}\n\nexport async function deleteCacheEntry(cacheFilePath: string) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst filePath = path.join(cacheDir, cacheFilePath)\n\t\tawait fsExtra.remove(filePath)\n\t} catch (error) {\n\t\tconsole.error(`Error deleting cache entry ${cacheFilePath}:`, error)\n\t}\n}\n\nexport async function deleteWorkshopCache(\n\tworkshopId: string,\n\tcacheName?: string,\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (cacheName) {\n\t\t\t// Delete specific cache within workshop\n\t\t\tconst cachePath = path.join(cacheDir, workshopId, cacheName)\n\t\t\tif (await fsExtra.exists(cachePath)) {\n\t\t\t\tawait fsExtra.remove(cachePath)\n\t\t\t}\n\t\t} else {\n\t\t\t// Delete entire workshop cache directory\n\t\t\tconst workshopCachePath = path.join(cacheDir, workshopId)\n\t\t\tif (await fsExtra.exists(workshopCachePath)) {\n\t\t\t\tawait fsExtra.remove(workshopCachePath)\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t`Error deleting workshop cache ${workshopId}/${cacheName || 'all'}:`,\n\t\t\terror,\n\t\t)\n\t}\n}\n\nexport async function updateCacheEntry(cacheFilePath: string, newEntry: any) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst filePath = path.join(cacheDir, cacheFilePath)\n\t\tconst existingData = await readJSONWithRetries(filePath)\n\t\tif (!existingData) {\n\t\t\tthrow new Error(`Cache file does not exist: ${cacheFilePath}`)\n\t\t}\n\n\t\tconst updatedData = {\n\t\t\t...existingData,\n\t\t\tentry: {\n\t\t\t\t...existingData.entry,\n\t\t\t\tvalue: newEntry,\n\t\t\t\tmetadata: {\n\t\t\t\t\t...existingData.entry.metadata,\n\t\t\t\t\tcreatedTime: Date.now(), // Update timestamp\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tawait fsExtra.writeJSON(filePath, updatedData)\n\t\treturn updatedData.entry\n\t} catch (error) {\n\t\tconsole.error(`Error updating cache entry ${cacheFilePath}:`, error)\n\t\tthrow error\n\t}\n}\n\nexport function makeSingletonCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst lruInstance = new LRUCache<string, CacheEntry<CacheEntryType>>({\n\t\t\tmax: 1000,\n\t\t})\n\n\t\tconst lru = {\n\t\t\tname,\n\t\t\tset: (key, value) => {\n\t\t\t\tconst ttl = C.totalTtl(value.metadata)\n\t\t\t\tlruInstance.set(key, value, {\n\t\t\t\t\tttl: ttl === Infinity ? undefined : ttl,\n\t\t\t\t\tstart: value.metadata.createdTime,\n\t\t\t\t})\n\t\t\t\treturn value\n\t\t\t},\n\t\t\tget: (key) => lruInstance.get(key),\n\t\t\tdelete: (key) => lruInstance.delete(key),\n\t\t} satisfies C.Cache<CacheEntryType>\n\n\t\treturn lru\n\t})\n}\n\nexport function makeSingletonFsCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst cacheInstanceDir = path.join(\n\t\t\tcacheDir,\n\t\t\tgetEnv().EPICSHOP_WORKSHOP_INSTANCE_ID,\n\t\t\tname,\n\t\t)\n\n\t\tconst fsCache: C.Cache<CacheEntryType> = {\n\t\t\tname: `Filesystem cache (${name})`,\n\t\t\tasync get(key) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\n\t\t\t\t// Check file size before attempting to read\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\t\t\tif (stats.size > MAX_CACHE_FILE_SIZE) {\n\t\t\t\t\t\tconst sizeInMB = (stats.size / (1024 * 1024)).toFixed(2)\n\t\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t\t`Skipping large cache file ${filePath} (${sizeInMB}MB > 3MB limit). ` +\n\t\t\t\t\t\t\t\t`Consider clearing \"${name}\" cache for key: ${key}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t\t// For other stat errors, continue with the read attempt\n\t\t\t\t}\n\n\t\t\t\t// Use the shared helper which retries and deletes corrupted files\n\t\t\t\tconst data = await readJSONWithRetries(filePath)\n\t\t\t\tif (data?.entry) return data.entry\n\t\t\t\treturn null\n\n\t\t\t\t// This should never be reached, but just in case\n\t\t\t\treturn null\n\t\t\t},\n\t\t\tasync set(key, entry) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\t\t\t\tconst tempPath = `${filePath}.tmp`\n\t\t\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t\t\t// Write to temp file first, then atomically move to final location\n\t\t\t\t// This prevents race conditions where readers see partially written JSON files\n\t\t\t\tawait fsExtra.writeJSON(tempPath, { key, entry })\n\t\t\t\tawait fsExtra.move(tempPath, filePath, { overwrite: true })\n\t\t\t},\n\t\t\tasync delete(key) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t},\n\t\t}\n\n\t\treturn fsCache\n\t})\n}\n\n/**\n * This wraps @epic-web/cachified to add a few handy features:\n *\n * 1. Automatic timing for timing headers\n * 2. Automatic force refresh based on the request and enhancement of forceFresh\n * to support comma-separated keys to force\n * 3. Offline fallback support. If a fallback is given and we are detected to be\n * offline, then the cached value is used regardless of whether it's expired and\n * if one is not present then the given fallback will be used.\n */\nexport async function cachified<Value>({\n\trequest,\n\ttimings,\n\tkey,\n\ttimingKey = key.length > 18 ? `${key.slice(0, 7)}...${key.slice(-8)}` : key,\n\tofflineFallbackValue,\n\t...options\n}: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean | string\n\ttimingKey?: string\n\tofflineFallbackValue?: Value\n}): Promise<Value> {\n\tif (offlineFallbackValue !== undefined) {\n\t\tconst isOnline = await checkConnection({ request, timings })\n\t\tif (!isOnline) {\n\t\t\tconst cacheEntry = await options.cache.get(key)\n\t\t\treturn cacheEntry?.value ?? offlineFallbackValue\n\t\t}\n\t}\n\tconst forceFresh = await shouldForceFresh({\n\t\tforceFresh: options.forceFresh,\n\t\trequest,\n\t\tkey,\n\t})\n\treturn C.cachified(\n\t\t{\n\t\t\t...options,\n\t\t\tkey,\n\t\t\tforceFresh,\n\t\t},\n\t\tC.mergeReporters(\n\t\t\tcachifiedTimingReporter(timings, timingKey),\n\t\t\tepicCacheReporter(),\n\t\t),\n\t)\n}\n\nexport async function shouldForceFresh({\n\tforceFresh,\n\trequest,\n\tkey,\n}: {\n\tforceFresh?: boolean | string\n\trequest?: Request\n\tkey?: string\n}) {\n\tif (typeof forceFresh === 'boolean') return forceFresh\n\tif (typeof forceFresh === 'string' && key) {\n\t\treturn forceFresh.split(',').includes(key)\n\t}\n\n\tif (!request) return false\n\tconst fresh = new URL(request.url).searchParams.get('fresh')\n\tif (typeof fresh !== 'string') return false\n\tif (fresh === '') return true\n\tif (!key) return false\n\n\treturn fresh.split(',').includes(key)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cache.server.js","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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","sourcesContent":["// eslint-disable-next-line import/order -- this must be first\nimport { getEnv } from './init-env.js'\n\nimport path from 'path'\nimport * as C from '@epic-web/cachified'\nimport { type CacheEntry, type CreateReporter } from '@epic-web/cachified'\nimport { remember } from '@epic-web/remember'\nimport fsExtra from 'fs-extra'\nimport { LRUCache } from 'lru-cache'\nimport md5 from 'md5-hex'\nimport z from 'zod'\nimport {\n\ttype ExampleApp,\n\ttype PlaygroundApp,\n\ttype ProblemApp,\n\ttype SolutionApp,\n} from './apps.server.js'\nimport { resolveCacheDir } from './data-storage.server.js'\nimport { logger } from './logger.js'\nimport { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnection } from './utils.server.js'\n\nconst MAX_CACHE_FILE_SIZE = 3 * 1024 * 1024 // 3MB in bytes\nconst cacheDir = resolveCacheDir()\nconst log = logger('epic:cache')\n\n// Throttle repeated Sentry reports for corrupted cache files to reduce noise\nconst corruptedReportThrottle = remember(\n\t'epic:cache:corruption-throttle',\n\t() => new LRUCache<string, number>({ max: 2000, ttl: 60_000 }),\n)\n\n// Format cache time helper function (copied from @epic-web/cachified for consistency)\nfunction formatCacheTime(\n\tmetadata: any,\n\tformatDuration: (ms: number) => string,\n): string {\n\tconst ttl = metadata?.ttl\n\tif (ttl === undefined || ttl === Infinity) return 'forever'\n\treturn formatDuration(ttl)\n}\n\n// Default duration formatter (copied from @epic-web/cachified for consistency)\nfunction defaultFormatDuration(ms: number): string {\n\tif (ms < 1000) return `${Math.round(ms)}ms`\n\tif (ms < 60000) return `${Math.round(ms / 1000)}s`\n\tif (ms < 3600000) return `${Math.round(ms / 60000)}m`\n\treturn `${Math.round(ms / 3600000)}h`\n}\n\n/**\n * Creates a cachified reporter that integrates with the Epic Workshop logger system.\n * Uses the pattern `epic:cache:{name-of-cache}` for logger namespaces.\n * Only logs when the specific cache namespace is enabled via NODE_DEBUG.\n */\nexport function epicCacheReporter<Value>({\n\tformatDuration = defaultFormatDuration,\n\tperformance = globalThis.performance || Date,\n}: {\n\tformatDuration?: (ms: number) => string\n\tperformance?: Pick<typeof Date, 'now'>\n} = {}): CreateReporter<Value> {\n\treturn ({ key, fallbackToCache, forceFresh, metadata, cache }) => {\n\t\t// Determine cache name for logger namespace\n\t\tconst cacheName =\n\t\t\tcache.name || cache.toString().replace(/^\\[object (.*?)]$/, '$1')\n\n\t\t// Create logger with epic:cache:{name} pattern\n\t\t// Extract a reasonable cache name from longer descriptions\n\t\tlet loggerSuffix = 'unknown'\n\t\tif (cacheName.includes('(') && cacheName.includes(')')) {\n\t\t\t// Extract name from \"Filesystem cache (CacheName)\" format\n\t\t\tconst match = cacheName.match(/\\(([^)]+)\\)/)\n\t\t\tloggerSuffix = (match?.[1] ?? 'unknown').toLowerCase()\n\t\t} else if (cacheName === 'LRUCache') {\n\t\t\t// For LRU caches, we can't determine the name from the cache object alone\n\t\t\tloggerSuffix = 'lru'\n\t\t} else {\n\t\t\tloggerSuffix = cacheName.toLowerCase()\n\t\t}\n\n\t\tconst cacheLog = log.logger(loggerSuffix)\n\n\t\tlet freshValue: unknown\n\t\tlet getFreshValueStartTs: number\n\t\tlet refreshValueStartTS: number\n\n\t\treturn (event) => {\n\t\t\tswitch (event.name) {\n\t\t\t\tcase 'getCachedValueStart': {\n\t\t\t\t\tcacheLog(`Starting cache lookup for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueEmpty': {\n\t\t\t\t\tcacheLog(`Cache miss for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueSuccess': {\n\t\t\t\t\tcacheLog(`Cache hit for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'done': {\n\t\t\t\t\tcacheLog(`Cache operation done for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueRead': {\n\t\t\t\t\tcacheLog(`Read cached value for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueHookPending': {\n\t\t\t\t\tcacheLog(`Waiting for ongoing fetch for fresh value for ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueOutdated': {\n\t\t\t\t\tcacheLog(`Cached value for ${key} is outdated`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkCachedValueErrorObj': {\n\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t`check failed for cached value of ${key}\\nReason: ${event.reason}.\\nDeleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkFreshValueErrorObj': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`check failed for fresh value of ${key}\\nReason: ${event.reason}.`,\n\t\t\t\t\t\tfreshValue,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueCacheFallback': {\n\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t`Falling back to cached value for ${key} due to error getting fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkCachedValueError': {\n\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t`check failed for cached value of ${key}\\nReason: ${event.reason}.\\nDeleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getCachedValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`error with cache at ${key}. Deleting the cache key and trying to get a fresh value.`,\n\t\t\t\t\t\tevent.error,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`getting a fresh value for ${key} failed`,\n\t\t\t\t\t\t{ fallbackToCache, forceFresh },\n\t\t\t\t\t\tevent.error,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueStart': {\n\t\t\t\t\tgetFreshValueStartTs = performance.now()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'writeFreshValueSuccess': {\n\t\t\t\t\tconst totalTime = performance.now() - getFreshValueStartTs\n\t\t\t\t\tif (event.written) {\n\t\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t\t`Updated the cache value for ${key}.`,\n\t\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\t\ttotalTime,\n\t\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t\t`Caching for ${formatCacheTime(\n\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t\t)} in ${cacheName}.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t\t`Not updating the cache value for ${key}.`,\n\t\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\t\ttotalTime,\n\t\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t\t`Thereby exceeding caching time of ${formatCacheTime(\n\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t\t)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'writeFreshValueError': {\n\t\t\t\t\tlog.error(`error setting cache: ${key}`, event.error)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'getFreshValueSuccess': {\n\t\t\t\t\tfreshValue = event.value\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'checkFreshValueError': {\n\t\t\t\t\tlog.error(\n\t\t\t\t\t\t`check failed for fresh value of ${key}\\nReason: ${event.reason}.`,\n\t\t\t\t\t\tfreshValue,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueStart': {\n\t\t\t\t\trefreshValueStartTS = performance.now()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueSuccess': {\n\t\t\t\t\tcacheLog(\n\t\t\t\t\t\t`Background refresh for ${key} successful.`,\n\t\t\t\t\t\t`Getting a fresh value for this took ${formatDuration(\n\t\t\t\t\t\t\tperformance.now() - refreshValueStartTS,\n\t\t\t\t\t\t)}.`,\n\t\t\t\t\t\t`Caching for ${formatCacheTime(\n\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\tformatDuration,\n\t\t\t\t\t\t)} in ${cacheName}.`,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcase 'refreshValueError': {\n\t\t\t\t\tlog.error(`Background refresh for ${key} failed.`, event.error)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\t// @ts-expect-error Defensive programming: log unknown events for debugging\n\t\t\t\t\tcacheLog(`Unknown cache event \"${event.name}\" for key ${key}`)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const solutionAppCache =\n\tmakeSingletonFsCache<SolutionApp>('SolutionAppCache')\nexport const problemAppCache =\n\tmakeSingletonFsCache<ProblemApp>('ProblemAppCache')\nexport const exampleAppCache =\n\tmakeSingletonFsCache<ExampleApp>('ExampleAppCache')\nexport const playgroundAppCache =\n\tmakeSingletonFsCache<PlaygroundApp>('PlaygroundAppCache')\nexport const diffCodeCache = makeSingletonFsCache<string>('DiffCodeCache')\nexport const diffFilesCache = makeSingletonFsCache<string>('DiffFilesCache')\nexport const copyUnignoredFilesCache = makeSingletonCache<string>(\n\t'CopyUnignoredFilesCache',\n)\nexport const compiledMarkdownCache = makeSingletonFsCache<string>(\n\t'CompiledMarkdownCache',\n)\nexport const compiledCodeCache = makeSingletonCache<string>('CompiledCodeCache')\nexport const ogCache = makeSingletonCache<string>('OgCache')\nexport const compiledInstructionMarkdownCache = makeSingletonFsCache<{\n\tcode: string\n\ttitle: string | null\n\tepicVideoEmbeds: Array<string>\n}>('CompiledInstructionMarkdownCache')\nexport const dirModifiedTimeCache = makeSingletonCache<number>(\n\t'DirModifiedTimeCache',\n)\nexport const connectionCache = makeSingletonCache<boolean>('ConnectionCache')\nexport const checkForUpdatesCache = makeSingletonCache<{\n\tupdatesAvailable: boolean\n\tlocalCommit: string\n\tremoteCommit: string\n\tdiffLink: string | null\n}>('CheckForUpdatesCache')\nexport const notificationsCache =\n\tmakeSingletonCache<Array<Notification>>('NotificationsCache')\nexport const directoryEmptyCache = makeSingletonCache<boolean>(\n\t'DirectoryEmptyCache',\n)\n\nexport const discordCache = makeSingletonFsCache('DiscordCache')\nexport const epicApiCache = makeSingletonFsCache('EpicApiCache')\n\nasync function readJsonFilesInDirectory(\n\tdir: string,\n): Promise<Record<string, any>> {\n\tconst files = await fsExtra.readdir(dir)\n\tconst entries = await Promise.all(\n\t\tfiles\n\t\t\t.filter((file) => {\n\t\t\t\t// Filter out system files that should not be parsed as JSON\n\t\t\t\tconst lowercaseFile = file.toLowerCase()\n\t\t\t\treturn (\n\t\t\t\t\t!lowercaseFile.startsWith('.ds_store') &&\n\t\t\t\t\t!lowercaseFile.startsWith('.') &&\n\t\t\t\t\t!lowercaseFile.includes('thumbs.db')\n\t\t\t\t)\n\t\t\t})\n\t\t\t.map(async (file) => {\n\t\t\t\tconst filePath = path.join(dir, file)\n\t\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\tconst subEntries = await readJsonFilesInDirectory(filePath)\n\t\t\t\t\treturn [file, subEntries]\n\t\t\t\t} else {\n\t\t\t\t\t// Check file size before attempting to read JSON\n\t\t\t\t\tif (stats.size > MAX_CACHE_FILE_SIZE) {\n\t\t\t\t\t\tconst sizeInMB = (stats.size / (1024 * 1024)).toFixed(2)\n\t\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t\t`Skipping large cache file ${filePath} (${sizeInMB}MB > 3MB limit). ` +\n\t\t\t\t\t\t\t\t`Consider clearing cache or excluding this file type from the admin interface.`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\tfile,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\terror: `File too large (${sizeInMB}MB > 3MB limit)`,\n\t\t\t\t\t\t\t\tsize: stats.size,\n\t\t\t\t\t\t\t\tskipped: true,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t]\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = await readJSONWithRetries(filePath)\n\t\t\t\t\tif (data) {\n\t\t\t\t\t\treturn [file, { ...data, size: stats.size, filepath: filePath }]\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [file, null]\n\t\t\t\t}\n\t\t\t}),\n\t)\n\treturn Object.fromEntries(entries)\n}\n\n// Helper to read JSON with a couple retries; deletes corrupted files and warns\nasync function readJSONWithRetries(filePath: string): Promise<any | null> {\n\tconst maxRetries = 3\n\tconst baseDelay = 10\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\treturn await fsExtra.readJSON(filePath)\n\t\t} catch (error: unknown) {\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\t'code' in error &&\n\t\t\t\t(error as NodeJS.ErrnoException).code === 'ENOENT'\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst isJsonParseError =\n\t\t\t\terror instanceof SyntaxError ||\n\t\t\t\t(error instanceof Error && error.message.includes('JSON'))\n\n\t\t\tif (isJsonParseError) {\n\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for ${filePath}, retrying in ${delay}ms...`,\n\t\t\t\t\t)\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, delay))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Final attempt failed: optionally report and delete file\n\t\t\t\tif (getEnv().SENTRY_DSN && getEnv().EPICSHOP_IS_PUBLISHED) {\n\t\t\t\t\tconst throttleKey = `readJSON:${md5(filePath)}`\n\t\t\t\t\tif (!corruptedReportThrottle.has(throttleKey)) {\n\t\t\t\t\t\tcorruptedReportThrottle.set(throttleKey, Date.now())\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\t\t\tSentry.withScope((scope) => {\n\t\t\t\t\t\t\t\tscope.setLevel('warning')\n\t\t\t\t\t\t\t\tscope.setTag('error_type', 'corrupted_cache_file')\n\t\t\t\t\t\t\t\tscope.setExtra('filePath', filePath)\n\t\t\t\t\t\t\t\tscope.setExtra('errorMessage', (error as Error).message)\n\t\t\t\t\t\t\t\tscope.setExtra('retryAttempts', attempt)\n\t\t\t\t\t\t\t\tSentry.captureException(error)\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} catch (sentryError) {\n\t\t\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Always delete corrupted files so subsequent reads can refetch\n\t\t\t\ttry {\n\t\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Deleted corrupted cache file after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t)\n\t\t\t\t} catch (deleteError) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Failed to delete corrupted cache file ${filePath}:`,\n\t\t\t\t\t\tdeleteError,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Other errors: do not retry\n\t\t\tthrow error\n\t\t}\n\t}\n\n\treturn null\n}\n\nconst CacheEntrySchema = z.object({\n\tkey: z.string(),\n\tentry: z.object({\n\t\tvalue: z.unknown(),\n\t\tmetadata: z.object({\n\t\t\tcreatedTime: z.number(),\n\t\t\t// Stored JSON may serialize Infinity as null; allow number | null | undefined\n\t\t\tttl: z.number().nullable().optional(),\n\t\t\t// Some entries may omit swr; allow optional\n\t\t\tswr: z.number().optional(),\n\t\t}),\n\t}),\n\tsize: z.number().optional(), // File size in bytes\n\tfilepath: z.string().optional(), // Full filesystem path to the cache file\n})\n\n// Schema for files that were skipped due to size limits\nconst SkippedFileSchema = z.object({\n\terror: z.string(),\n\tsize: z.number(),\n\tskipped: z.literal(true),\n})\n\n// Combined schema that can handle both cache entries and skipped files\nconst CacheFileSchema = z.union([CacheEntrySchema, SkippedFileSchema])\n\ntype CacheEntryType = z.infer<typeof CacheEntrySchema>\n\nexport const WorkshopCacheSchema = z\n\t.record(z.record(z.record(CacheFileSchema)))\n\t.transform((workshopCaches) => {\n\t\ttype CacheEntryWithFilename = CacheEntryType & { filename: string }\n\t\ttype SkippedFileWithFilename = {\n\t\t\tfilename: string\n\t\t\terror: string\n\t\t\tsize: number\n\t\t\tskipped: true\n\t\t}\n\t\ttype Cache = {\n\t\t\tname: string\n\t\t\tentries: Array<CacheEntryWithFilename>\n\t\t\tskippedFiles?: Array<SkippedFileWithFilename>\n\t\t}\n\n\t\tconst cachesArray: Array<{\n\t\t\tworkshopId: string\n\t\t\tcaches: Array<Cache>\n\t\t}> = []\n\n\t\tfor (const [workshopId, caches] of Object.entries(workshopCaches)) {\n\t\t\tconst cachesInDir: Array<Cache> = []\n\t\t\tfor (const [cacheName, entriesObj] of Object.entries(caches)) {\n\t\t\t\tconst entries: Array<CacheEntryWithFilename> = []\n\t\t\t\tconst skippedFiles: Array<SkippedFileWithFilename> = []\n\n\t\t\t\tfor (const [key, value] of Object.entries(entriesObj)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue &&\n\t\t\t\t\t\ttypeof value === 'object' &&\n\t\t\t\t\t\t'skipped' in value &&\n\t\t\t\t\t\tvalue.skipped\n\t\t\t\t\t) {\n\t\t\t\t\t\t// This is a skipped file\n\t\t\t\t\t\tskippedFiles.push({\n\t\t\t\t\t\t\tfilename: key,\n\t\t\t\t\t\t\terror: value.error as string,\n\t\t\t\t\t\t\tsize: value.size as number,\n\t\t\t\t\t\t\tskipped: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This is a regular cache entry\n\t\t\t\t\t\tentries.push({ ...(value as CacheEntryType), filename: key })\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst cache: Cache = { name: cacheName, entries }\n\t\t\t\tif (skippedFiles.length > 0) {\n\t\t\t\t\tcache.skippedFiles = skippedFiles\n\t\t\t\t}\n\t\t\t\tcachesInDir.push(cache)\n\t\t\t}\n\t\t\tcachesArray.push({ workshopId, caches: cachesInDir })\n\t\t}\n\n\t\treturn cachesArray\n\t})\n\nexport async function getAllWorkshopCaches() {\n\tconst files = await readJsonFilesInDirectory(cacheDir)\n\tconst parseResult = WorkshopCacheSchema.safeParse(files)\n\tif (!parseResult.success) {\n\t\tlog.error('Failed to parse workshop caches:', parseResult.error)\n\t\treturn []\n\t}\n\treturn parseResult.data\n}\n\nexport async function getWorkshopFileCaches() {\n\tconst workshopCacheDir = path.join(\n\t\tcacheDir,\n\t\tgetEnv().EPICSHOP_WORKSHOP_INSTANCE_ID,\n\t)\n\tconst caches = readJsonFilesInDirectory(workshopCacheDir)\n\treturn caches\n}\n\nexport async function readEntryByPath(cacheFilePath: string) {\n\tconst filePath = path.join(cacheDir, cacheFilePath)\n\tconst data = await readJSONWithRetries(filePath)\n\treturn data?.entry ?? null\n}\n\nexport async function deleteCache() {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(cacheDir)) {\n\t\t\tawait fsExtra.remove(cacheDir)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the cache in ${cacheDir}`, error)\n\t}\n}\n\nexport async function deleteCacheEntry(cacheFilePath: string) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst filePath = path.join(cacheDir, cacheFilePath)\n\t\tawait fsExtra.remove(filePath)\n\t} catch (error) {\n\t\tconsole.error(`Error deleting cache entry ${cacheFilePath}:`, error)\n\t}\n}\n\nexport async function deleteWorkshopCache(\n\tworkshopId: string,\n\tcacheName?: string,\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (cacheName) {\n\t\t\t// Delete specific cache within workshop\n\t\t\tconst cachePath = path.join(cacheDir, workshopId, cacheName)\n\t\t\tif (await fsExtra.exists(cachePath)) {\n\t\t\t\tawait fsExtra.remove(cachePath)\n\t\t\t}\n\t\t} else {\n\t\t\t// Delete entire workshop cache directory\n\t\t\tconst workshopCachePath = path.join(cacheDir, workshopId)\n\t\t\tif (await fsExtra.exists(workshopCachePath)) {\n\t\t\t\tawait fsExtra.remove(workshopCachePath)\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t`Error deleting workshop cache ${workshopId}/${cacheName || 'all'}:`,\n\t\t\terror,\n\t\t)\n\t}\n}\n\nexport async function updateCacheEntry(cacheFilePath: string, newEntry: any) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst filePath = path.join(cacheDir, cacheFilePath)\n\t\tconst existingData = await readJSONWithRetries(filePath)\n\t\tif (!existingData) {\n\t\t\tthrow new Error(`Cache file does not exist: ${cacheFilePath}`)\n\t\t}\n\n\t\tconst updatedData = {\n\t\t\t...existingData,\n\t\t\tentry: {\n\t\t\t\t...existingData.entry,\n\t\t\t\tvalue: newEntry,\n\t\t\t\tmetadata: {\n\t\t\t\t\t...existingData.entry.metadata,\n\t\t\t\t\tcreatedTime: Date.now(), // Update timestamp\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tawait fsExtra.writeJSON(filePath, updatedData)\n\t\treturn updatedData.entry\n\t} catch (error) {\n\t\tconsole.error(`Error updating cache entry ${cacheFilePath}:`, error)\n\t\tthrow error\n\t}\n}\n\nexport function makeSingletonCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst lruInstance = new LRUCache<string, CacheEntry<CacheEntryType>>({\n\t\t\tmax: 1000,\n\t\t})\n\n\t\tconst lru = {\n\t\t\tname,\n\t\t\tset: (key, value) => {\n\t\t\t\tconst ttl = C.totalTtl(value.metadata)\n\t\t\t\tlruInstance.set(key, value, {\n\t\t\t\t\tttl: ttl === Infinity ? undefined : ttl,\n\t\t\t\t\tstart: value.metadata.createdTime,\n\t\t\t\t})\n\t\t\t\treturn value\n\t\t\t},\n\t\t\tget: (key) => lruInstance.get(key),\n\t\t\tdelete: (key) => lruInstance.delete(key),\n\t\t} satisfies C.Cache<CacheEntryType>\n\n\t\treturn lru\n\t})\n}\n\nexport function makeSingletonFsCache<CacheEntryType>(name: string) {\n\treturn remember(name, () => {\n\t\tconst cacheInstanceDir = path.join(\n\t\t\tcacheDir,\n\t\t\tgetEnv().EPICSHOP_WORKSHOP_INSTANCE_ID,\n\t\t\tname,\n\t\t)\n\n\t\tconst fsCache: C.Cache<CacheEntryType> = {\n\t\t\tname: `Filesystem cache (${name})`,\n\t\t\tasync get(key) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\n\t\t\t\t// Check file size before attempting to read\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\t\t\tif (stats.size > MAX_CACHE_FILE_SIZE) {\n\t\t\t\t\t\tconst sizeInMB = (stats.size / (1024 * 1024)).toFixed(2)\n\t\t\t\t\t\tlog.warn(\n\t\t\t\t\t\t\t`Skipping large cache file ${filePath} (${sizeInMB}MB > 3MB limit). ` +\n\t\t\t\t\t\t\t\t`Consider clearing \"${name}\" cache for key: ${key}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t\t// For other stat errors, continue with the read attempt\n\t\t\t\t}\n\n\t\t\t\t// Use the shared helper which retries and deletes corrupted files\n\t\t\t\tconst data = await readJSONWithRetries(filePath)\n\t\t\t\tif (data?.entry) return data.entry\n\t\t\t\treturn null\n\n\t\t\t\t// This should never be reached, but just in case\n\t\t\t\treturn null\n\t\t\t},\n\t\t\tasync set(key, entry) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\t\t\t\tconst tempPath = `${filePath}.tmp`\n\t\t\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t\t\t// Write to temp file first, then atomically move to final location\n\t\t\t\t// This prevents race conditions where readers see partially written JSON files\n\t\t\t\tawait fsExtra.writeJSON(tempPath, { key, entry })\n\t\t\t\tawait fsExtra.move(tempPath, filePath, { overwrite: true })\n\t\t\t},\n\t\t\tasync delete(key) {\n\t\t\t\tconst filePath = path.join(cacheInstanceDir, md5(key))\n\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t},\n\t\t}\n\n\t\treturn fsCache\n\t})\n}\n\n/**\n * This wraps @epic-web/cachified to add a few handy features:\n *\n * 1. Automatic timing for timing headers\n * 2. Automatic force refresh based on the request and enhancement of forceFresh\n * to support comma-separated keys to force\n * 3. Offline fallback support. If a fallback is given and we are detected to be\n * offline, then the cached value is used regardless of whether it's expired and\n * if one is not present then the given fallback will be used.\n */\nexport async function cachified<Value>({\n\trequest,\n\ttimings,\n\tkey,\n\ttimingKey = key.length > 18 ? `${key.slice(0, 7)}...${key.slice(-8)}` : key,\n\tofflineFallbackValue,\n\t...options\n}: Omit<C.CachifiedOptions<Value>, 'forceFresh'> & {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean | string\n\ttimingKey?: string\n\tofflineFallbackValue?: Value\n}): Promise<Value> {\n\tif (offlineFallbackValue !== undefined) {\n\t\tconst isOnline = await checkConnection({ request, timings })\n\t\tif (!isOnline) {\n\t\t\tlog.warn(\n\t\t\t\t`Offline: using cached value for ${key} or offline fallback if no cache is present`,\n\t\t\t)\n\t\t\tconst cacheEntry = await options.cache.get(key)\n\t\t\treturn cacheEntry?.value ?? offlineFallbackValue\n\t\t}\n\t}\n\tconst forceFresh = await shouldForceFresh({\n\t\tforceFresh: options.forceFresh,\n\t\trequest,\n\t\tkey,\n\t})\n\treturn C.cachified(\n\t\t{\n\t\t\t...options,\n\t\t\tkey,\n\t\t\tforceFresh,\n\t\t},\n\t\tC.mergeReporters(\n\t\t\tcachifiedTimingReporter(timings, timingKey),\n\t\t\tepicCacheReporter(),\n\t\t),\n\t)\n}\n\nexport async function shouldForceFresh({\n\tforceFresh,\n\trequest,\n\tkey,\n}: {\n\tforceFresh?: boolean | string\n\trequest?: Request\n\tkey?: string\n}) {\n\tif (typeof forceFresh === 'boolean') return forceFresh\n\tif (typeof forceFresh === 'string' && key) {\n\t\treturn forceFresh.split(',').includes(key)\n\t}\n\n\tif (!request) return false\n\tconst fresh = new URL(request.url).searchParams.get('fresh')\n\tif (typeof fresh !== 'string') return false\n\tif (fresh === '') return true\n\tif (!key) return false\n\n\treturn fresh.split(',').includes(key)\n}\n"]}
|
package/dist/esm/db.server.d.ts
CHANGED
|
@@ -381,6 +381,62 @@ declare const DataSchema: z.ZodObject<{
|
|
|
381
381
|
export declare function getClientId(): Promise<string>;
|
|
382
382
|
export declare function logout(): Promise<void>;
|
|
383
383
|
export declare function deleteDb(): Promise<null | undefined>;
|
|
384
|
+
export declare function readDb(): Promise<{
|
|
385
|
+
onboarding: {
|
|
386
|
+
tourVideosWatched: string[];
|
|
387
|
+
} & {
|
|
388
|
+
[k: string]: unknown;
|
|
389
|
+
};
|
|
390
|
+
preferences: {
|
|
391
|
+
player: {
|
|
392
|
+
subtitle: {
|
|
393
|
+
mode: "disabled" | "hidden" | "showing" | null;
|
|
394
|
+
id: string | null;
|
|
395
|
+
};
|
|
396
|
+
minResolution?: number | undefined;
|
|
397
|
+
maxResolution?: number | undefined;
|
|
398
|
+
volumeRate?: number | undefined;
|
|
399
|
+
playbackRate?: number | undefined;
|
|
400
|
+
autoplay?: boolean | undefined;
|
|
401
|
+
muted?: boolean | undefined;
|
|
402
|
+
theater?: boolean | undefined;
|
|
403
|
+
defaultView?: string | undefined;
|
|
404
|
+
activeSidebarTab?: number | undefined;
|
|
405
|
+
};
|
|
406
|
+
presence: {
|
|
407
|
+
optOut: boolean;
|
|
408
|
+
};
|
|
409
|
+
exerciseWarning: {
|
|
410
|
+
dismissed: boolean;
|
|
411
|
+
};
|
|
412
|
+
playground?: {
|
|
413
|
+
persist: boolean;
|
|
414
|
+
} | undefined;
|
|
415
|
+
fontSize?: number | undefined;
|
|
416
|
+
};
|
|
417
|
+
authInfo?: {
|
|
418
|
+
id: string;
|
|
419
|
+
email: string;
|
|
420
|
+
tokenSet: {
|
|
421
|
+
access_token: string;
|
|
422
|
+
token_type: string;
|
|
423
|
+
scope: string;
|
|
424
|
+
};
|
|
425
|
+
name?: string | null | undefined;
|
|
426
|
+
} | undefined;
|
|
427
|
+
authInfos?: Record<string, {
|
|
428
|
+
id: string;
|
|
429
|
+
email: string;
|
|
430
|
+
tokenSet: {
|
|
431
|
+
access_token: string;
|
|
432
|
+
token_type: string;
|
|
433
|
+
scope: string;
|
|
434
|
+
};
|
|
435
|
+
name?: string | null | undefined;
|
|
436
|
+
}> | undefined;
|
|
437
|
+
clientId?: string | undefined;
|
|
438
|
+
mutedNotifications?: string[] | undefined;
|
|
439
|
+
} | null>;
|
|
384
440
|
export declare function getAuthInfo(): Promise<{
|
|
385
441
|
id: string;
|
|
386
442
|
email: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.server.d.ts","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAKtB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAA;AACF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyBvB,CAAA;AAkBb,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCd,CAAA;AAEF,wBAAsB,WAAW,oBAOhC;AAED,wBAAsB,MAAM,kBAY3B;AAED,wBAAsB,QAAQ,8BAW7B;
|
|
1
|
+
{"version":3,"file":"db.server.d.ts","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAKtB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAA;AACF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyBvB,CAAA;AAkBb,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCd,CAAA;AAEF,wBAAsB,WAAW,oBAOhC;AAED,wBAAsB,MAAM,kBAY3B;AAED,wBAAsB,QAAQ,8BAW7B;AAED,wBAAsB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6D3B;AAED,wBAAsB,WAAW;;;;;;;;;sBAuBhC;AAED,wBAAsB,eAAe,CAAC,EACrC,OAAO,EACP,UAAU,GACV,EAAE;IACF,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;;;;;;;;;GAeA;AAED,wBAAsB,WAAW,CAAC,EACjC,EAAE,EACF,QAAQ,EACR,KAA6B,EAC7B,IAAI,GACJ,EAAE;IACF,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;;;;;;;;;GAgBA;AAED,wBAAsB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;UAGnC;AAED,wBAAsB,cAAc,CACnC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBtD;AAED,wBAAsB,qBAAqB,sBAG1C;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,qBAWhD;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,+BAQvE;AAED,wBAAsB,qBAAqB,2BAG1C;AAED,wBAAsB,kBAAkB;;;;WAGvC;AAED,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM;;GAchE;AAED,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM;;GAYlE;AAED,wBAAsB,6BAA6B,CAClD,gBAAgB,EAAE,MAAM,EAAE,oBAK1B"}
|
package/dist/esm/db.server.js
CHANGED
|
@@ -119,7 +119,7 @@ export async function deleteDb() {
|
|
|
119
119
|
console.error(`Error deleting the database`, error);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
async function readDb() {
|
|
122
|
+
export async function readDb() {
|
|
123
123
|
if (process.env.EPICSHOP_DEPLOYED)
|
|
124
124
|
return null;
|
|
125
125
|
const maxRetries = 3;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.server.js","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"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","sourcesContent":["import './init-env.js'\n\nimport { createId as cuid } from '@paralleldrive/cuid2'\nimport fsExtra from 'fs-extra'\nimport { redirect } from 'react-router'\nimport { z } from 'zod'\nimport { getWorkshopConfig } from './config.server.js'\nimport { saveJSON, loadJSON, migrateLegacyData } from './data-storage.server.js'\n\n// Attempt migration from legacy ~/.epicshop\nawait migrateLegacyData().catch(() => {})\n\nconst TokenSetSchema = z.object({\n\taccess_token: z.string(),\n\ttoken_type: z.string(),\n\tscope: z.string(),\n})\nexport const PlayerPreferencesSchema = z\n\t.object({\n\t\tminResolution: z.number().optional(),\n\t\tmaxResolution: z.number().optional(),\n\t\tvolumeRate: z.number().optional(),\n\t\tplaybackRate: z.number().optional(),\n\t\tautoplay: z.boolean().optional(),\n\t\tsubtitle: z\n\t\t\t.object({\n\t\t\t\tid: z.string().nullable().default(null),\n\t\t\t\tmode: z\n\t\t\t\t\t.literal('disabled')\n\t\t\t\t\t.or(z.literal('hidden'))\n\t\t\t\t\t.or(z.literal('showing'))\n\t\t\t\t\t.nullable()\n\t\t\t\t\t.default('disabled'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.default({}),\n\t\tmuted: z.boolean().optional(),\n\t\ttheater: z.boolean().optional(),\n\t\tdefaultView: z.string().optional(),\n\t\tactiveSidebarTab: z.number().optional(),\n\t})\n\t.optional()\n\t.default({})\n\nconst PresencePreferencesSchema = z\n\t.object({\n\t\toptOut: z.boolean(),\n\t})\n\t.optional()\n\t.default({ optOut: false })\n\nconst AuthInfoSchema = z.object({\n\tid: z.string(),\n\ttokenSet: TokenSetSchema,\n\temail: z.string(),\n\tname: z.string().nullable().optional(),\n})\n\nconst MutedNotificationSchema = z.array(z.string()).default([])\n\nconst DataSchema = z.object({\n\tonboarding: z\n\t\t.object({\n\t\t\ttourVideosWatched: z.array(z.string()).default([]),\n\t\t})\n\t\t.passthrough()\n\t\t.optional()\n\t\t.default({ tourVideosWatched: [] }),\n\tpreferences: z\n\t\t.object({\n\t\t\tplayer: PlayerPreferencesSchema,\n\t\t\tpresence: PresencePreferencesSchema,\n\t\t\tplayground: z\n\t\t\t\t.object({\n\t\t\t\t\tpersist: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tfontSize: z.number().optional(),\n\t\t\texerciseWarning: z\n\t\t\t\t.object({\n\t\t\t\t\tdismissed: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional()\n\t\t\t\t.default({ dismissed: false }),\n\t\t})\n\t\t.optional()\n\t\t.default({}),\n\t// deprecated. Probably safe to remove in May 2026:\n\tauthInfo: AuthInfoSchema.optional(),\n\t// new:\n\tauthInfos: z.record(z.string(), AuthInfoSchema).optional(),\n\tclientId: z.string().optional(),\n\tmutedNotifications: MutedNotificationSchema.optional(),\n})\n\nexport async function getClientId() {\n\tconst data = await readDb()\n\tif (data?.clientId) return data.clientId\n\n\tconst clientId = cuid()\n\tawait saveJSON({ ...data, clientId })\n\treturn clientId\n}\n\nexport async function logout() {\n\tconst config = getWorkshopConfig()\n\tconst host = config.product.host\n\tif (host) {\n\t\tconst data = await readDb()\n\t\tconst newAuthInfos = { ...data?.authInfos }\n\t\tdelete newAuthInfos[host]\n\t\tawait saveJSON({\n\t\t\t...data,\n\t\t\tauthInfos: newAuthInfos,\n\t\t})\n\t}\n}\n\nexport async function deleteDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst { path: dbPath } = await loadJSON()\n\t\tif (dbPath && (await fsExtra.exists(dbPath))) {\n\t\t\tawait fsExtra.remove(dbPath)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the database`, error)\n\t}\n}\n\nasync function readDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\tconst maxRetries = 3\n\tconst baseDelay = 10\n\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\tconst { data, path: dbPath } = await loadJSON()\n\t\t\tif (data && dbPath) {\n\t\t\t\tconst db = DataSchema.parse(data)\n\t\t\t\treturn db\n\t\t\t}\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\tif (attempt < maxRetries) {\n\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Database read error on attempt ${attempt + 1}/${maxRetries + 1}, retrying in ${delay}ms...`,\n\t\t\t\t)\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Final attempt failed, handle as corrupted file\n\t\t\tconsole.error(\n\t\t\t\t`Error reading the database after ${attempt + 1} attempts, moving it to a .bkp file to avoid parsing errors in the future`,\n\t\t\t\terror,\n\t\t\t)\n\n\t\t\t// Log to Sentry if available\n\t\t\tif (process.env.SENTRY_DSN && process.env.EPICSHOP_IS_PUBLISHED) {\n\t\t\t\ttry {\n\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\tSentry.captureException(error, {\n\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\terror_type: 'corrupted_database_file',\n\t\t\t\t\t\t\tretry_attempts: attempt.toString(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\textra: {\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\tretryAttempts: attempt,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch (sentryError) {\n\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Try to move corrupted file to backup if we can determine the path\n\t\t\ttry {\n\t\t\t\tconst { path: dbPath } = await loadJSON()\n\t\t\t\tif (dbPath && (await fsExtra.exists(dbPath))) {\n\t\t\t\t\tvoid fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {})\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn null\n}\n\nexport async function getAuthInfo() {\n\tconst config = getWorkshopConfig()\n\tconst data = await readDb()\n\tif (config.product.host && typeof data?.authInfos === 'object') {\n\t\tif (config.product.host in data.authInfos) {\n\t\t\treturn data.authInfos[config.product.host]\n\t\t}\n\t}\n\n\t// special case for non-epicweb/epicreact workshops\n\tif (\n\t\t!config.product.host ||\n\t\tconfig.product.host === 'epicweb.dev' ||\n\t\tconfig.product.host === 'epicreact.dev'\n\t) {\n\t\t// upgrade from old authInfo to new authInfos\n\t\tif (data?.authInfo && config.product.host) {\n\t\t\tawait setAuthInfo(data.authInfo)\n\t\t}\n\t\treturn data?.authInfo ?? null\n\t}\n\n\treturn null\n}\n\nexport async function requireAuthInfo({\n\trequest,\n\tredirectTo,\n}: {\n\trequest: Request\n\tredirectTo?: string | null\n}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\tconst requestUrl = new URL(request.url)\n\t\tredirectTo =\n\t\t\tredirectTo === null\n\t\t\t\t? null\n\t\t\t\t: (redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`)\n\t\tconst loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null\n\t\tconst loginRedirect = ['/login', loginParams?.toString()]\n\t\t\t.filter(Boolean)\n\t\t\t.join('?')\n\t\tthrow redirect(loginRedirect)\n\t}\n\treturn authInfo\n}\n\nexport async function setAuthInfo({\n\tid,\n\ttokenSet,\n\temail = 'unknown@example.com',\n\tname,\n}: {\n\tid: string\n\ttokenSet: Partial<z.infer<typeof TokenSetSchema>>\n\temail?: string | null\n\tname?: string | null\n}) {\n\tconst data = await readDb()\n\tconst authInfo = AuthInfoSchema.parse({ id, tokenSet, email, name })\n\tconst config = getWorkshopConfig()\n\tif (config.product.host) {\n\t\tawait saveJSON({\n\t\t\t...data,\n\t\t\tauthInfos: {\n\t\t\t\t...data?.authInfos,\n\t\t\t\t[config.product.host]: authInfo,\n\t\t\t},\n\t\t})\n\t} else {\n\t\tawait saveJSON({ ...data, authInfo })\n\t}\n\treturn authInfo\n}\n\nexport async function getPreferences() {\n\tconst data = await readDb()\n\treturn data?.preferences ?? null\n}\n\nexport async function setPreferences(\n\tpreferences: z.input<typeof DataSchema>['preferences'],\n) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: {\n\t\t\t...data?.preferences,\n\t\t\t...preferences,\n\t\t\tplayer: {\n\t\t\t\t...data?.preferences?.player,\n\t\t\t\t...preferences?.player,\n\t\t\t},\n\t\t\tpresence: {\n\t\t\t\t...data?.preferences?.presence,\n\t\t\t\t...preferences?.presence,\n\t\t\t},\n\t\t\texerciseWarning: {\n\t\t\t\t...data?.preferences?.exerciseWarning,\n\t\t\t\t...preferences?.exerciseWarning,\n\t\t\t},\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.preferences\n}\n\nexport async function getMutedNotifications() {\n\tconst data = await readDb()\n\treturn data?.mutedNotifications ?? []\n}\n\nexport async function muteNotification(id: string) {\n\tconst data = await readDb()\n\tconst mutedNotifications = Array.from(\n\t\tnew Set([...(data?.mutedNotifications ?? []), id]),\n\t)\n\tconst updatedData = {\n\t\t...data,\n\t\tmutedNotifications,\n\t}\n\tawait saveJSON(updatedData)\n\treturn mutedNotifications\n}\n\nexport async function setFontSizePreference(fontSize: number | undefined) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: { ...data?.preferences, fontSize },\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.preferences.fontSize\n}\n\nexport async function getFontSizePreference() {\n\tconst data = await readDb()\n\treturn data?.preferences?.fontSize ?? null\n}\n\nexport async function readOnboardingData() {\n\tconst data = await readDb()\n\treturn data?.onboarding ?? null\n}\n\nexport async function markOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: [\n\t\t\t\t...(data?.onboarding.tourVideosWatched ?? []),\n\t\t\t\tvideoUrl,\n\t\t\t].filter(Boolean),\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.onboarding\n}\n\nexport async function unmarkOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst watchedVideos = data?.onboarding?.tourVideosWatched ?? []\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: watchedVideos.filter((url) => url !== videoUrl),\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.onboarding\n}\n\nexport async function areAllOnboardingVideosWatched(\n\tonboardingVideos: string[],\n) {\n\tconst data = await readDb()\n\tconst watchedVideos = data?.onboarding?.tourVideosWatched ?? []\n\treturn onboardingVideos.every((video) => watchedVideos.includes(video))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"db.server.js","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"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","sourcesContent":["import './init-env.js'\n\nimport { createId as cuid } from '@paralleldrive/cuid2'\nimport fsExtra from 'fs-extra'\nimport { redirect } from 'react-router'\nimport { z } from 'zod'\nimport { getWorkshopConfig } from './config.server.js'\nimport { saveJSON, loadJSON, migrateLegacyData } from './data-storage.server.js'\n\n// Attempt migration from legacy ~/.epicshop\nawait migrateLegacyData().catch(() => {})\n\nconst TokenSetSchema = z.object({\n\taccess_token: z.string(),\n\ttoken_type: z.string(),\n\tscope: z.string(),\n})\nexport const PlayerPreferencesSchema = z\n\t.object({\n\t\tminResolution: z.number().optional(),\n\t\tmaxResolution: z.number().optional(),\n\t\tvolumeRate: z.number().optional(),\n\t\tplaybackRate: z.number().optional(),\n\t\tautoplay: z.boolean().optional(),\n\t\tsubtitle: z\n\t\t\t.object({\n\t\t\t\tid: z.string().nullable().default(null),\n\t\t\t\tmode: z\n\t\t\t\t\t.literal('disabled')\n\t\t\t\t\t.or(z.literal('hidden'))\n\t\t\t\t\t.or(z.literal('showing'))\n\t\t\t\t\t.nullable()\n\t\t\t\t\t.default('disabled'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.default({}),\n\t\tmuted: z.boolean().optional(),\n\t\ttheater: z.boolean().optional(),\n\t\tdefaultView: z.string().optional(),\n\t\tactiveSidebarTab: z.number().optional(),\n\t})\n\t.optional()\n\t.default({})\n\nconst PresencePreferencesSchema = z\n\t.object({\n\t\toptOut: z.boolean(),\n\t})\n\t.optional()\n\t.default({ optOut: false })\n\nconst AuthInfoSchema = z.object({\n\tid: z.string(),\n\ttokenSet: TokenSetSchema,\n\temail: z.string(),\n\tname: z.string().nullable().optional(),\n})\n\nconst MutedNotificationSchema = z.array(z.string()).default([])\n\nconst DataSchema = z.object({\n\tonboarding: z\n\t\t.object({\n\t\t\ttourVideosWatched: z.array(z.string()).default([]),\n\t\t})\n\t\t.passthrough()\n\t\t.optional()\n\t\t.default({ tourVideosWatched: [] }),\n\tpreferences: z\n\t\t.object({\n\t\t\tplayer: PlayerPreferencesSchema,\n\t\t\tpresence: PresencePreferencesSchema,\n\t\t\tplayground: z\n\t\t\t\t.object({\n\t\t\t\t\tpersist: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tfontSize: z.number().optional(),\n\t\t\texerciseWarning: z\n\t\t\t\t.object({\n\t\t\t\t\tdismissed: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional()\n\t\t\t\t.default({ dismissed: false }),\n\t\t})\n\t\t.optional()\n\t\t.default({}),\n\t// deprecated. Probably safe to remove in May 2026:\n\tauthInfo: AuthInfoSchema.optional(),\n\t// new:\n\tauthInfos: z.record(z.string(), AuthInfoSchema).optional(),\n\tclientId: z.string().optional(),\n\tmutedNotifications: MutedNotificationSchema.optional(),\n})\n\nexport async function getClientId() {\n\tconst data = await readDb()\n\tif (data?.clientId) return data.clientId\n\n\tconst clientId = cuid()\n\tawait saveJSON({ ...data, clientId })\n\treturn clientId\n}\n\nexport async function logout() {\n\tconst config = getWorkshopConfig()\n\tconst host = config.product.host\n\tif (host) {\n\t\tconst data = await readDb()\n\t\tconst newAuthInfos = { ...data?.authInfos }\n\t\tdelete newAuthInfos[host]\n\t\tawait saveJSON({\n\t\t\t...data,\n\t\t\tauthInfos: newAuthInfos,\n\t\t})\n\t}\n}\n\nexport async function deleteDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tconst { path: dbPath } = await loadJSON()\n\t\tif (dbPath && (await fsExtra.exists(dbPath))) {\n\t\t\tawait fsExtra.remove(dbPath)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the database`, error)\n\t}\n}\n\nexport async function readDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\tconst maxRetries = 3\n\tconst baseDelay = 10\n\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\tconst { data, path: dbPath } = await loadJSON()\n\t\t\tif (data && dbPath) {\n\t\t\t\tconst db = DataSchema.parse(data)\n\t\t\t\treturn db\n\t\t\t}\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\tif (attempt < maxRetries) {\n\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Database read error on attempt ${attempt + 1}/${maxRetries + 1}, retrying in ${delay}ms...`,\n\t\t\t\t)\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Final attempt failed, handle as corrupted file\n\t\t\tconsole.error(\n\t\t\t\t`Error reading the database after ${attempt + 1} attempts, moving it to a .bkp file to avoid parsing errors in the future`,\n\t\t\t\terror,\n\t\t\t)\n\n\t\t\t// Log to Sentry if available\n\t\t\tif (process.env.SENTRY_DSN && process.env.EPICSHOP_IS_PUBLISHED) {\n\t\t\t\ttry {\n\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\tSentry.captureException(error, {\n\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\terror_type: 'corrupted_database_file',\n\t\t\t\t\t\t\tretry_attempts: attempt.toString(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\textra: {\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\tretryAttempts: attempt,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch (sentryError) {\n\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Try to move corrupted file to backup if we can determine the path\n\t\t\ttry {\n\t\t\t\tconst { path: dbPath } = await loadJSON()\n\t\t\t\tif (dbPath && (await fsExtra.exists(dbPath))) {\n\t\t\t\t\tvoid fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {})\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn null\n}\n\nexport async function getAuthInfo() {\n\tconst config = getWorkshopConfig()\n\tconst data = await readDb()\n\tif (config.product.host && typeof data?.authInfos === 'object') {\n\t\tif (config.product.host in data.authInfos) {\n\t\t\treturn data.authInfos[config.product.host]\n\t\t}\n\t}\n\n\t// special case for non-epicweb/epicreact workshops\n\tif (\n\t\t!config.product.host ||\n\t\tconfig.product.host === 'epicweb.dev' ||\n\t\tconfig.product.host === 'epicreact.dev'\n\t) {\n\t\t// upgrade from old authInfo to new authInfos\n\t\tif (data?.authInfo && config.product.host) {\n\t\t\tawait setAuthInfo(data.authInfo)\n\t\t}\n\t\treturn data?.authInfo ?? null\n\t}\n\n\treturn null\n}\n\nexport async function requireAuthInfo({\n\trequest,\n\tredirectTo,\n}: {\n\trequest: Request\n\tredirectTo?: string | null\n}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\tconst requestUrl = new URL(request.url)\n\t\tredirectTo =\n\t\t\tredirectTo === null\n\t\t\t\t? null\n\t\t\t\t: (redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`)\n\t\tconst loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null\n\t\tconst loginRedirect = ['/login', loginParams?.toString()]\n\t\t\t.filter(Boolean)\n\t\t\t.join('?')\n\t\tthrow redirect(loginRedirect)\n\t}\n\treturn authInfo\n}\n\nexport async function setAuthInfo({\n\tid,\n\ttokenSet,\n\temail = 'unknown@example.com',\n\tname,\n}: {\n\tid: string\n\ttokenSet: Partial<z.infer<typeof TokenSetSchema>>\n\temail?: string | null\n\tname?: string | null\n}) {\n\tconst data = await readDb()\n\tconst authInfo = AuthInfoSchema.parse({ id, tokenSet, email, name })\n\tconst config = getWorkshopConfig()\n\tif (config.product.host) {\n\t\tawait saveJSON({\n\t\t\t...data,\n\t\t\tauthInfos: {\n\t\t\t\t...data?.authInfos,\n\t\t\t\t[config.product.host]: authInfo,\n\t\t\t},\n\t\t})\n\t} else {\n\t\tawait saveJSON({ ...data, authInfo })\n\t}\n\treturn authInfo\n}\n\nexport async function getPreferences() {\n\tconst data = await readDb()\n\treturn data?.preferences ?? null\n}\n\nexport async function setPreferences(\n\tpreferences: z.input<typeof DataSchema>['preferences'],\n) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: {\n\t\t\t...data?.preferences,\n\t\t\t...preferences,\n\t\t\tplayer: {\n\t\t\t\t...data?.preferences?.player,\n\t\t\t\t...preferences?.player,\n\t\t\t},\n\t\t\tpresence: {\n\t\t\t\t...data?.preferences?.presence,\n\t\t\t\t...preferences?.presence,\n\t\t\t},\n\t\t\texerciseWarning: {\n\t\t\t\t...data?.preferences?.exerciseWarning,\n\t\t\t\t...preferences?.exerciseWarning,\n\t\t\t},\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.preferences\n}\n\nexport async function getMutedNotifications() {\n\tconst data = await readDb()\n\treturn data?.mutedNotifications ?? []\n}\n\nexport async function muteNotification(id: string) {\n\tconst data = await readDb()\n\tconst mutedNotifications = Array.from(\n\t\tnew Set([...(data?.mutedNotifications ?? []), id]),\n\t)\n\tconst updatedData = {\n\t\t...data,\n\t\tmutedNotifications,\n\t}\n\tawait saveJSON(updatedData)\n\treturn mutedNotifications\n}\n\nexport async function setFontSizePreference(fontSize: number | undefined) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: { ...data?.preferences, fontSize },\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.preferences.fontSize\n}\n\nexport async function getFontSizePreference() {\n\tconst data = await readDb()\n\treturn data?.preferences?.fontSize ?? null\n}\n\nexport async function readOnboardingData() {\n\tconst data = await readDb()\n\treturn data?.onboarding ?? null\n}\n\nexport async function markOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: [\n\t\t\t\t...(data?.onboarding.tourVideosWatched ?? []),\n\t\t\t\tvideoUrl,\n\t\t\t].filter(Boolean),\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.onboarding\n}\n\nexport async function unmarkOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst watchedVideos = data?.onboarding?.tourVideosWatched ?? []\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: watchedVideos.filter((url) => url !== videoUrl),\n\t\t},\n\t}\n\tawait saveJSON(updatedData)\n\treturn updatedData.onboarding\n}\n\nexport async function areAllOnboardingVideosWatched(\n\tonboardingVideos: string[],\n) {\n\tconst data = await readDb()\n\tconst watchedVideos = data?.onboarding?.tourVideosWatched ?? []\n\treturn onboardingVideos.every((video) => watchedVideos.includes(video))\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epic-api.server.d.ts","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAoDjD,MAAM,MAAM,cAAc,GAAG,MAAM,CAClC,MAAM,EACN,OAAO,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAC5C,CAAA;
|
|
1
|
+
{"version":3,"file":"epic-api.server.d.ts","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAWvB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAoDjD,MAAM,MAAM,cAAc,GAAG,MAAM,CAClC,MAAM,EACN,OAAO,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAC5C,CAAA;AAID,wBAAsB,iBAAiB,CACtC,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,EAClC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,2BA2BnE;AAED,iBAAe,gBAAgB,CAAC,EAC/B,cAAc,EACd,WAAW,EACX,OAAO,EACP,OAAO,GACP,EAAE;IACF,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;;;;;;;;;;;;;;;;;;;UA0HA;AAkHD,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACtE,wBAAsB,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,GACP,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACZ;mBA2BW,MAAM;oBACL,MAAM;qBACL,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;UAIsB,SAAS;mBAqC9D;AAqDD,wBAAsB,cAAc,CACnC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACpE,EACC,OAAO,EACP,OAAO,GACP,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACZ;;;;;;;;;;;;GAkDN;AAoBD,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EACZ,EACC,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf;;;;;;;;;;;;GAoDN;AAED,wBAAsB,uBAAuB,CAAC,EAC7C,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf,oBAsDL;AAED,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCjB,CAAA;AAwCH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAGrD,wBAAsB,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf;;;;;;;;;;;;;;;;UA0EL;AAED,wBAAsB,SAAS,kBAE9B"}
|
|
@@ -50,15 +50,16 @@ const CachedEpicVideoInfoSchema = z
|
|
|
50
50
|
restrictedCountry: z.string(),
|
|
51
51
|
}))
|
|
52
52
|
.or(z.null());
|
|
53
|
+
const videoInfoLog = log.logger('video-info');
|
|
53
54
|
export async function getEpicVideoInfos(epicWebUrls, { request, timings } = {}) {
|
|
54
55
|
if (!epicWebUrls) {
|
|
55
|
-
|
|
56
|
+
videoInfoLog.warn('no epic web URLs provided, returning empty object');
|
|
56
57
|
return {};
|
|
57
58
|
}
|
|
58
59
|
const authInfo = await getAuthInfo();
|
|
59
60
|
if (getEnv().EPICSHOP_DEPLOYED)
|
|
60
61
|
return {};
|
|
61
|
-
|
|
62
|
+
videoInfoLog(`fetching epic video infos for ${epicWebUrls.length} URLs`);
|
|
62
63
|
const epicVideoInfos = {};
|
|
63
64
|
for (const epicVideoEmbed of epicWebUrls) {
|
|
64
65
|
const epicVideoInfo = await getEpicVideoInfo({
|
|
@@ -71,13 +72,13 @@ export async function getEpicVideoInfos(epicWebUrls, { request, timings } = {})
|
|
|
71
72
|
epicVideoInfos[epicVideoEmbed] = epicVideoInfo;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
videoInfoLog(`successfully fetched ${Object.keys(epicVideoInfos).length} epic video infos`);
|
|
75
76
|
return epicVideoInfos;
|
|
76
77
|
}
|
|
77
78
|
async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings, }) {
|
|
78
79
|
const tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated';
|
|
79
80
|
const key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`;
|
|
80
|
-
|
|
81
|
+
videoInfoLog(`fetching video info for URL: ${epicVideoEmbed}`);
|
|
81
82
|
return cachified({
|
|
82
83
|
key,
|
|
83
84
|
request,
|
|
@@ -92,7 +93,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
92
93
|
if (epicUrl.host !== 'www.epicweb.dev' &&
|
|
93
94
|
epicUrl.host !== 'www.epicreact.dev' &&
|
|
94
95
|
epicUrl.host !== 'www.epicai.pro') {
|
|
95
|
-
|
|
96
|
+
videoInfoLog.error(`unsupported host for video URL: ${epicUrl.host}`);
|
|
96
97
|
return null;
|
|
97
98
|
}
|
|
98
99
|
// this may be temporary until the /tutorials/ endpoint supports /api
|
|
@@ -103,12 +104,12 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
103
104
|
const apiUrl = epicUrl.host === 'www.epicai.pro'
|
|
104
105
|
? getEpicAIVideoAPIUrl(epicVideoEmbed)
|
|
105
106
|
: `https://${epicUrl.host}/api${epicUrl.pathname}`;
|
|
106
|
-
|
|
107
|
+
videoInfoLog(`making API request to: ${apiUrl}`);
|
|
107
108
|
const infoResponse = await fetch(apiUrl, accessToken
|
|
108
109
|
? { headers: { authorization: `Bearer ${accessToken}` } }
|
|
109
110
|
: undefined);
|
|
110
111
|
const { status, statusText } = infoResponse;
|
|
111
|
-
|
|
112
|
+
videoInfoLog(`API response: ${status} ${statusText}`);
|
|
112
113
|
if (infoResponse.status >= 200 && infoResponse.status < 300) {
|
|
113
114
|
let rawInfo = await infoResponse.json();
|
|
114
115
|
// another special case for epicai.pro videos
|
|
@@ -117,7 +118,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
117
118
|
}
|
|
118
119
|
const infoResult = EpicVideoInfoSchema.safeParse(rawInfo);
|
|
119
120
|
if (infoResult.success) {
|
|
120
|
-
|
|
121
|
+
videoInfoLog(`successfully parsed video info for ${epicVideoEmbed}`);
|
|
121
122
|
return {
|
|
122
123
|
status: 'success',
|
|
123
124
|
statusCode: status,
|
|
@@ -131,7 +132,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
131
132
|
context.metadata.swr = 0;
|
|
132
133
|
const restrictedResult = EpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo);
|
|
133
134
|
if (restrictedResult.success) {
|
|
134
|
-
|
|
135
|
+
videoInfoLog.warn(`video is region restricted: ${epicVideoEmbed}`);
|
|
135
136
|
return {
|
|
136
137
|
status: 'error',
|
|
137
138
|
statusCode: status,
|
|
@@ -141,12 +142,11 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
141
142
|
};
|
|
142
143
|
}
|
|
143
144
|
else {
|
|
144
|
-
|
|
145
|
+
videoInfoLog.error(`API response parsing failed for ${epicVideoEmbed}`, {
|
|
145
146
|
url: epicUrl.pathname,
|
|
146
147
|
rawInfo,
|
|
147
148
|
parseError: infoResult.error,
|
|
148
149
|
});
|
|
149
|
-
console.warn(`Response from EpicWeb for "${epicUrl.pathname}" does not match expectation`, infoResult.error);
|
|
150
150
|
return {
|
|
151
151
|
status: 'error',
|
|
152
152
|
statusCode: 500,
|
|
@@ -160,7 +160,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
160
160
|
// don't cache errors for long...
|
|
161
161
|
context.metadata.ttl = 1000 * 2;
|
|
162
162
|
context.metadata.swr = 0;
|
|
163
|
-
|
|
163
|
+
videoInfoLog.error(`video API request failed for ${epicVideoEmbed}`, {
|
|
164
164
|
status,
|
|
165
165
|
statusText,
|
|
166
166
|
url: apiUrl,
|
|
@@ -174,7 +174,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
174
174
|
}
|
|
175
175
|
},
|
|
176
176
|
}).catch((e) => {
|
|
177
|
-
|
|
177
|
+
videoInfoLog.error(`failed to fetch epic video info for ${epicVideoEmbed}:`, e);
|
|
178
178
|
throw e;
|
|
179
179
|
});
|
|
180
180
|
}
|
|
@@ -554,6 +554,7 @@ function resolveDiscordAvatar(user, { size }) {
|
|
|
554
554
|
url.searchParams.set('size', size.toString());
|
|
555
555
|
return url.toString();
|
|
556
556
|
}
|
|
557
|
+
const userinfoLog = log.logger('userinfo');
|
|
557
558
|
export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
558
559
|
const authInfo = await getAuthInfo();
|
|
559
560
|
if (!authInfo)
|
|
@@ -562,7 +563,7 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
562
563
|
const { product: { host }, } = getWorkshopConfig();
|
|
563
564
|
const accessToken = tokenSet.access_token;
|
|
564
565
|
const url = `https://${host}/oauth/userinfo`;
|
|
565
|
-
|
|
566
|
+
userinfoLog(`calling cachified to get user info from: ${url}`);
|
|
566
567
|
const userInfo = await cachified({
|
|
567
568
|
key: `${url}:${md5(accessToken)}`,
|
|
568
569
|
cache: epicApiCache,
|
|
@@ -574,13 +575,13 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
574
575
|
offlineFallbackValue: null,
|
|
575
576
|
checkValue: UserInfoSchema,
|
|
576
577
|
async getFreshValue() {
|
|
577
|
-
|
|
578
|
+
userinfoLog(`getting fresh value for user info from: ${url}`);
|
|
578
579
|
const response = await fetch(url, {
|
|
579
580
|
headers: { authorization: `Bearer ${accessToken}` },
|
|
580
581
|
}).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
581
|
-
|
|
582
|
+
userinfoLog(`user info API response: ${response.status} ${response.statusText}`);
|
|
582
583
|
if (!response.ok) {
|
|
583
|
-
|
|
584
|
+
userinfoLog(`user info API request failed: ${response.status} ${response.statusText}`);
|
|
584
585
|
if (response.headers.get('content-type')?.includes('application/json')) {
|
|
585
586
|
const data = await response.json();
|
|
586
587
|
throw new Error(`Failed to fetch user info: ${JSON.stringify(data)}`);
|
|
@@ -592,11 +593,11 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
592
593
|
}
|
|
593
594
|
const data = await response.json();
|
|
594
595
|
const parsedUserInfo = UserInfoSchema.parse(data);
|
|
595
|
-
|
|
596
|
+
userinfoLog(`successfully fetched user info for user: ${parsedUserInfo.id} (${parsedUserInfo.email})`);
|
|
596
597
|
return parsedUserInfo;
|
|
597
598
|
},
|
|
598
599
|
}).catch((e) => {
|
|
599
|
-
|
|
600
|
+
userinfoLog.error(`failed to get user info:`, e);
|
|
600
601
|
return null;
|
|
601
602
|
});
|
|
602
603
|
// we used to md5 hash the email to get the id
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epic-api.server.js","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from '@epic-web/invariant'\nimport * as cookie from 'cookie'\n\nimport md5 from 'md5-hex'\nimport { z } from 'zod'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n\tgetWorkshopInstructions,\n} from './apps.server.js'\nimport { cachified, epicApiCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getAuthInfo, setAuthInfo } from './db.server.js'\nimport { getEnv } from './init-env.js'\nimport { logger } from './logger.js'\nimport { type Timings } from './timing.server.js'\nimport { getErrorMessage } from './utils.js'\n\n// Module-level logger for epic-api operations\nconst log = logger('epic:api')\n\nconst Transcript = z\n\t.string()\n\t.nullable()\n\t.optional()\n\t.transform((s) => s ?? 'Transcripts not available')\nconst EpicVideoInfoSchema = z.object({\n\ttitle: z.string().nullable().optional(),\n\ttranscript: Transcript,\n\tmuxPlaybackId: z.string(),\n})\n\nconst EpicVideoRegionRestrictedErrorSchema = z.object({\n\trequestCountry: z.string(),\n\trestrictedCountry: z.string(),\n\tisRegionRestricted: z.literal(true),\n})\n\nconst CachedEpicVideoInfoSchema = z\n\t.object({\n\t\ttitle: z.string().nullable().optional(),\n\t\ttranscript: Transcript,\n\t\tmuxPlaybackId: z.string(),\n\t\tstatus: z.literal('success'),\n\t\tstatusCode: z.number(),\n\t\tstatusText: z.string(),\n\t})\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('unknown'),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('region-restricted'),\n\t\t\trequestCountry: z.string(),\n\t\t\trestrictedCountry: z.string(),\n\t\t}),\n\t)\n\t.or(z.null())\n\nexport type EpicVideoInfos = Record<\n\tstring,\n\tAwaited<ReturnType<typeof getEpicVideoInfo>>\n>\n\nexport async function getEpicVideoInfos(\n\tepicWebUrls?: Array<string> | null,\n\t{ request, timings }: { request?: Request; timings?: Timings } = {},\n) {\n\tif (!epicWebUrls) {\n\t\tlog('no epic web URLs provided, returning empty object')\n\t\treturn {}\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (getEnv().EPICSHOP_DEPLOYED) return {}\n\n\tlog(`fetching epic video infos for ${epicWebUrls.length} URLs`)\n\tconst epicVideoInfos: EpicVideoInfos = {}\n\tfor (const epicVideoEmbed of epicWebUrls) {\n\t\tconst epicVideoInfo = await getEpicVideoInfo({\n\t\t\tepicVideoEmbed,\n\t\t\taccessToken: authInfo?.tokenSet.access_token,\n\t\t\trequest,\n\t\t\ttimings,\n\t\t})\n\t\tif (epicVideoInfo) {\n\t\t\tepicVideoInfos[epicVideoEmbed] = epicVideoInfo\n\t\t}\n\t}\n\tlog(\n\t\t`successfully fetched ${Object.keys(epicVideoInfos).length} epic video infos`,\n\t)\n\treturn epicVideoInfos\n}\n\nasync function getEpicVideoInfo({\n\tepicVideoEmbed,\n\taccessToken,\n\trequest,\n\ttimings,\n}: {\n\tepicVideoEmbed: string\n\taccessToken?: string\n\trequest?: Request\n\ttimings?: Timings\n}) {\n\tconst tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated'\n\tconst key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`\n\n\tlog(`fetching video info for URL: ${epicVideoEmbed}`)\n\treturn cachified({\n\t\tkey,\n\t\trequest,\n\t\tcache: epicApiCache,\n\t\ttimings,\n\t\tttl: 1000 * 60 * 60,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: CachedEpicVideoInfoSchema,\n\t\tasync getFreshValue(\n\t\t\tcontext,\n\t\t): Promise<z.infer<typeof CachedEpicVideoInfoSchema>> {\n\t\t\tconst epicUrl = new URL(epicVideoEmbed)\n\t\t\tif (\n\t\t\t\tepicUrl.host !== 'www.epicweb.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicreact.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicai.pro'\n\t\t\t) {\n\t\t\t\tlog(`unsupported host for video URL: ${epicUrl.host}`)\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// this may be temporary until the /tutorials/ endpoint supports /api\n\t\t\tif (epicUrl.pathname.startsWith('/tutorials/')) {\n\t\t\t\tepicUrl.pathname = epicUrl.pathname.replace(\n\t\t\t\t\t/^\\/tutorials\\//,\n\t\t\t\t\t'/workshops/',\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// special case for epicai.pro videos\n\t\t\tconst apiUrl =\n\t\t\t\tepicUrl.host === 'www.epicai.pro'\n\t\t\t\t\t? getEpicAIVideoAPIUrl(epicVideoEmbed)\n\t\t\t\t\t: `https://${epicUrl.host}/api${epicUrl.pathname}`\n\n\t\t\tlog(`making API request to: ${apiUrl}`)\n\t\t\tconst infoResponse = await fetch(\n\t\t\t\tapiUrl,\n\t\t\t\taccessToken\n\t\t\t\t\t? { headers: { authorization: `Bearer ${accessToken}` } }\n\t\t\t\t\t: undefined,\n\t\t\t)\n\t\t\tconst { status, statusText } = infoResponse\n\t\t\tlog(`API response: ${status} ${statusText}`)\n\n\t\t\tif (infoResponse.status >= 200 && infoResponse.status < 300) {\n\t\t\t\tlet rawInfo = await infoResponse.json()\n\t\t\t\t// another special case for epicai.pro videos\n\t\t\t\tif (epicUrl.host === 'www.epicai.pro') {\n\t\t\t\t\trawInfo = preprocessEpicAIVideoAPIResult(rawInfo)\n\t\t\t\t}\n\t\t\t\tconst infoResult = EpicVideoInfoSchema.safeParse(rawInfo)\n\t\t\t\tif (infoResult.success) {\n\t\t\t\t\tlog(`successfully parsed video info for ${epicVideoEmbed}`)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t...infoResult.data,\n\t\t\t\t\t} as const\n\t\t\t\t} else {\n\t\t\t\t\t// don't cache errors for long...\n\t\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\t\tconst restrictedResult =\n\t\t\t\t\t\tEpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo)\n\t\t\t\t\tif (restrictedResult.success) {\n\t\t\t\t\t\tlog.warn(`video is region restricted: ${epicVideoEmbed}`)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\ttype: 'region-restricted',\n\t\t\t\t\t\t\t...restrictedResult.data,\n\t\t\t\t\t\t} as const\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.error(`API response parsing failed for ${epicVideoEmbed}`, {\n\t\t\t\t\t\t\turl: epicUrl.pathname,\n\t\t\t\t\t\t\trawInfo,\n\t\t\t\t\t\t\tparseError: infoResult.error,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`Response from EpicWeb for \"${epicUrl.pathname}\" does not match expectation`,\n\t\t\t\t\t\t\tinfoResult.error,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: 500,\n\t\t\t\t\t\t\tstatusText: 'API Data Type Mismatch',\n\t\t\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\t\t} as const\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\tlog.error(`video API request failed for ${epicVideoEmbed}`, {\n\t\t\t\t\tstatus,\n\t\t\t\t\tstatusText,\n\t\t\t\t\turl: apiUrl,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tstatusCode: status,\n\t\t\t\t\tstatusText,\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t} as const\n\t\t\t}\n\t\t},\n\t}).catch((e) => {\n\t\tlog.error(`failed to fetch epic video info for ${epicVideoEmbed}:`, e)\n\t\tthrow e\n\t})\n}\n\nfunction getEpicAIVideoAPIUrl(epicVideoEmbed: string) {\n\tconst epicUrl = new URL(epicVideoEmbed)\n\tconst pathSegments = epicUrl.pathname.split('/').filter(Boolean)\n\n\tif (epicUrl.pathname.endsWith('/solution')) {\n\t\t// slug is right before 'solution'\n\t\tconst slug = pathSegments.at(-2)\n\t\tinvariant(slug, 'Expected slug before /solution in pathname')\n\t\treturn `https://www.epicai.pro/api/lessons/${slug}/solution`\n\t} else if (epicUrl.pathname.includes('/workshops')) {\n\t\tconst slug = pathSegments.at(-1)\n\t\tinvariant(slug, 'Expected slug at end of /workshops pathname')\n\t\treturn `https://www.epicai.pro/api/lessons?slugOrId=${slug}`\n\t} else {\n\t\tconst slug = pathSegments.at(-1)\n\t\tinvariant(slug, 'Expected slug at end of pathname')\n\t\treturn `https://www.epicai.pro/api/posts?slugOrId=${slug}`\n\t}\n}\n\nfunction preprocessEpicAIVideoAPIResult(result: any) {\n\tconst PostVideoResourceSchema = z.object({\n\t\tresource: z.object({\n\t\t\ttype: z.literal('videoResource'),\n\t\t\tfields: EpicVideoInfoSchema,\n\t\t}),\n\t})\n\tconst PostSchema = z.object({\n\t\tfields: z.object({ title: z.string() }),\n\t\tresources: z.array(z.any()).nullable(),\n\t})\n\tconst post = PostSchema.safeParse(result)\n\tif (!post.success) return null\n\tfor (const resource of post.data.resources ?? []) {\n\t\tconst videoResource = PostVideoResourceSchema.safeParse(resource)\n\n\t\tif (videoResource.success) {\n\t\t\treturn {\n\t\t\t\t...videoResource.data.resource.fields,\n\t\t\t\ttitle: post.data.fields.title,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\nasync function getEpicProgress({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: { timings?: Timings; request?: Request; forceFresh?: boolean } = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\n\tconst authInfo = await getAuthInfo()\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\tif (!authInfo) return []\n\n\tconst tokenPart = md5(authInfo.tokenSet.access_token)\n\tconst EpicProgressSchema = z.array(\n\t\tz.object({\n\t\t\tlessonId: z.string(),\n\t\t\tcompletedAt: z.string().nullable(),\n\t\t}),\n\t)\n\n\tlog(`fetching progress from EpicWeb host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-progress:${host}:${tokenPart}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t\tttl: 1000 * 2,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: [],\n\t\tcheckValue: EpicProgressSchema,\n\t\tasync getFreshValue(context): Promise<z.infer<typeof EpicProgressSchema>> {\n\t\t\tconst progressUrl = `https://${host}/api/progress`\n\t\t\tlog(`making progress API request to: ${progressUrl}`)\n\n\t\t\tconst response = await fetch(progressUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(`progress API response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(\n\t\t\t\t\t`failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tconst progressData = await response.json()\n\t\t\tconst parsedProgress = EpicProgressSchema.parse(progressData)\n\t\t\tlog(`successfully fetched ${parsedProgress.length} progress entries`)\n\t\t\treturn parsedProgress\n\t\t},\n\t})\n}\n\nexport type Progress = Awaited<ReturnType<typeof getProgress>>[number]\nexport async function getProgress({\n\ttimings,\n\trequest,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n} = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return []\n\n\tconst {\n\t\tproduct: { slug, host },\n\t} = getWorkshopConfig()\n\tif (!slug) return []\n\n\tlog(`aggregating progress data for workshop: ${slug}`)\n\tconst [\n\t\tworkshopData,\n\t\tepicProgress,\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t] = await Promise.all([\n\t\tgetWorkshopData(slug, { request, timings }),\n\t\tgetEpicProgress({ request, timings }),\n\t\tgetWorkshopInstructions({ request }),\n\t\tgetWorkshopFinished({ request }),\n\t\tgetExercises({ request, timings }),\n\t])\n\n\ttype ProgressInfo = {\n\t\tepicLessonUrl: string\n\t\tepicLessonSlug: string\n\t\tepicCompletedAt: string | null\n\t}\n\tconst progress: Array<\n\t\tProgressInfo &\n\t\t\t(ReturnType<typeof getProgressForLesson> | { type: 'unknown' })\n\t> = []\n\n\tfor (const resource of workshopData.resources ?? []) {\n\t\tconst lessons = resource._type === 'section' ? resource.lessons : [resource]\n\t\tfor (const lesson of lessons) {\n\t\t\tconst epicLessonSlug = lesson.slug\n\t\t\tconst lessonProgress = epicProgress.find(\n\t\t\t\t({ lessonId }) => lessonId === lesson._id,\n\t\t\t)\n\t\t\tconst epicCompletedAt = lessonProgress ? lessonProgress.completedAt : null\n\t\t\tconst progressForLesson = getProgressForLesson(epicLessonSlug, {\n\t\t\t\tworkshopInstructions,\n\t\t\t\tworkshopFinished,\n\t\t\t\texercises,\n\t\t\t})\n\t\t\tconst epicLessonUrl = `https://${host}/workshops/${slug}/${epicLessonSlug}`\n\t\t\tif (progressForLesson) {\n\t\t\t\tprogress.push({\n\t\t\t\t\t...progressForLesson,\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tprogress.push({\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tlog(`processed ${progress.length} progress entries for workshop: ${slug}`)\n\treturn progress\n}\n\nfunction getProgressForLesson(\n\tepicLessonSlug: string,\n\t{\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t}: {\n\t\tworkshopInstructions: Awaited<ReturnType<typeof getWorkshopInstructions>>\n\t\tworkshopFinished: Awaited<ReturnType<typeof getWorkshopFinished>>\n\t\texercises: Awaited<ReturnType<typeof getExercises>>\n\t},\n) {\n\tconst hasEmbed = (embed?: Array<string>) =>\n\t\tembed?.some((e) => e.split('/').at(-1) === epicLessonSlug)\n\tif (\n\t\tworkshopInstructions.compiled.status === 'success' &&\n\t\thasEmbed(workshopInstructions.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-instructions' } as const\n\t}\n\tif (\n\t\tworkshopFinished.compiled.status === 'success' &&\n\t\thasEmbed(workshopFinished.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-finished' } as const\n\t}\n\tfor (const exercise of exercises) {\n\t\tif (hasEmbed(exercise.instructionsEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'instructions',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tif (hasEmbed(exercise.finishedEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'finished',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tfor (const step of exercise.steps.filter(Boolean)) {\n\t\t\tif (hasEmbed(step.problem?.epicVideoEmbeds)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'step',\n\t\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t} as const\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function updateProgress(\n\t{ lessonSlug, complete }: { lessonSlug: string; complete?: boolean },\n\t{\n\t\ttimings,\n\t\trequest,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: 'cannot update progress when deployed',\n\t\t} as const\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\treturn { status: 'error', error: 'not authenticated' } as const\n\t}\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst progressUrl = `https://${host}/api/progress`\n\tconst payload = complete ? { lessonSlug } : { lessonSlug, remove: true }\n\n\tlog(`updating progress for lesson: ${lessonSlug} (complete: ${complete})`)\n\tlog(\n\t\t`making POST request to: ${progressUrl} with payload: ${JSON.stringify(payload)}`,\n\t)\n\n\tconst response = await fetch(progressUrl, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t'content-type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(payload),\n\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\tlog(`progress update response: ${response.status} ${response.statusText}`)\n\n\t// force the progress to be fresh whether or not we're successful\n\tawait getEpicProgress({ forceFresh: true, request, timings })\n\n\tif (response.status < 200 || response.status >= 300) {\n\t\tlog(`progress update failed: ${response.status} ${response.statusText}`)\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: `${response.status} ${response.statusText}`,\n\t\t} as const\n\t}\n\n\tlog(`progress update successful for lesson: ${lessonSlug}`)\n\treturn { status: 'success' } as const\n}\n\nconst ModuleSchema = z.object({\n\tresources: z\n\t\t.array(\n\t\t\tz.union([\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('lesson'),\n\t\t\t\t\t_id: z.string(),\n\t\t\t\t\tslug: z.string(),\n\t\t\t\t}),\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('section'),\n\t\t\t\t\tlessons: z.array(z.object({ _id: z.string(), slug: z.string() })),\n\t\t\t\t}),\n\t\t\t]),\n\t\t)\n\t\t.nullable(),\n})\n\nexport async function getWorkshopData(\n\tslug: string,\n\t{\n\t\ttimings,\n\t\trequest,\n\t\tforceFresh,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t\tforceFresh?: boolean\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return { resources: [] }\n\n\tconst authInfo = await getAuthInfo()\n\t// auth is not required, but we only use it for progress which is only needed\n\t// if you're authenticated anyway.\n\tif (!authInfo) return { resources: [] }\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tlog(`fetching workshop data for slug: ${slug} from host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-workshop-data:${host}:${slug}`,\n\t\tttl: 1000 * 60 * 5,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tofflineFallbackValue: { resources: [] },\n\t\tcheckValue: ModuleSchema,\n\t\tasync getFreshValue(): Promise<z.infer<typeof ModuleSchema>> {\n\t\t\tconst workshopUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}`\n\t\t\tlog(`making workshop data request to: ${workshopUrl}`)\n\n\t\t\tconst response = await fetch(workshopUrl).catch(\n\t\t\t\t(e) => new Response(getErrorMessage(e), { status: 500 }),\n\t\t\t)\n\n\t\t\tlog(`workshop data response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(\n\t\t\t\t\t`failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\treturn { resources: [] }\n\t\t\t}\n\n\t\t\tconst jsonResponse = await response.json()\n\t\t\tconst parsedData = ModuleSchema.parse(jsonResponse)\n\t\t\tlog(\n\t\t\t\t`successfully fetched workshop data for ${slug} with ${parsedData.resources?.length ?? 0} resources`,\n\t\t\t)\n\t\t\treturn parsedData\n\t\t},\n\t})\n}\n\nexport async function userHasAccessToWorkshop({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean\n} = {}) {\n\tconst config = getWorkshopConfig()\n\tconst {\n\t\tproduct: { host, slug },\n\t} = config\n\tif (!slug) return true\n\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\tconst cookieHeader = request?.headers.get('Cookie')\n\t\tif (!cookieHeader) return false\n\t\tconst cookies = cookie.parse(cookieHeader)\n\t\treturn cookies.skill?.split(',').includes(slug) ?? false\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return false\n\n\treturn cachified({\n\t\tkey: `user-has-access-to-workshop:${host}:${slug}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 5,\n\t\tofflineFallbackValue: false,\n\t\tcheckValue: z.boolean(),\n\t\tasync getFreshValue(context) {\n\t\t\tconst accessUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}/access`\n\t\t\tlog(`checking workshop access via API: ${accessUrl}`)\n\n\t\t\tconst response = await fetch(accessUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(\n\t\t\t\t`workshop access API response: ${response.status} ${response.statusText}`,\n\t\t\t)\n\n\t\t\tconst hasAccess = response.ok ? (await response.json()) === true : false\n\t\t\tlog(`workshop access result for ${slug}: ${hasAccess}`)\n\n\t\t\tif (hasAccess) {\n\t\t\t\tcontext.metadata.ttl = 1000 * 60 * 5\n\t\t\t\tcontext.metadata.swr = 1000 * 60 * 60 * 24 * 365 * 10\n\t\t\t}\n\n\t\t\treturn hasAccess\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error('Failed to check workshop access', e)\n\t\treturn false\n\t})\n}\n\nconst UserInfoSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tname: z.string().nullable(),\n\t\temail: z.string().email(),\n\t\timage: z.string().nullable(),\n\t\tdiscordProfile: z\n\t\t\t.object({\n\t\t\t\tnick: z.string().nullable().optional(),\n\t\t\t\tuser: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\tid: z.string(),\n\t\t\t\t\t\tusername: z.string(),\n\t\t\t\t\t\tavatar: z.string().nullable().optional(),\n\t\t\t\t\t\tglobal_name: z.string().nullable().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.optional(),\n\t\t\t})\n\t\t\t.nullable()\n\t\t\t.optional(),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\timageUrlSmall:\n\t\t\t\tresizeImageUrl(data.image, { size: 64 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 64,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 64 }),\n\t\t\timageUrlLarge:\n\t\t\t\tresizeImageUrl(data.image, { size: 512 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 512,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 512 }),\n\t\t}\n\t})\n\nfunction resizeImageUrl(url: string | null, { size }: { size: number }) {\n\tif (!url) return null\n\tconst urlObj = new URL(url)\n\turlObj.searchParams.set('size', size.toString())\n\treturn urlObj.toString()\n}\n\nfunction resolveGravatarUrl(\n\temail: string | undefined,\n\t{ size }: { size: number },\n) {\n\tif (!email) return null\n\n\tconst hash = md5(email.toLowerCase())\n\tconst gravatarOptions = new URLSearchParams({\n\t\tsize: size.toString(),\n\t\tdefault: 'identicon',\n\t})\n\treturn `https://www.gravatar.com/avatar/${hash}?${gravatarOptions.toString()}`\n}\n\nfunction resolveDiscordAvatar(\n\tuser: { avatar?: string | null; id: string } | undefined,\n\t{ size }: { size: 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 },\n) {\n\tif (!user) return null\n\n\tconst { avatar, id: userId } = user\n\tif (!avatar) return null\n\tconst isGif = avatar.startsWith('a_')\n\tconst url = new URL(\n\t\t`/avatars/${userId}/${avatar}.${isGif ? 'gif' : 'png'}`,\n\t\t'https://cdn.discordapp.com',\n\t)\n\turl.searchParams.set('size', size.toString())\n\treturn url.toString()\n}\n\nexport type UserInfo = z.infer<typeof UserInfoSchema>\n\nexport async function getUserInfo({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n\tforceFresh?: boolean\n} = {}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return null\n\n\tconst { tokenSet } = authInfo\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst accessToken = tokenSet.access_token\n\tconst url = `https://${host}/oauth/userinfo`\n\n\tlog('fetching user info from: %s', url)\n\tconst userInfo = await cachified({\n\t\tkey: `${url}:${md5(accessToken)}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 30,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: UserInfoSchema,\n\t\tasync getFreshValue(): Promise<UserInfo> {\n\t\t\tlog(`making user info API request to: ${url}`)\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${accessToken}` },\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(`user info API response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlog(\n\t\t\t\t\t`user info API request failed: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tif (\n\t\t\t\t\tresponse.headers.get('content-type')?.includes('application/json')\n\t\t\t\t) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthrow new Error(`Failed to fetch user info: ${JSON.stringify(data)}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst text = await response.text()\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Failed to fetch user info: ${text || response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = await response.json()\n\t\t\tconst parsedUserInfo = UserInfoSchema.parse(data)\n\t\t\tlog(\n\t\t\t\t`successfully fetched user info for user: ${parsedUserInfo.id} (${parsedUserInfo.email})`,\n\t\t\t)\n\t\t\treturn parsedUserInfo\n\t\t},\n\t}).catch((e) => {\n\t\tlog.error(`failed to get user info:`, e)\n\t\treturn null\n\t})\n\n\t// we used to md5 hash the email to get the id\n\t// if the id doesn't match what we have on file, update it\n\t// you can probably safely remove this in January 2025\n\tif (userInfo && authInfo.id !== userInfo.id) {\n\t\tawait setAuthInfo({\n\t\t\t...authInfo,\n\t\t\tid: userInfo.id,\n\t\t})\n\t}\n\n\treturn userInfo\n}\n\nexport async function warmCache() {\n\tawait Promise.all([getUserInfo(), getProgress()])\n}\n"]}
|
|
1
|
+
{"version":3,"file":"epic-api.server.js","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from '@epic-web/invariant'\nimport * as cookie from 'cookie'\n\nimport md5 from 'md5-hex'\nimport { z } from 'zod'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n\tgetWorkshopInstructions,\n} from './apps.server.js'\nimport { cachified, epicApiCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getAuthInfo, setAuthInfo } from './db.server.js'\nimport { getEnv } from './init-env.js'\nimport { logger } from './logger.js'\nimport { type Timings } from './timing.server.js'\nimport { getErrorMessage } from './utils.js'\n\n// Module-level logger for epic-api operations\nconst log = logger('epic:api')\n\nconst Transcript = z\n\t.string()\n\t.nullable()\n\t.optional()\n\t.transform((s) => s ?? 'Transcripts not available')\nconst EpicVideoInfoSchema = z.object({\n\ttitle: z.string().nullable().optional(),\n\ttranscript: Transcript,\n\tmuxPlaybackId: z.string(),\n})\n\nconst EpicVideoRegionRestrictedErrorSchema = z.object({\n\trequestCountry: z.string(),\n\trestrictedCountry: z.string(),\n\tisRegionRestricted: z.literal(true),\n})\n\nconst CachedEpicVideoInfoSchema = z\n\t.object({\n\t\ttitle: z.string().nullable().optional(),\n\t\ttranscript: Transcript,\n\t\tmuxPlaybackId: z.string(),\n\t\tstatus: z.literal('success'),\n\t\tstatusCode: z.number(),\n\t\tstatusText: z.string(),\n\t})\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('unknown'),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('region-restricted'),\n\t\t\trequestCountry: z.string(),\n\t\t\trestrictedCountry: z.string(),\n\t\t}),\n\t)\n\t.or(z.null())\n\nexport type EpicVideoInfos = Record<\n\tstring,\n\tAwaited<ReturnType<typeof getEpicVideoInfo>>\n>\n\nconst videoInfoLog = log.logger('video-info')\n\nexport async function getEpicVideoInfos(\n\tepicWebUrls?: Array<string> | null,\n\t{ request, timings }: { request?: Request; timings?: Timings } = {},\n) {\n\tif (!epicWebUrls) {\n\t\tvideoInfoLog.warn('no epic web URLs provided, returning empty object')\n\t\treturn {}\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (getEnv().EPICSHOP_DEPLOYED) return {}\n\n\tvideoInfoLog(`fetching epic video infos for ${epicWebUrls.length} URLs`)\n\tconst epicVideoInfos: EpicVideoInfos = {}\n\tfor (const epicVideoEmbed of epicWebUrls) {\n\t\tconst epicVideoInfo = await getEpicVideoInfo({\n\t\t\tepicVideoEmbed,\n\t\t\taccessToken: authInfo?.tokenSet.access_token,\n\t\t\trequest,\n\t\t\ttimings,\n\t\t})\n\t\tif (epicVideoInfo) {\n\t\t\tepicVideoInfos[epicVideoEmbed] = epicVideoInfo\n\t\t}\n\t}\n\tvideoInfoLog(\n\t\t`successfully fetched ${Object.keys(epicVideoInfos).length} epic video infos`,\n\t)\n\treturn epicVideoInfos\n}\n\nasync function getEpicVideoInfo({\n\tepicVideoEmbed,\n\taccessToken,\n\trequest,\n\ttimings,\n}: {\n\tepicVideoEmbed: string\n\taccessToken?: string\n\trequest?: Request\n\ttimings?: Timings\n}) {\n\tconst tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated'\n\tconst key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`\n\n\tvideoInfoLog(`fetching video info for URL: ${epicVideoEmbed}`)\n\treturn cachified({\n\t\tkey,\n\t\trequest,\n\t\tcache: epicApiCache,\n\t\ttimings,\n\t\tttl: 1000 * 60 * 60,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: CachedEpicVideoInfoSchema,\n\t\tasync getFreshValue(\n\t\t\tcontext,\n\t\t): Promise<z.infer<typeof CachedEpicVideoInfoSchema>> {\n\t\t\tconst epicUrl = new URL(epicVideoEmbed)\n\t\t\tif (\n\t\t\t\tepicUrl.host !== 'www.epicweb.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicreact.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicai.pro'\n\t\t\t) {\n\t\t\t\tvideoInfoLog.error(`unsupported host for video URL: ${epicUrl.host}`)\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// this may be temporary until the /tutorials/ endpoint supports /api\n\t\t\tif (epicUrl.pathname.startsWith('/tutorials/')) {\n\t\t\t\tepicUrl.pathname = epicUrl.pathname.replace(\n\t\t\t\t\t/^\\/tutorials\\//,\n\t\t\t\t\t'/workshops/',\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// special case for epicai.pro videos\n\t\t\tconst apiUrl =\n\t\t\t\tepicUrl.host === 'www.epicai.pro'\n\t\t\t\t\t? getEpicAIVideoAPIUrl(epicVideoEmbed)\n\t\t\t\t\t: `https://${epicUrl.host}/api${epicUrl.pathname}`\n\n\t\t\tvideoInfoLog(`making API request to: ${apiUrl}`)\n\t\t\tconst infoResponse = await fetch(\n\t\t\t\tapiUrl,\n\t\t\t\taccessToken\n\t\t\t\t\t? { headers: { authorization: `Bearer ${accessToken}` } }\n\t\t\t\t\t: undefined,\n\t\t\t)\n\t\t\tconst { status, statusText } = infoResponse\n\t\t\tvideoInfoLog(`API response: ${status} ${statusText}`)\n\n\t\t\tif (infoResponse.status >= 200 && infoResponse.status < 300) {\n\t\t\t\tlet rawInfo = await infoResponse.json()\n\t\t\t\t// another special case for epicai.pro videos\n\t\t\t\tif (epicUrl.host === 'www.epicai.pro') {\n\t\t\t\t\trawInfo = preprocessEpicAIVideoAPIResult(rawInfo)\n\t\t\t\t}\n\t\t\t\tconst infoResult = EpicVideoInfoSchema.safeParse(rawInfo)\n\t\t\t\tif (infoResult.success) {\n\t\t\t\t\tvideoInfoLog(`successfully parsed video info for ${epicVideoEmbed}`)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t...infoResult.data,\n\t\t\t\t\t} as const\n\t\t\t\t} else {\n\t\t\t\t\t// don't cache errors for long...\n\t\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\t\tconst restrictedResult =\n\t\t\t\t\t\tEpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo)\n\t\t\t\t\tif (restrictedResult.success) {\n\t\t\t\t\t\tvideoInfoLog.warn(`video is region restricted: ${epicVideoEmbed}`)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\ttype: 'region-restricted',\n\t\t\t\t\t\t\t...restrictedResult.data,\n\t\t\t\t\t\t} as const\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvideoInfoLog.error(\n\t\t\t\t\t\t\t`API response parsing failed for ${epicVideoEmbed}`,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\turl: epicUrl.pathname,\n\t\t\t\t\t\t\t\trawInfo,\n\t\t\t\t\t\t\t\tparseError: infoResult.error,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: 500,\n\t\t\t\t\t\t\tstatusText: 'API Data Type Mismatch',\n\t\t\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\t\t} as const\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\tvideoInfoLog.error(`video API request failed for ${epicVideoEmbed}`, {\n\t\t\t\t\tstatus,\n\t\t\t\t\tstatusText,\n\t\t\t\t\turl: apiUrl,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tstatusCode: status,\n\t\t\t\t\tstatusText,\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t} as const\n\t\t\t}\n\t\t},\n\t}).catch((e) => {\n\t\tvideoInfoLog.error(\n\t\t\t`failed to fetch epic video info for ${epicVideoEmbed}:`,\n\t\t\te,\n\t\t)\n\t\tthrow e\n\t})\n}\n\nfunction getEpicAIVideoAPIUrl(epicVideoEmbed: string) {\n\tconst epicUrl = new URL(epicVideoEmbed)\n\tconst pathSegments = epicUrl.pathname.split('/').filter(Boolean)\n\n\tif (epicUrl.pathname.endsWith('/solution')) {\n\t\t// slug is right before 'solution'\n\t\tconst slug = pathSegments.at(-2)\n\t\tinvariant(slug, 'Expected slug before /solution in pathname')\n\t\treturn `https://www.epicai.pro/api/lessons/${slug}/solution`\n\t} else if (epicUrl.pathname.includes('/workshops')) {\n\t\tconst slug = pathSegments.at(-1)\n\t\tinvariant(slug, 'Expected slug at end of /workshops pathname')\n\t\treturn `https://www.epicai.pro/api/lessons?slugOrId=${slug}`\n\t} else {\n\t\tconst slug = pathSegments.at(-1)\n\t\tinvariant(slug, 'Expected slug at end of pathname')\n\t\treturn `https://www.epicai.pro/api/posts?slugOrId=${slug}`\n\t}\n}\n\nfunction preprocessEpicAIVideoAPIResult(result: any) {\n\tconst PostVideoResourceSchema = z.object({\n\t\tresource: z.object({\n\t\t\ttype: z.literal('videoResource'),\n\t\t\tfields: EpicVideoInfoSchema,\n\t\t}),\n\t})\n\tconst PostSchema = z.object({\n\t\tfields: z.object({ title: z.string() }),\n\t\tresources: z.array(z.any()).nullable(),\n\t})\n\tconst post = PostSchema.safeParse(result)\n\tif (!post.success) return null\n\tfor (const resource of post.data.resources ?? []) {\n\t\tconst videoResource = PostVideoResourceSchema.safeParse(resource)\n\n\t\tif (videoResource.success) {\n\t\t\treturn {\n\t\t\t\t...videoResource.data.resource.fields,\n\t\t\t\ttitle: post.data.fields.title,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\nasync function getEpicProgress({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: { timings?: Timings; request?: Request; forceFresh?: boolean } = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\n\tconst authInfo = await getAuthInfo()\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\tif (!authInfo) return []\n\n\tconst tokenPart = md5(authInfo.tokenSet.access_token)\n\tconst EpicProgressSchema = z.array(\n\t\tz.object({\n\t\t\tlessonId: z.string(),\n\t\t\tcompletedAt: z.string().nullable(),\n\t\t}),\n\t)\n\n\tlog(`fetching progress from EpicWeb host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-progress:${host}:${tokenPart}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t\tttl: 1000 * 2,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: [],\n\t\tcheckValue: EpicProgressSchema,\n\t\tasync getFreshValue(context): Promise<z.infer<typeof EpicProgressSchema>> {\n\t\t\tconst progressUrl = `https://${host}/api/progress`\n\t\t\tlog(`making progress API request to: ${progressUrl}`)\n\n\t\t\tconst response = await fetch(progressUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(`progress API response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(\n\t\t\t\t\t`failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tconst progressData = await response.json()\n\t\t\tconst parsedProgress = EpicProgressSchema.parse(progressData)\n\t\t\tlog(`successfully fetched ${parsedProgress.length} progress entries`)\n\t\t\treturn parsedProgress\n\t\t},\n\t})\n}\n\nexport type Progress = Awaited<ReturnType<typeof getProgress>>[number]\nexport async function getProgress({\n\ttimings,\n\trequest,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n} = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return []\n\n\tconst {\n\t\tproduct: { slug, host },\n\t} = getWorkshopConfig()\n\tif (!slug) return []\n\n\tlog(`aggregating progress data for workshop: ${slug}`)\n\tconst [\n\t\tworkshopData,\n\t\tepicProgress,\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t] = await Promise.all([\n\t\tgetWorkshopData(slug, { request, timings }),\n\t\tgetEpicProgress({ request, timings }),\n\t\tgetWorkshopInstructions({ request }),\n\t\tgetWorkshopFinished({ request }),\n\t\tgetExercises({ request, timings }),\n\t])\n\n\ttype ProgressInfo = {\n\t\tepicLessonUrl: string\n\t\tepicLessonSlug: string\n\t\tepicCompletedAt: string | null\n\t}\n\tconst progress: Array<\n\t\tProgressInfo &\n\t\t\t(ReturnType<typeof getProgressForLesson> | { type: 'unknown' })\n\t> = []\n\n\tfor (const resource of workshopData.resources ?? []) {\n\t\tconst lessons = resource._type === 'section' ? resource.lessons : [resource]\n\t\tfor (const lesson of lessons) {\n\t\t\tconst epicLessonSlug = lesson.slug\n\t\t\tconst lessonProgress = epicProgress.find(\n\t\t\t\t({ lessonId }) => lessonId === lesson._id,\n\t\t\t)\n\t\t\tconst epicCompletedAt = lessonProgress ? lessonProgress.completedAt : null\n\t\t\tconst progressForLesson = getProgressForLesson(epicLessonSlug, {\n\t\t\t\tworkshopInstructions,\n\t\t\t\tworkshopFinished,\n\t\t\t\texercises,\n\t\t\t})\n\t\t\tconst epicLessonUrl = `https://${host}/workshops/${slug}/${epicLessonSlug}`\n\t\t\tif (progressForLesson) {\n\t\t\t\tprogress.push({\n\t\t\t\t\t...progressForLesson,\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tprogress.push({\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tlog(`processed ${progress.length} progress entries for workshop: ${slug}`)\n\treturn progress\n}\n\nfunction getProgressForLesson(\n\tepicLessonSlug: string,\n\t{\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t}: {\n\t\tworkshopInstructions: Awaited<ReturnType<typeof getWorkshopInstructions>>\n\t\tworkshopFinished: Awaited<ReturnType<typeof getWorkshopFinished>>\n\t\texercises: Awaited<ReturnType<typeof getExercises>>\n\t},\n) {\n\tconst hasEmbed = (embed?: Array<string>) =>\n\t\tembed?.some((e) => e.split('/').at(-1) === epicLessonSlug)\n\tif (\n\t\tworkshopInstructions.compiled.status === 'success' &&\n\t\thasEmbed(workshopInstructions.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-instructions' } as const\n\t}\n\tif (\n\t\tworkshopFinished.compiled.status === 'success' &&\n\t\thasEmbed(workshopFinished.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-finished' } as const\n\t}\n\tfor (const exercise of exercises) {\n\t\tif (hasEmbed(exercise.instructionsEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'instructions',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tif (hasEmbed(exercise.finishedEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'finished',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tfor (const step of exercise.steps.filter(Boolean)) {\n\t\t\tif (hasEmbed(step.problem?.epicVideoEmbeds)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'step',\n\t\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t} as const\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function updateProgress(\n\t{ lessonSlug, complete }: { lessonSlug: string; complete?: boolean },\n\t{\n\t\ttimings,\n\t\trequest,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: 'cannot update progress when deployed',\n\t\t} as const\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\treturn { status: 'error', error: 'not authenticated' } as const\n\t}\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst progressUrl = `https://${host}/api/progress`\n\tconst payload = complete ? { lessonSlug } : { lessonSlug, remove: true }\n\n\tlog(`updating progress for lesson: ${lessonSlug} (complete: ${complete})`)\n\tlog(\n\t\t`making POST request to: ${progressUrl} with payload: ${JSON.stringify(payload)}`,\n\t)\n\n\tconst response = await fetch(progressUrl, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t'content-type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(payload),\n\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\tlog(`progress update response: ${response.status} ${response.statusText}`)\n\n\t// force the progress to be fresh whether or not we're successful\n\tawait getEpicProgress({ forceFresh: true, request, timings })\n\n\tif (response.status < 200 || response.status >= 300) {\n\t\tlog(`progress update failed: ${response.status} ${response.statusText}`)\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: `${response.status} ${response.statusText}`,\n\t\t} as const\n\t}\n\n\tlog(`progress update successful for lesson: ${lessonSlug}`)\n\treturn { status: 'success' } as const\n}\n\nconst ModuleSchema = z.object({\n\tresources: z\n\t\t.array(\n\t\t\tz.union([\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('lesson'),\n\t\t\t\t\t_id: z.string(),\n\t\t\t\t\tslug: z.string(),\n\t\t\t\t}),\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('section'),\n\t\t\t\t\tlessons: z.array(z.object({ _id: z.string(), slug: z.string() })),\n\t\t\t\t}),\n\t\t\t]),\n\t\t)\n\t\t.nullable(),\n})\n\nexport async function getWorkshopData(\n\tslug: string,\n\t{\n\t\ttimings,\n\t\trequest,\n\t\tforceFresh,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t\tforceFresh?: boolean\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return { resources: [] }\n\n\tconst authInfo = await getAuthInfo()\n\t// auth is not required, but we only use it for progress which is only needed\n\t// if you're authenticated anyway.\n\tif (!authInfo) return { resources: [] }\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tlog(`fetching workshop data for slug: ${slug} from host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-workshop-data:${host}:${slug}`,\n\t\tttl: 1000 * 60 * 5,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tofflineFallbackValue: { resources: [] },\n\t\tcheckValue: ModuleSchema,\n\t\tasync getFreshValue(): Promise<z.infer<typeof ModuleSchema>> {\n\t\t\tconst workshopUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}`\n\t\t\tlog(`making workshop data request to: ${workshopUrl}`)\n\n\t\t\tconst response = await fetch(workshopUrl).catch(\n\t\t\t\t(e) => new Response(getErrorMessage(e), { status: 500 }),\n\t\t\t)\n\n\t\t\tlog(`workshop data response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(\n\t\t\t\t\t`failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\treturn { resources: [] }\n\t\t\t}\n\n\t\t\tconst jsonResponse = await response.json()\n\t\t\tconst parsedData = ModuleSchema.parse(jsonResponse)\n\t\t\tlog(\n\t\t\t\t`successfully fetched workshop data for ${slug} with ${parsedData.resources?.length ?? 0} resources`,\n\t\t\t)\n\t\t\treturn parsedData\n\t\t},\n\t})\n}\n\nexport async function userHasAccessToWorkshop({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean\n} = {}) {\n\tconst config = getWorkshopConfig()\n\tconst {\n\t\tproduct: { host, slug },\n\t} = config\n\tif (!slug) return true\n\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\tconst cookieHeader = request?.headers.get('Cookie')\n\t\tif (!cookieHeader) return false\n\t\tconst cookies = cookie.parse(cookieHeader)\n\t\treturn cookies.skill?.split(',').includes(slug) ?? false\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return false\n\n\treturn cachified({\n\t\tkey: `user-has-access-to-workshop:${host}:${slug}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 5,\n\t\tofflineFallbackValue: false,\n\t\tcheckValue: z.boolean(),\n\t\tasync getFreshValue(context) {\n\t\t\tconst accessUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}/access`\n\t\t\tlog(`checking workshop access via API: ${accessUrl}`)\n\n\t\t\tconst response = await fetch(accessUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(\n\t\t\t\t`workshop access API response: ${response.status} ${response.statusText}`,\n\t\t\t)\n\n\t\t\tconst hasAccess = response.ok ? (await response.json()) === true : false\n\t\t\tlog(`workshop access result for ${slug}: ${hasAccess}`)\n\n\t\t\tif (hasAccess) {\n\t\t\t\tcontext.metadata.ttl = 1000 * 60 * 5\n\t\t\t\tcontext.metadata.swr = 1000 * 60 * 60 * 24 * 365 * 10\n\t\t\t}\n\n\t\t\treturn hasAccess\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error('Failed to check workshop access', e)\n\t\treturn false\n\t})\n}\n\nconst UserInfoSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tname: z.string().nullable(),\n\t\temail: z.string().email(),\n\t\timage: z.string().nullable(),\n\t\tdiscordProfile: z\n\t\t\t.object({\n\t\t\t\tnick: z.string().nullable().optional(),\n\t\t\t\tuser: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\tid: z.string(),\n\t\t\t\t\t\tusername: z.string(),\n\t\t\t\t\t\tavatar: z.string().nullable().optional(),\n\t\t\t\t\t\tglobal_name: z.string().nullable().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.optional(),\n\t\t\t})\n\t\t\t.nullable()\n\t\t\t.optional(),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\timageUrlSmall:\n\t\t\t\tresizeImageUrl(data.image, { size: 64 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 64,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 64 }),\n\t\t\timageUrlLarge:\n\t\t\t\tresizeImageUrl(data.image, { size: 512 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 512,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 512 }),\n\t\t}\n\t})\n\nfunction resizeImageUrl(url: string | null, { size }: { size: number }) {\n\tif (!url) return null\n\tconst urlObj = new URL(url)\n\turlObj.searchParams.set('size', size.toString())\n\treturn urlObj.toString()\n}\n\nfunction resolveGravatarUrl(\n\temail: string | undefined,\n\t{ size }: { size: number },\n) {\n\tif (!email) return null\n\n\tconst hash = md5(email.toLowerCase())\n\tconst gravatarOptions = new URLSearchParams({\n\t\tsize: size.toString(),\n\t\tdefault: 'identicon',\n\t})\n\treturn `https://www.gravatar.com/avatar/${hash}?${gravatarOptions.toString()}`\n}\n\nfunction resolveDiscordAvatar(\n\tuser: { avatar?: string | null; id: string } | undefined,\n\t{ size }: { size: 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 },\n) {\n\tif (!user) return null\n\n\tconst { avatar, id: userId } = user\n\tif (!avatar) return null\n\tconst isGif = avatar.startsWith('a_')\n\tconst url = new URL(\n\t\t`/avatars/${userId}/${avatar}.${isGif ? 'gif' : 'png'}`,\n\t\t'https://cdn.discordapp.com',\n\t)\n\turl.searchParams.set('size', size.toString())\n\treturn url.toString()\n}\n\nexport type UserInfo = z.infer<typeof UserInfoSchema>\n\nconst userinfoLog = log.logger('userinfo')\nexport async function getUserInfo({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n\tforceFresh?: boolean\n} = {}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return null\n\n\tconst { tokenSet } = authInfo\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst accessToken = tokenSet.access_token\n\tconst url = `https://${host}/oauth/userinfo`\n\n\tuserinfoLog(`calling cachified to get user info from: ${url}`)\n\tconst userInfo = await cachified({\n\t\tkey: `${url}:${md5(accessToken)}`,\n\t\tcache: epicApiCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 30,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: UserInfoSchema,\n\t\tasync getFreshValue(): Promise<UserInfo> {\n\t\t\tuserinfoLog(`getting fresh value for user info from: ${url}`)\n\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${accessToken}` },\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tuserinfoLog(\n\t\t\t\t`user info API response: ${response.status} ${response.statusText}`,\n\t\t\t)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tuserinfoLog(\n\t\t\t\t\t`user info API request failed: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\tif (\n\t\t\t\t\tresponse.headers.get('content-type')?.includes('application/json')\n\t\t\t\t) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthrow new Error(`Failed to fetch user info: ${JSON.stringify(data)}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst text = await response.text()\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Failed to fetch user info: ${text || response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = await response.json()\n\t\t\tconst parsedUserInfo = UserInfoSchema.parse(data)\n\t\t\tuserinfoLog(\n\t\t\t\t`successfully fetched user info for user: ${parsedUserInfo.id} (${parsedUserInfo.email})`,\n\t\t\t)\n\t\t\treturn parsedUserInfo\n\t\t},\n\t}).catch((e) => {\n\t\tuserinfoLog.error(`failed to get user info:`, e)\n\t\treturn null\n\t})\n\n\t// we used to md5 hash the email to get the id\n\t// if the id doesn't match what we have on file, update it\n\t// you can probably safely remove this in January 2025\n\tif (userInfo && authInfo.id !== userInfo.id) {\n\t\tawait setAuthInfo({\n\t\t\t...authInfo,\n\t\t\tid: userInfo.id,\n\t\t})\n\t}\n\n\treturn userInfo\n}\n\nexport async function warmCache() {\n\tawait Promise.all([getUserInfo(), getProgress()])\n}\n"]}
|
package/dist/esm/logger.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type
|
|
3
|
-
|
|
1
|
+
import { type DebugLogger } from 'node:util';
|
|
2
|
+
type Msg = string | (() => string);
|
|
3
|
+
type Params = unknown[];
|
|
4
|
+
type LogFunction = (msg: Msg, ...param: Params) => void;
|
|
5
|
+
interface Logger extends LogFunction, DebugLogger {
|
|
4
6
|
error: LogFunction;
|
|
5
7
|
warn: LogFunction;
|
|
6
8
|
info: LogFunction;
|
|
7
9
|
namespace: string;
|
|
8
10
|
logger: typeof logger;
|
|
9
|
-
isEnabled: () => boolean;
|
|
10
11
|
}
|
|
11
12
|
export declare function logger(ns: string): Logger;
|
|
12
13
|
export {};
|
package/dist/esm/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAEtD,KAAK,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;AAClC,KAAK,MAAM,GAAG,OAAO,EAAE,CAAA;AACvB,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;AAEvD,UAAU,MAAO,SAAQ,WAAW,EAAE,WAAW;IAChD,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,OAAO,MAAM,CAAA;CACrB;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAyBzC"}
|
package/dist/esm/logger.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { debuglog } from 'node:util';
|
|
2
2
|
export function logger(ns) {
|
|
3
3
|
const log = debuglog(ns);
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const prefixedLoggerFn = (prefix, stringOrFn, ...params) => {
|
|
5
|
+
if (!debuglog(ns).enabled)
|
|
6
|
+
return;
|
|
7
|
+
const string = typeof stringOrFn === 'function' ? stringOrFn() : stringOrFn;
|
|
8
|
+
log(prefix ? `${prefix} ${string}` : string, ...params);
|
|
9
|
+
};
|
|
10
|
+
const loggerFn = prefixedLoggerFn.bind(null, null);
|
|
11
|
+
loggerFn.error = prefixedLoggerFn.bind(null, '🚨');
|
|
12
|
+
loggerFn.warn = prefixedLoggerFn.bind(null, '⚠️');
|
|
13
|
+
loggerFn.info = prefixedLoggerFn.bind(null, 'ℹ️');
|
|
8
14
|
loggerFn.namespace = ns;
|
|
9
15
|
loggerFn.logger = (subNs) => logger(`${ns}:${subNs}`);
|
|
10
|
-
|
|
16
|
+
Object.defineProperty(loggerFn, 'enabled', {
|
|
17
|
+
get: () => log.enabled,
|
|
18
|
+
});
|
|
11
19
|
return loggerFn;
|
|
12
20
|
}
|
|
13
21
|
//# sourceMappingURL=logger.js.map
|
package/dist/esm/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAoB,MAAM,WAAW,CAAA;AActD,MAAM,UAAU,MAAM,CAAC,EAAU;IAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAExB,MAAM,gBAAgB,GAAG,CACxB,MAAqB,EACrB,UAAe,EACf,GAAG,MAAc,EAChB,EAAE;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO;YAAE,OAAM;QACjC,MAAM,MAAM,GAAG,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;QAC3E,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAA;IACxD,CAAC,CAAA;IAED,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAW,CAAA;IAE5D,QAAQ,CAAC,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAClD,QAAQ,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACjD,QAAQ,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACjD,QAAQ,CAAC,SAAS,GAAG,EAAE,CAAA;IACvB,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,KAAK,EAAE,CAAC,CAAA;IAC7D,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE;QAC1C,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO;KACtB,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AAChB,CAAC","sourcesContent":["import { debuglog, type DebugLogger } from 'node:util'\n\ntype Msg = string | (() => string)\ntype Params = unknown[]\ntype LogFunction = (msg: Msg, ...param: Params) => void\n\ninterface Logger extends LogFunction, DebugLogger {\n\terror: LogFunction\n\twarn: LogFunction\n\tinfo: LogFunction\n\tnamespace: string\n\tlogger: typeof logger\n}\n\nexport function logger(ns: string): Logger {\n\tconst log = debuglog(ns)\n\n\tconst prefixedLoggerFn = (\n\t\tprefix: string | null,\n\t\tstringOrFn: Msg,\n\t\t...params: Params\n\t) => {\n\t\tif (!debuglog(ns).enabled) return\n\t\tconst string = typeof stringOrFn === 'function' ? stringOrFn() : stringOrFn\n\t\tlog(prefix ? `${prefix} ${string}` : string, ...params)\n\t}\n\n\tconst loggerFn = prefixedLoggerFn.bind(null, null) as Logger\n\n\tloggerFn.error = prefixedLoggerFn.bind(null, '🚨')\n\tloggerFn.warn = prefixedLoggerFn.bind(null, '⚠️')\n\tloggerFn.info = prefixedLoggerFn.bind(null, 'ℹ️')\n\tloggerFn.namespace = ns\n\tloggerFn.logger = (subNs: string) => logger(`${ns}:${subNs}`)\n\tObject.defineProperty(loggerFn, 'enabled', {\n\t\tget: () => log.enabled,\n\t})\n\n\treturn loggerFn\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.server.d.ts","sourceRoot":"","sources":["../../src/utils.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAGtB,OAAO,QAAQ,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.server.d.ts","sourceRoot":"","sources":["../../src/utils.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAGtB,OAAO,QAAQ,MAAM,OAAO,CAAA;AAM5B,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAEjD,eAAO,MAAM,KAAK,iBAKhB,CAAA;AAkDF,wBAAsB,eAAe,CAAC,EACrC,OAAO,EACP,OAAO,GACP,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACZ,oBAwBL"}
|
package/dist/esm/utils.server.js
CHANGED
|
@@ -5,13 +5,47 @@ import relativeTimePlugin from 'dayjs/plugin/relativeTime.js';
|
|
|
5
5
|
import timeZonePlugin from 'dayjs/plugin/timezone.js';
|
|
6
6
|
import utcPlugin from 'dayjs/plugin/utc.js';
|
|
7
7
|
import { cachified, connectionCache } from './cache.server.js';
|
|
8
|
+
import { logger } from './logger.js';
|
|
8
9
|
export const dayjs = remember('dayjs', () => {
|
|
9
10
|
dayjsLib.extend(utcPlugin);
|
|
10
11
|
dayjsLib.extend(timeZonePlugin);
|
|
11
12
|
dayjsLib.extend(relativeTimePlugin);
|
|
12
13
|
return dayjsLib;
|
|
13
14
|
});
|
|
15
|
+
const connectionLog = logger('epic:connection');
|
|
16
|
+
async function probeOnce(url, expectedOk = (s) => s >= 200 && s < 300, init) {
|
|
17
|
+
const signal = AbortSignal.timeout(10_000);
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
connectionLog(`probing ${url}`);
|
|
20
|
+
const res = await fetch(url, {
|
|
21
|
+
method: 'HEAD',
|
|
22
|
+
cache: 'no-store',
|
|
23
|
+
signal,
|
|
24
|
+
...init,
|
|
25
|
+
});
|
|
26
|
+
connectionLog(`probe to ${url} returned ${res.status} in ${Date.now() - start}ms`);
|
|
27
|
+
return expectedOk(res.status);
|
|
28
|
+
}
|
|
29
|
+
async function raceConnectivity() {
|
|
30
|
+
// we have multiple just in case some VPN blocks one or another
|
|
31
|
+
const candidates = [
|
|
32
|
+
probeOnce('https://connected.kentcdodds.workers.dev'),
|
|
33
|
+
// Non-CF options (different providers / networks):
|
|
34
|
+
probeOnce('https://www.gstatic.com/generate_204', (s) => s === 204),
|
|
35
|
+
probeOnce('http://www.msftconnecttest.com/connecttest.txt', (s) => s === 200),
|
|
36
|
+
];
|
|
37
|
+
// Use Promise.any to succeed on first truthy result; treat rejections/non-2xx as false
|
|
38
|
+
const wrapped = candidates.map((p) => p.then((ok) => (ok ? true : Promise.reject(new Error('not ok')))));
|
|
39
|
+
try {
|
|
40
|
+
await Promise.any(wrapped);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
14
47
|
export async function checkConnection({ request, timings, } = {}) {
|
|
48
|
+
connectionLog('calling cachified to check connection');
|
|
15
49
|
const isOnline = await cachified({
|
|
16
50
|
cache: connectionCache,
|
|
17
51
|
request,
|
|
@@ -19,10 +53,9 @@ export async function checkConnection({ request, timings, } = {}) {
|
|
|
19
53
|
key: 'connected',
|
|
20
54
|
ttl: 1000 * 10,
|
|
21
55
|
async getFreshValue(context) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (response.ok) {
|
|
56
|
+
connectionLog('getting fresh connection value');
|
|
57
|
+
const isOnline = await raceConnectivity();
|
|
58
|
+
if (isOnline) {
|
|
26
59
|
context.metadata.ttl = 1000 * 60;
|
|
27
60
|
context.metadata.swr = 1000 * 60 * 30;
|
|
28
61
|
return true;
|
|
@@ -32,6 +65,7 @@ export async function checkConnection({ request, timings, } = {}) {
|
|
|
32
65
|
}
|
|
33
66
|
},
|
|
34
67
|
});
|
|
68
|
+
connectionLog(`connection check says we are ${isOnline ? 'online' : 'offline'}`);
|
|
35
69
|
return isOnline;
|
|
36
70
|
}
|
|
37
71
|
//# sourceMappingURL=utils.server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.server.js","sourceRoot":"","sources":["../../src/utils.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,QAAQ,MAAM,OAAO,CAAA;AAC5B,OAAO,kBAAkB,MAAM,8BAA8B,CAAA;AAC7D,OAAO,cAAc,MAAM,0BAA0B,CAAA;AACrD,OAAO,SAAS,MAAM,qBAAqB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.server.js","sourceRoot":"","sources":["../../src/utils.server.ts"],"names":[],"mappings":"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","sourcesContent":["import './init-env.js'\n\nimport { remember } from '@epic-web/remember'\nimport dayjsLib from 'dayjs'\nimport relativeTimePlugin from 'dayjs/plugin/relativeTime.js'\nimport timeZonePlugin from 'dayjs/plugin/timezone.js'\nimport utcPlugin from 'dayjs/plugin/utc.js'\nimport { cachified, connectionCache } from './cache.server.js'\nimport { logger } from './logger.js'\nimport { type Timings } from './timing.server.js'\n\nexport const dayjs = remember('dayjs', () => {\n\tdayjsLib.extend(utcPlugin)\n\tdayjsLib.extend(timeZonePlugin)\n\tdayjsLib.extend(relativeTimePlugin)\n\treturn dayjsLib\n})\n\nconst connectionLog = logger('epic:connection')\n\nasync function probeOnce(\n\turl: string,\n\texpectedOk: (status: number) => boolean = (s) => s >= 200 && s < 300,\n\tinit?: RequestInit,\n): Promise<boolean> {\n\tconst signal = AbortSignal.timeout(10_000)\n\tconst start = Date.now()\n\tconnectionLog(`probing ${url}`)\n\tconst res = await fetch(url, {\n\t\tmethod: 'HEAD',\n\t\tcache: 'no-store',\n\t\tsignal,\n\t\t...init,\n\t})\n\tconnectionLog(\n\t\t`probe to ${url} returned ${res.status} in ${Date.now() - start}ms`,\n\t)\n\treturn expectedOk(res.status)\n}\n\nasync function raceConnectivity(): Promise<boolean> {\n\t// we have multiple just in case some VPN blocks one or another\n\tconst candidates: Array<Promise<boolean>> = [\n\t\tprobeOnce('https://connected.kentcdodds.workers.dev'),\n\n\t\t// Non-CF options (different providers / networks):\n\t\tprobeOnce('https://www.gstatic.com/generate_204', (s) => s === 204),\n\t\tprobeOnce(\n\t\t\t'http://www.msftconnecttest.com/connecttest.txt',\n\t\t\t(s) => s === 200,\n\t\t),\n\t]\n\n\t// Use Promise.any to succeed on first truthy result; treat rejections/non-2xx as false\n\tconst wrapped = candidates.map((p) =>\n\t\tp.then((ok) => (ok ? true : Promise.reject(new Error('not ok')))),\n\t)\n\n\ttry {\n\t\tawait Promise.any(wrapped)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport async function checkConnection({\n\trequest,\n\ttimings,\n}: {\n\trequest?: Request\n\ttimings?: Timings\n} = {}) {\n\tconnectionLog('calling cachified to check connection')\n\tconst isOnline = await cachified({\n\t\tcache: connectionCache,\n\t\trequest,\n\t\ttimings,\n\t\tkey: 'connected',\n\t\tttl: 1000 * 10,\n\t\tasync getFreshValue(context) {\n\t\t\tconnectionLog('getting fresh connection value')\n\t\t\tconst isOnline = await raceConnectivity()\n\t\t\tif (isOnline) {\n\t\t\t\tcontext.metadata.ttl = 1000 * 60\n\t\t\t\tcontext.metadata.swr = 1000 * 60 * 30\n\t\t\t\treturn true\n\t\t\t} else {\n\t\t\t\treturn false\n\t\t\t}\n\t\t},\n\t})\n\tconnectionLog(\n\t\t`connection check says we are ${isOnline ? 'online' : 'offline'}`,\n\t)\n\treturn isOnline\n}\n"]}
|