@epic-web/workshop-utils 6.14.4 → 6.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,qBAAqB,CAAA;AAcxC,OAAO,EAA2B,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAG1E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACyB,CAAA;AACtD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACwB,CAAA;AACpD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACwB,CAAA;AACpD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAC2B,CAAA;AAC1D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAyC,CAAA;AAC/D,eAAO,MAAM,aAAa,iBAAgD,CAAA;AAC1E,eAAO,MAAM,cAAc,iBAAiD,CAAA;AAC5E,eAAO,MAAM,qBAAqB,iBAEjC,CAAA;AACD,eAAO,MAAM,iBAAiB,iBACoB,CAAA;AAClD,eAAO,MAAM,OAAO;;;;;CAAwC,CAAA;AAC5D,eAAO,MAAM,gCAAgC;UACtC,MAAM;WACL,MAAM,GAAG,IAAI;qBACH,KAAK,CAAC,MAAM,CAAC;EACO,CAAA;AACtC,eAAO,MAAM,oBAAoB;;;;;CAEhC,CAAA;AACD,eAAO,MAAM,eAAe;;;;;CAAiD,CAAA;AAC7E,eAAO,MAAM,oBAAoB;;;0BACd,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;0BAHL,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;;0BAHL,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;;CACE,CAAA;AAC1B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAC+B,CAAA;AAC9D,eAAO,MAAM,mBAAmB;;;;;CAE/B,CAAA;AAID,eAAO,MAAM,OAAO,kBAAkC,CAAA;AAsBtD,wBAAsB,sBAAsB,iCAE3C;AAED,wBAAsB,WAAW,8BAUhC;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM;;;;;EAsB9D;AAED,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,2BAoChE;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,EACtC,OAAO,EACP,OAAO,EACP,GAAG,EACH,SAA2E,EAC3E,oBAAoB,EACpB,GAAG,OAAO,EACV,EAAE,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,GAAG;IAClD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oBAAoB,CAAC,EAAE,KAAK,CAAA;CAC5B,GAAG,OAAO,CAAC,KAAK,CAAC,CAwBjB;AAED,wBAAsB,gBAAgB,CAAC,EACtC,UAAU,EACV,OAAO,EACP,GAAG,GACH,EAAE;IACF,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ,oBAaA"}
1
+ {"version":3,"file":"cache.server.d.ts","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,qBAAqB,CAAA;AAcxC,OAAO,EAA2B,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAG1E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACyB,CAAA;AACtD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACwB,CAAA;AACpD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EACwB,CAAA;AACpD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAC2B,CAAA;AAC1D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAyC,CAAA;AAC/D,eAAO,MAAM,aAAa,iBAAgD,CAAA;AAC1E,eAAO,MAAM,cAAc,iBAAiD,CAAA;AAC5E,eAAO,MAAM,qBAAqB,iBAEjC,CAAA;AACD,eAAO,MAAM,iBAAiB,iBACoB,CAAA;AAClD,eAAO,MAAM,OAAO;;;;;CAAwC,CAAA;AAC5D,eAAO,MAAM,gCAAgC;UACtC,MAAM;WACL,MAAM,GAAG,IAAI;qBACH,KAAK,CAAC,MAAM,CAAC;EACO,CAAA;AACtC,eAAO,MAAM,oBAAoB;;;;;CAEhC,CAAA;AACD,eAAO,MAAM,eAAe;;;;;CAAiD,CAAA;AAC7E,eAAO,MAAM,oBAAoB;;;0BACd,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;0BAHL,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;;0BAHL,OAAO;qBACZ,MAAM;sBACL,MAAM;kBACV,MAAM,GAAG,IAAI;;;CACE,CAAA;AAC1B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAC+B,CAAA;AAC9D,eAAO,MAAM,mBAAmB;;;;;CAE/B,CAAA;AAID,eAAO,MAAM,OAAO,kBAAkC,CAAA;AAuDtD,wBAAsB,sBAAsB,iCAE3C;AAED,wBAAsB,WAAW,8BAUhC;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM;;;;;EAsB9D;AAED,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,2BA4GhE;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,EACtC,OAAO,EACP,OAAO,EACP,GAAG,EACH,SAA2E,EAC3E,oBAAoB,EACpB,GAAG,OAAO,EACV,EAAE,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,GAAG;IAClD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oBAAoB,CAAC,EAAE,KAAK,CAAA;CAC5B,GAAG,OAAO,CAAC,KAAK,CAAC,CAwBjB;AAED,wBAAsB,gBAAgB,CAAC,EACtC,UAAU,EACV,OAAO,EACP,GAAG,GACH,EAAE;IACF,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;CACZ,oBAaA"}
@@ -36,8 +36,33 @@ async function readJsonFilesInDirectory(dir) {
36
36
  return [file, subEntries];
37
37
  }
38
38
  else {
39
- const data = await fsExtra.readJSON(filePath);
40
- return [file, data];
39
+ const maxRetries = 2;
40
+ const baseDelay = 25; // shorter delay for directory listing
41
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
42
+ try {
43
+ const data = await fsExtra.readJSON(filePath);
44
+ return [file, data];
45
+ }
46
+ catch (error) {
47
+ // Handle JSON parsing errors (could be race condition or corruption)
48
+ if (error instanceof SyntaxError &&
49
+ error.message.includes('JSON')) {
50
+ // If this is a retry attempt, it might be a race condition
51
+ if (attempt < maxRetries) {
52
+ const delay = baseDelay * Math.pow(2, attempt);
53
+ console.warn(`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for directory listing ${filePath}, retrying in ${delay}ms...`);
54
+ await new Promise((resolve) => setTimeout(resolve, delay));
55
+ continue;
56
+ }
57
+ // Final attempt failed, skip the file
58
+ console.warn(`Skipping corrupted JSON file in directory listing after ${attempt + 1} attempts: ${filePath}`);
59
+ return [file, null];
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ // This should never be reached, but just in case
65
+ return [file, null];
41
66
  }
42
67
  }));
43
68
  return Object.fromEntries(entries);
@@ -80,25 +105,77 @@ export function makeSingletonCache(name) {
80
105
  }
