11const fs = require ( 'fs-extra' ) ;
22const os = require ( 'os' ) ;
3+ const semver = require ( 'semver' ) ;
34const { getEdlDevices } = require ( 'particle-usb' ) ;
45const { delay } = require ( './utilities' ) ;
6+ const unzip = require ( 'unzipper' ) ;
57const DEVICE_READY_WAIT_TIME = 500 ; // ms
68const UI = require ( './ui' ) ;
79const QdlFlasher = require ( './qdl' ) ;
@@ -485,6 +487,61 @@ async function handleFlashError({ error, ui }) {
485487 return false ;
486488}
487489
490+ /**
491+ *
492+ * @param ui
493+ * @return {Promise<Workflow> }
494+ */
495+ async function promptOSSelection ( { ui, workflows } ) {
496+ const choices = Object . values ( workflows ) ;
497+ const question = [ {
498+ type : 'list' ,
499+ name : 'osType' ,
500+ message : ui . chalk . bold . white ( 'Select the OS Type to setup in your device' ) ,
501+ choices
502+ } ] ;
503+ const { osType } = await ui . prompt ( question ) ;
504+ return workflows [ osType ] ;
505+ }
506+
507+ async function isFile ( version ) {
508+ const validChannels = [ 'latest' , 'stable' , 'beta' , 'rc' ] ;
509+ const isValidChannel = validChannels . includes ( version ) ;
510+ const isValidSemver = semver . valid ( version ) ;
511+ const isFile = ! isValidChannel && ! isValidSemver ;
512+
513+ // access(OK
514+ if ( isFile ) {
515+ try {
516+ await fs . access ( version , fs . constants . F_OK | fs . constants . R_OK ) ;
517+ } catch ( error ) {
518+ if ( error . code === 'ENOENT' ) {
519+ throw new Error ( `The file "${ version } " does not exist.` ) ;
520+ } else if ( error . code === 'EACCES' ) {
521+ throw new Error ( `The file "${ version } " is not accessible (permission denied).` ) ;
522+ }
523+ throw error ;
524+ }
525+ }
526+ return isFile ;
527+ }
528+
529+ async function readManifestFromLocalFile ( path , targetFile = 'manifest.json' ) {
530+ const directory = await unzip . Open . file ( path ) ;
531+ const entry = directory . files . find ( f => f . path . endsWith ( targetFile ) ) ;
532+
533+ if ( ! entry ) {
534+ throw new Error ( `File "${ targetFile } " not found in ${ path } ` ) ;
535+ }
536+ // Stream and parse
537+ const content = await entry . buffer ( ) ;
538+ try {
539+ return JSON . parse ( content . toString ( 'utf8' ) ) ;
540+ } catch ( err ) {
541+ throw new Error ( `Invalid JSON in ${ targetFile } : ${ err . message } ` ) ;
542+ }
543+ }
544+
488545module . exports = {
489546 addLogHeaders,
490547 addManifestInfoLog,
@@ -493,5 +550,8 @@ module.exports = {
493550 prepareFlashFiles,
494551 getTachyonInfo,
495552 promptWifiNetworks,
496- handleFlashError
553+ handleFlashError,
554+ promptOSSelection,
555+ isFile,
556+ readManifestFromLocalFile
497557} ;
0 commit comments