11import * as fs from 'node:fs' ;
2- import * as path from 'node:path' ;
2+ import { join as pathJoin } from 'node:path' ;
33
4- import { Octokit } from '@octokit/rest' ;
4+ import { Octokit , RestEndpointMethodTypes } from '@octokit/rest' ;
55import { IpcMainInvokeEvent , app , safeStorage } from 'electron' ;
66
7+ import { getTemplate } from './content' ;
78import { ipcMainManager } from './ipc' ;
8- import { GistRevision } from '../interfaces' ;
9+ import { EditorValues , GistRevision } from '../interfaces' ;
910import { IpcEvents } from '../ipc-events' ;
11+ import { isSupportedFile } from '../utils/editor-utils' ;
1012
1113// --- Input validation ---
1214
15+ const ELECTRON_ORG = 'electron' ;
16+
17+ const ELECTRON_REPO = 'electron' ;
18+
1319const TOKEN_PATTERN =
1420 / ^ ( g h p _ [ a - z A - Z 0 - 9 ] { 36 } | g i t h u b _ p a t _ [ a - z A - Z 0 - 9 ] { 22 } _ [ a - z A - Z 0 - 9 ] { 59 } ) $ / ;
1521
@@ -79,7 +85,7 @@ function areValidGistFiles(
7985
8086function getCredentialsPath ( ) : string {
8187 const CREDENTIALS_FILE = '.github-credentials' ;
82- return path . join ( app . getPath ( 'userData' ) , CREDENTIALS_FILE ) ;
88+ return pathJoin ( app . getPath ( 'userData' ) , CREDENTIALS_FILE ) ;
8389}
8490
8591function saveToken ( token : string ) : void {
@@ -365,9 +371,64 @@ async function handleGistListCommits(
365371 } ) ) ;
366372}
367373
374+ async function handleFetchExample (
375+ _event : IpcMainInvokeEvent ,
376+ params : unknown ,
377+ ) : Promise < EditorValues > {
378+ if ( typeof params !== 'object' || params === null )
379+ throw new Error ( 'Invalid parameters.' ) ;
380+ const { ref, path } = params as Record < string , unknown > ;
381+ if ( typeof ref !== 'string' ) throw new Error ( 'Invalid ref.' ) ;
382+ if ( typeof path !== 'string' ) throw new Error ( 'Invalid path.' ) ;
383+ return fetchExample ( ref , path ) ;
384+ }
385+
386+ async function fetchExample ( ref : string , path : string ) : Promise < EditorValues > {
387+ if ( ! ref ) throw new Error ( 'Invalid ref.' ) ;
388+ if ( ! path ) throw new Error ( 'Invalid path.' ) ;
389+
390+ // `repos.getContent` returns a union; the directory variant is the array form.
391+ type RepoContentEntry = Extract <
392+ RestEndpointMethodTypes [ 'repos' ] [ 'getContent' ] [ 'response' ] [ 'data' ] ,
393+ readonly unknown [ ]
394+ > [ number ] ;
395+
396+ const owner = ELECTRON_ORG ;
397+ const repo = ELECTRON_REPO ;
398+ const octo = getOctokit ( ) ;
399+
400+ // Fetch the example folder listing.
401+ const folder = await octo . repos . getContent ( { owner, path, ref, repo } ) ;
402+ if ( ! Array . isArray ( folder . data ) )
403+ throw new Error ( `${ owner } :${ repo } /${ path } :${ ref } is not a valid example` ) ;
404+ const files = ( folder . data as RepoContentEntry [ ] ) . filter (
405+ ( file ) =>
406+ typeof file . download_url === 'string' &&
407+ typeof file . name === 'string' &&
408+ isSupportedFile ( file . name ) ,
409+ ) ;
410+
411+ // Get the base template for this version: 'v42.0.0' -> '42.0.0'.
412+ const version = ref . replace ( / ^ v / , '' ) ;
413+ const values : EditorValues = { ...( await getTemplate ( version ) ) } ;
414+
415+ // Download each supported file and overlay onto the template.
416+ await Promise . all (
417+ files . map ( async ( file ) => {
418+ const resp = await fetch ( file . download_url as string ) ;
419+ if ( ! resp . ok )
420+ throw new Error ( `Failed to download ${ file . name } : ${ resp . status } ` ) ;
421+ values [ file . name as keyof EditorValues ] = await resp . text ( ) ;
422+ } ) ,
423+ ) ;
424+
425+ return values ;
426+ }
427+
368428// --- Setup ---
369429
370430export function setupGitHub ( ) {
431+ ipcMainManager . handle ( IpcEvents . GITHUB_FETCH_EXAMPLE , handleFetchExample ) ;
371432 ipcMainManager . handle ( IpcEvents . GITHUB_GIST_CREATE , handleGistCreate ) ;
372433 ipcMainManager . handle ( IpcEvents . GITHUB_GIST_DELETE , handleGistDelete ) ;
373434 ipcMainManager . handle (
@@ -386,6 +447,7 @@ export function setupGitHub() {
386447
387448// Exported for testing
388449export const testing = {
450+ fetchExample,
389451 handleGistCreate,
390452 handleGistDelete,
391453 handleGistListCommits,
0 commit comments