@epic-web/workshop-utils 6.23.8 â 6.23.9
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/epic-api.server.d.ts.map +1 -1
- package/dist/esm/epic-api.server.js +77 -12
- package/dist/esm/epic-api.server.js.map +1 -1
- package/dist/esm/logger.d.ts +11 -0
- package/dist/esm/logger.d.ts.map +1 -0
- package/dist/esm/logger.js +14 -0
- package/dist/esm/logger.js.map +1 -0
- package/package.json +8 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epic-api.server.d.ts","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"epic-api.server.d.ts","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAUvB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAoDjD,MAAM,MAAM,cAAc,GAAG,MAAM,CAClC,MAAM,EACN,OAAO,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAC5C,CAAA;AAED,wBAAsB,iBAAiB,CACtC,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,EAClC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,2BAyBnE;AAED,iBAAe,gBAAgB,CAAC,EAC/B,cAAc,EACd,WAAW,EACX,OAAO,EACP,OAAO,GACP,EAAE;IACF,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;;;;;;;;;;;;;;;;;;;UAwHA;AAyGD,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACtE,wBAAsB,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,GACP,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACZ;mBA2BW,MAAM;oBACL,MAAM;qBACL,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;UAIsB,SAAS;mBAqC9D;AAqDD,wBAAsB,cAAc,CACnC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACpE,EACC,OAAO,EACP,OAAO,GACP,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CACZ;;;;;;;;;;;;GAgDN;AAoBD,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EACZ,EACC,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf;;;;;;;;;;;;GA4CN;AAED,wBAAsB,uBAAuB,CAAC,EAC7C,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf,oBAoDL;AAED,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCjB,CAAA;AAwCH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAErD,wBAAsB,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,EACP,UAAU,GACV,GAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACf;;;;;;;;;;;;;;;;UAoEL"}
|
|
@@ -7,7 +7,10 @@ import { getExercises, getWorkshopFinished, getWorkshopInstructions, } from './a
|
|
|
7
7
|
import { cachified, fsCache } from './cache.server.js';
|
|
8
8
|
import { getWorkshopConfig } from './config.server.js';
|
|
9
9
|
import { getAuthInfo, setAuthInfo } from './db.server.js';
|
|
10
|
+
import { logger } from './logger.js';
|
|
10
11
|
import { getErrorMessage } from './utils.js';
|
|
12
|
+
// Module-level logger for epic-api operations
|
|
13
|
+
const log = logger('epic:api');
|
|
11
14
|
const Transcript = z
|
|
12
15
|
.string()
|
|
13
16
|
.nullable()
|
|
@@ -48,11 +51,14 @@ const CachedEpicVideoInfoSchema = z
|
|
|
48
51
|
}))
|
|
49
52
|
.or(z.null());
|
|
50
53
|
export async function getEpicVideoInfos(epicWebUrls, { request, timings } = {}) {
|
|
51
|
-
if (!epicWebUrls)
|
|
54
|
+
if (!epicWebUrls) {
|
|
55
|
+
log('no epic web URLs provided, returning empty object');
|
|
52
56
|
return {};
|
|
57
|
+
}
|
|
53
58
|
const authInfo = await getAuthInfo();
|
|
54
59
|
if (getEnv().EPICSHOP_DEPLOYED)
|
|
55
60
|
return {};
|
|
61
|
+
log(`fetching epic video infos for ${epicWebUrls.length} URLs`);
|
|
56
62
|
const epicVideoInfos = {};
|
|
57
63
|
for (const epicVideoEmbed of epicWebUrls) {
|
|
58
64
|
const epicVideoInfo = await getEpicVideoInfo({
|
|
@@ -65,11 +71,13 @@ export async function getEpicVideoInfos(epicWebUrls, { request, timings } = {})
|
|
|
65
71
|
epicVideoInfos[epicVideoEmbed] = epicVideoInfo;
|
|
66
72
|
}
|
|
67
73
|
}
|
|
74
|
+
log(`successfully fetched ${Object.keys(epicVideoInfos).length} epic video infos`);
|
|
68
75
|
return epicVideoInfos;
|
|
69
76
|
}
|
|
70
77
|
async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings, }) {
|
|
71
78
|
const tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated';
|
|
72
79
|
const key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`;
|
|
80
|
+
log(`fetching video info for URL: ${epicVideoEmbed}`);
|
|
73
81
|
return cachified({
|
|
74
82
|
key,
|
|
75
83
|
request,
|
|
@@ -84,6 +92,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
84
92
|
if (epicUrl.host !== 'www.epicweb.dev' &&
|
|
85
93
|
epicUrl.host !== 'www.epicreact.dev' &&
|
|
86
94
|
epicUrl.host !== 'www.epicai.pro') {
|
|
95
|
+
log(`unsupported host for video URL: ${epicUrl.host}`);
|
|
87
96
|
return null;
|
|
88
97
|
}
|
|
89
98
|
// this may be temporary until the /tutorials/ endpoint supports /api
|
|
@@ -94,10 +103,12 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
94
103
|
const apiUrl = epicUrl.host === 'www.epicai.pro'
|
|
95
104
|
? getEpicAIVideoAPIUrl(epicVideoEmbed)
|
|
96
105
|
: `https://${epicUrl.host}/api${epicUrl.pathname}`;
|
|
106
|
+
log(`making API request to: ${apiUrl}`);
|
|
97
107
|
const infoResponse = await fetch(apiUrl, accessToken
|
|
98
108
|
? { headers: { authorization: `Bearer ${accessToken}` } }
|
|
99
109
|
: undefined);
|
|
100
110
|
const { status, statusText } = infoResponse;
|
|
111
|
+
log(`API response: ${status} ${statusText}`);
|
|
101
112
|
if (infoResponse.status >= 200 && infoResponse.status < 300) {
|
|
102
113
|
let rawInfo = await infoResponse.json();
|
|
103
114
|
// another special case for epicai.pro videos
|
|
@@ -106,6 +117,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
106
117
|
}
|
|
107
118
|
const infoResult = EpicVideoInfoSchema.safeParse(rawInfo);
|
|
108
119
|
if (infoResult.success) {
|
|
120
|
+
log(`successfully parsed video info for ${epicVideoEmbed}`);
|
|
109
121
|
return {
|
|
110
122
|
status: 'success',
|
|
111
123
|
statusCode: status,
|
|
@@ -119,6 +131,7 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
119
131
|
context.metadata.swr = 0;
|
|
120
132
|
const restrictedResult = EpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo);
|
|
121
133
|
if (restrictedResult.success) {
|
|
134
|
+
log.warn(`video is region restricted: ${epicVideoEmbed}`);
|
|
122
135
|
return {
|
|
123
136
|
status: 'error',
|
|
124
137
|
statusCode: status,
|
|
@@ -128,6 +141,11 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
128
141
|
};
|
|
129
142
|
}
|
|
130
143
|
else {
|
|
144
|
+
log.error(`API response parsing failed for ${epicVideoEmbed}`, {
|
|
145
|
+
url: epicUrl.pathname,
|
|
146
|
+
rawInfo,
|
|
147
|
+
parseError: infoResult.error,
|
|
148
|
+
});
|
|
131
149
|
console.warn(`Response from EpicWeb for "${epicUrl.pathname}" does not match expectation`, infoResult.error);
|
|
132
150
|
return {
|
|
133
151
|
status: 'error',
|
|
@@ -142,6 +160,11 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
142
160
|
// don't cache errors for long...
|
|
143
161
|
context.metadata.ttl = 1000 * 2;
|
|
144
162
|
context.metadata.swr = 0;
|
|
163
|
+
log.error(`video API request failed for ${epicVideoEmbed}`, {
|
|
164
|
+
status,
|
|
165
|
+
statusText,
|
|
166
|
+
url: apiUrl,
|
|
167
|
+
});
|
|
145
168
|
return {
|
|
146
169
|
status: 'error',
|
|
147
170
|
statusCode: status,
|
|
@@ -151,14 +174,22 @@ async function getEpicVideoInfo({ epicVideoEmbed, accessToken, request, timings,
|
|
|
151
174
|
}
|
|
152
175
|
},
|
|
153
176
|
}).catch((e) => {
|
|
154
|
-
|
|
177
|
+
log.error(`failed to fetch epic video info for ${epicVideoEmbed}:`, e);
|
|
155
178
|
throw e;
|
|
156
179
|
});
|
|
157
180
|
}
|
|
158
181
|
function getEpicAIVideoAPIUrl(epicVideoEmbed) {
|
|
159
182
|
const epicUrl = new URL(epicVideoEmbed);
|
|
160
183
|
const slug = epicUrl.pathname.split('/').at(-1);
|
|
161
|
-
|
|
184
|
+
if (epicUrl.pathname.includes('/workshops')) {
|
|
185
|
+
return `https://epicai.pro/api/lessons?slugOrId=${slug}`;
|
|
186
|
+
}
|
|
187
|
+
else if (epicUrl.pathname.endsWith('/solution')) {
|
|
188
|
+
return `https://epicai.pro/api/lessons/${slug}/solution`;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
return `https://epicai.pro/api/posts?slugOrId=${slug}`;
|
|
192
|
+
}
|
|
162
193
|
}
|
|
163
194
|
function preprocessEpicAIVideoAPIResult(result) {
|
|
164
195
|
const PostVideoResourceSchema = z.object({
|
|
@@ -197,6 +228,7 @@ async function getEpicProgress({ timings, request, forceFresh, } = {}) {
|
|
|
197
228
|
lessonId: z.string(),
|
|
198
229
|
completedAt: z.string().nullable(),
|
|
199
230
|
}));
|
|
231
|
+
log(`fetching progress from EpicWeb host: ${host}`);
|
|
200
232
|
return cachified({
|
|
201
233
|
key: `epic-progress:${host}:${tokenPart}`,
|
|
202
234
|
cache: fsCache,
|
|
@@ -208,19 +240,26 @@ async function getEpicProgress({ timings, request, forceFresh, } = {}) {
|
|
|
208
240
|
offlineFallbackValue: [],
|
|
209
241
|
checkValue: EpicProgressSchema,
|
|
210
242
|
async getFreshValue(context) {
|
|
211
|
-
const
|
|
243
|
+
const progressUrl = `https://${host}/api/progress`;
|
|
244
|
+
log(`making progress API request to: ${progressUrl}`);
|
|
245
|
+
const response = await fetch(progressUrl, {
|
|
212
246
|
headers: {
|
|
213
247
|
authorization: `Bearer ${authInfo.tokenSet.access_token}`,
|
|
214
248
|
},
|
|
215
249
|
}).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
250
|
+
log(`progress API response: ${response.status} ${response.statusText}`);
|
|
216
251
|
if (response.status < 200 || response.status >= 300) {
|
|
252
|
+
log.error(`failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`);
|
|
217
253
|
console.error(`Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`);
|
|
218
254
|
// don't cache errors for long...
|
|
219
255
|
context.metadata.ttl = 1000 * 2;
|
|
220
256
|
context.metadata.swr = 0;
|
|
221
257
|
return [];
|
|
222
258
|
}
|
|
223
|
-
|
|
259
|
+
const progressData = await response.json();
|
|
260
|
+
const parsedProgress = EpicProgressSchema.parse(progressData);
|
|
261
|
+
log(`successfully fetched ${parsedProgress.length} progress entries`);
|
|
262
|
+
return parsedProgress;
|
|
224
263
|
},
|
|
225
264
|
});
|
|
226
265
|
}
|
|
@@ -233,6 +272,7 @@ export async function getProgress({ timings, request, } = {}) {
|
|
|
233
272
|
const { product: { slug, host }, } = getWorkshopConfig();
|
|
234
273
|
if (!slug)
|
|
235
274
|
return [];
|
|
275
|
+
log(`aggregating progress data for workshop: ${slug}`);
|
|
236
276
|
const [workshopData, epicProgress, workshopInstructions, workshopFinished, exercises,] = await Promise.all([
|
|
237
277
|
getWorkshopData(slug, { request, timings }),
|
|
238
278
|
getEpicProgress({ request, timings }),
|
|
@@ -271,6 +311,7 @@ export async function getProgress({ timings, request, } = {}) {
|
|
|
271
311
|
}
|
|
272
312
|
}
|
|
273
313
|
}
|
|
314
|
+
log(`processed ${progress.length} progress entries for workshop: ${slug}`);
|
|
274
315
|
return progress;
|
|
275
316
|
}
|
|
276
317
|
function getProgressForLesson(epicLessonSlug, { workshopInstructions, workshopFinished, exercises, }) {
|
|
@@ -319,22 +360,29 @@ export async function updateProgress({ lessonSlug, complete }, { timings, reques
|
|
|
319
360
|
return { status: 'error', error: 'not authenticated' };
|
|
320
361
|
}
|
|
321
362
|
const { product: { host }, } = getWorkshopConfig();
|
|
322
|
-
const
|
|
363
|
+
const progressUrl = `https://${host}/api/progress`;
|
|
364
|
+
const payload = complete ? { lessonSlug } : { lessonSlug, remove: true };
|
|
365
|
+
log(`updating progress for lesson: ${lessonSlug} (complete: ${complete})`);
|
|
366
|
+
log(`making POST request to: ${progressUrl} with payload: ${JSON.stringify(payload)}`);
|
|
367
|
+
const response = await fetch(progressUrl, {
|
|
323
368
|
method: 'POST',
|
|
324
369
|
headers: {
|
|
325
370
|
authorization: `Bearer ${authInfo.tokenSet.access_token}`,
|
|
326
371
|
'content-type': 'application/json',
|
|
327
372
|
},
|
|
328
|
-
body: JSON.stringify(
|
|
373
|
+
body: JSON.stringify(payload),
|
|
329
374
|
}).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
375
|
+
log(`progress update response: ${response.status} ${response.statusText}`);
|
|
330
376
|
// force the progress to be fresh whether or not we're successful
|
|
331
377
|
await getEpicProgress({ forceFresh: true, request, timings });
|
|
332
378
|
if (response.status < 200 || response.status >= 300) {
|
|
379
|
+
log(`progress update failed: ${response.status} ${response.statusText}`);
|
|
333
380
|
return {
|
|
334
381
|
status: 'error',
|
|
335
382
|
error: `${response.status} ${response.statusText}`,
|
|
336
383
|
};
|
|
337
384
|
}
|
|
385
|
+
log(`progress update successful for lesson: ${lessonSlug}`);
|
|
338
386
|
return { status: 'success' };
|
|
339
387
|
}
|
|
340
388
|
const ModuleSchema = z.object({
|
|
@@ -361,6 +409,7 @@ export async function getWorkshopData(slug, { timings, request, forceFresh, } =
|
|
|
361
409
|
if (!authInfo)
|
|
362
410
|
return { resources: [] };
|
|
363
411
|
const { product: { host }, } = getWorkshopConfig();
|
|
412
|
+
log(`fetching workshop data for slug: ${slug} from host: ${host}`);
|
|
364
413
|
return cachified({
|
|
365
414
|
key: `epic-workshop-data:${host}:${slug}`,
|
|
366
415
|
cache: fsCache,
|
|
@@ -370,13 +419,19 @@ export async function getWorkshopData(slug, { timings, request, forceFresh, } =
|
|
|
370
419
|
offlineFallbackValue: { resources: [] },
|
|
371
420
|
checkValue: ModuleSchema,
|
|
372
421
|
async getFreshValue() {
|
|
373
|
-
const
|
|
422
|
+
const workshopUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}`;
|
|
423
|
+
log(`making workshop data request to: ${workshopUrl}`);
|
|
424
|
+
const response = await fetch(workshopUrl).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
425
|
+
log(`workshop data response: ${response.status} ${response.statusText}`);
|
|
374
426
|
if (response.status < 200 || response.status >= 300) {
|
|
427
|
+
log.error(`failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`);
|
|
375
428
|
console.error(`Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`);
|
|
376
429
|
return { resources: [] };
|
|
377
430
|
}
|
|
378
431
|
const jsonResponse = await response.json();
|
|
379
|
-
|
|
432
|
+
const parsedData = ModuleSchema.parse(jsonResponse);
|
|
433
|
+
log(`successfully fetched workshop data for ${slug} with ${parsedData.resources?.length ?? 0} resources`);
|
|
434
|
+
return parsedData;
|
|
380
435
|
},
|
|
381
436
|
});
|
|
382
437
|
}
|
|
@@ -405,12 +460,16 @@ export async function userHasAccessToWorkshop({ timings, request, forceFresh, }
|
|
|
405
460
|
offlineFallbackValue: false,
|
|
406
461
|
checkValue: z.boolean(),
|
|
407
462
|
async getFreshValue(context) {
|
|
408
|
-
const
|
|
463
|
+
const accessUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}/access`;
|
|
464
|
+
log(`checking workshop access via API: ${accessUrl}`);
|
|
465
|
+
const response = await fetch(accessUrl, {
|
|
409
466
|
headers: {
|
|
410
467
|
authorization: `Bearer ${authInfo.tokenSet.access_token}`,
|
|
411
468
|
},
|
|
412
469
|
}).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
470
|
+
log(`workshop access API response: ${response.status} ${response.statusText}`);
|
|
413
471
|
const hasAccess = response.ok ? (await response.json()) === true : false;
|
|
472
|
+
log(`workshop access result for ${slug}: ${hasAccess}`);
|
|
414
473
|
if (hasAccess) {
|
|
415
474
|
context.metadata.ttl = 1000 * 60 * 5;
|
|
416
475
|
context.metadata.swr = 1000 * 60 * 60 * 24 * 365 * 10;
|
|
@@ -494,6 +553,7 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
494
553
|
const { product: { host }, } = getWorkshopConfig();
|
|
495
554
|
const accessToken = tokenSet.access_token;
|
|
496
555
|
const url = `https://${host}/oauth/userinfo`;
|
|
556
|
+
log('fetching user info from: %s', url);
|
|
497
557
|
const userInfo = await cachified({
|
|
498
558
|
key: `${url}:${md5(accessToken)}`,
|
|
499
559
|
cache: fsCache,
|
|
@@ -505,10 +565,13 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
505
565
|
offlineFallbackValue: null,
|
|
506
566
|
checkValue: UserInfoSchema,
|
|
507
567
|
async getFreshValue() {
|
|
568
|
+
log(`making user info API request to: ${url}`);
|
|
508
569
|
const response = await fetch(url, {
|
|
509
570
|
headers: { authorization: `Bearer ${accessToken}` },
|
|
510
571
|
}).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
|
|
572
|
+
log(`user info API response: ${response.status} ${response.statusText}`);
|
|
511
573
|
if (!response.ok) {
|
|
574
|
+
log(`user info API request failed: ${response.status} ${response.statusText}`);
|
|
512
575
|
if (response.headers.get('content-type')?.includes('application/json')) {
|
|
513
576
|
const data = await response.json();
|
|
514
577
|
throw new Error(`Failed to fetch user info: ${JSON.stringify(data)}`);
|
|
@@ -519,10 +582,12 @@ export async function getUserInfo({ timings, request, forceFresh, } = {}) {
|
|
|
519
582
|
}
|
|
520
583
|
}
|
|
521
584
|
const data = await response.json();
|
|
522
|
-
|
|
585
|
+
const parsedUserInfo = UserInfoSchema.parse(data);
|
|
586
|
+
log(`successfully fetched user info for user: ${parsedUserInfo.id} (${parsedUserInfo.email})`);
|
|
587
|
+
return parsedUserInfo;
|
|
523
588
|
},
|
|
524
589
|
}).catch((e) => {
|
|
525
|
-
|
|
590
|
+
log.error(`failed to get user info:`, e);
|
|
526
591
|
return null;
|
|
527
592
|
});
|
|
528
593
|
// we used to md5 hash the email to get the id
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"epic-api.server.js","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"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","sourcesContent":["// eslint-disable-next-line import/order -- this must be first\nimport { getEnv } from './init-env.js'\n\nimport * as cookie from 'cookie'\nimport md5 from 'md5-hex'\nimport { z } from 'zod'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n\tgetWorkshopInstructions,\n} from './apps.server.js'\nimport { cachified, fsCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getAuthInfo, setAuthInfo } from './db.server.js'\nimport { type Timings } from './timing.server.js'\nimport { getErrorMessage } from './utils.js'\n\nconst Transcript = z\n\t.string()\n\t.nullable()\n\t.optional()\n\t.transform((s) => s ?? 'Transcripts not available')\nconst EpicVideoInfoSchema = z.object({\n\ttitle: z.string().nullable().optional(),\n\ttranscript: Transcript,\n\tmuxPlaybackId: z.string(),\n})\n\nconst EpicVideoRegionRestrictedErrorSchema = z.object({\n\trequestCountry: z.string(),\n\trestrictedCountry: z.string(),\n\tisRegionRestricted: z.literal(true),\n})\n\nconst CachedEpicVideoInfoSchema = z\n\t.object({\n\t\ttitle: z.string().nullable().optional(),\n\t\ttranscript: Transcript,\n\t\tmuxPlaybackId: z.string(),\n\t\tstatus: z.literal('success'),\n\t\tstatusCode: z.number(),\n\t\tstatusText: z.string(),\n\t})\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('unknown'),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('region-restricted'),\n\t\t\trequestCountry: z.string(),\n\t\t\trestrictedCountry: z.string(),\n\t\t}),\n\t)\n\t.or(z.null())\n\nexport type EpicVideoInfos = Record<\n\tstring,\n\tAwaited<ReturnType<typeof getEpicVideoInfo>>\n>\n\nexport async function getEpicVideoInfos(\n\tepicWebUrls?: Array<string> | null,\n\t{ request, timings }: { request?: Request; timings?: Timings } = {},\n) {\n\tif (!epicWebUrls) return {}\n\tconst authInfo = await getAuthInfo()\n\tif (getEnv().EPICSHOP_DEPLOYED) return {}\n\n\tconst epicVideoInfos: EpicVideoInfos = {}\n\tfor (const epicVideoEmbed of epicWebUrls) {\n\t\tconst epicVideoInfo = await getEpicVideoInfo({\n\t\t\tepicVideoEmbed,\n\t\t\taccessToken: authInfo?.tokenSet.access_token,\n\t\t\trequest,\n\t\t\ttimings,\n\t\t})\n\t\tif (epicVideoInfo) {\n\t\t\tepicVideoInfos[epicVideoEmbed] = epicVideoInfo\n\t\t}\n\t}\n\treturn epicVideoInfos\n}\n\nasync function getEpicVideoInfo({\n\tepicVideoEmbed,\n\taccessToken,\n\trequest,\n\ttimings,\n}: {\n\tepicVideoEmbed: string\n\taccessToken?: string\n\trequest?: Request\n\ttimings?: Timings\n}) {\n\tconst tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated'\n\tconst key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`\n\n\treturn cachified({\n\t\tkey,\n\t\trequest,\n\t\tcache: fsCache,\n\t\ttimings,\n\t\tttl: 1000 * 60 * 60,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: CachedEpicVideoInfoSchema,\n\t\tasync getFreshValue(\n\t\t\tcontext,\n\t\t): Promise<z.infer<typeof CachedEpicVideoInfoSchema>> {\n\t\t\tconst epicUrl = new URL(epicVideoEmbed)\n\t\t\tif (\n\t\t\t\tepicUrl.host !== 'www.epicweb.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicreact.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicai.pro'\n\t\t\t) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// this may be temporary until the /tutorials/ endpoint supports /api\n\t\t\tif (epicUrl.pathname.startsWith('/tutorials/')) {\n\t\t\t\tepicUrl.pathname = epicUrl.pathname.replace(\n\t\t\t\t\t/^\\/tutorials\\//,\n\t\t\t\t\t'/workshops/',\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// special case for epicai.pro videos\n\t\t\tconst apiUrl =\n\t\t\t\tepicUrl.host === 'www.epicai.pro'\n\t\t\t\t\t? getEpicAIVideoAPIUrl(epicVideoEmbed)\n\t\t\t\t\t: `https://${epicUrl.host}/api${epicUrl.pathname}`\n\n\t\t\tconst infoResponse = await fetch(\n\t\t\t\tapiUrl,\n\t\t\t\taccessToken\n\t\t\t\t\t? { headers: { authorization: `Bearer ${accessToken}` } }\n\t\t\t\t\t: undefined,\n\t\t\t)\n\t\t\tconst { status, statusText } = infoResponse\n\t\t\tif (infoResponse.status >= 200 && infoResponse.status < 300) {\n\t\t\t\tlet rawInfo = await infoResponse.json()\n\t\t\t\t// another special case for epicai.pro videos\n\t\t\t\tif (epicUrl.host === 'www.epicai.pro') {\n\t\t\t\t\trawInfo = preprocessEpicAIVideoAPIResult(rawInfo)\n\t\t\t\t}\n\t\t\t\tconst infoResult = EpicVideoInfoSchema.safeParse(rawInfo)\n\t\t\t\tif (infoResult.success) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t...infoResult.data,\n\t\t\t\t\t} as const\n\t\t\t\t} else {\n\t\t\t\t\t// don't cache errors for long...\n\t\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\t\tconst restrictedResult =\n\t\t\t\t\t\tEpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo)\n\t\t\t\t\tif (restrictedResult.success) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\ttype: 'region-restricted',\n\t\t\t\t\t\t\t...restrictedResult.data,\n\t\t\t\t\t\t} as const\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`Response from EpicWeb for \"${epicUrl.pathname}\" does not match expectation`,\n\t\t\t\t\t\t\tinfoResult.error,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: 500,\n\t\t\t\t\t\t\tstatusText: 'API Data Type Mismatch',\n\t\t\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\t\t} as const\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tstatusCode: status,\n\t\t\t\t\tstatusText,\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t} as const\n\t\t\t}\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error(`Failed to fetch epic video info for ${epicVideoEmbed}`, e)\n\t\tthrow e\n\t})\n}\n\nfunction getEpicAIVideoAPIUrl(epicVideoEmbed: string) {\n\tconst epicUrl = new URL(epicVideoEmbed)\n\tconst slug = epicUrl.pathname.split('/').at(-1)!\n\treturn `https://epicai.pro/api/posts?slugOrId=${slug}`\n}\n\nfunction preprocessEpicAIVideoAPIResult(result: any) {\n\tconst PostVideoResourceSchema = z.object({\n\t\tresource: z.object({\n\t\t\ttype: z.literal('videoResource'),\n\t\t\tfields: EpicVideoInfoSchema,\n\t\t}),\n\t})\n\tconst PostSchema = z.object({\n\t\tfields: z.object({ title: z.string() }),\n\t\tresources: z.array(z.any()).nullable(),\n\t})\n\tconst post = PostSchema.safeParse(result)\n\tif (!post.success) return null\n\tfor (const resource of post.data.resources ?? []) {\n\t\tconst videoResource = PostVideoResourceSchema.safeParse(resource)\n\n\t\tif (videoResource.success) {\n\t\t\treturn {\n\t\t\t\t...videoResource.data.resource.fields,\n\t\t\t\ttitle: post.data.fields.title,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\nasync function getEpicProgress({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: { timings?: Timings; request?: Request; forceFresh?: boolean } = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\tconst authInfo = await getAuthInfo()\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\tif (!authInfo) return []\n\tconst tokenPart = md5(authInfo.tokenSet.access_token)\n\tconst EpicProgressSchema = z.array(\n\t\tz.object({\n\t\t\tlessonId: z.string(),\n\t\t\tcompletedAt: z.string().nullable(),\n\t\t}),\n\t)\n\treturn cachified({\n\t\tkey: `epic-progress:${host}:${tokenPart}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t\tttl: 1000 * 2,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: [],\n\t\tcheckValue: EpicProgressSchema,\n\t\tasync getFreshValue(context): Promise<z.infer<typeof EpicProgressSchema>> {\n\t\t\tconst response = await fetch(`https://${host}/api/progress`, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn EpicProgressSchema.parse(await response.json())\n\t\t},\n\t})\n}\n\nexport type Progress = Awaited<ReturnType<typeof getProgress>>[number]\nexport async function getProgress({\n\ttimings,\n\trequest,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n} = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return []\n\tconst {\n\t\tproduct: { slug, host },\n\t} = getWorkshopConfig()\n\tif (!slug) return []\n\n\tconst [\n\t\tworkshopData,\n\t\tepicProgress,\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t] = await Promise.all([\n\t\tgetWorkshopData(slug, { request, timings }),\n\t\tgetEpicProgress({ request, timings }),\n\t\tgetWorkshopInstructions({ request }),\n\t\tgetWorkshopFinished({ request }),\n\t\tgetExercises({ request, timings }),\n\t])\n\n\ttype ProgressInfo = {\n\t\tepicLessonUrl: string\n\t\tepicLessonSlug: string\n\t\tepicCompletedAt: string | null\n\t}\n\tconst progress: Array<\n\t\tProgressInfo &\n\t\t\t(ReturnType<typeof getProgressForLesson> | { type: 'unknown' })\n\t> = []\n\n\tfor (const resource of workshopData.resources ?? []) {\n\t\tconst lessons = resource._type === 'section' ? resource.lessons : [resource]\n\t\tfor (const lesson of lessons) {\n\t\t\tconst epicLessonSlug = lesson.slug\n\t\t\tconst lessonProgress = epicProgress.find(\n\t\t\t\t({ lessonId }) => lessonId === lesson._id,\n\t\t\t)\n\t\t\tconst epicCompletedAt = lessonProgress ? lessonProgress.completedAt : null\n\t\t\tconst progressForLesson = getProgressForLesson(epicLessonSlug, {\n\t\t\t\tworkshopInstructions,\n\t\t\t\tworkshopFinished,\n\t\t\t\texercises,\n\t\t\t})\n\t\t\tconst epicLessonUrl = `https://${host}/workshops/${slug}/${epicLessonSlug}`\n\t\t\tif (progressForLesson) {\n\t\t\t\tprogress.push({\n\t\t\t\t\t...progressForLesson,\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tprogress.push({\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn progress\n}\n\nfunction getProgressForLesson(\n\tepicLessonSlug: string,\n\t{\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t}: {\n\t\tworkshopInstructions: Awaited<ReturnType<typeof getWorkshopInstructions>>\n\t\tworkshopFinished: Awaited<ReturnType<typeof getWorkshopFinished>>\n\t\texercises: Awaited<ReturnType<typeof getExercises>>\n\t},\n) {\n\tconst hasEmbed = (embed?: Array<string>) =>\n\t\tembed?.some((e) => e.split('/').at(-1) === epicLessonSlug)\n\tif (\n\t\tworkshopInstructions.compiled.status === 'success' &&\n\t\thasEmbed(workshopInstructions.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-instructions' } as const\n\t}\n\tif (\n\t\tworkshopFinished.compiled.status === 'success' &&\n\t\thasEmbed(workshopFinished.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-finished' } as const\n\t}\n\tfor (const exercise of exercises) {\n\t\tif (hasEmbed(exercise.instructionsEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'instructions',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tif (hasEmbed(exercise.finishedEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'finished',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tfor (const step of exercise.steps.filter(Boolean)) {\n\t\t\tif (hasEmbed(step.problem?.epicVideoEmbeds)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'step',\n\t\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t} as const\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function updateProgress(\n\t{ lessonSlug, complete }: { lessonSlug: string; complete?: boolean },\n\t{\n\t\ttimings,\n\t\trequest,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: 'cannot update progress when deployed',\n\t\t} as const\n\t}\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\treturn { status: 'error', error: 'not authenticated' } as const\n\t}\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst response = await fetch(`https://${host}/api/progress`, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t'content-type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(\n\t\t\tcomplete ? { lessonSlug } : { lessonSlug, remove: true },\n\t\t),\n\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t// force the progress to be fresh whether or not we're successful\n\tawait getEpicProgress({ forceFresh: true, request, timings })\n\n\tif (response.status < 200 || response.status >= 300) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: `${response.status} ${response.statusText}`,\n\t\t} as const\n\t}\n\n\treturn { status: 'success' } as const\n}\n\nconst ModuleSchema = z.object({\n\tresources: z\n\t\t.array(\n\t\t\tz.union([\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('lesson'),\n\t\t\t\t\t_id: z.string(),\n\t\t\t\t\tslug: z.string(),\n\t\t\t\t}),\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('section'),\n\t\t\t\t\tlessons: z.array(z.object({ _id: z.string(), slug: z.string() })),\n\t\t\t\t}),\n\t\t\t]),\n\t\t)\n\t\t.nullable(),\n})\n\nexport async function getWorkshopData(\n\tslug: string,\n\t{\n\t\ttimings,\n\t\trequest,\n\t\tforceFresh,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t\tforceFresh?: boolean\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return { resources: [] }\n\tconst authInfo = await getAuthInfo()\n\t// auth is not required, but we only use it for progress which is only needed\n\t// if you're authenticated anyway.\n\tif (!authInfo) return { resources: [] }\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\treturn cachified({\n\t\tkey: `epic-workshop-data:${host}:${slug}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tofflineFallbackValue: { resources: [] },\n\t\tcheckValue: ModuleSchema,\n\t\tasync getFreshValue(): Promise<z.infer<typeof ModuleSchema>> {\n\t\t\tconst response = await fetch(\n\t\t\t\t`https://${host}/api/workshops/${encodeURIComponent(slug)}`,\n\t\t\t).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\treturn { resources: [] }\n\t\t\t}\n\t\t\tconst jsonResponse = await response.json()\n\t\t\treturn ModuleSchema.parse(jsonResponse)\n\t\t},\n\t})\n}\n\nexport async function userHasAccessToWorkshop({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean\n} = {}) {\n\tconst config = getWorkshopConfig()\n\tconst {\n\t\tproduct: { host, slug },\n\t} = config\n\tif (!slug) return true\n\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\tconst cookieHeader = request?.headers.get('Cookie')\n\t\tif (!cookieHeader) return false\n\t\tconst cookies = cookie.parse(cookieHeader)\n\t\treturn cookies.skill?.split(',').includes(slug) ?? false\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return false\n\n\treturn cachified({\n\t\tkey: `user-has-access-to-workshop:${host}:${slug}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 5,\n\t\tofflineFallbackValue: false,\n\t\tcheckValue: z.boolean(),\n\t\tasync getFreshValue(context) {\n\t\t\tconst response = await fetch(\n\t\t\t\t`https://${host}/api/workshops/${encodeURIComponent(slug)}/access`,\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\tconst hasAccess = response.ok ? (await response.json()) === true : false\n\n\t\t\tif (hasAccess) {\n\t\t\t\tcontext.metadata.ttl = 1000 * 60 * 5\n\t\t\t\tcontext.metadata.swr = 1000 * 60 * 60 * 24 * 365 * 10\n\t\t\t}\n\n\t\t\treturn hasAccess\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error('Failed to check workshop access', e)\n\t\treturn false\n\t})\n}\n\nconst UserInfoSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tname: z.string().nullable(),\n\t\temail: z.string().email(),\n\t\timage: z.string().nullable(),\n\t\tdiscordProfile: z\n\t\t\t.object({\n\t\t\t\tnick: z.string().nullable().optional(),\n\t\t\t\tuser: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\tid: z.string(),\n\t\t\t\t\t\tusername: z.string(),\n\t\t\t\t\t\tavatar: z.string().nullable().optional(),\n\t\t\t\t\t\tglobal_name: z.string().nullable().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.optional(),\n\t\t\t})\n\t\t\t.nullable()\n\t\t\t.optional(),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\timageUrlSmall:\n\t\t\t\tresizeImageUrl(data.image, { size: 64 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 64,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 64 }),\n\t\t\timageUrlLarge:\n\t\t\t\tresizeImageUrl(data.image, { size: 512 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 512,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 512 }),\n\t\t}\n\t})\n\nfunction resizeImageUrl(url: string | null, { size }: { size: number }) {\n\tif (!url) return null\n\tconst urlObj = new URL(url)\n\turlObj.searchParams.set('size', size.toString())\n\treturn urlObj.toString()\n}\n\nfunction resolveGravatarUrl(\n\temail: string | undefined,\n\t{ size }: { size: number },\n) {\n\tif (!email) return null\n\n\tconst hash = md5(email.toLowerCase())\n\tconst gravatarOptions = new URLSearchParams({\n\t\tsize: size.toString(),\n\t\tdefault: 'identicon',\n\t})\n\treturn `https://www.gravatar.com/avatar/${hash}?${gravatarOptions.toString()}`\n}\n\nfunction resolveDiscordAvatar(\n\tuser: { avatar?: string | null; id: string } | undefined,\n\t{ size }: { size: 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 },\n) {\n\tif (!user) return null\n\n\tconst { avatar, id: userId } = user\n\tif (!avatar) return null\n\tconst isGif = avatar.startsWith('a_')\n\tconst url = new URL(\n\t\t`/avatars/${userId}/${avatar}.${isGif ? 'gif' : 'png'}`,\n\t\t'https://cdn.discordapp.com',\n\t)\n\turl.searchParams.set('size', size.toString())\n\treturn url.toString()\n}\n\nexport type UserInfo = z.infer<typeof UserInfoSchema>\n\nexport async function getUserInfo({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n\tforceFresh?: boolean\n} = {}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return null\n\tconst { tokenSet } = authInfo\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst accessToken = tokenSet.access_token\n\tconst url = `https://${host}/oauth/userinfo`\n\n\tconst userInfo = await cachified({\n\t\tkey: `${url}:${md5(accessToken)}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 30,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: UserInfoSchema,\n\t\tasync getFreshValue(): Promise<UserInfo> {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${accessToken}` },\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tif (!response.ok) {\n\t\t\t\tif (\n\t\t\t\t\tresponse.headers.get('content-type')?.includes('application/json')\n\t\t\t\t) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthrow new Error(`Failed to fetch user info: ${JSON.stringify(data)}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst text = await response.text()\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Failed to fetch user info: ${text || response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = await response.json()\n\t\t\treturn UserInfoSchema.parse(data)\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error('Failed to get user info', e)\n\t\treturn null\n\t})\n\n\t// we used to md5 hash the email to get the id\n\t// if the id doesn't match what we have on file, update it\n\t// you can probably safely remove this in January 2025\n\tif (userInfo && authInfo.id !== userInfo.id) {\n\t\tawait setAuthInfo({\n\t\t\t...authInfo,\n\t\t\tid: userInfo.id,\n\t\t})\n\t}\n\n\treturn userInfo\n}\n"]}
|
|
1
|
+
{"version":3,"file":"epic-api.server.js","sourceRoot":"","sources":["../../src/epic-api.server.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAChC,OAAO,GAAG,MAAM,SAAS,CAAA;AACzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EACN,YAAY,EACZ,mBAAmB,EACnB,uBAAuB,GACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE5C,8CAA8C;AAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA;AAE9B,MAAM,UAAU,GAAG,CAAC;KAClB,MAAM,EAAE;KACR,QAAQ,EAAE;KACV,QAAQ,EAAE;KACV,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,2BAA2B,CAAC,CAAA;AACpD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,UAAU;IACtB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;CACzB,CAAC,CAAA;AAEF,MAAM,oCAAoC,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC7B,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;CACnC,CAAC,CAAA;AAEF,MAAM,yBAAyB,GAAG,CAAC;KACjC,MAAM,CAAC;IACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,UAAU,EAAE,UAAU;IACtB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC;KACD,EAAE,CACF,CAAC,CAAC,MAAM,CAAC;IACR,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;CAC1B,CAAC,CACF;KACA,EAAE,CACF,CAAC,CAAC,MAAM,CAAC;IACR,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC;IACpC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;IAC1B,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;CAC7B,CAAC,CACF;KACA,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;AAOd,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,WAAkC,EAClC,EAAE,OAAO,EAAE,OAAO,KAA+C,EAAE;IAEnE,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,GAAG,CAAC,mDAAmD,CAAC,CAAA;QACxD,OAAO,EAAE,CAAA;IACV,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,MAAM,EAAE,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAA;IAEzC,GAAG,CAAC,iCAAiC,WAAW,CAAC,MAAM,OAAO,CAAC,CAAA;IAC/D,MAAM,cAAc,GAAmB,EAAE,CAAA;IACzC,KAAK,MAAM,cAAc,IAAI,WAAW,EAAE,CAAC;QAC1C,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC;YAC5C,cAAc;YACd,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,YAAY;YAC5C,OAAO;YACP,OAAO;SACP,CAAC,CAAA;QACF,IAAI,aAAa,EAAE,CAAC;YACnB,cAAc,CAAC,cAAc,CAAC,GAAG,aAAa,CAAA;QAC/C,CAAC;IACF,CAAC;IACD,GAAG,CAAC,wBAAwB,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,mBAAmB,CAAC,CAAA;IAClF,OAAO,cAAc,CAAA;AACtB,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,EAC/B,cAAc,EACd,WAAW,EACX,OAAO,EACP,OAAO,GAMP;IACA,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAA;IACvE,MAAM,GAAG,GAAG,mBAAmB,YAAY,IAAI,cAAc,EAAE,CAAA;IAE/D,GAAG,CAAC,gCAAgC,cAAc,EAAE,CAAC,CAAA;IACrD,OAAO,SAAS,CAAC;QAChB,GAAG;QACH,OAAO;QACP,KAAK,EAAE,OAAO;QACd,OAAO;QACP,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE;QACnB,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;QACnC,oBAAoB,EAAE,IAAI;QAC1B,UAAU,EAAE,yBAAyB;QACrC,KAAK,CAAC,aAAa,CAClB,OAAO;YAEP,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;YACvC,IACC,OAAO,CAAC,IAAI,KAAK,iBAAiB;gBAClC,OAAO,CAAC,IAAI,KAAK,mBAAmB;gBACpC,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAChC,CAAC;gBACF,GAAG,CAAC,mCAAmC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;gBACtD,OAAO,IAAI,CAAA;YACZ,CAAC;YAED,qEAAqE;YACrE,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAChD,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAC1C,gBAAgB,EAChB,aAAa,CACb,CAAA;YACF,CAAC;YAED,qCAAqC;YACrC,MAAM,MAAM,GACX,OAAO,CAAC,IAAI,KAAK,gBAAgB;gBAChC,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC;gBACtC,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAA;YAEpD,GAAG,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAA;YACvC,MAAM,YAAY,GAAG,MAAM,KAAK,CAC/B,MAAM,EACN,WAAW;gBACV,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE,EAAE;gBACzD,CAAC,CAAC,SAAS,CACZ,CAAA;YACD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,CAAA;YAC3C,GAAG,CAAC,iBAAiB,MAAM,IAAI,UAAU,EAAE,CAAC,CAAA;YAE5C,IAAI,YAAY,CAAC,MAAM,IAAI,GAAG,IAAI,YAAY,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC7D,IAAI,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAA;gBACvC,6CAA6C;gBAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;oBACvC,OAAO,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAA;gBAClD,CAAC;gBACD,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;gBACzD,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;oBACxB,GAAG,CAAC,sCAAsC,cAAc,EAAE,CAAC,CAAA;oBAC3D,OAAO;wBACN,MAAM,EAAE,SAAS;wBACjB,UAAU,EAAE,MAAM;wBAClB,UAAU;wBACV,GAAG,UAAU,CAAC,IAAI;qBACT,CAAA;gBACX,CAAC;qBAAM,CAAC;oBACP,iCAAiC;oBACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,CAAA;oBAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAA;oBACxB,MAAM,gBAAgB,GACrB,oCAAoC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;oBACxD,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;wBAC9B,GAAG,CAAC,IAAI,CAAC,+BAA+B,cAAc,EAAE,CAAC,CAAA;wBACzD,OAAO;4BACN,MAAM,EAAE,OAAO;4BACf,UAAU,EAAE,MAAM;4BAClB,UAAU;4BACV,IAAI,EAAE,mBAAmB;4BACzB,GAAG,gBAAgB,CAAC,IAAI;yBACf,CAAA;oBACX,CAAC;yBAAM,CAAC;wBACP,GAAG,CAAC,KAAK,CAAC,mCAAmC,cAAc,EAAE,EAAE;4BAC9D,GAAG,EAAE,OAAO,CAAC,QAAQ;4BACrB,OAAO;4BACP,UAAU,EAAE,UAAU,CAAC,KAAK;yBAC5B,CAAC,CAAA;wBACF,OAAO,CAAC,IAAI,CACX,8BAA8B,OAAO,CAAC,QAAQ,8BAA8B,EAC5E,UAAU,CAAC,KAAK,CAChB,CAAA;wBACD,OAAO;4BACN,MAAM,EAAE,OAAO;4BACf,UAAU,EAAE,GAAG;4BACf,UAAU,EAAE,wBAAwB;4BACpC,IAAI,EAAE,SAAS;yBACN,CAAA;oBACX,CAAC;gBACF,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,iCAAiC;gBACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,CAAA;gBAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAA;gBACxB,GAAG,CAAC,KAAK,CAAC,gCAAgC,cAAc,EAAE,EAAE;oBAC3D,MAAM;oBACN,UAAU;oBACV,GAAG,EAAE,MAAM;iBACX,CAAC,CAAA;gBACF,OAAO;oBACN,MAAM,EAAE,OAAO;oBACf,UAAU,EAAE,MAAM;oBAClB,UAAU;oBACV,IAAI,EAAE,SAAS;iBACN,CAAA;YACX,CAAC;QACF,CAAC;KACD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;QACd,GAAG,CAAC,KAAK,CAAC,uCAAuC,cAAc,GAAG,EAAE,CAAC,CAAC,CAAA;QACtE,MAAM,CAAC,CAAA;IACR,CAAC,CAAC,CAAA;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,cAAsB;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAA;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAA;IAEhD,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7C,OAAO,2CAA2C,IAAI,EAAE,CAAA;IACzD,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACnD,OAAO,kCAAkC,IAAI,WAAW,CAAA;IACzD,CAAC;SAAM,CAAC;QACP,OAAO,yCAAyC,IAAI,EAAE,CAAA;IACvD,CAAC;AACF,CAAC;AAED,SAAS,8BAA8B,CAAC,MAAW;IAClD,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;QACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;YAClB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YAChC,MAAM,EAAE,mBAAmB;SAC3B,CAAC;KACF,CAAC,CAAA;IACF,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;QAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QACvC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE;KACtC,CAAC,CAAA;IACF,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACzC,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAC9B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;QAClD,MAAM,aAAa,GAAG,uBAAuB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAEjE,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YAC3B,OAAO;gBACN,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM;gBACrC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK;aAC7B,CAAA;QACF,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAA;AACZ,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,OAAO,EACP,UAAU,MACyD,EAAE;IACrE,IAAI,MAAM,EAAE,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAA;IAEzC,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,GACjB,GAAG,iBAAiB,EAAE,CAAA;IACvB,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IAExB,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IACrD,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CACjC,CAAC,CAAC,MAAM,CAAC;QACR,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAClC,CAAC,CACF,CAAA;IAED,GAAG,CAAC,wCAAwC,IAAI,EAAE,CAAC,CAAA;IACnD,OAAO,SAAS,CAAC;QAChB,GAAG,EAAE,iBAAiB,IAAI,IAAI,SAAS,EAAE;QACzC,KAAK,EAAE,OAAO;QACd,OAAO;QACP,OAAO;QACP,UAAU;QACV,GAAG,EAAE,IAAI,GAAG,CAAC;QACb,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;QACnC,oBAAoB,EAAE,EAAE;QACxB,UAAU,EAAE,kBAAkB;QAC9B,KAAK,CAAC,aAAa,CAAC,OAAO;YAC1B,MAAM,WAAW,GAAG,WAAW,IAAI,eAAe,CAAA;YAClD,GAAG,CAAC,mCAAmC,WAAW,EAAE,CAAC,CAAA;YAErD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE;gBACzC,OAAO,EAAE;oBACR,aAAa,EAAE,UAAU,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE;iBACzD;aACD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;YAElE,GAAG,CAAC,0BAA0B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YAEvE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBACrD,GAAG,CAAC,KAAK,CAAC,0CAA0C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;gBAC7F,OAAO,CAAC,KAAK,CACZ,0CAA0C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAClF,CAAA;gBACD,iCAAiC;gBACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,CAAA;gBAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAA;gBACxB,OAAO,EAAE,CAAA;YACV,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC1C,MAAM,cAAc,GAAG,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;YAC7D,GAAG,CAAC,wBAAwB,cAAc,CAAC,MAAM,mBAAmB,CAAC,CAAA;YACrE,OAAO,cAAc,CAAA;QACtB,CAAC;KACD,CAAC,CAAA;AACH,CAAC;AAGD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,MAIJ,EAAE;IACL,IAAI,MAAM,EAAE,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAA;IAEzC,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IAExB,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GACvB,GAAG,iBAAiB,EAAE,CAAA;IACvB,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,GAAG,CAAC,2CAA2C,IAAI,EAAE,CAAC,CAAA;IACtD,MAAM,CACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,EACT,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACrB,eAAe,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAC3C,eAAe,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACrC,uBAAuB,CAAC,EAAE,OAAO,EAAE,CAAC;QACpC,mBAAmB,CAAC,EAAE,OAAO,EAAE,CAAC;QAChC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;KAClC,CAAC,CAAA;IAOF,MAAM,QAAQ,GAGV,EAAE,CAAA;IAEN,KAAK,MAAM,QAAQ,IAAI,YAAY,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;QAC5E,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAA;YAClC,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CACvC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,KAAK,MAAM,CAAC,GAAG,CACzC,CAAA;YACD,MAAM,eAAe,GAAG,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA;YAC1E,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,cAAc,EAAE;gBAC9D,oBAAoB;gBACpB,gBAAgB;gBAChB,SAAS;aACT,CAAC,CAAA;YACF,MAAM,aAAa,GAAG,WAAW,IAAI,cAAc,IAAI,IAAI,cAAc,EAAE,CAAA;YAC3E,IAAI,iBAAiB,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CAAC;oBACb,GAAG,iBAAiB;oBACpB,aAAa;oBACb,cAAc;oBACd,eAAe;iBACf,CAAC,CAAA;YACH,CAAC;iBAAM,CAAC;gBACP,QAAQ,CAAC,IAAI,CAAC;oBACb,IAAI,EAAE,SAAS;oBACf,aAAa;oBACb,cAAc;oBACd,eAAe;iBACf,CAAC,CAAA;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED,GAAG,CAAC,aAAa,QAAQ,CAAC,MAAM,mCAAmC,IAAI,EAAE,CAAC,CAAA;IAC1E,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,SAAS,oBAAoB,CAC5B,cAAsB,EACtB,EACC,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,GAKT;IAED,MAAM,QAAQ,GAAG,CAAC,KAAqB,EAAE,EAAE,CAC1C,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAA;IAC3D,IACC,oBAAoB,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS;QAClD,QAAQ,CAAC,oBAAoB,CAAC,QAAQ,CAAC,eAAe,CAAC,EACtD,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAW,CAAA;IAClD,CAAC;IACD,IACC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS;QAC9C,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,eAAe,CAAC,EAClD,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAW,CAAA;IAC9C,CAAC;IACD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,IAAI,QAAQ,CAAC,QAAQ,CAAC,2BAA2B,CAAC,EAAE,CAAC;YACpD,OAAO;gBACN,IAAI,EAAE,cAAc;gBACpB,cAAc,EAAE,QAAQ,CAAC,cAAc;aAC9B,CAAA;QACX,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;YAChD,OAAO;gBACN,IAAI,EAAE,UAAU;gBAChB,cAAc,EAAE,QAAQ,CAAC,cAAc;aAC9B,CAAA;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,CAAC;gBAC7C,OAAO;oBACN,IAAI,EAAE,MAAM;oBACZ,cAAc,EAAE,QAAQ,CAAC,cAAc;oBACvC,UAAU,EAAE,IAAI,CAAC,UAAU;iBAClB,CAAA;YACX,CAAC;QACF,CAAC;IACF,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,EAAE,UAAU,EAAE,QAAQ,EAA8C,EACpE,EACC,OAAO,EACP,OAAO,MAIJ,EAAE;IAEN,IAAI,MAAM,EAAE,CAAC,iBAAiB,EAAE,CAAC;QAChC,OAAO;YACN,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,sCAAsC;SACpC,CAAA;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAW,CAAA;IAChE,CAAC;IAED,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,GACjB,GAAG,iBAAiB,EAAE,CAAA;IAEvB,MAAM,WAAW,GAAG,WAAW,IAAI,eAAe,CAAA;IAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IAExE,GAAG,CAAC,iCAAiC,UAAU,eAAe,QAAQ,GAAG,CAAC,CAAA;IAC1E,GAAG,CAAC,2BAA2B,WAAW,kBAAkB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAEtF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACR,aAAa,EAAE,UAAU,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE;YACzD,cAAc,EAAE,kBAAkB;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC7B,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;IAElE,GAAG,CAAC,6BAA6B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAE1E,iEAAiE;IACjE,MAAM,eAAe,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAE7D,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACrD,GAAG,CAAC,2BAA2B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;QACxE,OAAO;YACN,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE;SACzC,CAAA;IACX,CAAC;IAED,GAAG,CAAC,0CAA0C,UAAU,EAAE,CAAC,CAAA;IAC3D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAW,CAAA;AACtC,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,SAAS,EAAE,CAAC;SACV,KAAK,CACL,CAAC,CAAC,KAAK,CAAC;QACP,CAAC,CAAC,MAAM,CAAC;YACR,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;YACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,CAAC;QACF,CAAC,CAAC,MAAM,CAAC;YACR,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;YAC3B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SACjE,CAAC;KACF,CAAC,CACF;SACA,QAAQ,EAAE;CACZ,CAAC,CAAA;AAEF,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,IAAY,EACZ,EACC,OAAO,EACP,OAAO,EACP,UAAU,MAKP,EAAE;IAEN,IAAI,MAAM,EAAE,CAAC,iBAAiB;QAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAExD,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,6EAA6E;IAC7E,kCAAkC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;IAEvC,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,GACjB,GAAG,iBAAiB,EAAE,CAAA;IAEvB,GAAG,CAAC,oCAAoC,IAAI,eAAe,IAAI,EAAE,CAAC,CAAA;IAClE,OAAO,SAAS,CAAC;QAChB,GAAG,EAAE,sBAAsB,IAAI,IAAI,IAAI,EAAE;QACzC,KAAK,EAAE,OAAO;QACd,OAAO;QACP,UAAU;QACV,OAAO;QACP,oBAAoB,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACvC,UAAU,EAAE,YAAY;QACxB,KAAK,CAAC,aAAa;YAClB,MAAM,WAAW,GAAG,WAAW,IAAI,kBAAkB,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAA;YAC/E,GAAG,CAAC,oCAAoC,WAAW,EAAE,CAAC,CAAA;YAEtD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;YAEzG,GAAG,CAAC,2BAA2B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YAExE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBACrD,GAAG,CAAC,KAAK,CAAC,kDAAkD,IAAI,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;gBAC9G,OAAO,CAAC,KAAK,CACZ,kDAAkD,IAAI,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACnG,CAAA;gBACD,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;YACzB,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC1C,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;YACnD,GAAG,CAAC,0CAA0C,IAAI,SAAS,UAAU,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,CAAA;YACzG,OAAO,UAAU,CAAA;QAClB,CAAC;KACD,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAC7C,OAAO,EACP,OAAO,EACP,UAAU,MAKP,EAAE;IACL,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GACvB,GAAG,MAAM,CAAA;IACV,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,IAAI,MAAM,EAAE,CAAC,iBAAiB,EAAE,CAAC;QAChC,MAAM,YAAY,GAAG,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnD,IAAI,CAAC,YAAY;YAAE,OAAO,KAAK,CAAA;QAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC1C,OAAO,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAA;IACzD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE3B,OAAO,SAAS,CAAC;QAChB,GAAG,EAAE,+BAA+B,IAAI,IAAI,IAAI,EAAE;QAClD,KAAK,EAAE,OAAO;QACd,OAAO;QACP,UAAU;QACV,OAAO;QACP,GAAG,EAAE,IAAI,GAAG,CAAC;QACb,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE;QACvB,KAAK,CAAC,aAAa,CAAC,OAAO;YAC1B,MAAM,SAAS,GAAG,WAAW,IAAI,kBAAkB,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAA;YACpF,GAAG,CAAC,qCAAqC,SAAS,EAAE,CAAC,CAAA;YAErD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;gBACvC,OAAO,EAAE;oBACR,aAAa,EAAE,UAAU,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE;iBACzD;aACD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;YAElE,GAAG,CAAC,iCAAiC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YAE9E,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;YACxE,GAAG,CAAC,8BAA8B,IAAI,KAAK,SAAS,EAAE,CAAC,CAAA;YAEvD,IAAI,SAAS,EAAE,CAAC;gBACf,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,GAAG,CAAC,CAAA;gBACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,CAAA;YACtD,CAAC;YAED,OAAO,SAAS,CAAA;QACjB,CAAC;KACD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;QACd,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,CAAC,CAAC,CAAA;QACnD,OAAO,KAAK,CAAA;IACb,CAAC,CAAC,CAAA;AACH,CAAC;AAED,MAAM,cAAc,GAAG,CAAC;KACtB,MAAM,CAAC;IACP,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IACzB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,cAAc,EAAE,CAAC;SACf,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QACtC,IAAI,EAAE,CAAC;aACL,MAAM,CAAC;YACP,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;YACd,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;YACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;YACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;SAC7C,CAAC;aACD,QAAQ,EAAE;KACZ,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,EAAE;CACZ,CAAC;KACD,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;IACnB,OAAO;QACN,GAAG,IAAI;QACP,aAAa,EACZ,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACxC,oBAAoB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE;gBAC/C,IAAI,EAAE,EAAE;aACR,CAAC;YACF,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAC7C,aAAa,EACZ,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YACzC,oBAAoB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,EAAE;gBAC/C,IAAI,EAAE,GAAG;aACT,CAAC;YACF,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;KAC9C,CAAA;AACF,CAAC,CAAC,CAAA;AAEH,SAAS,cAAc,CAAC,GAAkB,EAAE,EAAE,IAAI,EAAoB;IACrE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3B,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAChD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAA;AACzB,CAAC;AAED,SAAS,kBAAkB,CAC1B,KAAyB,EACzB,EAAE,IAAI,EAAoB;IAE1B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IACrC,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC;QAC3C,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;QACrB,OAAO,EAAE,WAAW;KACpB,CAAC,CAAA;IACF,OAAO,mCAAmC,IAAI,IAAI,eAAe,CAAC,QAAQ,EAAE,EAAE,CAAA;AAC/E,CAAC;AAED,SAAS,oBAAoB,CAC5B,IAAwD,EACxD,EAAE,IAAI,EAAiE;IAEvE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAClB,YAAY,MAAM,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EACvD,4BAA4B,CAC5B,CAAA;IACD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC7C,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACtB,CAAC;AAID,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EACjC,OAAO,EACP,OAAO,EACP,UAAU,MAKP,EAAE;IACL,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAE1B,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAA;IAC7B,MAAM,EACL,OAAO,EAAE,EAAE,IAAI,EAAE,GACjB,GAAG,iBAAiB,EAAE,CAAA;IAEvB,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,CAAA;IACzC,MAAM,GAAG,GAAG,WAAW,IAAI,iBAAiB,CAAA;IAE5C,GAAG,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC;QAChC,GAAG,EAAE,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE;QACjC,KAAK,EAAE,OAAO;QACd,OAAO;QACP,UAAU;QACV,OAAO;QACP,GAAG,EAAE,IAAI,GAAG,EAAE;QACd,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE;QACnC,oBAAoB,EAAE,IAAI;QAC1B,UAAU,EAAE,cAAc;QAC1B,KAAK,CAAC,aAAa;YAClB,GAAG,CAAC,oCAAoC,GAAG,EAAE,CAAC,CAAA;YAE9C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACjC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,EAAE,EAAE;aACnD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;YAElE,GAAG,CAAC,2BAA2B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YAExE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,GAAG,CAAC,iCAAiC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;gBAC9E,IACC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACjE,CAAC;oBACF,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;oBAClC,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBACtE,CAAC;qBAAM,CAAC;oBACP,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;oBAClC,MAAM,IAAI,KAAK,CACd,8BAA8B,IAAI,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC3D,CAAA;gBACF,CAAC;YACF,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,MAAM,cAAc,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACjD,GAAG,CAAC,4CAA4C,cAAc,CAAC,EAAE,KAAK,cAAc,CAAC,KAAK,GAAG,CAAC,CAAA;YAC9F,OAAO,cAAc,CAAA;QACtB,CAAC;KACD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;QACd,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE,CAAC,CAAC,CAAA;QACxC,OAAO,IAAI,CAAA;IACZ,CAAC,CAAC,CAAA;IAEF,8CAA8C;IAC9C,0DAA0D;IAC1D,sDAAsD;IACtD,IAAI,QAAQ,IAAI,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,WAAW,CAAC;YACjB,GAAG,QAAQ;YACX,EAAE,EAAE,QAAQ,CAAC,EAAE;SACf,CAAC,CAAA;IACH,CAAC;IAED,OAAO,QAAQ,CAAA;AAChB,CAAC","sourcesContent":["// eslint-disable-next-line import/order -- this must be first\nimport { getEnv } from './init-env.js'\n\nimport * as cookie from 'cookie'\nimport md5 from 'md5-hex'\nimport { z } from 'zod'\nimport {\n\tgetExercises,\n\tgetWorkshopFinished,\n\tgetWorkshopInstructions,\n} from './apps.server.js'\nimport { cachified, fsCache } from './cache.server.js'\nimport { getWorkshopConfig } from './config.server.js'\nimport { getAuthInfo, setAuthInfo } from './db.server.js'\nimport { logger } from './logger.js'\nimport { type Timings } from './timing.server.js'\nimport { getErrorMessage } from './utils.js'\n\n// Module-level logger for epic-api operations\nconst log = logger('epic:api')\n\nconst Transcript = z\n\t.string()\n\t.nullable()\n\t.optional()\n\t.transform((s) => s ?? 'Transcripts not available')\nconst EpicVideoInfoSchema = z.object({\n\ttitle: z.string().nullable().optional(),\n\ttranscript: Transcript,\n\tmuxPlaybackId: z.string(),\n})\n\nconst EpicVideoRegionRestrictedErrorSchema = z.object({\n\trequestCountry: z.string(),\n\trestrictedCountry: z.string(),\n\tisRegionRestricted: z.literal(true),\n})\n\nconst CachedEpicVideoInfoSchema = z\n\t.object({\n\t\ttitle: z.string().nullable().optional(),\n\t\ttranscript: Transcript,\n\t\tmuxPlaybackId: z.string(),\n\t\tstatus: z.literal('success'),\n\t\tstatusCode: z.number(),\n\t\tstatusText: z.string(),\n\t})\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('unknown'),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\tstatus: z.literal('error'),\n\t\t\tstatusCode: z.number(),\n\t\t\tstatusText: z.string(),\n\t\t\ttype: z.literal('region-restricted'),\n\t\t\trequestCountry: z.string(),\n\t\t\trestrictedCountry: z.string(),\n\t\t}),\n\t)\n\t.or(z.null())\n\nexport type EpicVideoInfos = Record<\n\tstring,\n\tAwaited<ReturnType<typeof getEpicVideoInfo>>\n>\n\nexport async function getEpicVideoInfos(\n\tepicWebUrls?: Array<string> | null,\n\t{ request, timings }: { request?: Request; timings?: Timings } = {},\n) {\n\tif (!epicWebUrls) {\n\t\tlog('no epic web URLs provided, returning empty object')\n\t\treturn {}\n\t}\n\t\n\tconst authInfo = await getAuthInfo()\n\tif (getEnv().EPICSHOP_DEPLOYED) return {}\n\n\tlog(`fetching epic video infos for ${epicWebUrls.length} URLs`)\n\tconst epicVideoInfos: EpicVideoInfos = {}\n\tfor (const epicVideoEmbed of epicWebUrls) {\n\t\tconst epicVideoInfo = await getEpicVideoInfo({\n\t\t\tepicVideoEmbed,\n\t\t\taccessToken: authInfo?.tokenSet.access_token,\n\t\t\trequest,\n\t\t\ttimings,\n\t\t})\n\t\tif (epicVideoInfo) {\n\t\t\tepicVideoInfos[epicVideoEmbed] = epicVideoInfo\n\t\t}\n\t}\n\tlog(`successfully fetched ${Object.keys(epicVideoInfos).length} epic video infos`)\n\treturn epicVideoInfos\n}\n\nasync function getEpicVideoInfo({\n\tepicVideoEmbed,\n\taccessToken,\n\trequest,\n\ttimings,\n}: {\n\tepicVideoEmbed: string\n\taccessToken?: string\n\trequest?: Request\n\ttimings?: Timings\n}) {\n\tconst tokenPortion = accessToken ? md5(accessToken) : 'unauthenticated'\n\tconst key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`\n\n\tlog(`fetching video info for URL: ${epicVideoEmbed}`)\n\treturn cachified({\n\t\tkey,\n\t\trequest,\n\t\tcache: fsCache,\n\t\ttimings,\n\t\tttl: 1000 * 60 * 60,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: CachedEpicVideoInfoSchema,\n\t\tasync getFreshValue(\n\t\t\tcontext,\n\t\t): Promise<z.infer<typeof CachedEpicVideoInfoSchema>> {\n\t\t\tconst epicUrl = new URL(epicVideoEmbed)\n\t\t\tif (\n\t\t\t\tepicUrl.host !== 'www.epicweb.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicreact.dev' &&\n\t\t\t\tepicUrl.host !== 'www.epicai.pro'\n\t\t\t) {\n\t\t\t\tlog(`unsupported host for video URL: ${epicUrl.host}`)\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// this may be temporary until the /tutorials/ endpoint supports /api\n\t\t\tif (epicUrl.pathname.startsWith('/tutorials/')) {\n\t\t\t\tepicUrl.pathname = epicUrl.pathname.replace(\n\t\t\t\t\t/^\\/tutorials\\//,\n\t\t\t\t\t'/workshops/',\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// special case for epicai.pro videos\n\t\t\tconst apiUrl =\n\t\t\t\tepicUrl.host === 'www.epicai.pro'\n\t\t\t\t\t? getEpicAIVideoAPIUrl(epicVideoEmbed)\n\t\t\t\t\t: `https://${epicUrl.host}/api${epicUrl.pathname}`\n\n\t\t\tlog(`making API request to: ${apiUrl}`)\n\t\t\tconst infoResponse = await fetch(\n\t\t\t\tapiUrl,\n\t\t\t\taccessToken\n\t\t\t\t\t? { headers: { authorization: `Bearer ${accessToken}` } }\n\t\t\t\t\t: undefined,\n\t\t\t)\n\t\t\tconst { status, statusText } = infoResponse\n\t\t\tlog(`API response: ${status} ${statusText}`)\n\t\t\t\n\t\t\tif (infoResponse.status >= 200 && infoResponse.status < 300) {\n\t\t\t\tlet rawInfo = await infoResponse.json()\n\t\t\t\t// another special case for epicai.pro videos\n\t\t\t\tif (epicUrl.host === 'www.epicai.pro') {\n\t\t\t\t\trawInfo = preprocessEpicAIVideoAPIResult(rawInfo)\n\t\t\t\t}\n\t\t\t\tconst infoResult = EpicVideoInfoSchema.safeParse(rawInfo)\n\t\t\t\tif (infoResult.success) {\n\t\t\t\t\tlog(`successfully parsed video info for ${epicVideoEmbed}`)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t...infoResult.data,\n\t\t\t\t\t} as const\n\t\t\t\t} else {\n\t\t\t\t\t// don't cache errors for long...\n\t\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\t\tconst restrictedResult =\n\t\t\t\t\t\tEpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo)\n\t\t\t\t\tif (restrictedResult.success) {\n\t\t\t\t\t\tlog.warn(`video is region restricted: ${epicVideoEmbed}`)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\ttype: 'region-restricted',\n\t\t\t\t\t\t\t...restrictedResult.data,\n\t\t\t\t\t\t} as const\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.error(`API response parsing failed for ${epicVideoEmbed}`, {\n\t\t\t\t\t\t\turl: epicUrl.pathname,\n\t\t\t\t\t\t\trawInfo,\n\t\t\t\t\t\t\tparseError: infoResult.error,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`Response from EpicWeb for \"${epicUrl.pathname}\" does not match expectation`,\n\t\t\t\t\t\t\tinfoResult.error,\n\t\t\t\t\t\t)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\t\tstatusCode: 500,\n\t\t\t\t\t\t\tstatusText: 'API Data Type Mismatch',\n\t\t\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\t\t} as const\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\tlog.error(`video API request failed for ${epicVideoEmbed}`, {\n\t\t\t\t\tstatus,\n\t\t\t\t\tstatusText,\n\t\t\t\t\turl: apiUrl,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tstatusCode: status,\n\t\t\t\t\tstatusText,\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t} as const\n\t\t\t}\n\t\t},\n\t}).catch((e) => {\n\t\tlog.error(`failed to fetch epic video info for ${epicVideoEmbed}:`, e)\n\t\tthrow e\n\t})\n}\n\nfunction getEpicAIVideoAPIUrl(epicVideoEmbed: string) {\n\tconst epicUrl = new URL(epicVideoEmbed)\n\tconst slug = epicUrl.pathname.split('/').at(-1)!\n\n\tif (epicUrl.pathname.includes('/workshops')) {\n\t\treturn `https://epicai.pro/api/lessons?slugOrId=${slug}`\n\t} else if (epicUrl.pathname.endsWith('/solution')) {\n\t\treturn `https://epicai.pro/api/lessons/${slug}/solution`\n\t} else {\n\t\treturn `https://epicai.pro/api/posts?slugOrId=${slug}`\n\t}\n}\n\nfunction preprocessEpicAIVideoAPIResult(result: any) {\n\tconst PostVideoResourceSchema = z.object({\n\t\tresource: z.object({\n\t\t\ttype: z.literal('videoResource'),\n\t\t\tfields: EpicVideoInfoSchema,\n\t\t}),\n\t})\n\tconst PostSchema = z.object({\n\t\tfields: z.object({ title: z.string() }),\n\t\tresources: z.array(z.any()).nullable(),\n\t})\n\tconst post = PostSchema.safeParse(result)\n\tif (!post.success) return null\n\tfor (const resource of post.data.resources ?? []) {\n\t\tconst videoResource = PostVideoResourceSchema.safeParse(resource)\n\n\t\tif (videoResource.success) {\n\t\t\treturn {\n\t\t\t\t...videoResource.data.resource.fields,\n\t\t\t\ttitle: post.data.fields.title,\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null\n}\n\nasync function getEpicProgress({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: { timings?: Timings; request?: Request; forceFresh?: boolean } = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\t\n\tconst authInfo = await getAuthInfo()\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\tif (!authInfo) return []\n\t\n\tconst tokenPart = md5(authInfo.tokenSet.access_token)\n\tconst EpicProgressSchema = z.array(\n\t\tz.object({\n\t\t\tlessonId: z.string(),\n\t\t\tcompletedAt: z.string().nullable(),\n\t\t}),\n\t)\n\t\n\tlog(`fetching progress from EpicWeb host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-progress:${host}:${tokenPart}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\ttimings,\n\t\tforceFresh,\n\t\tttl: 1000 * 2,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: [],\n\t\tcheckValue: EpicProgressSchema,\n\t\tasync getFreshValue(context): Promise<z.infer<typeof EpicProgressSchema>> {\n\t\t\tconst progressUrl = `https://${host}/api/progress`\n\t\t\tlog(`making progress API request to: ${progressUrl}`)\n\t\t\t\n\t\t\tconst response = await fetch(progressUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\t\n\t\t\tlog(`progress API response: ${response.status} ${response.statusText}`)\n\t\t\t\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(`failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\t// don't cache errors for long...\n\t\t\t\tcontext.metadata.ttl = 1000 * 2\n\t\t\t\tcontext.metadata.swr = 0\n\t\t\t\treturn []\n\t\t\t}\n\t\t\t\n\t\t\tconst progressData = await response.json()\n\t\t\tconst parsedProgress = EpicProgressSchema.parse(progressData)\n\t\t\tlog(`successfully fetched ${parsedProgress.length} progress entries`)\n\t\t\treturn parsedProgress\n\t\t},\n\t})\n}\n\nexport type Progress = Awaited<ReturnType<typeof getProgress>>[number]\nexport async function getProgress({\n\ttimings,\n\trequest,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n} = {}) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return []\n\t\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return []\n\t\n\tconst {\n\t\tproduct: { slug, host },\n\t} = getWorkshopConfig()\n\tif (!slug) return []\n\n\tlog(`aggregating progress data for workshop: ${slug}`)\n\tconst [\n\t\tworkshopData,\n\t\tepicProgress,\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t] = await Promise.all([\n\t\tgetWorkshopData(slug, { request, timings }),\n\t\tgetEpicProgress({ request, timings }),\n\t\tgetWorkshopInstructions({ request }),\n\t\tgetWorkshopFinished({ request }),\n\t\tgetExercises({ request, timings }),\n\t])\n\n\ttype ProgressInfo = {\n\t\tepicLessonUrl: string\n\t\tepicLessonSlug: string\n\t\tepicCompletedAt: string | null\n\t}\n\tconst progress: Array<\n\t\tProgressInfo &\n\t\t\t(ReturnType<typeof getProgressForLesson> | { type: 'unknown' })\n\t> = []\n\n\tfor (const resource of workshopData.resources ?? []) {\n\t\tconst lessons = resource._type === 'section' ? resource.lessons : [resource]\n\t\tfor (const lesson of lessons) {\n\t\t\tconst epicLessonSlug = lesson.slug\n\t\t\tconst lessonProgress = epicProgress.find(\n\t\t\t\t({ lessonId }) => lessonId === lesson._id,\n\t\t\t)\n\t\t\tconst epicCompletedAt = lessonProgress ? lessonProgress.completedAt : null\n\t\t\tconst progressForLesson = getProgressForLesson(epicLessonSlug, {\n\t\t\t\tworkshopInstructions,\n\t\t\t\tworkshopFinished,\n\t\t\t\texercises,\n\t\t\t})\n\t\t\tconst epicLessonUrl = `https://${host}/workshops/${slug}/${epicLessonSlug}`\n\t\t\tif (progressForLesson) {\n\t\t\t\tprogress.push({\n\t\t\t\t\t...progressForLesson,\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tprogress.push({\n\t\t\t\t\ttype: 'unknown',\n\t\t\t\t\tepicLessonUrl,\n\t\t\t\t\tepicLessonSlug,\n\t\t\t\t\tepicCompletedAt,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tlog(`processed ${progress.length} progress entries for workshop: ${slug}`)\n\treturn progress\n}\n\nfunction getProgressForLesson(\n\tepicLessonSlug: string,\n\t{\n\t\tworkshopInstructions,\n\t\tworkshopFinished,\n\t\texercises,\n\t}: {\n\t\tworkshopInstructions: Awaited<ReturnType<typeof getWorkshopInstructions>>\n\t\tworkshopFinished: Awaited<ReturnType<typeof getWorkshopFinished>>\n\t\texercises: Awaited<ReturnType<typeof getExercises>>\n\t},\n) {\n\tconst hasEmbed = (embed?: Array<string>) =>\n\t\tembed?.some((e) => e.split('/').at(-1) === epicLessonSlug)\n\tif (\n\t\tworkshopInstructions.compiled.status === 'success' &&\n\t\thasEmbed(workshopInstructions.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-instructions' } as const\n\t}\n\tif (\n\t\tworkshopFinished.compiled.status === 'success' &&\n\t\thasEmbed(workshopFinished.compiled.epicVideoEmbeds)\n\t) {\n\t\treturn { type: 'workshop-finished' } as const\n\t}\n\tfor (const exercise of exercises) {\n\t\tif (hasEmbed(exercise.instructionsEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'instructions',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tif (hasEmbed(exercise.finishedEpicVideoEmbeds)) {\n\t\t\treturn {\n\t\t\t\ttype: 'finished',\n\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t} as const\n\t\t}\n\t\tfor (const step of exercise.steps.filter(Boolean)) {\n\t\t\tif (hasEmbed(step.problem?.epicVideoEmbeds)) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'step',\n\t\t\t\t\texerciseNumber: exercise.exerciseNumber,\n\t\t\t\t\tstepNumber: step.stepNumber,\n\t\t\t\t} as const\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function updateProgress(\n\t{ lessonSlug, complete }: { lessonSlug: string; complete?: boolean },\n\t{\n\t\ttimings,\n\t\trequest,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: 'cannot update progress when deployed',\n\t\t} as const\n\t}\n\t\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) {\n\t\treturn { status: 'error', error: 'not authenticated' } as const\n\t}\n\t\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst progressUrl = `https://${host}/api/progress`\n\tconst payload = complete ? { lessonSlug } : { lessonSlug, remove: true }\n\t\n\tlog(`updating progress for lesson: ${lessonSlug} (complete: ${complete})`)\n\tlog(`making POST request to: ${progressUrl} with payload: ${JSON.stringify(payload)}`)\n\t\n\tconst response = await fetch(progressUrl, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t'content-type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(payload),\n\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\n\tlog(`progress update response: ${response.status} ${response.statusText}`)\n\t\n\t// force the progress to be fresh whether or not we're successful\n\tawait getEpicProgress({ forceFresh: true, request, timings })\n\n\tif (response.status < 200 || response.status >= 300) {\n\t\tlog(`progress update failed: ${response.status} ${response.statusText}`)\n\t\treturn {\n\t\t\tstatus: 'error',\n\t\t\terror: `${response.status} ${response.statusText}`,\n\t\t} as const\n\t}\n\n\tlog(`progress update successful for lesson: ${lessonSlug}`)\n\treturn { status: 'success' } as const\n}\n\nconst ModuleSchema = z.object({\n\tresources: z\n\t\t.array(\n\t\t\tz.union([\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('lesson'),\n\t\t\t\t\t_id: z.string(),\n\t\t\t\t\tslug: z.string(),\n\t\t\t\t}),\n\t\t\t\tz.object({\n\t\t\t\t\t_type: z.literal('section'),\n\t\t\t\t\tlessons: z.array(z.object({ _id: z.string(), slug: z.string() })),\n\t\t\t\t}),\n\t\t\t]),\n\t\t)\n\t\t.nullable(),\n})\n\nexport async function getWorkshopData(\n\tslug: string,\n\t{\n\t\ttimings,\n\t\trequest,\n\t\tforceFresh,\n\t}: {\n\t\ttimings?: Timings\n\t\trequest?: Request\n\t\tforceFresh?: boolean\n\t} = {},\n) {\n\tif (getEnv().EPICSHOP_DEPLOYED) return { resources: [] }\n\t\n\tconst authInfo = await getAuthInfo()\n\t// auth is not required, but we only use it for progress which is only needed\n\t// if you're authenticated anyway.\n\tif (!authInfo) return { resources: [] }\n\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tlog(`fetching workshop data for slug: ${slug} from host: ${host}`)\n\treturn cachified({\n\t\tkey: `epic-workshop-data:${host}:${slug}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tofflineFallbackValue: { resources: [] },\n\t\tcheckValue: ModuleSchema,\n\t\tasync getFreshValue(): Promise<z.infer<typeof ModuleSchema>> {\n\t\t\tconst workshopUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}`\n\t\t\tlog(`making workshop data request to: ${workshopUrl}`)\n\t\t\t\n\t\t\tconst response = await fetch(workshopUrl).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\t\n\t\t\tlog(`workshop data response: ${response.status} ${response.statusText}`)\n\t\t\t\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tlog.error(`failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`)\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`,\n\t\t\t\t)\n\t\t\t\treturn { resources: [] }\n\t\t\t}\n\t\t\t\n\t\t\tconst jsonResponse = await response.json()\n\t\t\tconst parsedData = ModuleSchema.parse(jsonResponse)\n\t\t\tlog(`successfully fetched workshop data for ${slug} with ${parsedData.resources?.length ?? 0} resources`)\n\t\t\treturn parsedData\n\t\t},\n\t})\n}\n\nexport async function userHasAccessToWorkshop({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\trequest?: Request\n\ttimings?: Timings\n\tforceFresh?: boolean\n} = {}) {\n\tconst config = getWorkshopConfig()\n\tconst {\n\t\tproduct: { host, slug },\n\t} = config\n\tif (!slug) return true\n\n\tif (getEnv().EPICSHOP_DEPLOYED) {\n\t\tconst cookieHeader = request?.headers.get('Cookie')\n\t\tif (!cookieHeader) return false\n\t\tconst cookies = cookie.parse(cookieHeader)\n\t\treturn cookies.skill?.split(',').includes(slug) ?? false\n\t}\n\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return false\n\n\treturn cachified({\n\t\tkey: `user-has-access-to-workshop:${host}:${slug}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 5,\n\t\tofflineFallbackValue: false,\n\t\tcheckValue: z.boolean(),\n\t\tasync getFreshValue(context) {\n\t\t\tconst accessUrl = `https://${host}/api/workshops/${encodeURIComponent(slug)}/access`\n\t\t\tlog(`checking workshop access via API: ${accessUrl}`)\n\t\t\t\n\t\t\tconst response = await fetch(accessUrl, {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${authInfo.tokenSet.access_token}`,\n\t\t\t\t},\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\t\t\t\n\t\t\tlog(`workshop access API response: ${response.status} ${response.statusText}`)\n\t\t\t\n\t\t\tconst hasAccess = response.ok ? (await response.json()) === true : false\n\t\t\tlog(`workshop access result for ${slug}: ${hasAccess}`)\n\n\t\t\tif (hasAccess) {\n\t\t\t\tcontext.metadata.ttl = 1000 * 60 * 5\n\t\t\t\tcontext.metadata.swr = 1000 * 60 * 60 * 24 * 365 * 10\n\t\t\t}\n\n\t\t\treturn hasAccess\n\t\t},\n\t}).catch((e) => {\n\t\tconsole.error('Failed to check workshop access', e)\n\t\treturn false\n\t})\n}\n\nconst UserInfoSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tname: z.string().nullable(),\n\t\temail: z.string().email(),\n\t\timage: z.string().nullable(),\n\t\tdiscordProfile: z\n\t\t\t.object({\n\t\t\t\tnick: z.string().nullable().optional(),\n\t\t\t\tuser: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\tid: z.string(),\n\t\t\t\t\t\tusername: z.string(),\n\t\t\t\t\t\tavatar: z.string().nullable().optional(),\n\t\t\t\t\t\tglobal_name: z.string().nullable().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.optional(),\n\t\t\t})\n\t\t\t.nullable()\n\t\t\t.optional(),\n\t})\n\t.transform((data) => {\n\t\treturn {\n\t\t\t...data,\n\t\t\timageUrlSmall:\n\t\t\t\tresizeImageUrl(data.image, { size: 64 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 64,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 64 }),\n\t\t\timageUrlLarge:\n\t\t\t\tresizeImageUrl(data.image, { size: 512 }) ??\n\t\t\t\tresolveDiscordAvatar(data.discordProfile?.user, {\n\t\t\t\t\tsize: 512,\n\t\t\t\t}) ??\n\t\t\t\tresolveGravatarUrl(data.email, { size: 512 }),\n\t\t}\n\t})\n\nfunction resizeImageUrl(url: string | null, { size }: { size: number }) {\n\tif (!url) return null\n\tconst urlObj = new URL(url)\n\turlObj.searchParams.set('size', size.toString())\n\treturn urlObj.toString()\n}\n\nfunction resolveGravatarUrl(\n\temail: string | undefined,\n\t{ size }: { size: number },\n) {\n\tif (!email) return null\n\n\tconst hash = md5(email.toLowerCase())\n\tconst gravatarOptions = new URLSearchParams({\n\t\tsize: size.toString(),\n\t\tdefault: 'identicon',\n\t})\n\treturn `https://www.gravatar.com/avatar/${hash}?${gravatarOptions.toString()}`\n}\n\nfunction resolveDiscordAvatar(\n\tuser: { avatar?: string | null; id: string } | undefined,\n\t{ size }: { size: 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 },\n) {\n\tif (!user) return null\n\n\tconst { avatar, id: userId } = user\n\tif (!avatar) return null\n\tconst isGif = avatar.startsWith('a_')\n\tconst url = new URL(\n\t\t`/avatars/${userId}/${avatar}.${isGif ? 'gif' : 'png'}`,\n\t\t'https://cdn.discordapp.com',\n\t)\n\turl.searchParams.set('size', size.toString())\n\treturn url.toString()\n}\n\nexport type UserInfo = z.infer<typeof UserInfoSchema>\n\nexport async function getUserInfo({\n\ttimings,\n\trequest,\n\tforceFresh,\n}: {\n\ttimings?: Timings\n\trequest?: Request\n\tforceFresh?: boolean\n} = {}) {\n\tconst authInfo = await getAuthInfo()\n\tif (!authInfo) return null\n\t\n\tconst { tokenSet } = authInfo\n\tconst {\n\t\tproduct: { host },\n\t} = getWorkshopConfig()\n\n\tconst accessToken = tokenSet.access_token\n\tconst url = `https://${host}/oauth/userinfo`\n\n\tlog('fetching user info from: %s', url)\n\tconst userInfo = await cachified({\n\t\tkey: `${url}:${md5(accessToken)}`,\n\t\tcache: fsCache,\n\t\trequest,\n\t\tforceFresh,\n\t\ttimings,\n\t\tttl: 1000 * 30,\n\t\tswr: 1000 * 60 * 60 * 24 * 365 * 10,\n\t\tofflineFallbackValue: null,\n\t\tcheckValue: UserInfoSchema,\n\t\tasync getFreshValue(): Promise<UserInfo> {\n\t\t\tlog(`making user info API request to: ${url}`)\n\t\t\t\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { authorization: `Bearer ${accessToken}` },\n\t\t\t}).catch((e) => new Response(getErrorMessage(e), { status: 500 }))\n\n\t\t\tlog(`user info API response: ${response.status} ${response.statusText}`)\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlog(`user info API request failed: ${response.status} ${response.statusText}`)\n\t\t\t\tif (\n\t\t\t\t\tresponse.headers.get('content-type')?.includes('application/json')\n\t\t\t\t) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthrow new Error(`Failed to fetch user info: ${JSON.stringify(data)}`)\n\t\t\t\t} else {\n\t\t\t\t\tconst text = await response.text()\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Failed to fetch user info: ${text || response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst data = await response.json()\n\t\t\tconst parsedUserInfo = UserInfoSchema.parse(data)\n\t\t\tlog(`successfully fetched user info for user: ${parsedUserInfo.id} (${parsedUserInfo.email})`)\n\t\t\treturn parsedUserInfo\n\t\t},\n\t}).catch((e) => {\n\t\tlog.error(`failed to get user info:`, e)\n\t\treturn null\n\t})\n\n\t// we used to md5 hash the email to get the id\n\t// if the id doesn't match what we have on file, update it\n\t// you can probably safely remove this in January 2025\n\tif (userInfo && authInfo.id !== userInfo.id) {\n\t\tawait setAuthInfo({\n\t\t\t...authInfo,\n\t\t\tid: userInfo.id,\n\t\t})\n\t}\n\n\treturn userInfo\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { debuglog } from 'node:util';
|
|
2
|
+
type LogFunction = (...args: Parameters<ReturnType<typeof debuglog>>) => void;
|
|
3
|
+
interface Logger extends LogFunction {
|
|
4
|
+
error: LogFunction;
|
|
5
|
+
warn: LogFunction;
|
|
6
|
+
info: LogFunction;
|
|
7
|
+
}
|
|
8
|
+
export declare function logger(ns: string): Logger;
|
|
9
|
+
export declare function isLoggingEnabled(ns: string): boolean;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEpC,KAAK,WAAW,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,KAAK,IAAI,CAAA;AAE7E,UAAU,MAAO,SAAQ,WAAW;IACnC,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;CACjB;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAUzC;AAGD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEpD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { debuglog } from 'node:util';
|
|
2
|
+
export function logger(ns) {
|
|
3
|
+
const log = debuglog(ns);
|
|
4
|
+
const loggerFn = ((...args) => log(...args));
|
|
5
|
+
loggerFn.error = (...args) => log('đ¨', ...args);
|
|
6
|
+
loggerFn.warn = (...args) => log('â ī¸', ...args);
|
|
7
|
+
loggerFn.info = (...args) => log('âšī¸', ...args);
|
|
8
|
+
return loggerFn;
|
|
9
|
+
}
|
|
10
|
+
// Convenience function to check if logging is enabled for a namespace
|
|
11
|
+
export function isLoggingEnabled(ns) {
|
|
12
|
+
return debuglog(ns).enabled;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +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\t\n\tconst loggerFn = ((...args: Parameters<typeof log>) => log(...args)) as Logger\n\t\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\t\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}"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epic-web/workshop-utils",
|
|
3
|
-
"version": "6.23.
|
|
3
|
+
"version": "6.23.9",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"./git.server": "./src/git.server.ts",
|
|
28
28
|
"./iframe-sync": "./src/iframe-sync.ts",
|
|
29
29
|
"./init-env": "./src/init-env.ts",
|
|
30
|
+
"./logger": "./src/logger.ts",
|
|
30
31
|
"./playwright.server": "./src/playwright.server.ts",
|
|
31
32
|
"./notifications.server": "./src/notifications.server.ts",
|
|
32
33
|
"./process-manager.server": "./src/process-manager.server.ts",
|
|
@@ -128,6 +129,12 @@
|
|
|
128
129
|
"default": "./dist/esm/init-env.js"
|
|
129
130
|
}
|
|
130
131
|
},
|
|
132
|
+
"./logger": {
|
|
133
|
+
"import": {
|
|
134
|
+
"types": "./dist/esm/logger.d.ts",
|
|
135
|
+
"default": "./dist/esm/logger.js"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
131
138
|
"./playwright.server": {
|
|
132
139
|
"import": {
|
|
133
140
|
"types": "./dist/esm/playwright.server.d.ts",
|