do-functions-server
Advanced tools
Comparing version 1.0.1 to 1.1.0
@@ -8,56 +8,9 @@ /* | ||
*/ | ||
import { createServer } from 'node:http'; | ||
import { resolve, join } from "node:path"; | ||
import { readFile } from "fs/promises"; | ||
import { parse } from "yaml"; | ||
import { fileExists } from "./utils.js"; | ||
import { printProjectYaml } from '../projectYaml/projectYaml.js'; | ||
import { resolve } from "node:path"; | ||
import { startServer } from "../server/startServer.js"; | ||
const builtDir = resolve(process.argv[2] ?? './build'); | ||
const server = createServer(async (req, res) => { | ||
console.log(new Date(), req.method, 'Request on ', req.url); | ||
if (req.method === 'GET' || req.method === 'HEAD') { | ||
const urlMatch = req.url?.match(/^\/([^\/?]+)\/([^\/?]+)\/?(\?.*)?$/); | ||
if (urlMatch) { | ||
const [, packageName, functionName, query] = urlMatch; | ||
const args = Object.fromEntries(new URLSearchParams(query).entries()); | ||
const result = await callFunction({ packageName, functionName, packagesDir: builtDir, args }) | ||
.catch(e => ({ | ||
headers: { 'content-type': 'application/json' }, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})); | ||
res.writeHead(result.statusCode, result.headers); | ||
res.end(result.body); | ||
} | ||
else { | ||
res.end(JSON.stringify({ msg: 'URL is in wrong format. Try packageName/functionName' })); | ||
} | ||
} | ||
else { | ||
res.writeHead(405, { Allow: 'GET,HEAD', 'content-type': 'application/json' }); | ||
res.end(JSON.stringify({ msg: 'HTTP method not allowed. Try doing a GET request rather.' })); | ||
} | ||
}); | ||
server.listen(62747, async () => { | ||
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(builtDir, 'packages')); | ||
console.log('Project config at', join(builtDir, 'project.yaml')); | ||
const projectYaml = await readFile(join(builtDir, 'project.yaml')).then(String).then(parse); | ||
printProjectYaml(projectYaml); | ||
}); | ||
async function callFunction({ packagesDir, packageName, functionName, entrypoint = 'main', args }) { | ||
const sourceName = join(packagesDir, 'packages', packageName, functionName + '.js'); // unlike DO, assume the function has been bundled | ||
const exists = await fileExists(sourceName); | ||
if (exists) { | ||
const functionModule = await import('file:///' + sourceName); | ||
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 server = await startServer(builtDir); | ||
// graceful shutdown | ||
async function shutdown() { | ||
await new Promise(res => server.close(res)); | ||
await server.close(); | ||
process.exit(); | ||
@@ -64,0 +17,0 @@ } |
@@ -10,58 +10,12 @@ /* | ||
import {createServer} from 'node:http' | ||
import {resolve, join} from "node:path"; | ||
import {readFile} from "fs/promises"; | ||
import {parse} from "yaml"; | ||
import {fileExists} from "./utils.js"; | ||
import {printProjectYaml} from '../projectYaml/projectYaml.js'; | ||
import {resolve} from "node:path"; | ||
import {startServer} from "../server/startServer.js"; | ||
const builtDir = resolve(process.argv[2] ?? './build') | ||
const server = createServer(async (req, res) => { | ||
console.log(new Date(), req.method, 'Request on ', req.url) | ||
if (req.method === 'GET' || req.method === 'HEAD') { | ||
const urlMatch = req.url?.match(/^\/([^\/?]+)\/([^\/?]+)\/?(\?.*)?$/) | ||
if (urlMatch) { | ||
const [, packageName, functionName, query] = urlMatch | ||
const args = Object.fromEntries(new URLSearchParams(query).entries()) | ||
const result = await callFunction({packageName, functionName, packagesDir: builtDir, args}) | ||
.catch(e => ({ | ||
headers: {'content-type': 'application/json'}, | ||
statusCode: 500, | ||
body: `{"msg":"Internal server error: ${e.message}"}` | ||
})) | ||
res.writeHead(result.statusCode, result.headers) | ||
res.end(result.body) | ||
} else { | ||
res.end(JSON.stringify({msg: 'URL is in wrong format. Try packageName/functionName'})) | ||
} | ||
} else { | ||
res.writeHead(405, {Allow: 'GET,HEAD', 'content-type': 'application/json'}) | ||
res.end(JSON.stringify({msg: 'HTTP method not allowed. Try doing a GET request rather.'})) | ||
} | ||
}) | ||
const server = await startServer(builtDir) | ||
server.listen(62747, async () => { | ||
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(builtDir, 'packages')) | ||
console.log('Project config at', join(builtDir, 'project.yaml')) | ||
const projectYaml = await readFile(join(builtDir, 'project.yaml')).then(String).then(parse) | ||
printProjectYaml(projectYaml) | ||
}) | ||
async function callFunction({packagesDir, packageName, functionName, entrypoint = 'main', args}) { | ||
const sourceName = join(packagesDir, 'packages', packageName, functionName + '.js') // unlike DO, assume the function has been bundled | ||
const exists = await fileExists(sourceName) | ||
if (exists) { | ||
const functionModule = await import('file:///' + sourceName) | ||
const entrypointFunction = functionModule[entrypoint] | ||
return entrypointFunction(args) | ||
} else throw new Error(`Function not found where package=${packageName} and function=${functionName}. Expected to find: ${sourceName}`) | ||
} | ||
// graceful shutdown | ||
async function shutdown() { | ||
await new Promise(res => server.close(res)) | ||
await server.close() | ||
process.exit() | ||
@@ -68,0 +22,0 @@ } |
{ | ||
"name": "do-functions-server", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"author": "Brian Evans", | ||
"type": "module", | ||
"repository": "https://github.com/mrbrianevans/do-functions", | ||
"repository": { | ||
"url": "https://github.com/mrbrianevans/do-functions", | ||
"directory": "test-server", | ||
"type": "git" | ||
}, | ||
"description": "A development server runner for Digital Ocean serverless functions", | ||
@@ -20,2 +24,4 @@ "keywords": [ | ||
}, | ||
"main": "index.js", | ||
"types": "index.d.ts", | ||
"dependencies": { | ||
@@ -22,0 +28,0 @@ "yaml": "^2.1.1" |
# Functions dev server | ||
[Documentation](https://mrbrianevans.github.io/do-functions/test-server.html) | ||
| | ||
[do-functions-server NPM](https://www.npmjs.com/package/do-functions-server) | ||
A server to test running your functions locally, before deploying. | ||
Spins up a simple node webserver that serves an endpoint for each function in a Digital Ocean Functions structured | ||
```bash | ||
npx do-functions-server . | ||
``` | ||
Spins up a simple nodejs webserver that serves an endpoint for each function in a Digital Ocean Functions structured | ||
project. | ||
See [getting-started#packages-structure](./getting-started.md#packages-structure) for the expected structure. | ||
Serves endpoints on `http://localhost:62747` with URL paths `{packageName}/{functionName}`. | ||
Expected structure: | ||
``` | ||
packages/ | ||
packageName/ | ||
functionName.js | ||
project.yml | ||
``` | ||
> **Highly recommended to use in conjunction with [`do-functions`](https://www.npmjs.com/package/do-functions)** | ||
> which produces the correct structure as build output. | ||
See [getting-started#packages-structure](https://mrbrianevans.github.io/do-functions/getting-started.html#packages-structure) | ||
for a guide to develop functions. | ||
# Package script | ||
You can add this script to your `package.json` to make it more convenient to test your functions: | ||
```json | ||
{ | ||
"name": "your-package", | ||
"scripts": { | ||
"serve": "do-functions-server ." | ||
}, | ||
"devDependencies": { | ||
"do-functions-server": "^1.0.1" | ||
} | ||
} | ||
``` | ||
Now you simply need to run `npm run serve` to start up a server of your functions. |
Sorry, the diff of this file is not supported yet
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
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
20884
34
267
50
2
1