do-functions-server
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -8,6 +8,18 @@ /* | ||
*/ | ||
import { parseArgs } from 'node:util'; | ||
import { resolve } from "node:path"; | ||
import { startServer } from "../server/startServer.js"; | ||
const builtDir = resolve(process.argv[2] ?? './build'); | ||
const server = await startServer(builtDir); | ||
const commandLineOptions = { | ||
builtDir: { type: 'string' }, | ||
'project-yml': { | ||
type: 'string' | ||
}, | ||
port: { type: 'string' } | ||
}; | ||
const args = parseArgs({ options: commandLineOptions, allowPositionals: true }); | ||
console.log(args); | ||
const builtDir = resolve(args.values.builtDir ?? args.positionals[0] ?? './build'); | ||
const projectYmlLocation = args.values["project-yml"] ?? 'project.yml'; | ||
const port = args.values.port ? parseInt(args.values.port) : undefined; | ||
const server = await startServer(builtDir, port, { projectYmlLocation }); | ||
// graceful shutdown | ||
@@ -14,0 +26,0 @@ async function shutdown() { |
@@ -9,10 +9,25 @@ /* | ||
import {parseArgs} from 'node:util'; | ||
import {resolve} from "node:path"; | ||
import {startServer} from "../server/startServer.js"; | ||
const builtDir = resolve(process.argv[2] ?? './build') | ||
const server = await startServer(builtDir) | ||
const commandLineOptions = { | ||
builtDir: {type: 'string'}, | ||
'project-yml': { | ||
type: 'string' | ||
}, | ||
port: {type: 'string'} | ||
} as const | ||
const args = parseArgs({options: commandLineOptions, allowPositionals: true}) | ||
console.log(args) | ||
const builtDir = resolve(args.values.builtDir ?? args.positionals[0] ?? './build') | ||
const projectYmlLocation = args.values["project-yml"] ?? 'project.yml' | ||
const port = args.values.port ? parseInt(args.values.port) : undefined | ||
const server = await startServer(builtDir, port, {projectYmlLocation}) | ||
// graceful shutdown | ||
@@ -19,0 +34,0 @@ async function shutdown() { |
{ | ||
"name": "do-functions-server", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"author": "Brian Evans", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -50,1 +50,12 @@ # Functions dev server | ||
Now you simply need to run `npm run serve` to start up a server of your functions. | ||
# Command line arguments | ||
```bash | ||
do-functions-server BUILD_DIRECTORY [OPTIONS] | ||
Available options: | ||
--project-yml project.yml a custom project.yml location to read the config from (relative path) | ||
--port 62747 port for server to listen on | ||
``` | ||
// a minimal function to test the dev server | ||
export function main(args) { | ||
return {body: '"Hello world"'} | ||
return {body: process.env.DO_FUNCTIONS_SERVER ?? '"Hello world"'} | ||
} |
@@ -1,2 +0,2 @@ | ||
export declare function invokeFunction({ packagesDir, packageName, functionName, entrypoint, args }: { | ||
export declare function invokeFunction({ packagesDir, packageName, functionName, entrypoint, args, env }: { | ||
packagesDir: any; | ||
@@ -7,2 +7,3 @@ packageName: any; | ||
args: any; | ||
env: any; | ||
}): Promise<any>; |
import { join } from "node:path"; | ||
import { fileExists } from "../cli/utils.js"; | ||
export async function invokeFunction({ packagesDir, packageName, functionName, entrypoint = 'main', args }) { | ||
const sourceName = join(packagesDir, packageName, functionName + '.js'); // unlike DO, assume the function has been bundled | ||
const exists = await fileExists(sourceName); | ||
if (exists) { | ||
const functionModule = await import('file:///' + sourceName); | ||
import { firstExists } from "../cli/utils.js"; | ||
export async function invokeFunction({ packagesDir, packageName, functionName, entrypoint = 'main', args, env }) { | ||
// check package/function.js, package/function/function.js and package/function/index.js | ||
const sourceName = await firstExists(join(packagesDir, packageName, functionName + '.js'), join(packagesDir, packageName, functionName, functionName + '.js'), join(packagesDir, packageName, functionName, 'index.js')); | ||
if (sourceName) { | ||
setEnv(env); | ||
// it would be preferable to run the function in a Worker thread, but can't figure out how to call the entrypoint function then. | ||
const functionModule = await import('file:///' + sourceName + '?' + Date.now()); | ||
const entrypointFunction = functionModule[entrypoint]; | ||
return entrypointFunction(args); | ||
const output = entrypointFunction(args); | ||
resetEnv(env); | ||
return output; | ||
} | ||
else | ||
throw new Error(`Function not found where package=${packageName} and function=${functionName}. Expected to find: ${sourceName}`); | ||
throw new Error(`Function not found where package=${packageName} and function=${functionName}. Expected to find: ${join(packagesDir, packageName, functionName + '.js')}`); | ||
} | ||
function setEnv(env) { | ||
for (const [key, value] of Object.entries(env)) { | ||
console.log("Setting", key, value); | ||
process.env[key] = value; | ||
} | ||
} | ||
function resetEnv(env) { | ||
for (const key of Object.keys(env)) { | ||
console.log("Deleting", key); | ||
// delete process.env[key] | ||
} | ||
} | ||
//# sourceMappingURL=invokeFunction.js.map |
import {join} from "node:path"; | ||
import {fileExists} from "../cli/utils.js"; | ||
import {firstExists} from "../cli/utils.js"; | ||
export async function invokeFunction({packagesDir, packageName, functionName, entrypoint = 'main', args}) { | ||
const sourceName = join(packagesDir, packageName, functionName + '.js') // unlike DO, assume the function has been bundled | ||
const exists = await fileExists(sourceName) | ||
if (exists) { | ||
const functionModule = await import('file:///' + sourceName) | ||
export async function invokeFunction({packagesDir, packageName, functionName, entrypoint = 'main', args, env}) { | ||
// check package/function.js, package/function/function.js and package/function/index.js | ||
const sourceName = await firstExists(join(packagesDir, packageName, functionName + '.js'), join(packagesDir, packageName, functionName, functionName + '.js'), join(packagesDir, packageName, functionName, 'index.js')) | ||
if (sourceName) { | ||
setEnv(env) | ||
// it would be preferable to run the function in a Worker thread, but can't figure out how to call the entrypoint function then. | ||
const functionModule = await import('file:///' + sourceName + '?' + Date.now()) | ||
const entrypointFunction = functionModule[entrypoint] | ||
return entrypointFunction(args) | ||
} else throw new Error(`Function not found where package=${packageName} and function=${functionName}. Expected to find: ${sourceName}`) | ||
const output = entrypointFunction(args) | ||
resetEnv(env) | ||
return output | ||
} else throw new Error(`Function not found where package=${packageName} and function=${functionName}. Expected to find: ${join(packagesDir, packageName, functionName + '.js')}`) | ||
} | ||
function setEnv(env: Record<string, string>) { | ||
for (const [key, value] of Object.entries(env)) { | ||
console.log("Setting", key, value) | ||
process.env[key] = value | ||
} | ||
} | ||
function resetEnv(env: Record<string, string>) { | ||
for (const key of Object.keys(env)) { | ||
console.log("Deleting", key) | ||
// delete process.env[key] | ||
} | ||
} |
import type { IncomingMessage, ServerResponse } from 'node:http'; | ||
export declare const requestHandler: (packagesDir: any) => (req: IncomingMessage, res: ServerResponse) => Promise<void>; | ||
import type { ProjectYml } from "../projectYml/projectYml.js"; | ||
export declare const requestHandler: (packagesDir: any, projectYml: ProjectYml) => (req: IncomingMessage, res: ServerResponse) => Promise<void>; |
import { invokeFunction } from "./invokeFunction.js"; | ||
export const requestHandler = (packagesDir) => async function (req, res) { | ||
export const requestHandler = (packagesDir, projectYml) => async (req, res) => { | ||
console.log('in requestHandler.ts', projectYml.packages[0]); | ||
console.log(new Date(), req.method, 'Request on ', req.url); | ||
@@ -9,10 +10,21 @@ if (req.method === 'GET' || req.method === 'HEAD') { | ||
const args = Object.fromEntries(new URLSearchParams(query).entries()); | ||
const result = await invokeFunction({ packageName, functionName, packagesDir, args }) | ||
.catch(e => ({ | ||
headers: { 'content-type': 'application/json' }, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})); | ||
res.writeHead(result.statusCode ?? 200, result.headers ?? {}); | ||
res.end(result.body); | ||
const projectYmlPackage = projectYml.packages.find(p => p.name === packageName); | ||
const projectYmlFunction = projectYmlPackage?.functions.find(f => f.name === functionName); | ||
if (projectYmlPackage && projectYmlFunction) { | ||
console.log({ projectYmlPackage, projectYmlFunction }); | ||
const env = Object.assign({}, projectYmlPackage.environment, projectYmlFunction.environment); | ||
console.log({ env }); | ||
const entrypoint = projectYmlFunction.entrypoint; | ||
const result = await invokeFunction({ packageName, functionName, packagesDir, args, env, entrypoint }) | ||
.catch(e => ({ | ||
headers: { 'content-type': 'application/json' }, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})); | ||
res.writeHead(result.statusCode ?? 200, result.headers ?? {}); | ||
res.end(result.body); | ||
} | ||
else { | ||
res.end(JSON.stringify({ msg: 'Function not in project.yml config. Try restart.', functionName, packageName })); | ||
} | ||
} | ||
@@ -19,0 +31,0 @@ else { |
import type {IncomingMessage, ServerResponse} from 'node:http' | ||
import {invokeFunction} from "./invokeFunction.js"; | ||
import type {ProjectYml} from "../projectYml/projectYml.js"; | ||
export const requestHandler = (packagesDir) => async function (req: IncomingMessage, res: ServerResponse) { | ||
export const requestHandler = (packagesDir, projectYml: ProjectYml) => async (req: IncomingMessage, res: ServerResponse) => { | ||
console.log('in requestHandler.ts', projectYml.packages[0]) | ||
console.log(new Date(), req.method, 'Request on ', req.url) | ||
@@ -11,10 +13,20 @@ if (req.method === 'GET' || req.method === 'HEAD') { | ||
const args = Object.fromEntries(new URLSearchParams(query).entries()) | ||
const result = await invokeFunction({packageName, functionName, packagesDir, args}) | ||
.catch(e => ({ | ||
headers: {'content-type': 'application/json'}, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})) | ||
res.writeHead(result.statusCode ?? 200, result.headers ?? {}) | ||
res.end(result.body) | ||
const projectYmlPackage = projectYml.packages.find(p => p.name === packageName) | ||
const projectYmlFunction = projectYmlPackage?.functions.find(f => f.name === functionName) | ||
if (projectYmlPackage && projectYmlFunction) { | ||
console.log({projectYmlPackage, projectYmlFunction}) | ||
const env = Object.assign({}, projectYmlPackage.environment, projectYmlFunction.environment) | ||
console.log({env}) | ||
const entrypoint = projectYmlFunction.entrypoint | ||
const result = await invokeFunction({packageName, functionName, packagesDir, args, env, entrypoint}) | ||
.catch(e => ({ | ||
headers: {'content-type': 'application/json'}, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})) | ||
res.writeHead(result.statusCode ?? 200, result.headers ?? {}) | ||
res.end(result.body) | ||
} else { | ||
res.end(JSON.stringify({msg: 'Function not in project.yml config. Try restart.', functionName, packageName})) | ||
} | ||
} else { | ||
@@ -21,0 +33,0 @@ res.end(JSON.stringify({msg: 'URL is in wrong format. Try packageName/functionName'})) |
@@ -5,6 +5,9 @@ /** | ||
* @param port - port to listen on. Default is 62747. Set to 0 for a random port. | ||
* @param projectYmlLocation - the file location of project.yml, which configures the functions. | ||
*/ | ||
export declare function startServer(absBuiltDir: any, port?: number): Promise<{ | ||
export declare function startServer(absBuiltDir: any, port?: number, { projectYmlLocation }?: { | ||
projectYmlLocation?: string | undefined; | ||
}): Promise<{ | ||
close: () => Promise<unknown>; | ||
address: string | null; | ||
}>; |
@@ -6,3 +6,3 @@ import { createServer } from "node:http"; | ||
import { parse } from "yaml"; | ||
import { printProjectYaml } from "../projectYaml/projectYaml.js"; | ||
import { printProjectYml } from "../projectYml/projectYml.js"; | ||
/** | ||
@@ -12,4 +12,5 @@ * | ||
* @param port - port to listen on. Default is 62747. Set to 0 for a random port. | ||
* @param projectYmlLocation - the file location of project.yml, which configures the functions. | ||
*/ | ||
export async function startServer(absBuiltDir, port = 62747) { | ||
export async function startServer(absBuiltDir, port = 62747, { projectYmlLocation = 'project.yml' } = {}) { | ||
if (!isAbsolute(absBuiltDir)) | ||
@@ -19,10 +20,11 @@ throw new Error('Path to functions project must be absolute. Got ' + absBuiltDir); | ||
absBuiltDir = absBuiltDir.slice(1); // remove leading slash on windows | ||
const server = createServer(requestHandler(join(absBuiltDir, 'packages'))); | ||
console.log('Serving packages from ', join(absBuiltDir, 'packages')); | ||
console.log('Looking for project config at', join(absBuiltDir, projectYmlLocation)); | ||
const projectYml = await readFile(join(absBuiltDir, projectYmlLocation)).then(String).then(parse); | ||
printProjectYml(projectYml); | ||
console.log('in server.ts', projectYml.packages[0]); | ||
const server = createServer(requestHandler(join(absBuiltDir, 'packages'), projectYml)); | ||
await new Promise(res => server.listen(port, res)); | ||
const address = server.address(); | ||
console.log('Server is listening', address === null || typeof address === 'string' ? address : ('http://localhost:' + address.port)); | ||
console.log('Serving packages from ', join(absBuiltDir, 'packages')); | ||
console.log('Looking for project config at', join(absBuiltDir, 'project.yml')); | ||
const projectYaml = await readFile(join(absBuiltDir, 'project.yml')).then(String).then(parse); | ||
printProjectYaml(projectYaml); | ||
return { | ||
@@ -29,0 +31,0 @@ close: () => new Promise(res => server.close(res)), |
@@ -6,3 +6,3 @@ import {createServer} from "node:http"; | ||
import {parse} from "yaml"; | ||
import {printProjectYaml} from "../projectYaml/projectYaml.js"; | ||
import {printProjectYml, ProjectYml} from "../projectYml/projectYml.js"; | ||
@@ -13,17 +13,18 @@ /** | ||
* @param port - port to listen on. Default is 62747. Set to 0 for a random port. | ||
* @param projectYmlLocation - the file location of project.yml, which configures the functions. | ||
*/ | ||
export async function startServer(absBuiltDir, port = 62747) { | ||
export async function startServer(absBuiltDir, port = 62747, {projectYmlLocation = 'project.yml'} = {}) { | ||
if (!isAbsolute(absBuiltDir)) throw new Error('Path to functions project must be absolute. Got ' + absBuiltDir) | ||
if (absBuiltDir.match(/^\\|\/[A-Z]:/)) absBuiltDir = absBuiltDir.slice(1) // remove leading slash on windows | ||
const server = createServer(requestHandler(join(absBuiltDir, 'packages'))) | ||
console.log('Serving packages from ', join(absBuiltDir, 'packages')) | ||
console.log('Looking for project config at', join(absBuiltDir, projectYmlLocation)) | ||
const projectYml = <ProjectYml>await readFile(join(absBuiltDir, projectYmlLocation)).then(String).then(parse) | ||
printProjectYml(projectYml) | ||
console.log('in server.ts', projectYml.packages[0]) | ||
const server = createServer(requestHandler(join(absBuiltDir, 'packages'), projectYml)) | ||
await new Promise<void>(res => server.listen(port, res)) | ||
const address = server.address() | ||
console.log('Server is listening', address === null || typeof address === 'string' ? address : ('http://localhost:' + address.port)) | ||
console.log('Serving packages from ', join(absBuiltDir, 'packages')) | ||
console.log('Looking for project config at', join(absBuiltDir, 'project.yml')) | ||
const projectYaml = await readFile(join(absBuiltDir, 'project.yml')).then(String).then(parse) | ||
printProjectYaml(projectYaml) | ||
return { | ||
@@ -30,0 +31,0 @@ close: () => new Promise(res => server.close(res)), |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
30381
35
370
61
0
3