81
106
  export function makeSingletonFsCache(name) {
82
107
  return remember(name, () => {
83
- const cacheDir = path.join(os.homedir(), '.epicshop', 'cache', name);
108
+ const cacheDir = path.join(os.homedir(), '.epicshop', 'cache', process.env.EPICSHOP_WORKSHOP_INSTANCE_ID, name);
84
109
  const fsCache = {
85
110
  name: `Filesystem cache (${name})`,
86
111
  async get(key) {
87
- try {
88
- const filePath = path.join(cacheDir, md5(key));
89
- const data = await fsExtra.readJSON(filePath);
90
- if (data.entry)
91
- return data.entry;
92
- return null;
93
- }
94
- catch (error) {
95
- if (error instanceof Error &&
96
- 'code' in error &&
97
- error.code === 'ENOENT') {
112
+ const filePath = path.join(cacheDir, md5(key));
113
+ const maxRetries = 3;
114
+ const baseDelay = 10;
115
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
116
+ try {
117
+ const data = await fsExtra.readJSON(filePath);
118
+ if (data.entry)
119
+ return data.entry;
98
120
  return null;
99
121
  }
100
- throw error;
122
+ catch (error) {
123
+ if (error instanceof Error &&
124
+ 'code' in error &&
125
+ error.code === 'ENOENT') {
126
+ return null;
127
+ }
128
+ // Handle JSON parsing errors (could be race condition or corruption)
129
+ if (error instanceof SyntaxError &&
130
+ error.message.includes('JSON')) {
131
+ // If this is a retry attempt, it might be a race condition
132
+ if (attempt < maxRetries) {
133
+ const delay = baseDelay * Math.pow(2, attempt); // exponential backoff
134
+ console.warn(`JSON parsing error on attempt ${attempt + 1}/${maxRetries + 1} for ${filePath}, retrying in ${delay}ms...`);
135
+ await new Promise((resolve) => setTimeout(resolve, delay));
136
+ continue;
137
+ }
138
+ // Final attempt failed, treat as corrupted file
139
+ // Log to Sentry if available
140
+ if (process.env.SENTRY_DSN && process.env.EPICSHOP_IS_PUBLISHED) {
141
+ try {
142
+ const Sentry = await import('@sentry/react-router');
143
+ Sentry.captureException(error, {
144
+ tags: {
145
+ error_type: 'corrupted_cache_file',
146
+ cache_name: name,
147
+ cache_key: key,
148
+ retry_attempts: attempt.toString(),
149
+ },
150
+ extra: {
151
+ filePath,
152
+ errorMessage: error.message,
153
+ cacheName: name,
154
+ cacheKey: key,
155
+ retryAttempts: attempt,
156
+ },
157
+ });
158
+ }
159
+ catch (sentryError) {
160
+ console.error('Failed to log to Sentry:', sentryError);
161
+ }
162
+ }
163
+ // Delete the corrupted file
164
+ try {
165
+ await fsExtra.remove(filePath);
166
+ console.warn(`Deleted corrupted cache file after ${attempt + 1} attempts: ${filePath}`);
167
+ }
168
+ catch (deleteError) {
169
+ console.error(`Failed to delete corrupted cache file ${filePath}:`, deleteError);
170
+ }
171
+ return null;
172
+ }
173
+ // For other errors, don't retry
174
+ throw error;
175
+ }
101
176
  }
177
+ // This should never be reached, but just in case
178
+ return null;
102
179
  },
103
180
  async set(key, entry) {
104
181
  const filePath = path.join(cacheDir, md5(key));
@@ -1 +1 @@
1
- {"version":3,"file":"cache.server.js","sourceRoot":"","sources":["../../src/cache.server.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'os'\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 { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnectionCached } from './utils.server.js'\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 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\nconst cacheDir = path.join(os.homedir(), '.epicshop', 'cache')\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 data = await fsExtra.readJSON(filePath)\n\t\t\t\treturn [file, data]\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 (process.env.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 cacheDir = path.join(os.homedir(), '.epicshop', 'cache', name)\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\ttry {\n\t\t\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\t\t\t\tconst data = await fsExtra.readJSON(filePath)\n\t\t\t\t\tif (data.entry) return data.entry\n\t\t\t\t\treturn null\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof Error &&\n\t\t\t\t\t\t'code' in error &&\n\t\t\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn null\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync set(key, entry) {\n\t\t\t\tconst filePath = path.join(cacheDir, md5(key))\n\t\t\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t\t\tawait fsExtra.writeJSON(filePath, { key, entry })\n\t\t\t},\n\t\t\tasync delete(key) {\n\t\t\t\tconst filePath = path.join(cacheDir, 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":["import os from 'os'\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 { type Notification } from './notifications.server.js'\nimport { cachifiedTimingReporter, type Timings } from './timing.server.js'\nimport { checkConnectionCached } from './utils.server.js'\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 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\nconst cacheDir = path.join(os.homedir(), '.epicshop', 'cache')\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 (process.env.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 cacheDir = path.join(\n\t\t\tos.homedir(),\n\t\t\t'.epicshop',\n\t\t\t'cache',\n\t\t\tprocess.env.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(cacheDir, 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 (process.env.SENTRY_DSN && process.env.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(cacheDir, md5(key))\n\t\t\t\tawait fsExtra.ensureDir(path.dirname(filePath))\n\t\t\t\tawait fsExtra.writeJSON(filePath, { key, entry })\n\t\t\t},\n\t\t\tasync delete(key) {\n\t\t\t\tconst filePath = path.join(cacheDir, 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 +1 @@
1
- {"version":3,"file":"db.server.d.ts","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAA;AACF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyBvB,CAAA;AAkBb,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCd,CAAA;AAKF,wBAAsB,WAAW,oBAQhC;AAED,wBAAsB,MAAM,kBAY3B;AAED,wBAAsB,QAAQ,8BAU7B;AAoBD,wBAAsB,WAAW;;;;;;;;;sBAuBhC;AAED,wBAAsB,eAAe,CAAC,EACrC,OAAO,EACP,UAAU,GACV,EAAE;IACF,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;;;;;;;;;GAeA;AAED,wBAAsB,WAAW,CAAC,EACjC,EAAE,EACF,QAAQ,EACR,KAA6B,EAC7B,IAAI,GACJ,EAAE;IACF,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;;;;;;;;;GAiBA;AAED,wBAAsB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;UAGnC;AAED,wBAAsB,cAAc,CACnC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;GAyBtD;AAED,wBAAsB,qBAAqB,sBAG1C;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,qBAYhD;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,+BASvE;AAED,wBAAsB,qBAAqB,2BAG1C;AAED,wBAAsB,kBAAkB;;;;WAGvC;AAED,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM;;GAehE"}
1
+ {"version":3,"file":"db.server.d.ts","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,QAAA,MAAM,cAAc;;;;;;;;;;;;EAIlB,CAAA;AACF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyBvB,CAAA;AAkBb,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCd,CAAA;AAKF,wBAAsB,WAAW,oBAQhC;AAED,wBAAsB,MAAM,kBAY3B;AAED,wBAAsB,QAAQ,8BAU7B;AA2DD,wBAAsB,WAAW;;;;;;;;;sBAuBhC;AAED,wBAAsB,eAAe,CAAC,EACrC,OAAO,EACP,UAAU,GACV,EAAE;IACF,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;;;;;;;;;GAeA;AAED,wBAAsB,WAAW,CAAC,EACjC,EAAE,EACF,QAAQ,EACR,KAA6B,EAC7B,IAAI,GACJ,EAAE;IACF,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;;;;;;;;;GAiBA;AAED,wBAAsB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;UAGnC;AAED,wBAAsB,cAAc,CACnC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;GAyBtD;AAED,wBAAsB,qBAAqB,sBAG1C;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,qBAYhD;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,+BASvE;AAED,wBAAsB,qBAAqB,2BAG1C;AAED,wBAAsB,kBAAkB;;;;WAGvC;AAED,wBAAsB,0BAA0B,CAAC,QAAQ,EAAE,MAAM;;GAehE"}
@@ -122,15 +122,48 @@ export async function deleteDb() {
122
122
  async function readDb() {
123
123
  if (process.env.EPICSHOP_DEPLOYED)
124
124
  return null;
125
- try {
126
- if (await fsExtra.exists(dbPath)) {
127
- const db = DataSchema.parse(await fsExtra.readJSON(dbPath));
128
- return db;
125
+ const maxRetries = 3;
126
+ const baseDelay = 10;
127
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
128
+ try {
129
+ if (await fsExtra.exists(dbPath)) {
130
+ const db = DataSchema.parse(await fsExtra.readJSON(dbPath));
131
+ return db;
132
+ }
133
+ return null;
134
+ }
135
+ catch (error) {
136
+ // If this is a retry attempt, it might be a race condition
137
+ if (attempt < maxRetries) {
138
+ const delay = baseDelay * Math.pow(2, attempt);
139
+ console.warn(`Database read error on attempt ${attempt + 1}/${maxRetries + 1}, retrying in ${delay}ms...`);
140
+ await new Promise((resolve) => setTimeout(resolve, delay));
141
+ continue;
142
+ }
143
+ // Final attempt failed, handle as corrupted file
144
+ console.error(`Error reading the database in ${dbPath} after ${attempt + 1} attempts, moving it to a .bkp file to avoid parsing errors in the future`, error);
145
+ // Log to Sentry if available
146
+ if (process.env.SENTRY_DSN && process.env.EPICSHOP_IS_PUBLISHED) {
147
+ try {
148
+ const Sentry = await import('@sentry/react-router');
149
+ Sentry.captureException(error, {
150
+ tags: {
151
+ error_type: 'corrupted_database_file',
152
+ retry_attempts: attempt.toString(),
153
+ },
154
+ extra: {
155
+ filePath: dbPath,
156
+ errorMessage: error instanceof Error ? error.message : String(error),
157
+ retryAttempts: attempt,
158
+ },
159
+ });
160
+ }
161
+ catch (sentryError) {
162
+ console.error('Failed to log to Sentry:', sentryError);
163
+ }
164
+ }
165
+ void fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => { });
129
166
  }
130
- }
131
- catch (error) {
132
- console.error(`Error reading the database in ${dbPath}, moving it to a .bkp file to avoid parsing errors in the future`, error);
133
- void fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => { });
134
167
  }
135
168
  return null;
136
169
  }
@@ -1 +1 @@
1
- {"version":3,"file":"db.server.js","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'os'\nimport path from 'path'\nimport { createId as cuid } from '@paralleldrive/cuid2'\nimport fsExtra from 'fs-extra'\nimport { redirect } from 'react-router'\nimport { z } from 'zod'\nimport { getWorkshopConfig } from './config.server.js'\n\nconst TokenSetSchema = z.object({\n\taccess_token: z.string(),\n\ttoken_type: z.string(),\n\tscope: z.string(),\n})\nexport const PlayerPreferencesSchema = z\n\t.object({\n\t\tminResolution: z.number().optional(),\n\t\tmaxResolution: z.number().optional(),\n\t\tvolumeRate: z.number().optional(),\n\t\tplaybackRate: z.number().optional(),\n\t\tautoplay: z.boolean().optional(),\n\t\tsubtitle: z\n\t\t\t.object({\n\t\t\t\tid: z.string().nullable().default(null),\n\t\t\t\tmode: z\n\t\t\t\t\t.literal('disabled')\n\t\t\t\t\t.or(z.literal('hidden'))\n\t\t\t\t\t.or(z.literal('showing'))\n\t\t\t\t\t.nullable()\n\t\t\t\t\t.default('disabled'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.default({}),\n\t\tmuted: z.boolean().optional(),\n\t\ttheater: z.boolean().optional(),\n\t\tdefaultView: z.string().optional(),\n\t\tactiveSidebarTab: z.number().optional(),\n\t})\n\t.optional()\n\t.default({})\n\nconst PresencePreferencesSchema = z\n\t.object({\n\t\toptOut: z.boolean(),\n\t})\n\t.optional()\n\t.default({ optOut: false })\n\nconst AuthInfoSchema = z.object({\n\tid: z.string(),\n\ttokenSet: TokenSetSchema,\n\temail: z.string(),\n\tname: z.string().nullable().optional(),\n})\n\nconst MutedNotificationSchema = z.array(z.string()).default([])\n\nconst DataSchema = z.object({\n\tonboarding: z\n\t\t.object({\n\t\t\ttourVideosWatched: z.array(z.string()).default([]),\n\t\t})\n\t\t.passthrough()\n\t\t.optional()\n\t\t.default({ tourVideosWatched: [] }),\n\tpreferences: z\n\t\t.object({\n\t\t\tplayer: PlayerPreferencesSchema,\n\t\t\tpresence: PresencePreferencesSchema,\n\t\t\tplayground: z\n\t\t\t\t.object({\n\t\t\t\t\tpersist: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tfontSize: z.number().optional(),\n\t\t\texerciseWarning: z\n\t\t\t\t.object({\n\t\t\t\t\tdismissed: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional()\n\t\t\t\t.default({ dismissed: false }),\n\t\t})\n\t\t.optional()\n\t\t.default({}),\n\t// deprecated. Probably safe to remove in May 2026:\n\tauthInfo: AuthInfoSchema.optional(),\n\t// new:\n\tauthInfos: z.record(z.string(), AuthInfoSchema).optional(),\n\tclientId: z.string().optional(),\n\tmutedNotifications: MutedNotificationSchema.optional(),\n})\n\nconst appDir = path.join(os.homedir(), '.epicshop')\nconst dbPath = path.join(appDir, 'data.json')\n\nexport async function getClientId() {\n\tconst data = await readDb()\n\tif (data?.clientId) return data.clientId\n\n\tconst clientId = cuid()\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, { ...data, clientId })\n\treturn clientId\n}\n\nexport async function logout() {\n\tconst config = getWorkshopConfig()\n\tconst host = config.product.host\n\tif (host) {\n\t\tconst data = await readDb()\n\t\tconst newAuthInfos = { ...data?.authInfos }\n\t\tdelete newAuthInfos[host]\n\t\tawait fsExtra.writeJSON(dbPath, {\n\t\t\t...data,\n\t\t\tauthInfos: newAuthInfos,\n\t\t})\n\t}\n}\n\nexport async function deleteDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(dbPath)) {\n\t\t\tawait fsExtra.remove(dbPath)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the database in ${dbPath}`, error)\n\t}\n}\n\nasync function readDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(dbPath)) {\n\t\t\tconst db = DataSchema.parse(await fsExtra.readJSON(dbPath))\n\t\t\treturn db\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t`Error reading the database in ${dbPath}, moving it to a .bkp file to avoid parsing errors in the future`,\n\t\t\terror,\n\t\t)\n\t\tvoid fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {})\n\t}\n\treturn null\n}\n\nexport async function getAuthInfo() {\n\tconst config = getWorkshopConfig()\n\tconst data = await readDb()\n\tif (config.product.host && typeof data?.authInfos === 'object') {\n\t\tif (config.product.host in data.authInfos) {\n\t\t\treturn data.authInfos[config.product.host]\n\t\t}\n\t}\n\n\t// special case for non-epicweb/epicreact workshops\n\tif (\n\t\t!config.product.host ||\n\t\tconfig.product.host === 'epicweb.dev' ||\n\t\tconfig.product.host === 'epicreact.dev'\n\t) {\n\t\t// upgrade from old authInfo to new authInfos\n\t\tif (data?.authInfo && config.product.host) {\n\t\t\tawait setAuthInfo(data.authInfo)\n\t\t}\n\t\treturn data?.authInfo ?? null\n\t}\n\n\treturn null\n}\n\nexport async function requireAuthInfo({\n\trequest,\n\tredirectTo,\n}: {\n\trequest: Request\n\tredirectTo?: string | null\n}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\tconst requestUrl = new URL(request.url)\n\t\tredirectTo =\n\t\t\tredirectTo === null\n\t\t\t\t? null\n\t\t\t\t: (redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`)\n\t\tconst loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null\n\t\tconst loginRedirect = ['/login', loginParams?.toString()]\n\t\t\t.filter(Boolean)\n\t\t\t.join('?')\n\t\tthrow redirect(loginRedirect)\n\t}\n\treturn authInfo\n}\n\nexport async function setAuthInfo({\n\tid,\n\ttokenSet,\n\temail = 'unknown@example.com',\n\tname,\n}: {\n\tid: string\n\ttokenSet: Partial<z.infer<typeof TokenSetSchema>>\n\temail?: string | null\n\tname?: string | null\n}) {\n\tconst data = await readDb()\n\tconst authInfo = AuthInfoSchema.parse({ id, tokenSet, email, name })\n\tawait fsExtra.ensureDir(appDir)\n\tconst config = getWorkshopConfig()\n\tif (config.product.host) {\n\t\tawait fsExtra.writeJSON(dbPath, {\n\t\t\t...data,\n\t\t\tauthInfos: {\n\t\t\t\t...data?.authInfos,\n\t\t\t\t[config.product.host]: authInfo,\n\t\t\t},\n\t\t})\n\t} else {\n\t\tawait fsExtra.writeJSON(dbPath, { ...data, authInfo })\n\t}\n\treturn authInfo\n}\n\nexport async function getPreferences() {\n\tconst data = await readDb()\n\treturn data?.preferences ?? null\n}\n\nexport async function setPreferences(\n\tpreferences: z.input<typeof DataSchema>['preferences'],\n) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: {\n\t\t\t...data?.preferences,\n\t\t\t...preferences,\n\t\t\tplayer: {\n\t\t\t\t...data?.preferences?.player,\n\t\t\t\t...preferences?.player,\n\t\t\t},\n\t\t\tpresence: {\n\t\t\t\t...data?.preferences?.presence,\n\t\t\t\t...preferences?.presence,\n\t\t\t},\n\t\t\texerciseWarning: {\n\t\t\t\t...data?.preferences?.exerciseWarning,\n\t\t\t\t...preferences?.exerciseWarning,\n\t\t\t},\n\t\t},\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.preferences\n}\n\nexport async function getMutedNotifications() {\n\tconst data = await readDb()\n\treturn data?.mutedNotifications ?? []\n}\n\nexport async function muteNotification(id: string) {\n\tconst data = await readDb()\n\tconst mutedNotifications = Array.from(\n\t\tnew Set([...(data?.mutedNotifications ?? []), id]),\n\t)\n\tconst updatedData = {\n\t\t...data,\n\t\tmutedNotifications,\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn mutedNotifications\n}\n\nexport async function setFontSizePreference(fontSize: number | undefined) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: { ...data?.preferences, fontSize },\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.preferences.fontSize\n}\n\nexport async function getFontSizePreference() {\n\tconst data = await readDb()\n\treturn data?.preferences?.fontSize ?? null\n}\n\nexport async function readOnboardingData() {\n\tconst data = await readDb()\n\treturn data?.onboarding ?? null\n}\n\nexport async function markOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: [\n\t\t\t\t...(data?.onboarding.tourVideosWatched ?? []),\n\t\t\t\tvideoUrl,\n\t\t\t].filter(Boolean),\n\t\t},\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.onboarding\n}\n"]}
1
+ {"version":3,"file":"db.server.js","sourceRoot":"","sources":["../../src/db.server.ts"],"names":[],"mappings":"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","sourcesContent":["import os from 'os'\nimport path from 'path'\nimport { createId as cuid } from '@paralleldrive/cuid2'\nimport fsExtra from 'fs-extra'\nimport { redirect } from 'react-router'\nimport { z } from 'zod'\nimport { getWorkshopConfig } from './config.server.js'\n\nconst TokenSetSchema = z.object({\n\taccess_token: z.string(),\n\ttoken_type: z.string(),\n\tscope: z.string(),\n})\nexport const PlayerPreferencesSchema = z\n\t.object({\n\t\tminResolution: z.number().optional(),\n\t\tmaxResolution: z.number().optional(),\n\t\tvolumeRate: z.number().optional(),\n\t\tplaybackRate: z.number().optional(),\n\t\tautoplay: z.boolean().optional(),\n\t\tsubtitle: z\n\t\t\t.object({\n\t\t\t\tid: z.string().nullable().default(null),\n\t\t\t\tmode: z\n\t\t\t\t\t.literal('disabled')\n\t\t\t\t\t.or(z.literal('hidden'))\n\t\t\t\t\t.or(z.literal('showing'))\n\t\t\t\t\t.nullable()\n\t\t\t\t\t.default('disabled'),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.default({}),\n\t\tmuted: z.boolean().optional(),\n\t\ttheater: z.boolean().optional(),\n\t\tdefaultView: z.string().optional(),\n\t\tactiveSidebarTab: z.number().optional(),\n\t})\n\t.optional()\n\t.default({})\n\nconst PresencePreferencesSchema = z\n\t.object({\n\t\toptOut: z.boolean(),\n\t})\n\t.optional()\n\t.default({ optOut: false })\n\nconst AuthInfoSchema = z.object({\n\tid: z.string(),\n\ttokenSet: TokenSetSchema,\n\temail: z.string(),\n\tname: z.string().nullable().optional(),\n})\n\nconst MutedNotificationSchema = z.array(z.string()).default([])\n\nconst DataSchema = z.object({\n\tonboarding: z\n\t\t.object({\n\t\t\ttourVideosWatched: z.array(z.string()).default([]),\n\t\t})\n\t\t.passthrough()\n\t\t.optional()\n\t\t.default({ tourVideosWatched: [] }),\n\tpreferences: z\n\t\t.object({\n\t\t\tplayer: PlayerPreferencesSchema,\n\t\t\tpresence: PresencePreferencesSchema,\n\t\t\tplayground: z\n\t\t\t\t.object({\n\t\t\t\t\tpersist: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional(),\n\t\t\tfontSize: z.number().optional(),\n\t\t\texerciseWarning: z\n\t\t\t\t.object({\n\t\t\t\t\tdismissed: z.boolean().default(false),\n\t\t\t\t})\n\t\t\t\t.optional()\n\t\t\t\t.default({ dismissed: false }),\n\t\t})\n\t\t.optional()\n\t\t.default({}),\n\t// deprecated. Probably safe to remove in May 2026:\n\tauthInfo: AuthInfoSchema.optional(),\n\t// new:\n\tauthInfos: z.record(z.string(), AuthInfoSchema).optional(),\n\tclientId: z.string().optional(),\n\tmutedNotifications: MutedNotificationSchema.optional(),\n})\n\nconst appDir = path.join(os.homedir(), '.epicshop')\nconst dbPath = path.join(appDir, 'data.json')\n\nexport async function getClientId() {\n\tconst data = await readDb()\n\tif (data?.clientId) return data.clientId\n\n\tconst clientId = cuid()\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, { ...data, clientId })\n\treturn clientId\n}\n\nexport async function logout() {\n\tconst config = getWorkshopConfig()\n\tconst host = config.product.host\n\tif (host) {\n\t\tconst data = await readDb()\n\t\tconst newAuthInfos = { ...data?.authInfos }\n\t\tdelete newAuthInfos[host]\n\t\tawait fsExtra.writeJSON(dbPath, {\n\t\t\t...data,\n\t\t\tauthInfos: newAuthInfos,\n\t\t})\n\t}\n}\n\nexport async function deleteDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\ttry {\n\t\tif (await fsExtra.exists(dbPath)) {\n\t\t\tawait fsExtra.remove(dbPath)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Error deleting the database in ${dbPath}`, error)\n\t}\n}\n\nasync function readDb() {\n\tif (process.env.EPICSHOP_DEPLOYED) return null\n\n\tconst maxRetries = 3\n\tconst baseDelay = 10\n\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\tif (await fsExtra.exists(dbPath)) {\n\t\t\t\tconst db = DataSchema.parse(await fsExtra.readJSON(dbPath))\n\t\t\t\treturn db\n\t\t\t}\n\t\t\treturn null\n\t\t} catch (error) {\n\t\t\t// If this is a retry attempt, it might be a race condition\n\t\t\tif (attempt < maxRetries) {\n\t\t\t\tconst delay = baseDelay * Math.pow(2, attempt)\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Database read error on attempt ${attempt + 1}/${maxRetries + 1}, retrying in ${delay}ms...`,\n\t\t\t\t)\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Final attempt failed, handle as corrupted file\n\t\t\tconsole.error(\n\t\t\t\t`Error reading the database in ${dbPath} after ${attempt + 1} attempts, moving it to a .bkp file to avoid parsing errors in the future`,\n\t\t\t\terror,\n\t\t\t)\n\n\t\t\t// Log to Sentry if available\n\t\t\tif (process.env.SENTRY_DSN && process.env.EPICSHOP_IS_PUBLISHED) {\n\t\t\t\ttry {\n\t\t\t\t\tconst Sentry = await import('@sentry/react-router')\n\t\t\t\t\tSentry.captureException(error, {\n\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\terror_type: 'corrupted_database_file',\n\t\t\t\t\t\t\tretry_attempts: attempt.toString(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\textra: {\n\t\t\t\t\t\t\tfilePath: dbPath,\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t\tretryAttempts: attempt,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t} catch (sentryError) {\n\t\t\t\t\tconsole.error('Failed to log to Sentry:', sentryError)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {})\n\t\t}\n\t}\n\treturn null\n}\n\nexport async function getAuthInfo() {\n\tconst config = getWorkshopConfig()\n\tconst data = await readDb()\n\tif (config.product.host && typeof data?.authInfos === 'object') {\n\t\tif (config.product.host in data.authInfos) {\n\t\t\treturn data.authInfos[config.product.host]\n\t\t}\n\t}\n\n\t// special case for non-epicweb/epicreact workshops\n\tif (\n\t\t!config.product.host ||\n\t\tconfig.product.host === 'epicweb.dev' ||\n\t\tconfig.product.host === 'epicreact.dev'\n\t) {\n\t\t// upgrade from old authInfo to new authInfos\n\t\tif (data?.authInfo && config.product.host) {\n\t\t\tawait setAuthInfo(data.authInfo)\n\t\t}\n\t\treturn data?.authInfo ?? null\n\t}\n\n\treturn null\n}\n\nexport async function requireAuthInfo({\n\trequest,\n\tredirectTo,\n}: {\n\trequest: Request\n\tredirectTo?: string | null\n}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\tconst requestUrl = new URL(request.url)\n\t\tredirectTo =\n\t\t\tredirectTo === null\n\t\t\t\t? null\n\t\t\t\t: (redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`)\n\t\tconst loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null\n\t\tconst loginRedirect = ['/login', loginParams?.toString()]\n\t\t\t.filter(Boolean)\n\t\t\t.join('?')\n\t\tthrow redirect(loginRedirect)\n\t}\n\treturn authInfo\n}\n\nexport async function setAuthInfo({\n\tid,\n\ttokenSet,\n\temail = 'unknown@example.com',\n\tname,\n}: {\n\tid: string\n\ttokenSet: Partial<z.infer<typeof TokenSetSchema>>\n\temail?: string | null\n\tname?: string | null\n}) {\n\tconst data = await readDb()\n\tconst authInfo = AuthInfoSchema.parse({ id, tokenSet, email, name })\n\tawait fsExtra.ensureDir(appDir)\n\tconst config = getWorkshopConfig()\n\tif (config.product.host) {\n\t\tawait fsExtra.writeJSON(dbPath, {\n\t\t\t...data,\n\t\t\tauthInfos: {\n\t\t\t\t...data?.authInfos,\n\t\t\t\t[config.product.host]: authInfo,\n\t\t\t},\n\t\t})\n\t} else {\n\t\tawait fsExtra.writeJSON(dbPath, { ...data, authInfo })\n\t}\n\treturn authInfo\n}\n\nexport async function getPreferences() {\n\tconst data = await readDb()\n\treturn data?.preferences ?? null\n}\n\nexport async function setPreferences(\n\tpreferences: z.input<typeof DataSchema>['preferences'],\n) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: {\n\t\t\t...data?.preferences,\n\t\t\t...preferences,\n\t\t\tplayer: {\n\t\t\t\t...data?.preferences?.player,\n\t\t\t\t...preferences?.player,\n\t\t\t},\n\t\t\tpresence: {\n\t\t\t\t...data?.preferences?.presence,\n\t\t\t\t...preferences?.presence,\n\t\t\t},\n\t\t\texerciseWarning: {\n\t\t\t\t...data?.preferences?.exerciseWarning,\n\t\t\t\t...preferences?.exerciseWarning,\n\t\t\t},\n\t\t},\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.preferences\n}\n\nexport async function getMutedNotifications() {\n\tconst data = await readDb()\n\treturn data?.mutedNotifications ?? []\n}\n\nexport async function muteNotification(id: string) {\n\tconst data = await readDb()\n\tconst mutedNotifications = Array.from(\n\t\tnew Set([...(data?.mutedNotifications ?? []), id]),\n\t)\n\tconst updatedData = {\n\t\t...data,\n\t\tmutedNotifications,\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn mutedNotifications\n}\n\nexport async function setFontSizePreference(fontSize: number | undefined) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tpreferences: { ...data?.preferences, fontSize },\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.preferences.fontSize\n}\n\nexport async function getFontSizePreference() {\n\tconst data = await readDb()\n\treturn data?.preferences?.fontSize ?? null\n}\n\nexport async function readOnboardingData() {\n\tconst data = await readDb()\n\treturn data?.onboarding ?? null\n}\n\nexport async function markOnboardingVideoWatched(videoUrl: string) {\n\tconst data = await readDb()\n\tconst updatedData = {\n\t\t...data,\n\t\tonboarding: {\n\t\t\t...data?.onboarding,\n\t\t\ttourVideosWatched: [\n\t\t\t\t...(data?.onboarding.tourVideosWatched ?? []),\n\t\t\t\tvideoUrl,\n\t\t\t].filter(Boolean),\n\t\t},\n\t}\n\tawait fsExtra.ensureDir(appDir)\n\tawait fsExtra.writeJSON(dbPath, updatedData)\n\treturn updatedData.onboarding\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  declare const schema: z.ZodEffects<z.ZodObject<{
3
3
  EPICSHOP_CONTEXT_CWD: z.ZodDefault<z.ZodString>;
4
+ EPICSHOP_WORKSHOP_INSTANCE_ID: z.ZodDefault<z.ZodString>;
4
5
  NODE_ENV: z.ZodDefault<z.ZodEnum<["production", "development", "test"]>>;
5
6
  EPICSHOP_GITHUB_REPO: z.ZodDefault<z.ZodString>;
6
7
  EPICSHOP_GITHUB_ROOT: z.ZodDefault<z.ZodString>;
@@ -15,6 +16,7 @@ declare const schema: z.ZodEffects<z.ZodObject<{
15
16
  SENTRY_PROJECT_ID: z.ZodDefault<z.ZodString>;
16
17
  }, "strip", z.ZodTypeAny, {
17
18
  EPICSHOP_CONTEXT_CWD: string;
19
+ EPICSHOP_WORKSHOP_INSTANCE_ID: string;
18
20
  NODE_ENV: "production" | "development" | "test";
19
21
  EPICSHOP_GITHUB_REPO: string;
20
22
  EPICSHOP_GITHUB_ROOT: string;
@@ -29,6 +31,7 @@ declare const schema: z.ZodEffects<z.ZodObject<{
29
31
  EPICSHOP_APP_LOCATION?: string | undefined;
30
32
  }, {
31
33
  EPICSHOP_CONTEXT_CWD?: string | undefined;
34
+ EPICSHOP_WORKSHOP_INSTANCE_ID?: string | undefined;
32
35
  NODE_ENV?: "production" | "development" | "test" | undefined;
33
36
  EPICSHOP_GITHUB_REPO?: string | undefined;
34
37
  EPICSHOP_GITHUB_ROOT?: string | undefined;
@@ -43,6 +46,7 @@ declare const schema: z.ZodEffects<z.ZodObject<{
43
46
  SENTRY_PROJECT_ID?: string | undefined;
44
47
  }>, {
45
48
  EPICSHOP_CONTEXT_CWD: string;
49
+ EPICSHOP_WORKSHOP_INSTANCE_ID: string;
46
50
  NODE_ENV: "production" | "development" | "test";
47
51
  EPICSHOP_GITHUB_REPO: string;
48
52
  EPICSHOP_GITHUB_ROOT: string;
@@ -57,6 +61,7 @@ declare const schema: z.ZodEffects<z.ZodObject<{
57
61
  EPICSHOP_APP_LOCATION?: string | undefined;
58
62
  }, {
59
63
  EPICSHOP_CONTEXT_CWD?: string | undefined;
64
+ EPICSHOP_WORKSHOP_INSTANCE_ID?: string | undefined;
60
65
  NODE_ENV?: "production" | "development" | "test" | undefined;
61
66
  EPICSHOP_GITHUB_REPO?: string | undefined;
62
67
  EPICSHOP_GITHUB_ROOT?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"env.server.d.ts","sourceRoot":"","sources":["../../src/env.server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgET,CAAA;AAEH,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM,CAAC;QAChB,UAAU,UAAW,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC;SAAG;KACtD;CACD;AAED,wBAAsB,IAAI,kBAazB;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM;;;;;;;;;;;;EAgBrB;AAED,KAAK,GAAG,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;AAEpC,OAAO,CAAC,MAAM,CAAC;IACd,IAAI,GAAG,EAAE,GAAG,CAAA;IACZ,UAAU,MAAM;QACf,GAAG,EAAE,GAAG,CAAA;KACR;CACD"}
1
+ {"version":3,"file":"env.server.d.ts","sourceRoot":"","sources":["../../src/env.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuET,CAAA;AAuBH,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM,CAAC;QAChB,UAAU,UAAW,SAAQ,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC;SAAG;KACtD;CACD;AAED,wBAAsB,IAAI,kBAazB;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM;;;;;;;;;;;;EAgBrB;AAED,KAAK,GAAG,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAA;AAEpC,OAAO,CAAC,MAAM,CAAC;IACd,IAAI,GAAG,EAAE,GAAG,CAAA;IACZ,UAAU,MAAM;QACf,GAAG,EAAE,GAAG,CAAA;KACR;CACD"}
@@ -1,12 +1,14 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ import md5 from 'md5-hex';
4
5
  import { z } from 'zod';
5
6
  import { handleGitHubRepoAndRoot } from './utils.js';
6
7
  const __dirname = path.dirname(new URL(import.meta.url).pathname);
7
8
  const schema = z
8
9
  .object({
9
- EPICSHOP_CONTEXT_CWD: z.string().default(process.cwd()),
10
+ EPICSHOP_CONTEXT_CWD: z.string().default(''),
11
+ EPICSHOP_WORKSHOP_INSTANCE_ID: z.string().default(''),
10
12
  NODE_ENV: z
11
13
  .enum(['production', 'development', 'test'])
12
14
  .default('development'),
@@ -28,6 +30,12 @@ const schema = z
28
30
  SENTRY_PROJECT_ID: z.string().default('4509630082252800'),
29
31
  })
30
32
  .transform(async (env) => {
33
+ if (env.EPICSHOP_CONTEXT_CWD === '') {
34
+ env.EPICSHOP_CONTEXT_CWD = await getEpicshopContextCwd();
35
+ }
36
+ if (env.EPICSHOP_WORKSHOP_INSTANCE_ID === '') {
37
+ env.EPICSHOP_WORKSHOP_INSTANCE_ID = md5(env.EPICSHOP_CONTEXT_CWD);
38
+ }
31
39
  const pkgJsonPath = path.join(env.EPICSHOP_CONTEXT_CWD, 'package.json');
32
40
  const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'));
33
41
  const epicshopConfig = pkgJson.epicshop ?? {};
@@ -53,6 +61,28 @@ const schema = z
53
61
  }
54
62
  return env;
55
63
  });
64
+ async function getEpicshopContextCwd() {
65
+ if (process.env.EPICSHOP_CONTEXT_CWD) {
66
+ return process.env.EPICSHOP_CONTEXT_CWD;
67
+ }
68
+ let dir = process.cwd();
69
+ while (true) {
70
+ const pkgPath = path.join(dir, 'package.json');
71
+ try {
72
+ const pkgRaw = await fs.readFile(pkgPath, 'utf8');
73
+ const pkg = JSON.parse(pkgRaw);
74
+ if (pkg.epicshop) {
75
+ return dir;
76
+ }
77
+ }
78
+ catch { }
79
+ const parentDir = path.dirname(dir);
80
+ if (parentDir === dir)
81
+ break;
82
+ dir = parentDir;
83
+ }
84
+ return process.cwd();
85
+ }
56
86
  export async function init() {
57
87
  const parsed = await schema.safeParseAsync(process.env);
58
88
  if (!parsed.success) {
@@ -1 +1 @@
1
- {"version":3,"file":"env.server.js","sourceRoot":"","sources":["../../src/env.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { z } from 'zod'\nimport { handleGitHubRepoAndRoot } from './utils.js'\n\nconst __dirname = path.dirname(new URL(import.meta.url).pathname)\n\nconst schema = z\n\t.object({\n\t\tEPICSHOP_CONTEXT_CWD: z.string().default(process.cwd()),\n\t\tNODE_ENV: z\n\t\t\t.enum(['production', 'development', 'test'] as const)\n\t\t\t.default('development'),\n\t\tEPICSHOP_GITHUB_REPO: z.string().default(''),\n\t\tEPICSHOP_GITHUB_ROOT: z.string().default(''),\n\t\tEPICSHOP_APP_VERSION: z.string().default('0.0.0-unknown'),\n\t\tEPICSHOP_PARENT_PORT: z.string().optional(),\n\t\tEPICSHOP_PARENT_TOKEN: z.string().optional(),\n\t\tEPICSHOP_APP_LOCATION: z.string().optional(),\n\t\tEPICSHOP_IS_PUBLISHED: z\n\t\t\t.string()\n\t\t\t.default(__dirname.includes('node_modules') ? 'true' : 'false'),\n\t\t// Sentry configuration\n\t\tSENTRY_DSN: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://cd51fbf4ca0834f7b3529a478a8ece4c@o913766.ingest.us.sentry.io/4509630082252800',\n\t\t\t),\n\t\tSENTRY_ORG: z.string().default('kent-c-dodds-tech-llc'),\n\t\tSENTRY_PROJECT: z.string().default('epicshop'),\n\t\tSENTRY_PROJECT_ID: z.string().default('4509630082252800'),\n\t})\n\t.transform(async (env) => {\n\t\tconst pkgJsonPath = path.join(env.EPICSHOP_CONTEXT_CWD, 'package.json')\n\t\tconst pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8')) as {\n\t\t\tepicshop?: {\n\t\t\t\tgithubRepo?: string\n\t\t\t\tgithubRoot?: string\n\t\t\t}\n\t\t}\n\t\tconst epicshopConfig = pkgJson.epicshop ?? {}\n\t\tif (!epicshopConfig) {\n\t\t\tthrow new Error(\n\t\t\t\t`No epicshop configuration found in \"${pkgJsonPath}\". If this is a workshop directory, please add an \"epicshop\" section to your package.json. If this is not a workshop directory, please set the EPICSHOP_CONTEXT_CWD environment variable to the directory containing your package.json with the \"epicshop\" config section.`,\n\t\t\t)\n\t\t}\n\t\tif (env.EPICSHOP_APP_LOCATION === undefined) {\n\t\t\tconst workshopAppPath = import.meta.resolve(\n\t\t\t\t'@epic-web/workshop-app/package.json',\n\t\t\t)\n\t\t\tconst packagePath = fileURLToPath(workshopAppPath)\n\t\t\tenv.EPICSHOP_APP_LOCATION = path.dirname(packagePath)\n\t\t}\n\t\tif (env.EPICSHOP_APP_VERSION === '0.0.0-unknown') {\n\t\t\tconst packageJson = JSON.parse(\n\t\t\t\tawait fs.readFile(\n\t\t\t\t\tpath.join(env.EPICSHOP_APP_LOCATION, 'package.json'),\n\t\t\t\t\t'utf-8',\n\t\t\t\t),\n\t\t\t) as { version: string }\n\t\t\tenv.EPICSHOP_APP_VERSION = packageJson.version\n\t\t}\n\t\tif (!env.EPICSHOP_GITHUB_REPO || !env.EPICSHOP_GITHUB_ROOT) {\n\t\t\tconst { githubRepo, githubRoot } = handleGitHubRepoAndRoot({\n\t\t\t\tgithubRepo: epicshopConfig.githubRepo,\n\t\t\t\tgithubRoot: epicshopConfig.githubRoot,\n\t\t\t})\n\t\t\tenv.EPICSHOP_GITHUB_REPO = githubRepo\n\t\t\tenv.EPICSHOP_GITHUB_ROOT = githubRoot\n\t\t}\n\t\treturn env\n\t})\n\ndeclare global {\n\tnamespace NodeJS {\n\t\tinterface ProcessEnv extends z.infer<typeof schema> {}\n\t}\n}\n\nexport async function init() {\n\tconst parsed = await schema.safeParseAsync(process.env)\n\n\tif (!parsed.success) {\n\t\tconsole.error(\n\t\t\t'❌ Invalid environment variables:',\n\t\t\tparsed.error.flatten().fieldErrors,\n\t\t)\n\n\t\tthrow new Error('Invalid environment variables')\n\t}\n\n\tObject.assign(process.env, parsed.data)\n}\n\n/**\n * This is used in both `entry.server.ts` and `root.tsx` to ensure that\n * the environment variables are set and globally available before the app is\n * started.\n *\n * NOTE: Do *not* add any environment variables in here that you do not wish to\n * be included in the client.\n * @returns all public ENV variables\n */\nexport function getEnv() {\n\treturn {\n\t\tMODE: process.env.NODE_ENV,\n\t\tEPICSHOP_CONTEXT_CWD: process.env.EPICSHOP_CONTEXT_CWD,\n\t\tEPICSHOP_GITHUB_REPO: process.env.EPICSHOP_GITHUB_REPO,\n\t\tEPICSHOP_GITHUB_ROOT: process.env.EPICSHOP_GITHUB_ROOT,\n\t\tEPICSHOP_DEPLOYED:\n\t\t\tprocess.env.EPICSHOP_DEPLOYED === 'true' ||\n\t\t\tprocess.env.EPICSHOP_DEPLOYED === '1',\n\t\tEPICSHOP_APP_VERSION: process.env.EPICSHOP_APP_VERSION,\n\t\tEPICSHOP_PARENT_PORT: process.env.EPICSHOP_PARENT_PORT,\n\t\tEPICSHOP_PARENT_TOKEN: process.env.EPICSHOP_PARENT_TOKEN,\n\t\tEPICSHOP_IS_PUBLISHED: process.env.EPICSHOP_IS_PUBLISHED === 'true',\n\t\tSENTRY_DSN: process.env.SENTRY_DSN,\n\t\tSENTRY_PROJECT_ID: process.env.SENTRY_PROJECT_ID,\n\t}\n}\n\ntype ENV = ReturnType<typeof getEnv>\n\ndeclare global {\n\tvar ENV: ENV\n\tinterface Window {\n\t\tENV: ENV\n\t}\n}\n"]}
1
+ {"version":3,"file":"env.server.js","sourceRoot":"","sources":["../../src/env.server.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport md5 from 'md5-hex'\nimport { z } from 'zod'\nimport { handleGitHubRepoAndRoot } from './utils.js'\n\nconst __dirname = path.dirname(new URL(import.meta.url).pathname)\n\nconst schema = z\n\t.object({\n\t\tEPICSHOP_CONTEXT_CWD: z.string().default(''),\n\t\tEPICSHOP_WORKSHOP_INSTANCE_ID: z.string().default(''),\n\t\tNODE_ENV: z\n\t\t\t.enum(['production', 'development', 'test'] as const)\n\t\t\t.default('development'),\n\t\tEPICSHOP_GITHUB_REPO: z.string().default(''),\n\t\tEPICSHOP_GITHUB_ROOT: z.string().default(''),\n\t\tEPICSHOP_APP_VERSION: z.string().default('0.0.0-unknown'),\n\t\tEPICSHOP_PARENT_PORT: z.string().optional(),\n\t\tEPICSHOP_PARENT_TOKEN: z.string().optional(),\n\t\tEPICSHOP_APP_LOCATION: z.string().optional(),\n\t\tEPICSHOP_IS_PUBLISHED: z\n\t\t\t.string()\n\t\t\t.default(__dirname.includes('node_modules') ? 'true' : 'false'),\n\t\t// Sentry configuration\n\t\tSENTRY_DSN: z\n\t\t\t.string()\n\t\t\t.default(\n\t\t\t\t'https://cd51fbf4ca0834f7b3529a478a8ece4c@o913766.ingest.us.sentry.io/4509630082252800',\n\t\t\t),\n\t\tSENTRY_ORG: z.string().default('kent-c-dodds-tech-llc'),\n\t\tSENTRY_PROJECT: z.string().default('epicshop'),\n\t\tSENTRY_PROJECT_ID: z.string().default('4509630082252800'),\n\t})\n\t.transform(async (env) => {\n\t\tif (env.EPICSHOP_CONTEXT_CWD === '') {\n\t\t\tenv.EPICSHOP_CONTEXT_CWD = await getEpicshopContextCwd()\n\t\t}\n\t\tif (env.EPICSHOP_WORKSHOP_INSTANCE_ID === '') {\n\t\t\tenv.EPICSHOP_WORKSHOP_INSTANCE_ID = md5(env.EPICSHOP_CONTEXT_CWD)\n\t\t}\n\t\tconst pkgJsonPath = path.join(env.EPICSHOP_CONTEXT_CWD, 'package.json')\n\t\tconst pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8')) as {\n\t\t\tepicshop?: {\n\t\t\t\tgithubRepo?: string\n\t\t\t\tgithubRoot?: string\n\t\t\t}\n\t\t}\n\t\tconst epicshopConfig = pkgJson.epicshop ?? {}\n\t\tif (!epicshopConfig) {\n\t\t\tthrow new Error(\n\t\t\t\t`No epicshop configuration found in \"${pkgJsonPath}\". If this is a workshop directory, please add an \"epicshop\" section to your package.json. If this is not a workshop directory, please set the EPICSHOP_CONTEXT_CWD environment variable to the directory containing your package.json with the \"epicshop\" config section.`,\n\t\t\t)\n\t\t}\n\t\tif (env.EPICSHOP_APP_LOCATION === undefined) {\n\t\t\tconst workshopAppPath = import.meta.resolve(\n\t\t\t\t'@epic-web/workshop-app/package.json',\n\t\t\t)\n\t\t\tconst packagePath = fileURLToPath(workshopAppPath)\n\t\t\tenv.EPICSHOP_APP_LOCATION = path.dirname(packagePath)\n\t\t}\n\t\tif (env.EPICSHOP_APP_VERSION === '0.0.0-unknown') {\n\t\t\tconst packageJson = JSON.parse(\n\t\t\t\tawait fs.readFile(\n\t\t\t\t\tpath.join(env.EPICSHOP_APP_LOCATION, 'package.json'),\n\t\t\t\t\t'utf-8',\n\t\t\t\t),\n\t\t\t) as { version: string }\n\t\t\tenv.EPICSHOP_APP_VERSION = packageJson.version\n\t\t}\n\t\tif (!env.EPICSHOP_GITHUB_REPO || !env.EPICSHOP_GITHUB_ROOT) {\n\t\t\tconst { githubRepo, githubRoot } = handleGitHubRepoAndRoot({\n\t\t\t\tgithubRepo: epicshopConfig.githubRepo,\n\t\t\t\tgithubRoot: epicshopConfig.githubRoot,\n\t\t\t})\n\t\t\tenv.EPICSHOP_GITHUB_REPO = githubRepo\n\t\t\tenv.EPICSHOP_GITHUB_ROOT = githubRoot\n\t\t}\n\t\treturn env\n\t})\n\nasync function getEpicshopContextCwd() {\n\tif (process.env.EPICSHOP_CONTEXT_CWD) {\n\t\treturn process.env.EPICSHOP_CONTEXT_CWD\n\t}\n\tlet dir = process.cwd()\n\twhile (true) {\n\t\tconst pkgPath = path.join(dir, 'package.json')\n\t\ttry {\n\t\t\tconst pkgRaw = await fs.readFile(pkgPath, 'utf8')\n\t\t\tconst pkg = JSON.parse(pkgRaw) as { epicshop?: boolean }\n\t\t\tif (pkg.epicshop) {\n\t\t\t\treturn dir\n\t\t\t}\n\t\t} catch {}\n\t\tconst parentDir = path.dirname(dir)\n\t\tif (parentDir === dir) break\n\t\tdir = parentDir\n\t}\n\treturn process.cwd()\n}\n\ndeclare global {\n\tnamespace NodeJS {\n\t\tinterface ProcessEnv extends z.infer<typeof schema> {}\n\t}\n}\n\nexport async function init() {\n\tconst parsed = await schema.safeParseAsync(process.env)\n\n\tif (!parsed.success) {\n\t\tconsole.error(\n\t\t\t'❌ Invalid environment variables:',\n\t\t\tparsed.error.flatten().fieldErrors,\n\t\t)\n\n\t\tthrow new Error('Invalid environment variables')\n\t}\n\n\tObject.assign(process.env, parsed.data)\n}\n\n/**\n * This is used in both `entry.server.ts` and `root.tsx` to ensure that\n * the environment variables are set and globally available before the app is\n * started.\n *\n * NOTE: Do *not* add any environment variables in here that you do not wish to\n * be included in the client.\n * @returns all public ENV variables\n */\nexport function getEnv() {\n\treturn {\n\t\tMODE: process.env.NODE_ENV,\n\t\tEPICSHOP_CONTEXT_CWD: process.env.EPICSHOP_CONTEXT_CWD,\n\t\tEPICSHOP_GITHUB_REPO: process.env.EPICSHOP_GITHUB_REPO,\n\t\tEPICSHOP_GITHUB_ROOT: process.env.EPICSHOP_GITHUB_ROOT,\n\t\tEPICSHOP_DEPLOYED:\n\t\t\tprocess.env.EPICSHOP_DEPLOYED === 'true' ||\n\t\t\tprocess.env.EPICSHOP_DEPLOYED === '1',\n\t\tEPICSHOP_APP_VERSION: process.env.EPICSHOP_APP_VERSION,\n\t\tEPICSHOP_PARENT_PORT: process.env.EPICSHOP_PARENT_PORT,\n\t\tEPICSHOP_PARENT_TOKEN: process.env.EPICSHOP_PARENT_TOKEN,\n\t\tEPICSHOP_IS_PUBLISHED: process.env.EPICSHOP_IS_PUBLISHED === 'true',\n\t\tSENTRY_DSN: process.env.SENTRY_DSN,\n\t\tSENTRY_PROJECT_ID: process.env.SENTRY_PROJECT_ID,\n\t}\n}\n\ntype ENV = ReturnType<typeof getEnv>\n\ndeclare global {\n\tvar ENV: ENV\n\tinterface Window {\n\t\tENV: ENV\n\t}\n}\n"]}
@@ -126,21 +126,21 @@ declare const UserInfoSchema: z.ZodEffects<z.ZodObject<{
126
126
  global_name?: string | null | undefined;
127
127
  }>;
128
128
  }, "strip", z.ZodTypeAny, {
129
- nick: string | null;
130
129
  user: {
131
130
  id: string;
132
131
  username: string;
133
132
  avatar?: string | null | undefined;
134
133
  global_name?: string | null | undefined;
135
134
  };
136
- }, {
137
135
  nick: string | null;
136
+ }, {
138
137
  user: {
139
138
  id: string;
140
139
  username: string;
141
140
  avatar?: string | null | undefined;
142
141
  global_name?: string | null | undefined;
143
142
  };
143
+ nick: string | null;
144
144
  }>>>;
145
145
  }, "strip", z.ZodTypeAny, {
146
146
  name: string | null;
@@ -148,13 +148,13 @@ declare const UserInfoSchema: z.ZodEffects<z.ZodObject<{
148
148
  email: string;
149
149
  image: string | null;
150
150
  discordProfile?: {
151
- nick: string | null;
152
151
  user: {
153
152
  id: string;
154
153
  username: string;
155
154
  avatar?: string | null | undefined;
156
155
  global_name?: string | null | undefined;
157
156
  };
157
+ nick: string | null;
158
158
  } | null | undefined;
159
159
  }, {
160
160
  name: string | null;
@@ -162,13 +162,13 @@ declare const UserInfoSchema: z.ZodEffects<z.ZodObject<{
162
162
  email: string;
163
163
  image: string | null;
164
164
  discordProfile?: {
165
- nick: string | null;
166
165
  user: {
167
166
  id: string;
168
167
  username: string;
169
168
  avatar?: string | null | undefined;
170
169
  global_name?: string | null | undefined;
171
170
  };
171
+ nick: string | null;
172
172
  } | null | undefined;
173
173
  }>, {
174
174
  imageUrlSmall: string | null;
@@ -178,13 +178,13 @@ declare const UserInfoSchema: z.ZodEffects<z.ZodObject<{
178
178
  email: string;
179
179
  image: string | null;
180
180
  discordProfile?: {
181
- nick: string | null;
182
181
  user: {
183
182
  id: string;
184
183
  username: string;
185
184
  avatar?: string | null | undefined;
186
185
  global_name?: string | null | undefined;
187
186
  };
187
+ nick: string | null;
188
188
  } | null | undefined;
189
189
  }, {
190
190
  name: string | null;
@@ -192,13 +192,13 @@ declare const UserInfoSchema: z.ZodEffects<z.ZodObject<{
192
192
  email: string;
193
193
  image: string | null;
194
194
  discordProfile?: {
195
- nick: string | null;
196
195
  user: {
197
196
  id: string;
198
197
  username: string;
199
198
  avatar?: string | null | undefined;
200
199
  global_name?: string | null | undefined;
201
200
  };
201
+ nick: string | null;
202
202
  } | null | undefined;
203
203
  }>;
204
204
  export type UserInfo = z.infer<typeof UserInfoSchema>;
@@ -214,13 +214,13 @@ export declare function getUserInfo({ timings, request, forceFresh, }?: {
214
214
  email: string;
215
215
  image: string | null;
216
216
  discordProfile?: {
217
- nick: string | null;
218
217
  user: {
219
218
  id: string;
220
219
  username: string;
221
220
  avatar?: string | null | undefined;
222
221
  global_name?: string | null | undefined;
223
222
  };
223
+ nick: string | null;
224
224
  } | null | undefined;
225
225
  } | null>;
226
226
  export {};
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@epic-web/workshop-utils","version":"6.14.4","publishConfig":{"access":"public"},"type":"module","tshy":{"project":"./tsconfig.build.json","dialects":["esm"],"exports":{"./package.json":"./package.json","./apps.server":"./src/apps.server.ts","./diff.server":"./src/diff.server.ts","./env.server":"./src/env.server.ts","./epic-api.server":"./src/epic-api.server.ts","./user.server":"./src/user.server.ts","./cache.server":"./src/cache.server.ts","./config.server":"./src/config.server.ts","./db.server":"./src/db.server.ts","./timing.server":"./src/timing.server.ts","./modified-time.server":"./src/modified-time.server.ts","./compile-mdx.server":"./src/compile-mdx.server.ts","./git.server":"./src/git.server.ts","./iframe-sync":"./src/iframe-sync.ts","./playwright.server":"./src/playwright.server.ts","./notifications.server":"./src/notifications.server.ts","./process-manager.server":"./src/process-manager.server.ts","./test":"./src/test.ts","./request-context.server":"./src/request-context.server.ts","./utils.server":"./src/utils.server.ts","./utils":"./src/utils.ts"}},"exports":{"./package.json":"./package.json","./apps.server":{"import":{"types":"./dist/esm/apps.server.d.ts","default":"./dist/esm/apps.server.js"}},"./diff.server":{"import":{"types":"./dist/esm/diff.server.d.ts","default":"./dist/esm/diff.server.js"}},"./env.server":{"import":{"types":"./dist/esm/env.server.d.ts","default":"./dist/esm/env.server.js"}},"./epic-api.server":{"import":{"types":"./dist/esm/epic-api.server.d.ts","default":"./dist/esm/epic-api.server.js"}},"./user.server":{"import":{"types":"./dist/esm/user.server.d.ts","default":"./dist/esm/user.server.js"}},"./cache.server":{"import":{"types":"./dist/esm/cache.server.d.ts","default":"./dist/esm/cache.server.js"}},"./config.server":{"import":{"types":"./dist/esm/config.server.d.ts","default":"./dist/esm/config.server.js"}},"./db.server":{"import":{"types":"./dist/esm/db.server.d.ts","default":"./dist/esm/db.server.js"}},"./timing.server":{"import":{"types":"./dist/esm/timing.server.d.ts","default":"./dist/esm/timing.server.js"}},"./modified-time.server":{"import":{"types":"./dist/esm/modified-time.server.d.ts","default":"./dist/esm/modified-time.server.js"}},"./compile-mdx.server":{"import":{"types":"./dist/esm/compile-mdx.server.d.ts","default":"./dist/esm/compile-mdx.server.js"}},"./git.server":{"import":{"types":"./dist/esm/git.server.d.ts","default":"./dist/esm/git.server.js"}},"./iframe-sync":{"import":{"types":"./dist/esm/iframe-sync.d.ts","default":"./dist/esm/iframe-sync.js"}},"./playwright.server":{"import":{"types":"./dist/esm/playwright.server.d.ts","default":"./dist/esm/playwright.server.js"}},"./notifications.server":{"import":{"types":"./dist/esm/notifications.server.d.ts","default":"./dist/esm/notifications.server.js"}},"./process-manager.server":{"import":{"types":"./dist/esm/process-manager.server.d.ts","default":"./dist/esm/process-manager.server.js"}},"./test":{"import":{"types":"./dist/esm/test.d.ts","default":"./dist/esm/test.js"}},"./request-context.server":{"import":{"types":"./dist/esm/request-context.server.d.ts","default":"./dist/esm/request-context.server.js"}},"./utils.server":{"import":{"types":"./dist/esm/utils.server.d.ts","default":"./dist/esm/utils.server.js"}},"./utils":{"import":{"types":"./dist/esm/utils.d.ts","default":"./dist/esm/utils.js"}}},"files":["dist"],"scripts":{"typecheck":"tsc --noEmit","build":"tshy","build:watch":"nx watch --projects=@epic-web/workshop-utils -- nx run \\$NX_PROJECT_NAME:build"},"dependencies":{"@epic-web/cachified":"^5.6.0","@epic-web/invariant":"^1.0.0","@epic-web/remember":"^1.1.0","@kentcdodds/md-temp":"^10.0.1","@mdx-js/mdx":"^3.1.0","@paralleldrive/cuid2":"^2.2.2","@playwright/test":"^1.53.2","@react-router/node":"^7.6.3","@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.6.3","@total-typescript/ts-reset":"^0.6.1","@types/chai":"^5.2.2","@types/chai-dom":"^1.11.3","@vitest/expect":"^3.2.4","chai":"^5.2.0","chai-dom":"^1.12.1","chalk":"^5.4.1","chokidar":"^4.0.3","close-with-grace":"^2.2.0","cookie":"^1.0.2","cross-spawn":"^7.0.6","dayjs":"^1.11.13","esbuild":"^0.25.5","execa":"^9.6.0","find-process":"^1.4.10","fkill":"^9.0.0","fs-extra":"^11.3.0","globby":"^14.1.0","ignore":"^7.0.5","json5":"^2.2.3","lru-cache":"^11.1.0","lz-string":"^1.5.0","md5-hex":"^5.0.0","mdast-util-mdx-jsx":"^3.2.0","mdx-bundler":"^10.1.1","p-queue":"^8.1.0","parse-git-diff":"^0.0.19","rehype":"^13.0.2","rehype-autolink-headings":"^7.1.0","remark":"^15.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.1","shiki":"^3.7.0","unified":"^11.0.5","unist-util-remove-position":"^5.0.0","unist-util-visit":"^5.0.0","zod":"^3.25.71"},"devDependencies":{"@types/hast":"^3.0.4","@types/mdast":"^4.0.4","@types/node":"^24.0.10","tshy":"^3.0.2"},"repository":{"type":"git","url":"https://github.com/epicweb-dev/epicshop.git","directory":"packages/workshop-utils"}}
1
+ {"name":"@epic-web/workshop-utils","version":"6.15.1","publishConfig":{"access":"public"},"type":"module","tshy":{"project":"./tsconfig.build.json","dialects":["esm"],"exports":{"./package.json":"./package.json","./apps.server":"./src/apps.server.ts","./diff.server":"./src/diff.server.ts","./env.server":"./src/env.server.ts","./epic-api.server":"./src/epic-api.server.ts","./user.server":"./src/user.server.ts","./cache.server":"./src/cache.server.ts","./config.server":"./src/config.server.ts","./db.server":"./src/db.server.ts","./timing.server":"./src/timing.server.ts","./modified-time.server":"./src/modified-time.server.ts","./compile-mdx.server":"./src/compile-mdx.server.ts","./git.server":"./src/git.server.ts","./iframe-sync":"./src/iframe-sync.ts","./playwright.server":"./src/playwright.server.ts","./notifications.server":"./src/notifications.server.ts","./process-manager.server":"./src/process-manager.server.ts","./test":"./src/test.ts","./request-context.server":"./src/request-context.server.ts","./utils.server":"./src/utils.server.ts","./utils":"./src/utils.ts"}},"exports":{"./package.json":"./package.json","./apps.server":{"import":{"types":"./dist/esm/apps.server.d.ts","default":"./dist/esm/apps.server.js"}},"./diff.server":{"import":{"types":"./dist/esm/diff.server.d.ts","default":"./dist/esm/diff.server.js"}},"./env.server":{"import":{"types":"./dist/esm/env.server.d.ts","default":"./dist/esm/env.server.js"}},"./epic-api.server":{"import":{"types":"./dist/esm/epic-api.server.d.ts","default":"./dist/esm/epic-api.server.js"}},"./user.server":{"import":{"types":"./dist/esm/user.server.d.ts","default":"./dist/esm/user.server.js"}},"./cache.server":{"import":{"types":"./dist/esm/cache.server.d.ts","default":"./dist/esm/cache.server.js"}},"./config.server":{"import":{"types":"./dist/esm/config.server.d.ts","default":"./dist/esm/config.server.js"}},"./db.server":{"import":{"types":"./dist/esm/db.server.d.ts","default":"./dist/esm/db.server.js"}},"./timing.server":{"import":{"types":"./dist/esm/timing.server.d.ts","default":"./dist/esm/timing.server.js"}},"./modified-time.server":{"import":{"types":"./dist/esm/modified-time.server.d.ts","default":"./dist/esm/modified-time.server.js"}},"./compile-mdx.server":{"import":{"types":"./dist/esm/compile-mdx.server.d.ts","default":"./dist/esm/compile-mdx.server.js"}},"./git.server":{"import":{"types":"./dist/esm/git.server.d.ts","default":"./dist/esm/git.server.js"}},"./iframe-sync":{"import":{"types":"./dist/esm/iframe-sync.d.ts","default":"./dist/esm/iframe-sync.js"}},"./playwright.server":{"import":{"types":"./dist/esm/playwright.server.d.ts","default":"./dist/esm/playwright.server.js"}},"./notifications.server":{"import":{"types":"./dist/esm/notifications.server.d.ts","default":"./dist/esm/notifications.server.js"}},"./process-manager.server":{"import":{"types":"./dist/esm/process-manager.server.d.ts","default":"./dist/esm/process-manager.server.js"}},"./test":{"import":{"types":"./dist/esm/test.d.ts","default":"./dist/esm/test.js"}},"./request-context.server":{"import":{"types":"./dist/esm/request-context.server.d.ts","default":"./dist/esm/request-context.server.js"}},"./utils.server":{"import":{"types":"./dist/esm/utils.server.d.ts","default":"./dist/esm/utils.server.js"}},"./utils":{"import":{"types":"./dist/esm/utils.d.ts","default":"./dist/esm/utils.js"}}},"files":["dist"],"scripts":{"typecheck":"tsc --noEmit","build":"tshy","build:watch":"nx watch --projects=@epic-web/workshop-utils -- nx run \\$NX_PROJECT_NAME:build"},"dependencies":{"@epic-web/cachified":"^5.6.0","@epic-web/invariant":"^1.0.0","@epic-web/remember":"^1.1.0","@kentcdodds/md-temp":"^10.0.1","@mdx-js/mdx":"^3.1.0","@paralleldrive/cuid2":"^2.2.2","@playwright/test":"^1.53.2","@react-router/node":"^7.6.3","@sentry/react-router":"^9.40.0","@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.6.3","@total-typescript/ts-reset":"^0.6.1","@types/chai":"^5.2.2","@types/chai-dom":"^1.11.3","@vitest/expect":"^3.2.4","chai":"^5.2.0","chai-dom":"^1.12.1","chalk":"^5.4.1","chokidar":"^4.0.3","close-with-grace":"^2.2.0","cookie":"^1.0.2","cross-spawn":"^7.0.6","dayjs":"^1.11.13","esbuild":"^0.25.5","execa":"^9.6.0","find-process":"^1.4.10","fkill":"^9.0.0","fs-extra":"^11.3.0","globby":"^14.1.0","ignore":"^7.0.5","json5":"^2.2.3","lru-cache":"^11.1.0","lz-string":"^1.5.0","md5-hex":"^5.0.0","mdast-util-mdx-jsx":"^3.2.0","mdx-bundler":"^10.1.1","p-queue":"^8.1.0","parse-git-diff":"^0.0.19","rehype":"^13.0.2","rehype-autolink-headings":"^7.1.0","remark":"^15.0.1","remark-emoji":"^5.0.1","remark-gfm":"^4.0.1","shiki":"^3.7.0","unified":"^11.0.5","unist-util-remove-position":"^5.0.0","unist-util-visit":"^5.0.0","zod":"^3.25.71"},"devDependencies":{"@types/hast":"^3.0.4","@types/mdast":"^4.0.4","@types/node":"^24.0.10","tshy":"^3.0.2"},"repository":{"type":"git","url":"https://github.com/epicweb-dev/epicshop.git","directory":"packages/workshop-utils"}}