1- import fs from "fs"
2- import Chalk from "chalk"
3- import { store } from "../../store"
4- import { execSync } from "child_process"
5- import { resolve } from "path"
6- import { parseBlsConfig } from "../../lib/blsConfig"
7- import { logger } from "../../lib/logger"
8- import { run as runBuild } from "./build"
9- import { run as runInstall } from "../offchain/install"
10- import prompRuntimeConfirm from "../../prompts/runtime/confirm"
11- import Fastify from "fastify"
1+ import fs from "fs" ;
2+ import Chalk from "chalk" ;
3+ import { store } from "../../store" ;
4+ import { execSync } from "child_process" ;
5+ import { resolve } from "path" ;
6+ import { parseBlsConfig } from "../../lib/blsConfig" ;
7+ import { logger } from "../../lib/logger" ;
8+ import { run as runBuild } from "./build" ;
9+ import { run as runInstall } from "../offchain/install" ;
10+ import prompRuntimeConfirm from "../../prompts/runtime/confirm" ;
11+ import Fastify from "fastify" ;
1212import { getPortPromise } from "portfinder" ;
1313
1414export const run = async ( options : any ) => {
15- const {
16- systemPath = `${ store . system . homedir } /.bls/` ,
17- path = process . cwd ( ) ,
18- debug = true ,
19- rebuild = false ,
20- } = options
21-
22- const runtimePath = `${ systemPath } runtime/bls-runtime`
23-
24- // Validate Runtime Path
25- try {
26- if ( ! fs . existsSync ( runtimePath ) ) {
27- const { confirm } = await prompRuntimeConfirm ( )
28-
29- if ( ! confirm ) {
30- throw new Error ( "Cancelled by user, aborting invoke." )
31- } else {
32- await runInstall ( { yes : true , inline : true } )
33- console . log ( Chalk . green ( 'Installation successful!' ) )
34- console . log ( '' )
35- console . log ( '' )
36- }
37- }
38- } catch ( error ) {
39- logger . error ( 'Failed to install blockless runtime, please try installing the runtime manually.' )
40- return
41- }
42-
43- try {
44- // Fetch BLS config
45- const { build, build_release } = parseBlsConfig ( )
46-
47- // Execute the build command
48- await runBuild ( { path, debug, rebuild } )
49-
50- // check for and store unmodified wasm file name to change later
51- const buildConfig = ! debug ? build_release : build
52- const buildDir = resolve ( path , buildConfig . dir || 'build' )
53- const manifestPath = resolve ( buildDir , 'manifest.json' )
54-
55- // the runtime requires absolute paths
56- let manifestData = fs . readFileSync ( manifestPath , "utf8" )
57- let manifest = JSON . parse ( manifestData )
58- manifest . drivers_root_path = `${ systemPath } /extensions`
59- manifest . modules = manifest . modules . map ( ( m : any ) => {
60- m . file = resolve ( buildDir , m . file )
61- return m
62- } )
63- fs . writeFileSync ( manifestPath , JSON . stringify ( manifest ) )
64-
65- // prepare environment variables
66- // pass environment variables to bls runtime
67- let envString = ''
68-
69- if ( ! ! options . env ) {
70- let envVars = [ ] as string [ ]
71- let envVarsKeys = [ ] as string [ ]
72-
73- // Validate environment variables
74- const vars = typeof options . env === 'string' ? [ options . env ] : options . env
75- vars . map ( ( v : string ) => {
76- const split = v . split ( '=' )
77- if ( split . length !== 2 ) return
78-
79- envVars . push ( v )
80- envVarsKeys . push ( split [ 0 ] )
81- } )
82-
83- // Include environment string if there are variables
84- if ( envVars . length > 0 ) {
85- envString = `env ${ envVars . join ( ' ' ) } BLS_LIST_VARS=\"${ envVarsKeys . join ( ';' ) } \"`
86- }
87- }
88-
89- const fastify = Fastify ( {
90- logger : false ,
91- maxParamLength : 10000
92- } )
93-
94- await fastify . register ( import ( '@fastify/rate-limit' ) , {
95- max : 100 ,
96- timeWindow : '1 minute'
97- } )
98-
99- await fastify . register ( import ( '@fastify/cors' ) )
100-
101- fastify . get ( "*" , async ( request , reply ) => {
102- let qs = ''
103- let headerString = ''
104- let requestPath = decodeURIComponent ( request . url . trim ( ) )
105-
106- if ( requestPath . includes ( '?' ) ) {
107- qs = requestPath . split ( '?' ) [ 1 ]
108- requestPath = requestPath . split ( '?' ) [ 0 ]
109- }
110-
111- if ( request . headers ) {
112- headerString = Object . entries ( request . headers )
113- . map ( ( [ key , value ] ) => `${ key } =${ value } ` )
114- . join ( '&' )
115- }
116-
117- let envString = ''
118- let envVars = [ ] as string [ ]
119- let envVarsKeys = [ ] as string [ ]
120-
121- if ( ! ! options . env ) {
122- // Validate environment variables
123- const vars = typeof options . env === 'string' ? [ options . env ] : options . env
124- vars . map ( ( v : string ) => {
125- const split = v . split ( '=' )
126- if ( split . length !== 2 ) return
127-
128- envVars . push ( v )
129- envVarsKeys . push ( split [ 0 ] )
130- } )
131- }
132-
133- envVars . push ( `BLS_REQUEST_PATH="${ requestPath } "` )
134- envVars . push ( `BLS_REQUEST_QUERY="${ qs } "` )
135- envVars . push ( `BLS_REQUEST_METHOD="${ request . method } "` )
136- envVars . push ( `BLS_REQUEST_HEADERS="${ headerString } "` )
137- envVarsKeys . push ( 'BLS_REQUEST_PATH' )
138- envVarsKeys . push ( 'BLS_REQUEST_QUERY' )
139- envVarsKeys . push ( 'BLS_REQUEST_METHOD' )
140- envVarsKeys . push ( 'BLS_REQUEST_HEADERS' )
141-
142- if ( request . body ) {
143- envVars . push ( `BLS_REQUEST_BODY="${ encodeURIComponent ( JSON . stringify ( request . body ) ) } "` )
144- envVarsKeys . push ( 'BLS_REQUEST_BODY' )
145- }
146-
147- // Include environment string if there are variables
148- if ( envVars . length > 0 ) {
149- envString = `env ${ envVars . join ( ' ' ) } BLS_LIST_VARS=\"${ envVarsKeys . join ( ';' ) } \"`
150- }
151-
152- const result = execSync ( `echo "${ decodeURIComponent ( request . url . trim ( ) ) } " | ${ envString } ${ runtimePath } ${ manifestPath } ` , {
153- cwd : path ,
154- maxBuffer : ( 10000 * 1024 )
155- } ) . toString ( )
156-
157- if ( ! manifest . contentType || manifest . contentType === 'json' && result ) {
158- try {
159- const resultJson = JSON . parse ( result )
160-
161- reply
162- . header ( "Content-Type" , "application/json" )
163- . send ( resultJson )
164- } catch ( error ) { }
165- } else if ( manifest . contentType === "html" && result ) {
166- const body = result
167-
168- if ( body . startsWith ( "data:" ) ) {
169- const data = body . split ( "," ) [ 1 ]
170- const contentType = body . split ( "," ) [ 0 ] . split ( ":" ) [ 1 ] . split ( ";" ) [ 0 ]
171- const base64data = Buffer . from ( data , "base64" )
172- reply . type ( contentType ) . send ( base64data )
173- } else {
174- reply
175- . header ( "Content-Type" , "text/html" )
176- . send ( body )
177- }
178- } else {
179- reply . send ( result )
180- }
181- } )
182-
183- const port = await getPortPromise ( { port : 3000 , stopPort : 4000 } )
184-
185- fastify . listen ( { port } ) . then ( async ( ) => {
186- console . log ( `Serving http://127.0.0.1:${ port } ...` )
187- } )
188- } catch ( error : any ) {
189- logger . error ( 'Failed to invoke function.' , error . message )
190- }
191- }
15+ const {
16+ systemPath = `${ store . system . homedir } /.bls/` ,
17+ path = process . cwd ( ) ,
18+ debug = true ,
19+ rebuild = false ,
20+ } = options ;
21+
22+ const runtimePath = `${ systemPath } runtime/bls-runtime` ;
23+
24+ // Validate Runtime Path
25+ try {
26+ if ( ! fs . existsSync ( runtimePath ) ) {
27+ const { confirm } = await prompRuntimeConfirm ( ) ;
28+
29+ if ( ! confirm ) {
30+ throw new Error ( "Cancelled by user, aborting invoke." ) ;
31+ } else {
32+ await runInstall ( { yes : true , inline : true } ) ;
33+ console . log ( Chalk . green ( "Installation successful!" ) ) ;
34+ console . log ( "" ) ;
35+ console . log ( "" ) ;
36+ }
37+ }
38+ } catch ( error ) {
39+ logger . error (
40+ "Failed to install blockless runtime, please try installing the runtime manually." ,
41+ ) ;
42+ return ;
43+ }
44+
45+ try {
46+ // Fetch BLS config
47+ const { build, build_release } = parseBlsConfig ( ) ;
48+
49+ // Execute the build command
50+ await runBuild ( { path, debug, rebuild } ) ;
51+
52+ // check for and store unmodified wasm file name to change later
53+ const buildConfig = ! debug ? build_release : build ;
54+ const buildDir = resolve ( path , buildConfig . dir || "build" ) ;
55+ const manifestPath = resolve ( buildDir , "manifest.json" ) ;
56+
57+ // the runtime requires absolute paths
58+ let manifestData = fs . readFileSync ( manifestPath , "utf8" ) ;
59+ let manifest = JSON . parse ( manifestData ) ;
60+ manifest . drivers_root_path = `${ systemPath } /extensions` ;
61+ manifest . modules = manifest . modules . map ( ( m : any ) => {
62+ m . file = resolve ( buildDir , m . file ) ;
63+ return m ;
64+ } ) ;
65+ fs . writeFileSync ( manifestPath , JSON . stringify ( manifest ) ) ;
66+
67+ // prepare environment variables
68+ // pass environment variables to bls runtime
69+ let envString = "" ;
70+
71+ if ( ! ! options . env ) {
72+ let envVars = [ ] as string [ ] ;
73+ let envVarsKeys = [ ] as string [ ] ;
74+
75+ // Validate environment variables
76+ const vars =
77+ typeof options . env === "string" ? [ options . env ] : options . env ;
78+ vars . map ( ( v : string ) => {
79+ const split = v . split ( "=" ) ;
80+ if ( split . length !== 2 ) return ;
81+
82+ envVars . push ( v ) ;
83+ envVarsKeys . push ( split [ 0 ] ) ;
84+ } ) ;
85+
86+ // Include environment string if there are variables
87+ if ( envVars . length > 0 ) {
88+ envString = `env ${ envVars . join ( " " ) } BLS_LIST_VARS=\"${ envVarsKeys . join ( ";" ) } \"` ;
89+ }
90+ }
91+
92+ const fastify = Fastify ( {
93+ logger : false ,
94+ maxParamLength : 10000 ,
95+ } ) ;
96+
97+ await fastify . register ( import ( "@fastify/rate-limit" ) , {
98+ max : 100 ,
99+ timeWindow : "1 minute" ,
100+ } ) ;
101+
102+ await fastify . register ( import ( "@fastify/cors" ) ) ;
103+
104+ fastify . get ( "*" , async ( request , reply ) => {
105+ let qs = "" ;
106+ let headerString = "" ;
107+ let requestPath = decodeURIComponent ( request . url . trim ( ) ) ;
108+
109+ if ( requestPath . includes ( "?" ) ) {
110+ qs = requestPath . split ( "?" ) [ 1 ] ;
111+ requestPath = requestPath . split ( "?" ) [ 0 ] ;
112+ }
113+
114+ if ( request . headers ) {
115+ headerString = Object . entries ( request . headers )
116+ . map ( ( [ key , value ] ) => `${ key } =${ encodeURIComponent ( `${ value } ` ) } ` )
117+ . join ( "&" ) ;
118+ }
119+
120+ let envString = "" ;
121+ let envVars = [ ] as string [ ] ;
122+ let envVarsKeys = [ ] as string [ ] ;
123+
124+ if ( ! ! options . env ) {
125+ // Validate environment variables
126+ const vars =
127+ typeof options . env === "string" ? [ options . env ] : options . env ;
128+ vars . map ( ( v : string ) => {
129+ const split = v . split ( "=" ) ;
130+ if ( split . length !== 2 ) return ;
131+
132+ envVars . push ( v ) ;
133+ envVarsKeys . push ( split [ 0 ] ) ;
134+ } ) ;
135+ }
136+
137+ envVars . push ( `BLS_REQUEST_PATH="${ requestPath } "` ) ;
138+ envVars . push ( `BLS_REQUEST_QUERY="${ qs } "` ) ;
139+ envVars . push ( `BLS_REQUEST_METHOD="${ request . method } "` ) ;
140+ envVars . push ( `BLS_REQUEST_HEADERS="${ headerString } "` ) ;
141+ envVarsKeys . push ( "BLS_REQUEST_PATH" ) ;
142+ envVarsKeys . push ( "BLS_REQUEST_QUERY" ) ;
143+ envVarsKeys . push ( "BLS_REQUEST_METHOD" ) ;
144+ envVarsKeys . push ( "BLS_REQUEST_HEADERS" ) ;
145+
146+ if ( request . body ) {
147+ envVars . push (
148+ `BLS_REQUEST_BODY="${ encodeURIComponent ( JSON . stringify ( request . body ) ) } "` ,
149+ ) ;
150+ envVarsKeys . push ( "BLS_REQUEST_BODY" ) ;
151+ }
152+
153+ // Include environment string if there are variables
154+ if ( envVars . length > 0 ) {
155+ envString = `env ${ envVars . join ( " " ) } BLS_LIST_VARS=\"${ envVarsKeys . join ( ";" ) } \"` ;
156+ }
157+
158+ const result = execSync (
159+ `echo "${ decodeURIComponent ( request . url . trim ( ) ) } " | ${ envString } ${ runtimePath } ${ manifestPath } ` ,
160+ {
161+ cwd : path ,
162+ maxBuffer : 10000 * 1024 ,
163+ } ,
164+ ) . toString ( ) ;
165+
166+ if (
167+ ! manifest . contentType ||
168+ ( manifest . contentType === "json" && result )
169+ ) {
170+ try {
171+ const resultJson = JSON . parse ( result ) ;
172+
173+ reply . header ( "Content-Type" , "application/json" ) . send ( resultJson ) ;
174+ } catch ( error ) { }
175+ } else if ( manifest . contentType === "html" && result ) {
176+ const body = result ;
177+
178+ if ( body . startsWith ( "data:" ) ) {
179+ const data = body . split ( "," ) [ 1 ] ;
180+ const contentType = body . split ( "," ) [ 0 ] . split ( ":" ) [ 1 ] . split ( ";" ) [ 0 ] ;
181+ const base64data = Buffer . from ( data , "base64" ) ;
182+ reply . type ( contentType ) . send ( base64data ) ;
183+ } else {
184+ reply . header ( "Content-Type" , "text/html" ) . send ( body ) ;
185+ }
186+ } else {
187+ reply . send ( result ) ;
188+ }
189+ } ) ;
190+
191+ const port = await getPortPromise ( { port : 3000 , stopPort : 4000 } ) ;
192+
193+ fastify . listen ( { port } ) . then ( async ( ) => {
194+ console . log ( `Serving http://127.0.0.1:${ port } ...` ) ;
195+ } ) ;
196+ } catch ( error : any ) {
197+ logger . error ( "Failed to invoke function." , error . message ) ;
198+ }
199+ } ;
0 commit comments