@epic-web/workshop-utils 6.23.11 → 6.23.13
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 +10 -0
- package/dist/esm/cache.server.d.ts.map +1 -1
- package/dist/esm/cache.server.js +107 -2
- package/dist/esm/cache.server.js.map +1 -1
- package/dist/esm/config.server.d.ts.map +1 -1
- package/dist/esm/config.server.js +1 -4
- package/dist/esm/config.server.js.map +1 -1
- package/dist/esm/epic-cache-reporter.test.d.ts +2 -0
- package/dist/esm/epic-cache-reporter.test.d.ts.map +1 -0
- package/dist/esm/epic-cache-reporter.test.js +111 -0
- package/dist/esm/epic-cache-reporter.test.js.map +1 -0
- package/dist/esm/logger.js.map +1 -1
- package/dist/esm/process-manager.server.d.ts.map +1 -1
- package/dist/esm/process-manager.server.js +1 -1
- package/dist/esm/process-manager.server.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import * as C from '@epic-web/cachified';
|
|
2
|
+
import { type CreateReporter } from '@epic-web/cachified';
|
|
2
3
|
import { type Timings } from './timing.server.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a cachified reporter that integrates with the Epic Workshop logger system.
|
|
6
|
+
* Uses the pattern `epic:cache:{name-of-cache}` for logger namespaces.
|
|
7
|
+
* Only logs when the specific cache namespace is enabled via NODE_DEBUG.
|
|
8
|
+
*/
|
|
9
|
+
export declare function epicCacheReporter<Value>({ formatDuration, performance, }?: {
|
|
10
|
+
formatDuration?: (ms: number) => string;
|
|
11
|
+
performance?: Pick<typeof Date, 'now'>;
|
|
12
|
+
}): CreateReporter<Value>;
|
|
3
13
|
export declare const solutionAppCache: C.Cache<{
|
|
4
14
|
test: {
|
|
5
15
|
type: "browser";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,CAAC,MAAM,qBAAqB,CAAA;
|
|
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
|
@@ -2,15 +2,120 @@
|
|
|
2
2
|
import { getEnv } from './init-env.js';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import * as C from '@epic-web/cachified';
|
|
5
|
-
import { verboseReporter } from '@epic-web/cachified';
|
|
6
5
|
import { remember } from '@epic-web/remember';
|
|
7
6
|
import fsExtra from 'fs-extra';
|
|
8
7
|
import { LRUCache } from 'lru-cache';
|
|
9
8
|
import md5 from 'md5-hex';
|
|
10
9
|
import { resolveCacheDir } from './data-storage.server.js';
|
|
10
|
+
import { logger, isLoggingEnabled } from './logger.js';
|
|
11
11
|
import { cachifiedTimingReporter } from './timing.server.js';
|
|
12
12
|
import { checkConnectionCached } from './utils.server.js';
|
|
13
13
|
const cacheDir = resolveCacheDir();
|
|
14
|
+
// Format cache time helper function (copied from @epic-web/cachified for consistency)
|
|
15
|
+
function formatCacheTime(metadata, formatDuration) {
|
|
16
|
+
const ttl = metadata?.ttl;
|
|
17
|
+
if (ttl === undefined || ttl === Infinity)
|
|
18
|
+
return 'forever';
|
|
19
|
+
return formatDuration(ttl);
|
|
20
|
+
}
|
|
21
|
+
// Default duration formatter (copied from @epic-web/cachified for consistency)
|
|
22
|
+
function defaultFormatDuration(ms) {
|
|
23
|
+
if (ms < 1000)
|
|
24
|
+
return `${Math.round(ms)}ms`;
|
|
25
|
+
if (ms < 60000)
|
|
26
|
+
return `${Math.round(ms / 1000)}s`;
|
|
27
|
+
if (ms < 3600000)
|
|
28
|
+
return `${Math.round(ms / 60000)}m`;
|
|
29
|
+
return `${Math.round(ms / 3600000)}h`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a cachified reporter that integrates with the Epic Workshop logger system.
|
|
33
|
+
* Uses the pattern `epic:cache:{name-of-cache}` for logger namespaces.
|
|
34
|
+
* Only logs when the specific cache namespace is enabled via NODE_DEBUG.
|
|
35
|
+
*/
|
|
36
|
+
export function epicCacheReporter({ formatDuration = defaultFormatDuration, performance = globalThis.performance || Date, } = {}) {
|
|
37
|
+
return ({ key, fallbackToCache, forceFresh, metadata, cache }) => {
|
|
38
|
+
// Determine cache name for logger namespace
|
|
39
|
+
const cacheName = cache.name || cache.toString().replace(/^\[object (.*?)]$/, '$1');
|
|
40
|
+
// Create logger with epic:cache:{name} pattern
|
|
41
|
+
// Extract a reasonable cache name from longer descriptions
|
|
42
|
+
let loggerSuffix = 'unknown';
|
|
43
|
+
if (cacheName.includes('(') && cacheName.includes(')')) {
|
|
44
|
+
// Extract name from "Filesystem cache (CacheName)" format
|
|
45
|
+
const match = cacheName.match(/\(([^)]+)\)/);
|
|
46
|
+
loggerSuffix = (match?.[1] ?? 'unknown').toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
else if (cacheName === 'LRUCache') {
|
|
49
|
+
// For LRU caches, we can't determine the name from the cache object alone
|
|
50
|
+
loggerSuffix = 'lru';
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
loggerSuffix = cacheName.toLowerCase();
|
|
54
|
+
}
|
|
55
|
+
const namespace = `epic:cache:${loggerSuffix}`;
|
|
56
|
+
// Only create logger if cache logging is enabled for this namespace
|
|
57
|
+
if (!isLoggingEnabled(namespace)) {
|
|
58
|
+
// Return a no-op reporter if logging is not enabled
|
|
59
|
+
return () => { };
|
|
60
|
+
}
|
|
61
|
+
const log = logger(namespace);
|
|
62
|
+
let cached;
|
|
63
|
+
let freshValue;
|
|
64
|
+
let getFreshValueStartTs;
|
|
65
|
+
let refreshValueStartTS;
|
|
66
|
+
return (event) => {
|
|
67
|
+
switch (event.name) {
|
|
68
|
+
case 'getCachedValueRead':
|
|
69
|
+
cached = event.entry;
|
|
70
|
+
break;
|
|
71
|
+
case 'checkCachedValueError':
|
|
72
|
+
log.warn(`check failed for cached value of ${key}\nReason: ${event.reason}.\nDeleting the cache key and trying to get a fresh value.`, cached);
|
|
73
|
+
break;
|
|
74
|
+
case 'getCachedValueError':
|
|
75
|
+
log.error(`error with cache at ${key}. Deleting the cache key and trying to get a fresh value.`, event.error);
|
|
76
|
+
break;
|
|
77
|
+
case 'getFreshValueError':
|
|
78
|
+
log.error(`getting a fresh value for ${key} failed`, { fallbackToCache, forceFresh }, event.error);
|
|
79
|
+
break;
|
|
80
|
+
case 'getFreshValueStart':
|
|
81
|
+
getFreshValueStartTs = performance.now();
|
|
82
|
+
break;
|
|
83
|
+
case 'writeFreshValueSuccess': {
|
|
84
|
+
const totalTime = performance.now() - getFreshValueStartTs;
|
|
85
|
+
if (event.written) {
|
|
86
|
+
log(`Updated the cache value for ${key}.`, `Getting a fresh value for this took ${formatDuration(totalTime)}.`, `Caching for ${formatCacheTime(metadata, formatDuration)} in ${cacheName}.`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
log(`Not updating the cache value for ${key}.`, `Getting a fresh value for this took ${formatDuration(totalTime)}.`, `Thereby exceeding caching time of ${formatCacheTime(metadata, formatDuration)}`);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 'writeFreshValueError':
|
|
94
|
+
log.error(`error setting cache: ${key}`, event.error);
|
|
95
|
+
break;
|
|
96
|
+
case 'getFreshValueSuccess':
|
|
97
|
+
freshValue = event.value;
|
|
98
|
+
break;
|
|
99
|
+
case 'checkFreshValueError':
|
|
100
|
+
log.error(`check failed for fresh value of ${key}\nReason: ${event.reason}.`, freshValue);
|
|
101
|
+
break;
|
|
102
|
+
case 'refreshValueStart':
|
|
103
|
+
refreshValueStartTS = performance.now();
|
|
104
|
+
break;
|
|
105
|
+
case 'refreshValueSuccess':
|
|
106
|
+
log(`Background refresh for ${key} successful.`, `Getting a fresh value for this took ${formatDuration(performance.now() - refreshValueStartTS)}.`, `Caching for ${formatCacheTime(metadata, formatDuration)} in ${cacheName}.`);
|
|
107
|
+
break;
|
|
108
|
+
case 'refreshValueError':
|
|
109
|
+
log.error(`Background refresh for ${key} failed.`, event.error);
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
// Defensive programming: log unknown events for debugging
|
|
113
|
+
log(`Unknown cache event: ${event.name}`);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}
|
|
14
119
|
export const solutionAppCache = makeSingletonFsCache('SolutionAppCache');
|
|
15
120
|
export const problemAppCache = makeSingletonFsCache('ProblemAppCache');
|
|
16
121
|
export const exampleAppCache = makeSingletonFsCache('ExampleAppCache');
|
|
@@ -224,7 +329,7 @@ export async function cachified({ request, timings, key, timingKey = key.length
|
|
|
224
329
|
...options,
|
|
225
330
|
key,
|
|
226
331
|
forceFresh,
|
|
227
|
-
}, C.mergeReporters(cachifiedTimingReporter(timings, timingKey),
|
|
332
|
+
}, C.mergeReporters(cachifiedTimingReporter(timings, timingKey), epicCacheReporter()));
|
|
228
333
|
}
|
|
229
334
|
export async function shouldForceFresh({ forceFresh, request, key, }) {
|
|
230
335
|
if (typeof forceFresh === 'boolean')
|
|
@@ -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 { verboseReporter, type CacheEntry } 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 {\n\ttype App,\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 { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnectionCached } from './utils.server.js'\n\nconst cacheDir = resolveCacheDir()\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 appsCache = makeSingletonFsCache<App>('AppsCache')\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 =\n\tmakeSingletonFsCache<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 fsCache = makeSingletonFsCache('FsCache')\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.map(async (file) => {\n\t\t\tconst filePath = path.join(dir, file)\n\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst subEntries = await readJsonFilesInDirectory(filePath)\n\t\t\t\treturn [file, subEntries]\n\t\t\t} else {\n\t\t\t\tconst maxRetries = 2\n\t\t\t\tconst baseDelay = 25 // shorter delay for directory listing\n\n\t\t\t\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\t\treturn [file, data]\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t// Handle JSON parsing errors (could be race condition or corruption)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof SyntaxError &&\n\t\t\t\t\t\t\terror.message.includes('JSON')\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\t\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for directory listing ${filePath}, retrying in ${delay}ms...`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Final attempt failed, skip the file\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t`Skipping corrupted JSON file in directory listing after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn [file, null]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// This should never be reached, but just in case\n\t\t\t\treturn [file, null]\n\t\t\t}\n\t\t}),\n\t)\n\treturn Object.fromEntries(entries)\n}\n\nexport async function getAllFileCacheEntries() {\n\treturn readJsonFilesInDirectory(cacheDir)\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 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\t\t\t\tconst maxRetries = 3\n\t\t\t\tconst baseDelay = 10\n\n\t\t\t\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\t\tif (data.entry) return data.entry\n\t\t\t\t\t\treturn null\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Handle JSON parsing errors (could be race condition or corruption)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof SyntaxError &&\n\t\t\t\t\t\t\terror.message.includes('JSON')\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\t\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt) // exponential backoff\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\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\t\t\t)\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Final attempt failed, treat as corrupted file\n\t\t\t\t\t\t\t// Log to Sentry if available\n\t\t\t\t\t\t\tif (getEnv().SENTRY_DSN && getEnv().EPICSHOP_IS_PUBLISHED) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\t\t\t\t\tSentry.captureException(error, {\n\t\t\t\t\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\t\t\t\t\terror_type: 'corrupted_cache_file',\n\t\t\t\t\t\t\t\t\t\t\tcache_name: name,\n\t\t\t\t\t\t\t\t\t\t\tcache_key: key,\n\t\t\t\t\t\t\t\t\t\t\tretry_attempts: attempt.toString(),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\textra: {\n\t\t\t\t\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\t\t\t\t\terrorMessage: error.message,\n\t\t\t\t\t\t\t\t\t\t\tcacheName: name,\n\t\t\t\t\t\t\t\t\t\t\tcacheKey: key,\n\t\t\t\t\t\t\t\t\t\t\tretryAttempts: attempt,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t} catch (sentryError) {\n\t\t\t\t\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Delete the corrupted file\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`Deleted corrupted cache file after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} catch (deleteError) {\n\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t`Failed to delete corrupted cache file ${filePath}:`,\n\t\t\t\t\t\t\t\t\tdeleteError,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// For other errors, don't retry\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\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 checkConnectionCached({ 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\tprocess.env.EPICSHOP_DEBUG_CACHE ? verboseReporter() : undefined,\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 {\n\tverboseReporter,\n\ttype CacheEntry,\n\ttype CreateReporter,\n} 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 {\n\ttype App,\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, isLoggingEnabled } from './logger.js'\nimport { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnectionCached } from './utils.server.js'\n\nconst cacheDir = resolveCacheDir()\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 namespace = `epic:cache:${loggerSuffix}`\n\n\t\t// Only create logger if cache logging is enabled for this namespace\n\t\tif (!isLoggingEnabled(namespace)) {\n\t\t\t// Return a no-op reporter if logging is not enabled\n\t\t\treturn () => {}\n\t\t}\n\n\t\tconst log = logger(namespace)\n\n\t\tlet cached: unknown\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 'getCachedValueRead':\n\t\t\t\t\tcached = event.entry\n\t\t\t\t\tbreak\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\tcached,\n\t\t\t\t\t)\n\t\t\t\t\tbreak\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\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\tcase 'getFreshValueStart':\n\t\t\t\t\tgetFreshValueStartTs = performance.now()\n\t\t\t\t\tbreak\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\tlog(\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\tlog(\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\tcase 'getFreshValueSuccess':\n\t\t\t\t\tfreshValue = event.value\n\t\t\t\t\tbreak\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\tcase 'refreshValueStart':\n\t\t\t\t\trefreshValueStartTS = performance.now()\n\t\t\t\t\tbreak\n\t\t\t\tcase 'refreshValueSuccess':\n\t\t\t\t\tlog(\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\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\tdefault:\n\t\t\t\t\t// Defensive programming: log unknown events for debugging\n\t\t\t\t\tlog(`Unknown cache event: ${event.name}`)\n\t\t\t\t\tbreak\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 appsCache = makeSingletonFsCache<App>('AppsCache')\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 =\n\tmakeSingletonFsCache<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 fsCache = makeSingletonFsCache('FsCache')\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.map(async (file) => {\n\t\t\tconst filePath = path.join(dir, file)\n\t\t\tconst stats = await fsExtra.stat(filePath)\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tconst subEntries = await readJsonFilesInDirectory(filePath)\n\t\t\t\treturn [file, subEntries]\n\t\t\t} else {\n\t\t\t\tconst maxRetries = 2\n\t\t\t\tconst baseDelay = 25 // shorter delay for directory listing\n\n\t\t\t\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\t\treturn [file, data]\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\t// Handle JSON parsing errors (could be race condition or corruption)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof SyntaxError &&\n\t\t\t\t\t\t\terror.message.includes('JSON')\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\t\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for directory listing ${filePath}, retrying in ${delay}ms...`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Final attempt failed, skip the file\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t`Skipping corrupted JSON file in directory listing after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn [file, null]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// This should never be reached, but just in case\n\t\t\t\treturn [file, null]\n\t\t\t}\n\t\t}),\n\t)\n\treturn Object.fromEntries(entries)\n}\n\nexport async function getAllFileCacheEntries() {\n\treturn readJsonFilesInDirectory(cacheDir)\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 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\t\t\t\tconst maxRetries = 3\n\t\t\t\tconst baseDelay = 10\n\n\t\t\t\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\t\tif (data.entry) return data.entry\n\t\t\t\t\t\treturn null\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Handle JSON parsing errors (could be race condition or corruption)\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\terror instanceof SyntaxError &&\n\t\t\t\t\t\t\terror.message.includes('JSON')\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\t\t\t\t\tif (attempt < maxRetries) {\n\t\t\t\t\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt) // exponential backoff\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\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\t\t\t)\n\t\t\t\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Final attempt failed, treat as corrupted file\n\t\t\t\t\t\t\t// Log to Sentry if available\n\t\t\t\t\t\t\tif (getEnv().SENTRY_DSN && getEnv().EPICSHOP_IS_PUBLISHED) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\t\t\t\t\tSentry.captureException(error, {\n\t\t\t\t\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\t\t\t\t\terror_type: 'corrupted_cache_file',\n\t\t\t\t\t\t\t\t\t\t\tcache_name: name,\n\t\t\t\t\t\t\t\t\t\t\tcache_key: key,\n\t\t\t\t\t\t\t\t\t\t\tretry_attempts: attempt.toString(),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\textra: {\n\t\t\t\t\t\t\t\t\t\t\tfilePath,\n\t\t\t\t\t\t\t\t\t\t\terrorMessage: error.message,\n\t\t\t\t\t\t\t\t\t\t\tcacheName: name,\n\t\t\t\t\t\t\t\t\t\t\tcacheKey: key,\n\t\t\t\t\t\t\t\t\t\t\tretryAttempts: attempt,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t} catch (sentryError) {\n\t\t\t\t\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Delete the corrupted file\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait fsExtra.remove(filePath)\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`Deleted corrupted cache file after ${attempt + 1} attempts: ${filePath}`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t} catch (deleteError) {\n\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t`Failed to delete corrupted cache file ${filePath}:`,\n\t\t\t\t\t\t\t\t\tdeleteError,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// For other errors, don't retry\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\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 checkConnectionCached({ 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;EAcjC,CAAA;AAUF,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"config.server.d.ts","sourceRoot":"","sources":["../../src/config.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;EAcjC,CAAA;AAUF,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmFvB,CAAA;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAiCjE;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,UAE1C;AAED,wBAAgB,iBAAiB,IAAI,cAAc,CAqClD;AAED,wBAAsB,gBAAgB,CAAC,EACtC,QAAQ,EACR,KAAK,EACL,IAAI,GACJ,EAAE;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACZ,0BA0EA;AAED,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;GA0ElD"}
|
|
@@ -88,10 +88,7 @@ const WorkshopConfigSchema = z
|
|
|
88
88
|
}))
|
|
89
89
|
.optional()
|
|
90
90
|
.default([]),
|
|
91
|
-
sidecarProcesses: z
|
|
92
|
-
.record(z.string(), z.string())
|
|
93
|
-
.optional()
|
|
94
|
-
.default({}),
|
|
91
|
+
sidecarProcesses: z.record(z.string(), z.string()).optional().default({}),
|
|
95
92
|
})
|
|
96
93
|
.transform((data) => {
|
|
97
94
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.server.js","sourceRoot":"","sources":["../../src/config.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 fs from 'node:fs'\nimport path from 'node:path'\nimport { z } from 'zod'\nimport { handleGitHubRepoAndRoot } from './utils.js'\n\nconst getRootPkgJsonPath = () =>\n\tpath.join(getEnv().EPICSHOP_CONTEXT_CWD, 'package.json')\n\nexport const StackBlitzConfigSchema = z.object({\n\t// we default this to `${exerciseTitle} (${type})`\n\ttitle: z.string().optional(),\n\t// stackblitz defaults this to dev automatically\n\tstartScript: z.string().optional(),\n\t// if no value is provided, then stackblitz defaults this to whatever\n\t// looks best based on the width of the screen\n\tview: z\n\t\t.union([z.literal('editor'), z.literal('preview'), z.literal('both')])\n\t\t.optional(),\n\tfile: z.string().optional(),\n\thidedevtools: z.string().optional(),\n\tterminalHeight: z.string().optional(),\n\thideNavigation: z.string().optional(),\n})\n\nconst InstructorSchema = z.object({\n\tname: z.string().optional(),\n\tavatar: z.string().optional(),\n\t𝕏: z.string().optional(),\n\txHandle: z.string().optional(),\n})\n\n// most defaults are for backwards compatibility\nconst WorkshopConfigSchema = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tsubtitle: z.string().optional(),\n\t\tinstructor: InstructorSchema.optional(),\n\t\tepicWorkshopHost: z.string().optional(),\n\t\tepicWorkshopSlug: z.string().optional(),\n\t\tsubdomain: z.string().optional(),\n\t\tproduct: z\n\t\t\t.object({\n\t\t\t\thost: z.string().default('www.epicweb.dev'),\n\t\t\t\tdisplayName: z.string().default('EpicWeb.dev'),\n\t\t\t\tdisplayNameShort: z.string().default('Epic Web'),\n\t\t\t\tlogo: z.string().default('/logo.svg'),\n\t\t\t\tslug: z.string().optional(),\n\t\t\t\tdiscordChannelId: z.string().default('1161045224907341972'),\n\t\t\t\tdiscordTags: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tonboardingVideo: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app',\n\t\t\t),\n\t\tgithubRepo: z\n\t\t\t.string()\n\t\t\t.transform((githubRepo) => githubRepo ?? getEnv().EPICSHOP_GITHUB_REPO),\n\t\tgithubRoot: z\n\t\t\t.string()\n\t\t\t.transform((githubRoot) => githubRoot ?? getEnv().EPICSHOP_GITHUB_ROOT),\n\t\tstackBlitzConfig: StackBlitzConfigSchema.optional(),\n\t\tforms: z\n\t\t\t.object({\n\t\t\t\tworkshop: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}',\n\t\t\t\t\t),\n\t\t\t\texercise: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}',\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.default({}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean().default(true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\tpostupdate: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\tinitialRoute: z.string().optional().default('/'),\n\t\tnotifications: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\ttitle: z.string(),\n\t\t\t\t\tmessage: z.string(),\n\t\t\t\t\tlink: z.string().optional(),\n\t\t\t\t\ttype: z.enum(['info', 'warning', 'danger']),\n\t\t\t\t\texpiresAt: z.date().nullable(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.optional()\n\t\t\t.default([]),\n\t\tsidecarProcesses: z\n\t\t\t.record(z.string(), z.string())\n\t\t\t.optional()\n\t\t\t.default({}),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\tproduct: {\n\t\t\t\t...data.product,\n\t\t\t\tdisplayNameShort:\n\t\t\t\t\tdata.product.displayNameShort ?? data.product.displayName,\n\t\t\t\t// for backwards compatibility\n\t\t\t\thost: data.product.host ?? data.epicWorkshopHost,\n\t\t\t\tslug: data.product.slug ?? data.epicWorkshopSlug,\n\t\t\t},\n\t\t}\n\t})\n\nexport type WorkshopConfig = z.infer<typeof WorkshopConfigSchema>\n\nconst configCache: {\n\tconfig: WorkshopConfig | null\n\tmodified: number\n} = {\n\tconfig: null,\n\tmodified: 0,\n}\n\n// Utility to read and parse the root package.json\nfunction readRootPkgJson(): any {\n\tconst packageJsonPath = getRootPkgJsonPath()\n\ttry {\n\t\tconst packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8')\n\t\treturn JSON.parse(packageJsonContent)\n\t} catch (error) {\n\t\tconsole.error(`Error reading or parsing package.json:`, error)\n\t\tif (error instanceof Error && error.message.includes('ENOENT')) {\n\t\t\tthrow new Error(\n\t\t\t\t`package.json not found at ${packageJsonPath}. Please ensure you're running the command from the correct directory.`,\n\t\t\t)\n\t\t} else if (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid JSON in package.json at ${packageJsonPath}. Please check the file for syntax errors.`,\n\t\t\t)\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Could not find and parse package.json at ${packageJsonPath}`,\n\t\t)\n\t}\n}\n\n/**\n * This used to support subdomains on localhost, but that caused too many issues.\n */\nexport function getWorkshopUrl(port: number) {\n\treturn `http://localhost:${port}`\n}\n\nexport function getWorkshopConfig(): WorkshopConfig {\n\tif (\n\t\tconfigCache.config &&\n\t\tconfigCache.modified > fs.statSync(getRootPkgJsonPath()).mtimeMs\n\t) {\n\t\treturn configCache.config\n\t}\n\n\tconst packageJson = readRootPkgJson()\n\n\tconst epicshopConfig = packageJson.epicshop || {}\n\n\t// Set githubRepo and githubRoot before parsing\n\tconst { githubRepo, githubRoot } = handleGitHubRepoAndRoot({\n\t\tgithubRepo: epicshopConfig.githubRepo,\n\t\tgithubRoot: epicshopConfig.githubRoot,\n\t})\n\tepicshopConfig.githubRepo = githubRepo\n\tepicshopConfig.githubRoot = githubRoot\n\n\ttry {\n\t\tconst parsedConfig = WorkshopConfigSchema.parse(epicshopConfig)\n\t\tconfigCache.config = parsedConfig\n\t\tconfigCache.modified = fs.statSync(getRootPkgJsonPath()).mtimeMs\n\t\treturn parsedConfig\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid epicshop configuration in ${getRootPkgJsonPath()}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nexport async function getStackBlitzUrl({\n\tfullPath,\n\ttitle,\n\ttype,\n}: {\n\tfullPath: string\n\ttitle: string\n\ttype: string\n}) {\n\tconst workshopConfig = getWorkshopConfig()\n\tconst appConfig = await getAppConfig(fullPath)\n\n\tif (appConfig.stackBlitzConfig === null) return null\n\n\tlet githubRootUrlString = workshopConfig.githubRoot\n\n\tconst githubRootUrl = new URL(\n\t\tgithubRootUrlString.replace(/\\/blob\\//, '/tree/'),\n\t)\n\n\tconst githubPart = githubRootUrl.pathname\n\n\t// Check if package.json exists to determine if this is a simple exercise\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tlet stackBlitzConfig = {\n\t\t...appConfig.stackBlitzConfig,\n\t\ttitle: appConfig.stackBlitzConfig?.title ?? `${title} (${type})`,\n\t}\n\n\t// For simple exercises without package.json, configure StackBlitz to show only editor\n\tif (!packageJsonExists) {\n\t\t// Find the first existing file from the priority list\n\t\tconst priorityFiles = [\n\t\t\t'index.html',\n\t\t\t'index.tsx',\n\t\t\t'index.ts',\n\t\t\t'index.jsx',\n\t\t\t'index.js',\n\t\t\t'README.mdx',\n\t\t\t'README.md',\n\t\t]\n\n\t\tlet defaultFile: string | null = null\n\t\tfor (const fileName of priorityFiles) {\n\t\t\tconst filePath = path.join(fullPath, fileName)\n\t\t\ttry {\n\t\t\t\tawait fs.promises.access(filePath, fs.constants.F_OK)\n\t\t\t\tdefaultFile = fileName\n\t\t\t\tbreak\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tstackBlitzConfig = {\n\t\t\t...stackBlitzConfig,\n\t\t\tview: 'editor', // Show only editor, no preview or terminal\n\t\t\thidedevtools: '1', // Hide the console/devtools\n\t\t\tterminalHeight: '0', // Hide the terminal completely\n\t\t\thideNavigation: '1', // Hide the preview's URL bar\n\t\t\t...(defaultFile && { file: defaultFile }), // Set default file if found\n\t\t}\n\t}\n\n\tconst params = new URLSearchParams(stackBlitzConfig as Record<string, string>)\n\n\tconst relativePath = fullPath.replace(\n\t\t`${getEnv().EPICSHOP_CONTEXT_CWD}${path.sep}`,\n\t\t'',\n\t)\n\n\tconst stackBlitzUrl = new URL(\n\t\t`/github${githubPart}/${relativePath}?${params}`,\n\t\t'https://stackblitz.com',\n\t)\n\n\treturn stackBlitzUrl.toString()\n}\n\nexport async function getAppConfig(fullPath: string) {\n\tconst workshopConfig = getWorkshopConfig()\n\n\tlet epicshopConfig: Record<string, any> = {}\n\tlet scripts: Record<string, string> = {}\n\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tif (packageJsonExists) {\n\t\tconst pkg = JSON.parse(\n\t\t\tawait fs.promises.readFile(path.join(fullPath, 'package.json'), 'utf8'),\n\t\t) as any\n\t\tepicshopConfig = pkg.epicshop ?? {}\n\t\tscripts = pkg.scripts ?? {}\n\t}\n\n\tconst AppConfigSchema = z.object({\n\t\tstackBlitzConfig: StackBlitzConfigSchema.nullable()\n\t\t\t.optional()\n\t\t\t.transform((appStackBlitzConfig) => {\n\t\t\t\tif (appStackBlitzConfig === null) return null\n\n\t\t\t\treturn {\n\t\t\t\t\t...workshopConfig.stackBlitzConfig,\n\t\t\t\t\t...appStackBlitzConfig,\n\t\t\t\t}\n\t\t\t}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z\n\t\t\t\t\t.boolean()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default(workshopConfig.testTab?.enabled ?? true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\ttest: z.string().optional(),\n\t\t\t\tdev: z.string().optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tinitialRoute: z.string().optional().default(workshopConfig.initialRoute),\n\t})\n\n\tconst appConfig = {\n\t\tstackBlitzConfig: epicshopConfig.stackBlitzConfig,\n\t\ttestTab: {\n\t\t\tenabled: epicshopConfig.testTab?.enabled,\n\t\t},\n\t\tscripts: {\n\t\t\ttest: scripts.test,\n\t\t\tdev: scripts.dev,\n\t\t},\n\t\tinitialRoute: epicshopConfig.initialRoute,\n\t}\n\n\ttry {\n\t\treturn AppConfigSchema.parse(appConfig)\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid app configuration for ${fullPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config.server.js","sourceRoot":"","sources":["../../src/config.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 fs from 'node:fs'\nimport path from 'node:path'\nimport { z } from 'zod'\nimport { handleGitHubRepoAndRoot } from './utils.js'\n\nconst getRootPkgJsonPath = () =>\n\tpath.join(getEnv().EPICSHOP_CONTEXT_CWD, 'package.json')\n\nexport const StackBlitzConfigSchema = z.object({\n\t// we default this to `${exerciseTitle} (${type})`\n\ttitle: z.string().optional(),\n\t// stackblitz defaults this to dev automatically\n\tstartScript: z.string().optional(),\n\t// if no value is provided, then stackblitz defaults this to whatever\n\t// looks best based on the width of the screen\n\tview: z\n\t\t.union([z.literal('editor'), z.literal('preview'), z.literal('both')])\n\t\t.optional(),\n\tfile: z.string().optional(),\n\thidedevtools: z.string().optional(),\n\tterminalHeight: z.string().optional(),\n\thideNavigation: z.string().optional(),\n})\n\nconst InstructorSchema = z.object({\n\tname: z.string().optional(),\n\tavatar: z.string().optional(),\n\t𝕏: z.string().optional(),\n\txHandle: z.string().optional(),\n})\n\n// most defaults are for backwards compatibility\nconst WorkshopConfigSchema = z\n\t.object({\n\t\ttitle: z.string(),\n\t\tsubtitle: z.string().optional(),\n\t\tinstructor: InstructorSchema.optional(),\n\t\tepicWorkshopHost: z.string().optional(),\n\t\tepicWorkshopSlug: z.string().optional(),\n\t\tsubdomain: z.string().optional(),\n\t\tproduct: z\n\t\t\t.object({\n\t\t\t\thost: z.string().default('www.epicweb.dev'),\n\t\t\t\tdisplayName: z.string().default('EpicWeb.dev'),\n\t\t\t\tdisplayNameShort: z.string().default('Epic Web'),\n\t\t\t\tlogo: z.string().default('/logo.svg'),\n\t\t\t\tslug: z.string().optional(),\n\t\t\t\tdiscordChannelId: z.string().default('1161045224907341972'),\n\t\t\t\tdiscordTags: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tonboardingVideo: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app',\n\t\t\t),\n\t\tgithubRepo: z\n\t\t\t.string()\n\t\t\t.transform((githubRepo) => githubRepo ?? getEnv().EPICSHOP_GITHUB_REPO),\n\t\tgithubRoot: z\n\t\t\t.string()\n\t\t\t.transform((githubRoot) => githubRoot ?? getEnv().EPICSHOP_GITHUB_ROOT),\n\t\tstackBlitzConfig: StackBlitzConfigSchema.optional(),\n\t\tforms: z\n\t\t\t.object({\n\t\t\t\tworkshop: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}',\n\t\t\t\t\t),\n\t\t\t\texercise: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.default(\n\t\t\t\t\t\t'https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}',\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.default({}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z.boolean().default(true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\tpostupdate: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t\tinitialRoute: z.string().optional().default('/'),\n\t\tnotifications: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tid: z.string(),\n\t\t\t\t\ttitle: z.string(),\n\t\t\t\t\tmessage: z.string(),\n\t\t\t\t\tlink: z.string().optional(),\n\t\t\t\t\ttype: z.enum(['info', 'warning', 'danger']),\n\t\t\t\t\texpiresAt: z.date().nullable(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.optional()\n\t\t\t.default([]),\n\t\tsidecarProcesses: z.record(z.string(), z.string()).optional().default({}),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\tproduct: {\n\t\t\t\t...data.product,\n\t\t\t\tdisplayNameShort:\n\t\t\t\t\tdata.product.displayNameShort ?? data.product.displayName,\n\t\t\t\t// for backwards compatibility\n\t\t\t\thost: data.product.host ?? data.epicWorkshopHost,\n\t\t\t\tslug: data.product.slug ?? data.epicWorkshopSlug,\n\t\t\t},\n\t\t}\n\t})\n\nexport type WorkshopConfig = z.infer<typeof WorkshopConfigSchema>\n\nconst configCache: {\n\tconfig: WorkshopConfig | null\n\tmodified: number\n} = {\n\tconfig: null,\n\tmodified: 0,\n}\n\n// Utility to read and parse the root package.json\nfunction readRootPkgJson(): any {\n\tconst packageJsonPath = getRootPkgJsonPath()\n\ttry {\n\t\tconst packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8')\n\t\treturn JSON.parse(packageJsonContent)\n\t} catch (error) {\n\t\tconsole.error(`Error reading or parsing package.json:`, error)\n\t\tif (error instanceof Error && error.message.includes('ENOENT')) {\n\t\t\tthrow new Error(\n\t\t\t\t`package.json not found at ${packageJsonPath}. Please ensure you're running the command from the correct directory.`,\n\t\t\t)\n\t\t} else if (error instanceof SyntaxError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid JSON in package.json at ${packageJsonPath}. Please check the file for syntax errors.`,\n\t\t\t)\n\t\t}\n\t\tthrow new Error(\n\t\t\t`Could not find and parse package.json at ${packageJsonPath}`,\n\t\t)\n\t}\n}\n\n/**\n * This used to support subdomains on localhost, but that caused too many issues.\n */\nexport function getWorkshopUrl(port: number) {\n\treturn `http://localhost:${port}`\n}\n\nexport function getWorkshopConfig(): WorkshopConfig {\n\tif (\n\t\tconfigCache.config &&\n\t\tconfigCache.modified > fs.statSync(getRootPkgJsonPath()).mtimeMs\n\t) {\n\t\treturn configCache.config\n\t}\n\n\tconst packageJson = readRootPkgJson()\n\n\tconst epicshopConfig = packageJson.epicshop || {}\n\n\t// Set githubRepo and githubRoot before parsing\n\tconst { githubRepo, githubRoot } = handleGitHubRepoAndRoot({\n\t\tgithubRepo: epicshopConfig.githubRepo,\n\t\tgithubRoot: epicshopConfig.githubRoot,\n\t})\n\tepicshopConfig.githubRepo = githubRepo\n\tepicshopConfig.githubRoot = githubRoot\n\n\ttry {\n\t\tconst parsedConfig = WorkshopConfigSchema.parse(epicshopConfig)\n\t\tconfigCache.config = parsedConfig\n\t\tconfigCache.modified = fs.statSync(getRootPkgJsonPath()).mtimeMs\n\t\treturn parsedConfig\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid epicshop configuration in ${getRootPkgJsonPath()}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nexport async function getStackBlitzUrl({\n\tfullPath,\n\ttitle,\n\ttype,\n}: {\n\tfullPath: string\n\ttitle: string\n\ttype: string\n}) {\n\tconst workshopConfig = getWorkshopConfig()\n\tconst appConfig = await getAppConfig(fullPath)\n\n\tif (appConfig.stackBlitzConfig === null) return null\n\n\tlet githubRootUrlString = workshopConfig.githubRoot\n\n\tconst githubRootUrl = new URL(\n\t\tgithubRootUrlString.replace(/\\/blob\\//, '/tree/'),\n\t)\n\n\tconst githubPart = githubRootUrl.pathname\n\n\t// Check if package.json exists to determine if this is a simple exercise\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tlet stackBlitzConfig = {\n\t\t...appConfig.stackBlitzConfig,\n\t\ttitle: appConfig.stackBlitzConfig?.title ?? `${title} (${type})`,\n\t}\n\n\t// For simple exercises without package.json, configure StackBlitz to show only editor\n\tif (!packageJsonExists) {\n\t\t// Find the first existing file from the priority list\n\t\tconst priorityFiles = [\n\t\t\t'index.html',\n\t\t\t'index.tsx',\n\t\t\t'index.ts',\n\t\t\t'index.jsx',\n\t\t\t'index.js',\n\t\t\t'README.mdx',\n\t\t\t'README.md',\n\t\t]\n\n\t\tlet defaultFile: string | null = null\n\t\tfor (const fileName of priorityFiles) {\n\t\t\tconst filePath = path.join(fullPath, fileName)\n\t\t\ttry {\n\t\t\t\tawait fs.promises.access(filePath, fs.constants.F_OK)\n\t\t\t\tdefaultFile = fileName\n\t\t\t\tbreak\n\t\t\t} catch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tstackBlitzConfig = {\n\t\t\t...stackBlitzConfig,\n\t\t\tview: 'editor', // Show only editor, no preview or terminal\n\t\t\thidedevtools: '1', // Hide the console/devtools\n\t\t\tterminalHeight: '0', // Hide the terminal completely\n\t\t\thideNavigation: '1', // Hide the preview's URL bar\n\t\t\t...(defaultFile && { file: defaultFile }), // Set default file if found\n\t\t}\n\t}\n\n\tconst params = new URLSearchParams(stackBlitzConfig as Record<string, string>)\n\n\tconst relativePath = fullPath.replace(\n\t\t`${getEnv().EPICSHOP_CONTEXT_CWD}${path.sep}`,\n\t\t'',\n\t)\n\n\tconst stackBlitzUrl = new URL(\n\t\t`/github${githubPart}/${relativePath}?${params}`,\n\t\t'https://stackblitz.com',\n\t)\n\n\treturn stackBlitzUrl.toString()\n}\n\nexport async function getAppConfig(fullPath: string) {\n\tconst workshopConfig = getWorkshopConfig()\n\n\tlet epicshopConfig: Record<string, any> = {}\n\tlet scripts: Record<string, string> = {}\n\n\tconst packageJsonPath = path.join(fullPath, 'package.json')\n\tconst packageJsonExists = await fs.promises\n\t\t.access(packageJsonPath, fs.constants.F_OK)\n\t\t.then(() => true)\n\t\t.catch(() => false)\n\n\tif (packageJsonExists) {\n\t\tconst pkg = JSON.parse(\n\t\t\tawait fs.promises.readFile(path.join(fullPath, 'package.json'), 'utf8'),\n\t\t) as any\n\t\tepicshopConfig = pkg.epicshop ?? {}\n\t\tscripts = pkg.scripts ?? {}\n\t}\n\n\tconst AppConfigSchema = z.object({\n\t\tstackBlitzConfig: StackBlitzConfigSchema.nullable()\n\t\t\t.optional()\n\t\t\t.transform((appStackBlitzConfig) => {\n\t\t\t\tif (appStackBlitzConfig === null) return null\n\n\t\t\t\treturn {\n\t\t\t\t\t...workshopConfig.stackBlitzConfig,\n\t\t\t\t\t...appStackBlitzConfig,\n\t\t\t\t}\n\t\t\t}),\n\t\ttestTab: z\n\t\t\t.object({\n\t\t\t\tenabled: z\n\t\t\t\t\t.boolean()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.default(workshopConfig.testTab?.enabled ?? true),\n\t\t\t})\n\t\t\t.default({}),\n\t\tscripts: z\n\t\t\t.object({\n\t\t\t\ttest: z.string().optional(),\n\t\t\t\tdev: z.string().optional(),\n\t\t\t})\n\t\t\t.default({}),\n\t\tinitialRoute: z.string().optional().default(workshopConfig.initialRoute),\n\t})\n\n\tconst appConfig = {\n\t\tstackBlitzConfig: epicshopConfig.stackBlitzConfig,\n\t\ttestTab: {\n\t\t\tenabled: epicshopConfig.testTab?.enabled,\n\t\t},\n\t\tscripts: {\n\t\t\ttest: scripts.test,\n\t\t\tdev: scripts.dev,\n\t\t},\n\t\tinitialRoute: epicshopConfig.initialRoute,\n\t}\n\n\ttry {\n\t\treturn AppConfigSchema.parse(appConfig)\n\t} catch (error) {\n\t\tif (error instanceof z.ZodError) {\n\t\t\tconst flattenedErrors = error.flatten()\n\t\t\tconst errorMessages = Object.entries(flattenedErrors.fieldErrors)\n\t\t\t\t.map(([field, errors]) => `${field}: ${errors?.join(', ')}`)\n\t\t\t\t.concat(flattenedErrors.formErrors)\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid app configuration for ${fullPath}:\\n${errorMessages.join('\\n')}`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epic-cache-reporter.test.d.ts","sourceRoot":"","sources":["../../src/epic-cache-reporter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { expect, test, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { epicCacheReporter } from './cache.server.js';
|
|
3
|
+
import { logger, isLoggingEnabled } from './logger.js';
|
|
4
|
+
// Mock the logger module
|
|
5
|
+
vi.mock('./logger.js', () => ({
|
|
6
|
+
logger: vi.fn(),
|
|
7
|
+
isLoggingEnabled: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
function createMockLogger() {
|
|
13
|
+
const mockLog = vi.fn();
|
|
14
|
+
const mockLogger = Object.assign(mockLog, {
|
|
15
|
+
info: vi.fn(),
|
|
16
|
+
warn: vi.fn(),
|
|
17
|
+
error: vi.fn(),
|
|
18
|
+
});
|
|
19
|
+
return { mockLog, mockLogger };
|
|
20
|
+
}
|
|
21
|
+
function setupMocks(loggingEnabled = true) {
|
|
22
|
+
const { mockLog, mockLogger } = createMockLogger();
|
|
23
|
+
const loggerMock = vi.mocked(logger);
|
|
24
|
+
const isLoggingEnabledMock = vi.mocked(isLoggingEnabled);
|
|
25
|
+
loggerMock.mockReturnValue(mockLogger);
|
|
26
|
+
isLoggingEnabledMock.mockReturnValue(loggingEnabled);
|
|
27
|
+
return { mockLog, mockLogger, loggerMock, isLoggingEnabledMock };
|
|
28
|
+
}
|
|
29
|
+
function createCacheContext(cacheConfig = {}) {
|
|
30
|
+
return {
|
|
31
|
+
key: 'test-key',
|
|
32
|
+
fallbackToCache: false,
|
|
33
|
+
forceFresh: false,
|
|
34
|
+
metadata: { ttl: 1000 },
|
|
35
|
+
cache: {
|
|
36
|
+
name: 'name' in cacheConfig
|
|
37
|
+
? cacheConfig.name
|
|
38
|
+
: 'Filesystem cache (TestCache)',
|
|
39
|
+
toString: cacheConfig.toString ?? (() => '[object Cache]'),
|
|
40
|
+
get: vi.fn(),
|
|
41
|
+
set: vi.fn(),
|
|
42
|
+
delete: vi.fn(),
|
|
43
|
+
},
|
|
44
|
+
getFreshValue: vi.fn(),
|
|
45
|
+
ttl: 1000,
|
|
46
|
+
staleWhileRevalidate: 0,
|
|
47
|
+
checkValue: vi.fn(),
|
|
48
|
+
report: vi.fn(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function createReporterAndEventHandler(cacheConfig, loggingEnabled = true) {
|
|
52
|
+
const mocks = setupMocks(loggingEnabled);
|
|
53
|
+
const reporter = epicCacheReporter();
|
|
54
|
+
const cacheContext = createCacheContext(cacheConfig);
|
|
55
|
+
const eventHandler = reporter(cacheContext);
|
|
56
|
+
return { ...mocks, eventHandler };
|
|
57
|
+
}
|
|
58
|
+
test('epicCacheReporter creates logger with correct namespace', () => {
|
|
59
|
+
const { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler();
|
|
60
|
+
expect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:testcache');
|
|
61
|
+
expect(loggerMock).toHaveBeenCalledWith('epic:cache:testcache');
|
|
62
|
+
});
|
|
63
|
+
test('epicCacheReporter handles LRU cache naming', () => {
|
|
64
|
+
const { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler({
|
|
65
|
+
name: 'LRUCache',
|
|
66
|
+
});
|
|
67
|
+
expect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:lru');
|
|
68
|
+
expect(loggerMock).toHaveBeenCalledWith('epic:cache:lru');
|
|
69
|
+
});
|
|
70
|
+
test('epicCacheReporter handles unknown cache naming', () => {
|
|
71
|
+
const { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler({
|
|
72
|
+
name: undefined,
|
|
73
|
+
toString: () => '[object Object]',
|
|
74
|
+
});
|
|
75
|
+
expect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:object');
|
|
76
|
+
expect(loggerMock).toHaveBeenCalledWith('epic:cache:object');
|
|
77
|
+
});
|
|
78
|
+
test('epicCacheReporter logs cache events correctly', () => {
|
|
79
|
+
const { mockLog, mockLogger, eventHandler } = createReporterAndEventHandler();
|
|
80
|
+
// Test a cache error event
|
|
81
|
+
eventHandler({
|
|
82
|
+
name: 'getCachedValueError',
|
|
83
|
+
error: new Error('Cache read failed'),
|
|
84
|
+
});
|
|
85
|
+
expect(mockLogger.error).toHaveBeenCalledWith('error with cache at test-key. Deleting the cache key and trying to get a fresh value.', expect.any(Error));
|
|
86
|
+
// Test a fresh value success event
|
|
87
|
+
eventHandler({ name: 'getFreshValueStart' });
|
|
88
|
+
eventHandler({
|
|
89
|
+
name: 'writeFreshValueSuccess',
|
|
90
|
+
written: true,
|
|
91
|
+
metadata: { ttl: 1000 },
|
|
92
|
+
migrated: false,
|
|
93
|
+
});
|
|
94
|
+
expect(mockLog).toHaveBeenCalledWith('Updated the cache value for test-key.', expect.stringContaining('Getting a fresh value for this took'), expect.stringContaining('Caching for'));
|
|
95
|
+
});
|
|
96
|
+
test('epicCacheReporter returns no-op when logging is disabled', () => {
|
|
97
|
+
const { mockLog, mockLogger, loggerMock, isLoggingEnabledMock, eventHandler, } = createReporterAndEventHandler(undefined, false);
|
|
98
|
+
// Verify isLoggingEnabled was called
|
|
99
|
+
expect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:testcache');
|
|
100
|
+
// Verify logger was NOT called since logging is disabled
|
|
101
|
+
expect(loggerMock).not.toHaveBeenCalled();
|
|
102
|
+
// Test that events are ignored when logging is disabled
|
|
103
|
+
eventHandler({
|
|
104
|
+
name: 'getCachedValueError',
|
|
105
|
+
error: new Error('Cache read failed'),
|
|
106
|
+
});
|
|
107
|
+
// No logging should occur
|
|
108
|
+
expect(mockLogger.error).not.toHaveBeenCalled();
|
|
109
|
+
expect(mockLog).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=epic-cache-reporter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epic-cache-reporter.test.js","sourceRoot":"","sources":["../../src/epic-cache-reporter.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test, vi, beforeEach } from 'vitest'\nimport { epicCacheReporter } from './cache.server.js'\nimport { logger, isLoggingEnabled } from './logger.js'\n\n// Mock the logger module\nvi.mock('./logger.js', () => ({\n\tlogger: vi.fn(),\n\tisLoggingEnabled: vi.fn(),\n}))\n\nbeforeEach(() => {\n\tvi.clearAllMocks()\n})\n\nfunction createMockLogger() {\n\tconst mockLog = vi.fn()\n\tconst mockLogger = Object.assign(mockLog, {\n\t\tinfo: vi.fn(),\n\t\twarn: vi.fn(),\n\t\terror: vi.fn(),\n\t})\n\treturn { mockLog, mockLogger }\n}\n\nfunction setupMocks(loggingEnabled: boolean = true) {\n\tconst { mockLog, mockLogger } = createMockLogger()\n\tconst loggerMock = vi.mocked(logger)\n\tconst isLoggingEnabledMock = vi.mocked(isLoggingEnabled)\n\n\tloggerMock.mockReturnValue(mockLogger)\n\tisLoggingEnabledMock.mockReturnValue(loggingEnabled)\n\n\treturn { mockLog, mockLogger, loggerMock, isLoggingEnabledMock }\n}\n\nfunction createCacheContext(\n\tcacheConfig: Partial<{ name: string; toString: () => string }> = {},\n) {\n\treturn {\n\t\tkey: 'test-key',\n\t\tfallbackToCache: false,\n\t\tforceFresh: false,\n\t\tmetadata: { ttl: 1000 },\n\t\tcache: {\n\t\t\tname:\n\t\t\t\t'name' in cacheConfig\n\t\t\t\t\t? cacheConfig.name\n\t\t\t\t\t: 'Filesystem cache (TestCache)',\n\t\t\ttoString: cacheConfig.toString ?? (() => '[object Cache]'),\n\t\t\tget: vi.fn(),\n\t\t\tset: vi.fn(),\n\t\t\tdelete: vi.fn(),\n\t\t},\n\t\tgetFreshValue: vi.fn(),\n\t\tttl: 1000,\n\t\tstaleWhileRevalidate: 0,\n\t\tcheckValue: vi.fn(),\n\t\treport: vi.fn(),\n\t} as any\n}\n\nfunction createReporterAndEventHandler(\n\tcacheConfig?: Partial<{ name: string; toString: () => string }>,\n\tloggingEnabled: boolean = true,\n) {\n\tconst mocks = setupMocks(loggingEnabled)\n\tconst reporter = epicCacheReporter()\n\tconst cacheContext = createCacheContext(cacheConfig)\n\tconst eventHandler = reporter(cacheContext)\n\n\treturn { ...mocks, eventHandler }\n}\n\ntest('epicCacheReporter creates logger with correct namespace', () => {\n\tconst { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler()\n\n\texpect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:testcache')\n\texpect(loggerMock).toHaveBeenCalledWith('epic:cache:testcache')\n})\n\ntest('epicCacheReporter handles LRU cache naming', () => {\n\tconst { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler({\n\t\tname: 'LRUCache',\n\t})\n\n\texpect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:lru')\n\texpect(loggerMock).toHaveBeenCalledWith('epic:cache:lru')\n})\n\ntest('epicCacheReporter handles unknown cache naming', () => {\n\tconst { isLoggingEnabledMock, loggerMock } = createReporterAndEventHandler({\n\t\tname: undefined,\n\t\ttoString: () => '[object Object]',\n\t})\n\n\texpect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:object')\n\texpect(loggerMock).toHaveBeenCalledWith('epic:cache:object')\n})\n\ntest('epicCacheReporter logs cache events correctly', () => {\n\tconst { mockLog, mockLogger, eventHandler } = createReporterAndEventHandler()\n\n\t// Test a cache error event\n\teventHandler({\n\t\tname: 'getCachedValueError',\n\t\terror: new Error('Cache read failed'),\n\t} as any)\n\n\texpect(mockLogger.error).toHaveBeenCalledWith(\n\t\t'error with cache at test-key. Deleting the cache key and trying to get a fresh value.',\n\t\texpect.any(Error),\n\t)\n\n\t// Test a fresh value success event\n\teventHandler({ name: 'getFreshValueStart' } as any)\n\teventHandler({\n\t\tname: 'writeFreshValueSuccess',\n\t\twritten: true,\n\t\tmetadata: { ttl: 1000 },\n\t\tmigrated: false,\n\t} as any)\n\n\texpect(mockLog).toHaveBeenCalledWith(\n\t\t'Updated the cache value for test-key.',\n\t\texpect.stringContaining('Getting a fresh value for this took'),\n\t\texpect.stringContaining('Caching for'),\n\t)\n})\n\ntest('epicCacheReporter returns no-op when logging is disabled', () => {\n\tconst {\n\t\tmockLog,\n\t\tmockLogger,\n\t\tloggerMock,\n\t\tisLoggingEnabledMock,\n\t\teventHandler,\n\t} = createReporterAndEventHandler(undefined, false)\n\n\t// Verify isLoggingEnabled was called\n\texpect(isLoggingEnabledMock).toHaveBeenCalledWith('epic:cache:testcache')\n\t// Verify logger was NOT called since logging is disabled\n\texpect(loggerMock).not.toHaveBeenCalled()\n\n\t// Test that events are ignored when logging is disabled\n\teventHandler({\n\t\tname: 'getCachedValueError',\n\t\terror: new Error('Cache read failed'),\n\t} as any)\n\n\t// No logging should occur\n\texpect(mockLogger.error).not.toHaveBeenCalled()\n\texpect(mockLog).not.toHaveBeenCalled()\n})\n"]}
|
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,EAAE,MAAM,WAAW,CAAA;AAUpC,MAAM,UAAU,MAAM,CAAC,EAAU;IAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAExB,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAW,CAAA;IAE9E,QAAQ,CAAC,KAAK,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IACxE,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IACvE,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IAEvE,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,gBAAgB,CAAC,EAAU;IAC1C,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,CAAA;AAC5B,CAAC","sourcesContent":["import { debuglog } from 'node:util'\n\ntype LogFunction = (...args: Parameters<ReturnType<typeof debuglog>>) => void\n\ninterface Logger extends LogFunction {\n\terror: LogFunction\n\twarn: LogFunction\n\tinfo: LogFunction\n}\n\nexport function logger(ns: string): Logger {\n\tconst log = debuglog(ns)\n\
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAUpC,MAAM,UAAU,MAAM,CAAC,EAAU;IAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;IAExB,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAW,CAAA;IAE9E,QAAQ,CAAC,KAAK,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IACxE,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IACvE,QAAQ,CAAC,IAAI,GAAG,CAAC,GAAG,IAA4B,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;IAEvE,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,gBAAgB,CAAC,EAAU;IAC1C,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,CAAA;AAC5B,CAAC","sourcesContent":["import { debuglog } from 'node:util'\n\ntype LogFunction = (...args: Parameters<ReturnType<typeof debuglog>>) => void\n\ninterface Logger extends LogFunction {\n\terror: LogFunction\n\twarn: LogFunction\n\tinfo: LogFunction\n}\n\nexport function logger(ns: string): Logger {\n\tconst log = debuglog(ns)\n\n\tconst loggerFn = ((...args: Parameters<typeof log>) => log(...args)) as Logger\n\n\tloggerFn.error = (...args: Parameters<typeof log>) => log('🚨', ...args)\n\tloggerFn.warn = (...args: Parameters<typeof log>) => log('⚠️', ...args)\n\tloggerFn.info = (...args: Parameters<typeof log>) => log('ℹ️', ...args)\n\n\treturn loggerFn\n}\n\n// Convenience function to check if logging is enabled for a namespace\nexport function isLoggingEnabled(ns: string): boolean {\n\treturn debuglog(ns).enabled\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager.server.d.ts","sourceRoot":"","sources":["../../src/process-manager.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAIxD,OAAO,cAAc,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAS3C,KAAK,eAAe,GAAG,GAAG,CACzB,MAAM,EACN;IACC,KAAK,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;CACZ,CACD,CAAA;AAED,KAAK,mBAAmB,GAAG,GAAG,CAC7B,MAAM,EACN;IACC,KAAK,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,YAAY,CAAA;CACrB,CACD,CAAA;AAED,KAAK,UAAU,GAAG;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,KAAK,gBAAgB,GAAG;IACvB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,KAAK,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AACrD,OAAO,CAAC,MAAM,CAAC;IACd,IAAI,uCAAuC,EAAE,UAAU,CACrD,OAAO,cAAc,CACrB,EACD,wCAAwC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,EAC3E,2CAA2C,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"process-manager.server.d.ts","sourceRoot":"","sources":["../../src/process-manager.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAIxD,OAAO,cAAc,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAE,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAS3C,KAAK,eAAe,GAAG,GAAG,CACzB,MAAM,EACN;IACC,KAAK,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;CACZ,CACD,CAAA;AAED,KAAK,mBAAmB,GAAG,GAAG,CAC7B,MAAM,EACN;IACC,KAAK,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,YAAY,CAAA;CACrB,CACD,CAAA;AAED,KAAK,UAAU,GAAG;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,KAAK,gBAAgB,GAAG;IACvB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,KAAK,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;AACrD,OAAO,CAAC,MAAM,CAAC;IACd,IAAI,uCAAuC,EAAE,UAAU,CACrD,OAAO,cAAc,CACrB,EACD,wCAAwC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,EAC3E,2CAA2C,EAAE,UAAU,CACtD,OAAO,cAAc,CACrB,CAAA;CACF;AAoDD,QAAA,MAAM,MAAM,mIAWF,CAAA;AA4BV,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;GAgEvC;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,GAAG;;;GAkDzC;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,GAAG;;;;;;UAwBvC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYvE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,oBASvD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,WAUlD;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,gCAExD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,WAE1D;AAED,wBAAgB,YAAY;;;;EAE3B;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAStE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAqEhE;AAED,wBAAgB,oBAAoB,SASnC;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,iBAoB7C;AAID,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,iBAInD;AAED,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,iBAWnE"}
|
|
@@ -72,7 +72,7 @@ function getNextAvailableColor() {
|
|
|
72
72
|
usedColors.add(proc.color);
|
|
73
73
|
}
|
|
74
74
|
// Find available colors
|
|
75
|
-
const availableColors = colors.filter(color => !usedColors.has(color));
|
|
75
|
+
const availableColors = colors.filter((color) => !usedColors.has(color));
|
|
76
76
|
if (availableColors.length === 0) {
|
|
77
77
|
// If all colors are used, cycle through them based on total process count
|
|
78
78
|
const totalProcesses = devProcesses.size + sidecarProcesses.size;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager.server.js","sourceRoot":"","sources":["../../src/process-manager.server.ts"],"names":[],"mappings":"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","sourcesContent":["import './init-env.js'\n\nimport { spawn, type ChildProcess } from 'child_process'\nimport net from 'node:net'\nimport { remember } from '@epic-web/remember'\nimport chalk from 'chalk'\nimport closeWithGrace from 'close-with-grace'\nimport findProcess from 'find-process'\nimport fkill from 'fkill'\nimport { type App } from './apps.server.js'\nimport { getWorkshopUrl } from './config.server.js'\nimport { getEnv } from './env.server.js'\nimport { getErrorMessage } from './utils.js'\n\nconst isDeployed =\n\tprocess.env.EPICSHOP_DEPLOYED === 'true' ||\n\tprocess.env.EPICSHOP_DEPLOYED === '1'\n\ntype DevProcessesMap = Map<\n\tstring,\n\t{\n\t\tcolor: (typeof colors)[number]\n\t\tprocess: ChildProcess\n\t\tport: number\n\t}\n>\n\ntype SidecarProcessesMap = Map<\n\tstring,\n\t{\n\t\tcolor: (typeof colors)[number]\n\t\tprocess: ChildProcess\n\t}\n>\n\ntype OutputLine = {\n\ttype: 'stdout' | 'stderr'\n\tcontent: string\n\ttimestamp: number\n}\n\ntype TestProcessEntry = {\n\tprocess: ChildProcess | null\n\toutput: Array<OutputLine>\n\texitCode?: number | null\n}\n\ntype TestProcessesMap = Map<string, TestProcessEntry>\ndeclare global {\n\tvar __process_dev_close_with_grace_return__: ReturnType<\n\t\t\ttypeof closeWithGrace\n\t\t>,\n\t\t__process_test_close_with_grace_return__: ReturnType<typeof closeWithGrace>,\n\t\t__process_sidecar_close_with_grace_return__: ReturnType<typeof closeWithGrace>\n}\n\nconst devProcesses = remember('dev_processes', getDevProcessesMap)\nconst testProcesses = remember('test_processes', getTestProcessesMap)\nconst sidecarProcesses = remember('sidecar_processes', getSidecarProcessesMap)\n\nfunction getDevProcessesMap() {\n\tconst procs: DevProcessesMap = new Map()\n\n\tglobal.__process_dev_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_dev_close_with_grace_return__ = closeWithGrace(async () => {\n\t\tfor (const [name, proc] of procs.entries()) {\n\t\t\tconsole.log('closing', name)\n\t\t\tproc.process.kill()\n\t\t}\n\t})\n\treturn procs\n}\n\nfunction getTestProcessesMap() {\n\tconst procs: TestProcessesMap = new Map()\n\n\tglobal.__process_test_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_test_close_with_grace_return__ = closeWithGrace(async () => {\n\t\tfor (const [id, proc] of procs.entries()) {\n\t\t\tif (proc.process) {\n\t\t\t\tconsole.log('closing', id)\n\t\t\t\tproc.process.kill()\n\t\t\t}\n\t\t}\n\t})\n\treturn procs\n}\n\nfunction getSidecarProcessesMap() {\n\tconst procs: SidecarProcessesMap = new Map()\n\n\tglobal.__process_sidecar_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_sidecar_close_with_grace_return__ = closeWithGrace(async () => {\n\t\tfor (const [name, proc] of procs.entries()) {\n\t\t\tconsole.log('closing sidecar', name)\n\t\t\tproc.process.kill()\n\t\t}\n\t})\n\treturn procs\n}\n\nconst colors = [\n\t'blue',\n\t'green',\n\t'yellow',\n\t'red',\n\t'magenta',\n\t'redBright',\n\t'greenBright',\n\t'yellowBright',\n\t'blueBright',\n\t'magentaBright',\n] as const\n\nfunction getNextAvailableColor(): (typeof colors)[number] {\n\tconst usedColors = new Set<(typeof colors)[number]>()\n\t\n\t// Collect colors used by dev processes\n\tfor (const proc of devProcesses.values()) {\n\t\tusedColors.add(proc.color)\n\t}\n\t\n\t// Collect colors used by sidecar processes\n\tfor (const proc of sidecarProcesses.values()) {\n\t\tusedColors.add(proc.color)\n\t}\n\t\n\t// Find available colors\n\tconst availableColors = colors.filter(color => !usedColors.has(color))\n\t\n\tif (availableColors.length === 0) {\n\t\t// If all colors are used, cycle through them based on total process count\n\t\tconst totalProcesses = devProcesses.size + sidecarProcesses.size\n\t\treturn colors[totalProcesses % colors.length] ?? 'blue'\n\t}\n\t\n\t// Use the first available color\n\treturn availableColors[0] ?? 'blue'\n}\n\nexport async function runAppDev(app: App) {\n\tif (isDeployed) throw new Error('cannot run apps in deployed mode')\n\tconst key = app.name\n\t// if the app is already running, don't start it again\n\tif (devProcesses.has(key)) {\n\t\treturn { status: 'process-running', running: true } as const\n\t}\n\n\tif (app.dev.type !== 'script') {\n\t\treturn { status: 'error', error: 'no-server' } as const\n\t}\n\n\tconst { portNumber } = app.dev\n\tif (!(await isPortAvailable(portNumber))) {\n\t\treturn { status: 'port-unavailable', running: false, portNumber } as const\n\t}\n\tconst color = getNextAvailableColor()\n\tconst appProcess = spawn('npm', ['run', 'dev', '--silent'], {\n\t\tcwd: app.fullPath,\n\t\tshell: true,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t// TODO: support specifying the env\n\t\t\tNODE_ENV: 'development',\n\t\t\t// TODO: support specifying the port\n\t\t\tPORT: String(portNumber),\n\t\t\tAPP_SERVER_PORT: String(portNumber),\n\t\t\t// let it pick a random port...\n\t\t\tREMIX_DEV_SERVER_WS_PORT: '',\n\t\t},\n\t})\n\tconst prefix = chalk[color](\n\t\t`[${app.name.replace(/^exercises\\./, '')}:${portNumber}]`,\n\t)\n\tfunction handleStdOutData(data: Buffer) {\n\t\tconsole.log(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tappProcess.stdout.on('data', handleStdOutData)\n\tfunction handleStdErrData(data: Buffer) {\n\t\tconsole.error(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tappProcess.stderr.on('data', handleStdErrData)\n\tdevProcesses.set(key, { color, process: appProcess, port: portNumber })\n\tappProcess.on('exit', (code) => {\n\t\tappProcess.stdout.off('data', handleStdOutData)\n\t\tappProcess.stderr.off('data', handleStdErrData)\n\t\tconsole.log(`${prefix} exited (${code})`)\n\t\tdevProcesses.delete(key)\n\t})\n\n\treturn { status: 'process-started', running: true } as const\n}\n\nexport async function runAppTests(app: App) {\n\tif (isDeployed) throw new Error('cannot run tests in deployed mode')\n\tconst key = app.name\n\n\tif (app.test.type !== 'script') {\n\t\treturn { status: 'error', error: 'no-test' } as const\n\t}\n\n\tconst testProcess = spawn('npm', ['run', 'test', '--silent'], {\n\t\tcwd: app.fullPath,\n\t\tshell: true,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t// TODO: support specifying the env\n\t\t\tNODE_ENV: 'development',\n\t\t\t// TODO: support specifying the port\n\t\t\tPORT: app.dev.type === 'script' ? String(app.dev.portNumber) : undefined,\n\t\t\tAPP_SERVER_PORT:\n\t\t\t\tapp.dev.type === 'script' ? String(app.dev.portNumber) : undefined,\n\t\t\t// let it pick a random port...\n\t\t\tREMIX_DEV_SERVER_WS_PORT: '',\n\t\t},\n\t})\n\tconst output: Array<OutputLine> = []\n\tconst entry: TestProcessEntry = { process: testProcess, output }\n\tfunction handleStdOutData(data: Buffer) {\n\t\toutput.push({\n\t\t\ttype: 'stdout',\n\t\t\tcontent: data.toString('utf-8'),\n\t\t\ttimestamp: Date.now(),\n\t\t})\n\t}\n\ttestProcess.stdout.on('data', handleStdOutData)\n\tfunction handleStdErrData(data: Buffer) {\n\t\toutput.push({\n\t\t\ttype: 'stderr',\n\t\t\tcontent: data.toString('utf-8'),\n\t\t\ttimestamp: Date.now(),\n\t\t})\n\t}\n\ttestProcess.stderr.on('data', handleStdErrData)\n\ttestProcess.on('exit', (code) => {\n\t\ttestProcess.stdout.off('data', handleStdOutData)\n\t\ttestProcess.stderr.off('data', handleStdErrData)\n\t\tentry.process = null\n\t\tentry.exitCode = code\n\t})\n\ttestProcesses.set(key, entry)\n\treturn testProcess\n}\n\nexport async function waitOnApp(app: App) {\n\tif (app.dev.type === 'script') {\n\t\tconst startTime = Date.now()\n\n\t\tconst retryInterval = 100\n\t\tconst timeout = 20_000\n\t\tlet lastError: unknown\n\t\twhile (Date.now() - startTime < timeout) {\n\t\t\ttry {\n\t\t\t\tconst url = getWorkshopUrl(app.dev.portNumber)\n\t\t\t\tawait fetch(url, {\n\t\t\t\t\tmethod: 'HEAD',\n\t\t\t\t\theaders: { Accept: '*/*' },\n\t\t\t\t})\n\t\t\t\treturn { status: 'success' } as const\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, retryInterval))\n\t\t\t}\n\t\t}\n\n\t\treturn { status: 'error', error: getErrorMessage(lastError) } as const\n\t}\n\treturn null\n}\n\nexport function isPortAvailable(port: number | string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst server = net.createServer()\n\t\tserver.unref()\n\t\tserver.on('error', () => resolve(false))\n\n\t\tserver.listen(Number(port), () => {\n\t\t\tserver.close(() => {\n\t\t\t\tresolve(true)\n\t\t\t})\n\t\t})\n\t})\n}\n\nexport async function isAppRunning(app: { name: string }) {\n\ttry {\n\t\tconst devProcess = devProcesses.get(app.name)\n\t\tif (!devProcess?.process.pid) return false\n\t\tconst found = await findProcess('pid', devProcess.process.pid)\n\t\treturn found.length > 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function isTestRunning(app: { name: string }) {\n\ttry {\n\t\tconst testProcess = testProcesses.get(app.name)\n\t\tif (!testProcess) return false\n\t\tif (testProcess.process === null) return false\n\t\ttestProcess.process.kill(0)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function getTestProcessEntry(app: { name: string }) {\n\treturn testProcesses.get(app.name)\n}\n\nexport function clearTestProcessEntry(app: { name: string }) {\n\treturn testProcesses.delete(app.name)\n}\n\nexport function getProcesses() {\n\treturn { devProcesses, testProcesses, sidecarProcesses }\n}\n\nexport function startSidecarProcesses(processes: Record<string, string>) {\n\tif (isDeployed) {\n\t\tconsole.log('Sidecar processes are not supported in deployed mode')\n\t\treturn\n\t}\n\n\tfor (const [name, command] of Object.entries(processes)) {\n\t\tstartSidecarProcess(name, command)\n\t}\n}\n\nexport function startSidecarProcess(name: string, command: string) {\n\tif (isDeployed) throw new Error('cannot run sidecar processes in deployed mode')\n\t\n\t// if the process is already running, don't start it again\n\tif (sidecarProcesses.has(name)) {\n\t\tconsole.log(`Sidecar process ${name} is already running`)\n\t\treturn\n\t}\n\n\tconst color = getNextAvailableColor()\n\n\t// Spawn the command using shell to handle complex commands properly\n\tconst workshopRoot = getEnv().EPICSHOP_CONTEXT_CWD\n\tconst sidecarProcess = spawn(command, [], {\n\t\tshell: true,\n\t\tcwd: workshopRoot,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tNODE_ENV: 'development',\n\t\t},\n\t})\n\n\tconst prefix = chalk[color](`[${name}]`)\n\t\n\tfunction handleStdOutData(data: Buffer) {\n\t\tconsole.log(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tsidecarProcess.stdout?.on('data', handleStdOutData)\n\t\n\tfunction handleStdErrData(data: Buffer) {\n\t\tconsole.error(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tsidecarProcess.stderr?.on('data', handleStdErrData)\n\t\n\tsidecarProcesses.set(name, { color, process: sidecarProcess })\n\t\n\tsidecarProcess.on('exit', (code: number | null, signal: string | null) => {\n\t\tsidecarProcess.stdout?.off('data', handleStdOutData)\n\t\tsidecarProcess.stderr?.off('data', handleStdErrData)\n\t\tif (code === 0) {\n\t\t\tconsole.log(`${prefix} exited successfully`)\n\t\t} else {\n\t\t\tconsole.log(`${prefix} exited with code ${code}${signal ? ` (signal: ${signal})` : ''}`)\n\t\t}\n\t\tsidecarProcesses.delete(name)\n\t})\n\n\tsidecarProcess.on('error', (error) => {\n\t\tconsole.error(`${prefix} failed to start: ${error.message}`)\n\t\tsidecarProcesses.delete(name)\n\t})\n\n\tconsole.log(`${prefix} started`)\n}\n\nexport function stopSidecarProcesses() {\n\tif (isDeployed) throw new Error('cannot stop sidecar processes in deployed mode')\n\t\n\tfor (const [name, proc] of sidecarProcesses.entries()) {\n\t\tconsole.log(`Stopping sidecar process: ${name}`)\n\t\tproc.process.kill()\n\t}\n\tsidecarProcesses.clear()\n}\n\nexport async function closeProcess(key: string) {\n\tif (isDeployed) throw new Error('cannot close processes in deployed mode')\n\tconst proc = devProcesses.get(key)\n\tif (proc) {\n\t\tconst exitedPromise = new Promise((resolve) =>\n\t\t\tproc.process.on('exit', resolve),\n\t\t)\n\t\tif (process.platform === 'win32') {\n\t\t\tconst { execa } = await import('execa')\n\t\t\tawait execa('taskkill', ['/pid', String(proc.process.pid), '/f', '/t'])\n\t\t} else {\n\t\t\tproc.process.kill()\n\t\t}\n\t\tawait Promise.race([\n\t\t\tnew Promise((resolve) => setTimeout(resolve, 500)),\n\t\t\texitedPromise,\n\t\t])\n\t\tawait stopPort(proc.port) // just in case 🤷♂️\n\t\tdevProcesses.delete(key)\n\t}\n}\n\nconst sleep = (t: number) => new Promise((resolve) => setTimeout(resolve, t))\n\nexport async function stopPort(port: string | number) {\n\tif (isDeployed) throw new Error('cannot stop ports in deployed mode')\n\tawait fkill(`:${port}`, { force: true, silent: true })\n\tawait waitForPortToBeAvailable(port)\n}\n\nexport async function waitForPortToBeAvailable(port: string | number) {\n\t// wait for the port to become available again\n\tconst timeout = Date.now() + 10_000\n\tlet portAvailable = false\n\tdo {\n\t\tportAvailable = await isPortAvailable(port)\n\t\tawait sleep(100)\n\t} while (!portAvailable && Date.now() < timeout)\n\tif (!portAvailable) {\n\t\tconsole.error('Timed out waiting for the port to become available')\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"process-manager.server.js","sourceRoot":"","sources":["../../src/process-manager.server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AAEtB,OAAO,EAAE,KAAK,EAAqB,MAAM,eAAe,CAAA;AACxD,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,cAAc,MAAM,kBAAkB,CAAA;AAC7C,OAAO,WAAW,MAAM,cAAc,CAAA;AACtC,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,MAAM,UAAU,GACf,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM;IACxC,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,GAAG,CAAA;AA0CtC,MAAM,YAAY,GAAG,QAAQ,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAA;AAClE,MAAM,aAAa,GAAG,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,CAAA;AACrE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,CAAA;AAE9E,SAAS,kBAAkB;IAC1B,MAAM,KAAK,GAAoB,IAAI,GAAG,EAAE,CAAA;IAExC,MAAM,CAAC,uCAAuC,EAAE,SAAS,EAAE,CAAA;IAE3D,MAAM,CAAC,uCAAuC,GAAG,cAAc,CAAC,KAAK,IAAI,EAAE;QAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;YAC5B,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QACpB,CAAC;IACF,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAA;AACb,CAAC;AAED,SAAS,mBAAmB;IAC3B,MAAM,KAAK,GAAqB,IAAI,GAAG,EAAE,CAAA;IAEzC,MAAM,CAAC,wCAAwC,EAAE,SAAS,EAAE,CAAA;IAE5D,MAAM,CAAC,wCAAwC,GAAG,cAAc,CAAC,KAAK,IAAI,EAAE;QAC3E,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;gBAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YACpB,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAA;AACb,CAAC;AAED,SAAS,sBAAsB;IAC9B,MAAM,KAAK,GAAwB,IAAI,GAAG,EAAE,CAAA;IAE5C,MAAM,CAAC,2CAA2C,EAAE,SAAS,EAAE,CAAA;IAE/D,MAAM,CAAC,2CAA2C,GAAG,cAAc,CAClE,KAAK,IAAI,EAAE;QACV,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAA;YACpC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QACpB,CAAC;IACF,CAAC,CACD,CAAA;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED,MAAM,MAAM,GAAG;IACd,MAAM;IACN,OAAO;IACP,QAAQ;IACR,KAAK;IACL,SAAS;IACT,WAAW;IACX,aAAa;IACb,cAAc;IACd,YAAY;IACZ,eAAe;CACN,CAAA;AAEV,SAAS,qBAAqB;IAC7B,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAA;IAErD,uCAAuC;IACvC,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QAC1C,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAED,2CAA2C;IAC3C,KAAK,MAAM,IAAI,IAAI,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9C,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;IAED,wBAAwB;IACxB,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;IAExE,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,0EAA0E;QAC1E,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAA;QAChE,OAAO,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAA;IACxD,CAAC;IAED,gCAAgC;IAChC,OAAO,eAAe,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAQ;IACvC,IAAI,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAA;IACnE,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAA;IACpB,sDAAsD;IACtD,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAW,CAAA;IAC7D,CAAC;IAED,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAW,CAAA;IACxD,CAAC;IAED,MAAM,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC,GAAG,CAAA;IAC9B,IAAI,CAAC,CAAC,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAW,CAAA;IAC3E,CAAC;IACD,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAA;IACrC,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE;QAC3D,GAAG,EAAE,GAAG,CAAC,QAAQ;QACjB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,GAAG,EAAE;YACJ,GAAG,OAAO,CAAC,GAAG;YACd,mCAAmC;YACnC,QAAQ,EAAE,aAAa;YACvB,oCAAoC;YACpC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC;YACxB,eAAe,EAAE,MAAM,CAAC,UAAU,CAAC;YACnC,+BAA+B;YAC/B,wBAAwB,EAAE,EAAE;SAC5B;KACD,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAC1B,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,UAAU,GAAG,CACzD,CAAA;IACD,SAAS,gBAAgB,CAAC,IAAY;QACrC,OAAO,CAAC,GAAG,CACV,IAAI;aACF,QAAQ,CAAC,OAAO,CAAC;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAA;IACF,CAAC;IACD,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAC9C,SAAS,gBAAgB,CAAC,IAAY;QACrC,OAAO,CAAC,KAAK,CACZ,IAAI;aACF,QAAQ,CAAC,OAAO,CAAC;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAA;IACF,CAAC;IACD,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAC9C,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;IACvE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC9B,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAC/C,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,YAAY,IAAI,GAAG,CAAC,CAAA;QACzC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,IAAI,EAAW,CAAA;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAQ;IACzC,IAAI,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACpE,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAA;IAEpB,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAW,CAAA;IACtD,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE;QAC7D,GAAG,EAAE,GAAG,CAAC,QAAQ;QACjB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,GAAG,EAAE;YACJ,GAAG,OAAO,CAAC,GAAG;YACd,mCAAmC;YACnC,QAAQ,EAAE,aAAa;YACvB,oCAAoC;YACpC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;YACxE,eAAe,EACd,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;YACnE,+BAA+B;YAC/B,wBAAwB,EAAE,EAAE;SAC5B;KACD,CAAC,CAAA;IACF,MAAM,MAAM,GAAsB,EAAE,CAAA;IACpC,MAAM,KAAK,GAAqB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAA;IAChE,SAAS,gBAAgB,CAAC,IAAY;QACrC,MAAM,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC,CAAA;IACH,CAAC;IACD,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAC/C,SAAS,gBAAgB,CAAC,IAAY;QACrC,MAAM,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC,CAAA;IACH,CAAC;IACD,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAC/C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC/B,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAChD,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAChD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAA;QACpB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAA;IACtB,CAAC,CAAC,CAAA;IACF,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC7B,OAAO,WAAW,CAAA;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAQ;IACvC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAE5B,MAAM,aAAa,GAAG,GAAG,CAAA;QACzB,MAAM,OAAO,GAAG,MAAM,CAAA;QACtB,IAAI,SAAkB,CAAA;QACtB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;YACzC,IAAI,CAAC;gBACJ,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;gBAC9C,MAAM,KAAK,CAAC,GAAG,EAAE;oBAChB,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;iBAC1B,CAAC,CAAA;gBACF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAW,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,SAAS,GAAG,KAAK,CAAA;gBACjB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAA;YACnE,CAAC;QACF,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,SAAS,CAAC,EAAW,CAAA;IACvE,CAAC;IACD,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAqB;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAA;QACjC,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAExC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE;YAChC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBACjB,OAAO,CAAC,IAAI,CAAC,CAAA;YACd,CAAC,CAAC,CAAA;QACH,CAAC,CAAC,CAAA;IACH,CAAC,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAqB;IACvD,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC7C,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG;YAAE,OAAO,KAAK,CAAA;QAC1C,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC9D,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IACxB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAA;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAqB;IAClD,IAAI,CAAC;QACJ,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC/C,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAC9B,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,KAAK,CAAA;QAC9C,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC3B,OAAO,IAAI,CAAA;IACZ,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAA;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAqB;IACxD,OAAO,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAqB;IAC1D,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,YAAY;IAC3B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,SAAiC;IACtE,IAAI,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAA;QACnE,OAAM;IACP,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,OAAe;IAChE,IAAI,UAAU;QACb,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;IAEjE,0DAA0D;IAC1D,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,qBAAqB,CAAC,CAAA;QACzD,OAAM;IACP,CAAC;IAED,MAAM,KAAK,GAAG,qBAAqB,EAAE,CAAA;IAErC,oEAAoE;IACpE,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAA;IAClD,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE;QACzC,KAAK,EAAE,IAAI;QACX,GAAG,EAAE,YAAY;QACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,GAAG,EAAE;YACJ,GAAG,OAAO,CAAC,GAAG;YACd,QAAQ,EAAE,aAAa;SACvB;KACD,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAA;IAExC,SAAS,gBAAgB,CAAC,IAAY;QACrC,OAAO,CAAC,GAAG,CACV,IAAI;aACF,QAAQ,CAAC,OAAO,CAAC;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAA;IACF,CAAC;IACD,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAEnD,SAAS,gBAAgB,CAAC,IAAY;QACrC,OAAO,CAAC,KAAK,CACZ,IAAI;aACF,QAAQ,CAAC,OAAO,CAAC;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAA;IACF,CAAC;IACD,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAEnD,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAA;IAE9D,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAmB,EAAE,MAAqB,EAAE,EAAE;QACxE,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QACpD,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QACpD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,sBAAsB,CAAC,CAAA;QAC7C,CAAC;aAAM,CAAC;YACP,OAAO,CAAC,GAAG,CACV,GAAG,MAAM,qBAAqB,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3E,CAAA;QACF,CAAC;QACD,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACpC,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,qBAAqB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAC5D,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;IAEF,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,UAAU,CAAC,CAAA;AACjC,CAAC;AAED,MAAM,UAAU,oBAAoB;IACnC,IAAI,UAAU;QACb,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;IAElE,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAA;QAChD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;IACpB,CAAC;IACD,gBAAgB,CAAC,KAAK,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC7C,IAAI,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAC1E,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC7C,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAChC,CAAA;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAA;YACvC,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;QACxE,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;QACpB,CAAC;QACD,MAAM,OAAO,CAAC,IAAI,CAAC;YAClB,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAClD,aAAa;SACb,CAAC,CAAA;QACF,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAC,qBAAqB;QAC/C,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACzB,CAAC;AACF,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;AAE7E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAqB;IACnD,IAAI,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACrE,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,MAAM,wBAAwB,CAAC,IAAI,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAqB;IACnE,8CAA8C;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAA;IACnC,IAAI,aAAa,GAAG,KAAK,CAAA;IACzB,GAAG,CAAC;QACH,aAAa,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;QAC3C,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;IACjB,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAC;IAChD,IAAI,CAAC,aAAa,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACpE,CAAC;AACF,CAAC","sourcesContent":["import './init-env.js'\n\nimport { spawn, type ChildProcess } from 'child_process'\nimport net from 'node:net'\nimport { remember } from '@epic-web/remember'\nimport chalk from 'chalk'\nimport closeWithGrace from 'close-with-grace'\nimport findProcess from 'find-process'\nimport fkill from 'fkill'\nimport { type App } from './apps.server.js'\nimport { getWorkshopUrl } from './config.server.js'\nimport { getEnv } from './env.server.js'\nimport { getErrorMessage } from './utils.js'\n\nconst isDeployed =\n\tprocess.env.EPICSHOP_DEPLOYED === 'true' ||\n\tprocess.env.EPICSHOP_DEPLOYED === '1'\n\ntype DevProcessesMap = Map<\n\tstring,\n\t{\n\t\tcolor: (typeof colors)[number]\n\t\tprocess: ChildProcess\n\t\tport: number\n\t}\n>\n\ntype SidecarProcessesMap = Map<\n\tstring,\n\t{\n\t\tcolor: (typeof colors)[number]\n\t\tprocess: ChildProcess\n\t}\n>\n\ntype OutputLine = {\n\ttype: 'stdout' | 'stderr'\n\tcontent: string\n\ttimestamp: number\n}\n\ntype TestProcessEntry = {\n\tprocess: ChildProcess | null\n\toutput: Array<OutputLine>\n\texitCode?: number | null\n}\n\ntype TestProcessesMap = Map<string, TestProcessEntry>\ndeclare global {\n\tvar __process_dev_close_with_grace_return__: ReturnType<\n\t\t\ttypeof closeWithGrace\n\t\t>,\n\t\t__process_test_close_with_grace_return__: ReturnType<typeof closeWithGrace>,\n\t\t__process_sidecar_close_with_grace_return__: ReturnType<\n\t\t\ttypeof closeWithGrace\n\t\t>\n}\n\nconst devProcesses = remember('dev_processes', getDevProcessesMap)\nconst testProcesses = remember('test_processes', getTestProcessesMap)\nconst sidecarProcesses = remember('sidecar_processes', getSidecarProcessesMap)\n\nfunction getDevProcessesMap() {\n\tconst procs: DevProcessesMap = new Map()\n\n\tglobal.__process_dev_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_dev_close_with_grace_return__ = closeWithGrace(async () => {\n\t\tfor (const [name, proc] of procs.entries()) {\n\t\t\tconsole.log('closing', name)\n\t\t\tproc.process.kill()\n\t\t}\n\t})\n\treturn procs\n}\n\nfunction getTestProcessesMap() {\n\tconst procs: TestProcessesMap = new Map()\n\n\tglobal.__process_test_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_test_close_with_grace_return__ = closeWithGrace(async () => {\n\t\tfor (const [id, proc] of procs.entries()) {\n\t\t\tif (proc.process) {\n\t\t\t\tconsole.log('closing', id)\n\t\t\t\tproc.process.kill()\n\t\t\t}\n\t\t}\n\t})\n\treturn procs\n}\n\nfunction getSidecarProcessesMap() {\n\tconst procs: SidecarProcessesMap = new Map()\n\n\tglobal.__process_sidecar_close_with_grace_return__?.uninstall()\n\n\tglobal.__process_sidecar_close_with_grace_return__ = closeWithGrace(\n\t\tasync () => {\n\t\t\tfor (const [name, proc] of procs.entries()) {\n\t\t\t\tconsole.log('closing sidecar', name)\n\t\t\t\tproc.process.kill()\n\t\t\t}\n\t\t},\n\t)\n\treturn procs\n}\n\nconst colors = [\n\t'blue',\n\t'green',\n\t'yellow',\n\t'red',\n\t'magenta',\n\t'redBright',\n\t'greenBright',\n\t'yellowBright',\n\t'blueBright',\n\t'magentaBright',\n] as const\n\nfunction getNextAvailableColor(): (typeof colors)[number] {\n\tconst usedColors = new Set<(typeof colors)[number]>()\n\n\t// Collect colors used by dev processes\n\tfor (const proc of devProcesses.values()) {\n\t\tusedColors.add(proc.color)\n\t}\n\n\t// Collect colors used by sidecar processes\n\tfor (const proc of sidecarProcesses.values()) {\n\t\tusedColors.add(proc.color)\n\t}\n\n\t// Find available colors\n\tconst availableColors = colors.filter((color) => !usedColors.has(color))\n\n\tif (availableColors.length === 0) {\n\t\t// If all colors are used, cycle through them based on total process count\n\t\tconst totalProcesses = devProcesses.size + sidecarProcesses.size\n\t\treturn colors[totalProcesses % colors.length] ?? 'blue'\n\t}\n\n\t// Use the first available color\n\treturn availableColors[0] ?? 'blue'\n}\n\nexport async function runAppDev(app: App) {\n\tif (isDeployed) throw new Error('cannot run apps in deployed mode')\n\tconst key = app.name\n\t// if the app is already running, don't start it again\n\tif (devProcesses.has(key)) {\n\t\treturn { status: 'process-running', running: true } as const\n\t}\n\n\tif (app.dev.type !== 'script') {\n\t\treturn { status: 'error', error: 'no-server' } as const\n\t}\n\n\tconst { portNumber } = app.dev\n\tif (!(await isPortAvailable(portNumber))) {\n\t\treturn { status: 'port-unavailable', running: false, portNumber } as const\n\t}\n\tconst color = getNextAvailableColor()\n\tconst appProcess = spawn('npm', ['run', 'dev', '--silent'], {\n\t\tcwd: app.fullPath,\n\t\tshell: true,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t// TODO: support specifying the env\n\t\t\tNODE_ENV: 'development',\n\t\t\t// TODO: support specifying the port\n\t\t\tPORT: String(portNumber),\n\t\t\tAPP_SERVER_PORT: String(portNumber),\n\t\t\t// let it pick a random port...\n\t\t\tREMIX_DEV_SERVER_WS_PORT: '',\n\t\t},\n\t})\n\tconst prefix = chalk[color](\n\t\t`[${app.name.replace(/^exercises\\./, '')}:${portNumber}]`,\n\t)\n\tfunction handleStdOutData(data: Buffer) {\n\t\tconsole.log(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tappProcess.stdout.on('data', handleStdOutData)\n\tfunction handleStdErrData(data: Buffer) {\n\t\tconsole.error(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tappProcess.stderr.on('data', handleStdErrData)\n\tdevProcesses.set(key, { color, process: appProcess, port: portNumber })\n\tappProcess.on('exit', (code) => {\n\t\tappProcess.stdout.off('data', handleStdOutData)\n\t\tappProcess.stderr.off('data', handleStdErrData)\n\t\tconsole.log(`${prefix} exited (${code})`)\n\t\tdevProcesses.delete(key)\n\t})\n\n\treturn { status: 'process-started', running: true } as const\n}\n\nexport async function runAppTests(app: App) {\n\tif (isDeployed) throw new Error('cannot run tests in deployed mode')\n\tconst key = app.name\n\n\tif (app.test.type !== 'script') {\n\t\treturn { status: 'error', error: 'no-test' } as const\n\t}\n\n\tconst testProcess = spawn('npm', ['run', 'test', '--silent'], {\n\t\tcwd: app.fullPath,\n\t\tshell: true,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t// TODO: support specifying the env\n\t\t\tNODE_ENV: 'development',\n\t\t\t// TODO: support specifying the port\n\t\t\tPORT: app.dev.type === 'script' ? String(app.dev.portNumber) : undefined,\n\t\t\tAPP_SERVER_PORT:\n\t\t\t\tapp.dev.type === 'script' ? String(app.dev.portNumber) : undefined,\n\t\t\t// let it pick a random port...\n\t\t\tREMIX_DEV_SERVER_WS_PORT: '',\n\t\t},\n\t})\n\tconst output: Array<OutputLine> = []\n\tconst entry: TestProcessEntry = { process: testProcess, output }\n\tfunction handleStdOutData(data: Buffer) {\n\t\toutput.push({\n\t\t\ttype: 'stdout',\n\t\t\tcontent: data.toString('utf-8'),\n\t\t\ttimestamp: Date.now(),\n\t\t})\n\t}\n\ttestProcess.stdout.on('data', handleStdOutData)\n\tfunction handleStdErrData(data: Buffer) {\n\t\toutput.push({\n\t\t\ttype: 'stderr',\n\t\t\tcontent: data.toString('utf-8'),\n\t\t\ttimestamp: Date.now(),\n\t\t})\n\t}\n\ttestProcess.stderr.on('data', handleStdErrData)\n\ttestProcess.on('exit', (code) => {\n\t\ttestProcess.stdout.off('data', handleStdOutData)\n\t\ttestProcess.stderr.off('data', handleStdErrData)\n\t\tentry.process = null\n\t\tentry.exitCode = code\n\t})\n\ttestProcesses.set(key, entry)\n\treturn testProcess\n}\n\nexport async function waitOnApp(app: App) {\n\tif (app.dev.type === 'script') {\n\t\tconst startTime = Date.now()\n\n\t\tconst retryInterval = 100\n\t\tconst timeout = 20_000\n\t\tlet lastError: unknown\n\t\twhile (Date.now() - startTime < timeout) {\n\t\t\ttry {\n\t\t\t\tconst url = getWorkshopUrl(app.dev.portNumber)\n\t\t\t\tawait fetch(url, {\n\t\t\t\t\tmethod: 'HEAD',\n\t\t\t\t\theaders: { Accept: '*/*' },\n\t\t\t\t})\n\t\t\t\treturn { status: 'success' } as const\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, retryInterval))\n\t\t\t}\n\t\t}\n\n\t\treturn { status: 'error', error: getErrorMessage(lastError) } as const\n\t}\n\treturn null\n}\n\nexport function isPortAvailable(port: number | string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst server = net.createServer()\n\t\tserver.unref()\n\t\tserver.on('error', () => resolve(false))\n\n\t\tserver.listen(Number(port), () => {\n\t\t\tserver.close(() => {\n\t\t\t\tresolve(true)\n\t\t\t})\n\t\t})\n\t})\n}\n\nexport async function isAppRunning(app: { name: string }) {\n\ttry {\n\t\tconst devProcess = devProcesses.get(app.name)\n\t\tif (!devProcess?.process.pid) return false\n\t\tconst found = await findProcess('pid', devProcess.process.pid)\n\t\treturn found.length > 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function isTestRunning(app: { name: string }) {\n\ttry {\n\t\tconst testProcess = testProcesses.get(app.name)\n\t\tif (!testProcess) return false\n\t\tif (testProcess.process === null) return false\n\t\ttestProcess.process.kill(0)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function getTestProcessEntry(app: { name: string }) {\n\treturn testProcesses.get(app.name)\n}\n\nexport function clearTestProcessEntry(app: { name: string }) {\n\treturn testProcesses.delete(app.name)\n}\n\nexport function getProcesses() {\n\treturn { devProcesses, testProcesses, sidecarProcesses }\n}\n\nexport function startSidecarProcesses(processes: Record<string, string>) {\n\tif (isDeployed) {\n\t\tconsole.log('Sidecar processes are not supported in deployed mode')\n\t\treturn\n\t}\n\n\tfor (const [name, command] of Object.entries(processes)) {\n\t\tstartSidecarProcess(name, command)\n\t}\n}\n\nexport function startSidecarProcess(name: string, command: string) {\n\tif (isDeployed)\n\t\tthrow new Error('cannot run sidecar processes in deployed mode')\n\n\t// if the process is already running, don't start it again\n\tif (sidecarProcesses.has(name)) {\n\t\tconsole.log(`Sidecar process ${name} is already running`)\n\t\treturn\n\t}\n\n\tconst color = getNextAvailableColor()\n\n\t// Spawn the command using shell to handle complex commands properly\n\tconst workshopRoot = getEnv().EPICSHOP_CONTEXT_CWD\n\tconst sidecarProcess = spawn(command, [], {\n\t\tshell: true,\n\t\tcwd: workshopRoot,\n\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\tNODE_ENV: 'development',\n\t\t},\n\t})\n\n\tconst prefix = chalk[color](`[${name}]`)\n\n\tfunction handleStdOutData(data: Buffer) {\n\t\tconsole.log(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tsidecarProcess.stdout?.on('data', handleStdOutData)\n\n\tfunction handleStdErrData(data: Buffer) {\n\t\tconsole.error(\n\t\t\tdata\n\t\t\t\t.toString('utf-8')\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => `${prefix} ${line}`)\n\t\t\t\t.join('\\n'),\n\t\t)\n\t}\n\tsidecarProcess.stderr?.on('data', handleStdErrData)\n\n\tsidecarProcesses.set(name, { color, process: sidecarProcess })\n\n\tsidecarProcess.on('exit', (code: number | null, signal: string | null) => {\n\t\tsidecarProcess.stdout?.off('data', handleStdOutData)\n\t\tsidecarProcess.stderr?.off('data', handleStdErrData)\n\t\tif (code === 0) {\n\t\t\tconsole.log(`${prefix} exited successfully`)\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\t`${prefix} exited with code ${code}${signal ? ` (signal: ${signal})` : ''}`,\n\t\t\t)\n\t\t}\n\t\tsidecarProcesses.delete(name)\n\t})\n\n\tsidecarProcess.on('error', (error) => {\n\t\tconsole.error(`${prefix} failed to start: ${error.message}`)\n\t\tsidecarProcesses.delete(name)\n\t})\n\n\tconsole.log(`${prefix} started`)\n}\n\nexport function stopSidecarProcesses() {\n\tif (isDeployed)\n\t\tthrow new Error('cannot stop sidecar processes in deployed mode')\n\n\tfor (const [name, proc] of sidecarProcesses.entries()) {\n\t\tconsole.log(`Stopping sidecar process: ${name}`)\n\t\tproc.process.kill()\n\t}\n\tsidecarProcesses.clear()\n}\n\nexport async function closeProcess(key: string) {\n\tif (isDeployed) throw new Error('cannot close processes in deployed mode')\n\tconst proc = devProcesses.get(key)\n\tif (proc) {\n\t\tconst exitedPromise = new Promise((resolve) =>\n\t\t\tproc.process.on('exit', resolve),\n\t\t)\n\t\tif (process.platform === 'win32') {\n\t\t\tconst { execa } = await import('execa')\n\t\t\tawait execa('taskkill', ['/pid', String(proc.process.pid), '/f', '/t'])\n\t\t} else {\n\t\t\tproc.process.kill()\n\t\t}\n\t\tawait Promise.race([\n\t\t\tnew Promise((resolve) => setTimeout(resolve, 500)),\n\t\t\texitedPromise,\n\t\t])\n\t\tawait stopPort(proc.port) // just in case 🤷♂️\n\t\tdevProcesses.delete(key)\n\t}\n}\n\nconst sleep = (t: number) => new Promise((resolve) => setTimeout(resolve, t))\n\nexport async function stopPort(port: string | number) {\n\tif (isDeployed) throw new Error('cannot stop ports in deployed mode')\n\tawait fkill(`:${port}`, { force: true, silent: true })\n\tawait waitForPortToBeAvailable(port)\n}\n\nexport async function waitForPortToBeAvailable(port: string | number) {\n\t// wait for the port to become available again\n\tconst timeout = Date.now() + 10_000\n\tlet portAvailable = false\n\tdo {\n\t\tportAvailable = await isPortAvailable(port)\n\t\tawait sleep(100)\n\t} while (!portAvailable && Date.now() < timeout)\n\tif (!portAvailable) {\n\t\tconsole.error('Timed out waiting for the port to become available')\n\t}\n}\n"]}
|