@@ -64,13 +64,6 @@ type ContentCheckFile = {
6464 relativePath : string
6565}
6666
67- < << << << Updated upstream
68- << << << < Updated upstream
69- === === =
70- === === =
71- >>> >>> > Stashed changes
72- type ProductModuleType = 'workshop' | 'tutorial'
73-
7467function stripEpicAiSlugSuffix ( value : string ) {
7568 // EpicAI embeds sometimes include a `~...` suffix in the slug segment.
7669 return value . replace ( / ~ [ ^ ] * $ / , '' )
@@ -79,15 +72,6 @@ function stripEpicAiSlugSuffix(value: string) {
7972function isProductLessonPathSegment ( segment : string | undefined ) {
8073 return segment === 'workshops' || segment === 'tutorials'
8174}
82-
83- function getProductModulePathSegment ( moduleType : ProductModuleType ) {
84- return moduleType === 'tutorial' ? 'tutorials' : 'workshops'
85- }
86-
87- << < < < << Updated upstream
88- >>> >>> > Stashed changes
89- = === ===
90- >>> >>> > Stashed changes
9175function normalizeHost ( host : string ) {
9276 return host . toLowerCase ( ) . replace ( / ^ w w w \. / , '' )
9377}
@@ -138,29 +122,6 @@ function parseEpicLessonSlugFromEmbedUrl(urlString: string): string | null {
138122 }
139123}
140124
141- << < < < << Updated upstream
142- === === =
143- function formatProductLessonUrl ( {
144- productHost,
145- productSlug,
146- moduleType,
147- lessonSlug,
148- sectionSlug,
149- } : {
150- productHost : string
151- productSlug : string
152- moduleType : ProductModuleType
153- lessonSlug : string
154- sectionSlug : string | null
155- } ) {
156- const productPath = getProductModulePathSegment ( moduleType )
157- // The product site will typically redirect to a section-specific path when needed.
158- return sectionSlug
159- ? `https ://${productHost}/${productPath}/${productSlug}/${sectionSlug}/${lessonSlug}`
160- : `https://${productHost } /${productPath } /${productSlug } /${lessonSlug } `
161- }
162-
163- > > >>> >> Stashed changes
164125function formatIssue ( issue : Issue , workshopRoot : string ) {
165126 const icon = issue . level === 'error' ? chalk . red ( '❌' ) : chalk . yellow ( '⚠️ ' )
166127 const filePart = issue . file
@@ -336,127 +297,6 @@ async function buildExpectedFiles({
336297 return { files, contentFiles, issues }
337298}
338299
339- << < < < << Updated upstream
340- === === =
341- async function fetchRemoteWorkshopLessonSlugs ( {
342- productHost,
343- workshopSlug,
344- } : {
345- productHost : string
346- workshopSlug : string
347- } ) : Promise <
348- | {
349- status : 'success'
350- lessons : Array < { slug : string ; sectionSlug : string | null } >
351- moduleType : ProductModuleType
352- }
353- | { status : 'error' ; message : string }
354- > {
355- const url = `https://${productHost } /api/workshops/${encodeURIComponent ( workshopSlug ) } `
356-
357- const fetchOnce = async ( accessToken ?: string ) => {
358- const timeout = AbortSignal . timeout ( 15_000 )
359- const headers : Record < string , string > = { }
360- if ( accessToken ) headers . authorization = `Bearer ${accessToken } `
361- return fetch ( url , { headers, signal : timeout } )
362- }
363-
364- let response : Response | null = null
365- try {
366- response = await fetchOnce ( )
367- } catch ( error ) {
368- return {
369- status : 'error' ,
370- message : `Failed to fetch product workshop data: ${getErrorMessage ( error ) } `,
371- }
372- }
373-
374- if ( response . status === 401 || response . status === 403 ) {
375- const authInfo = await getAuthInfo ( { productHost } ) . catch ( ( ) => null )
376- const accessToken = authInfo ?. tokenSet ?. access_token
377- if ( accessToken ) {
378- try {
379- response = await fetchOnce ( accessToken )
380- } catch ( error ) {
381- return {
382- status : 'error' ,
383- message : `Failed to fetch product workshop data (after auth): ${ getErrorMessage (
384- error ,
385- ) } `,
386- }
387- }
388- }
389- }
390-
391- if ( ! response . ok ) {
392- const body = await response . text ( ) . catch ( ( ) => '' )
393- const hint =
394- response . status === 401 || response . status === 403
395- ? ` (try: npx epicshop auth login ${ productHost . replace ( / ^ w w w \. / , '' ) } )`
396- : response . status === 404
397- ? ` (check epicshop.product.host + epicshop.product.slug)`
398- : ''
399- return {
400- status : 'error' ,
401- message : `Product API request failed: ${ response . status } ${ response . statusText } ${ hint } ${
402- body ? `\n${ body } ` : ''
403- } `,
404- }
405- }
406-
407- let data : any
408- try {
409- data = await response . json ( )
410- } catch ( error ) {
411- return {
412- status : 'error' ,
413- message : `Product API response was not valid JSON: ${ getErrorMessage ( error ) } ` ,
414- }
415- }
416-
417- const resources = data ?. resources
418- const moduleType : ProductModuleType =
419- data ?. moduleType === 'tutorial' ? 'tutorial' : 'workshop'
420- if ( ! Array . isArray ( resources ) ) {
421- return {
422- status : 'error' ,
423- message : `Product API response did not include an array "resources" field` ,
424- }
425- }
426-
427- const lessons : Array < { slug : string ; sectionSlug : string | null } > = [ ]
428- for ( const resource of resources ) {
429- if ( ! resource || typeof resource !== 'object' ) continue
430- const r = resource as Record < string , unknown >
431-
432- if ( r . _type === 'lesson' ) {
433- const slug = r . slug
434- if ( typeof slug === 'string' ) lessons . push ( { slug, sectionSlug : null } )
435- continue
436- }
437-
438- if ( r . _type === 'section' ) {
439- const sectionSlug =
440- typeof r . slug === 'string' && r . slug . trim ( ) . length > 0
441- ? r . slug . trim ( )
442- : null
443- const sectionLessons = r . lessons
444- if ( ! Array . isArray ( sectionLessons ) ) continue
445- for ( const lesson of sectionLessons ) {
446- if ( ! lesson || typeof lesson !== 'object' ) continue
447- const l = lesson as Record < string , unknown >
448- const slug = l . slug
449- if ( typeof slug === 'string' ) {
450- lessons . push ( { slug, sectionSlug } )
451- }
452- }
453- }
454- }
455-
456- return { status : 'success' , lessons, moduleType }
457- }
458-
459- >>> > >>> Stashed changes
460300async function checkMinContentLength ( {
461301 fullPath,
462302 minChars,
0 commit comments