Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@studio/lambda

Package Overview
Dependencies
Maintainers
0
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@studio/lambda - npm Package Compare versions

Comparing version
5.0.1
to
5.0.2
+12
-7
package.json
{
"name": "@studio/lambda",
"version": "5.0.1",
"version": "5.0.2",
"description": "JavaScript Studio lambda execution environment",

@@ -21,3 +21,3 @@ "author": "Maximilian Antoni <max@javascript.studio>",

"prettier:write": "prettier --write '**/*.{js,json,md}'",
"prepare": "husky install"
"prepare": "husky"
},

@@ -28,3 +28,3 @@ "eslintConfig": {

"dependencies": {
"@studio/log": "^2.0.0",
"@studio/log": "^2.1.3",
"@studio/ndjson": "^2.1.0"

@@ -37,8 +37,13 @@ },

"eslint": "^8.56.0",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"mocha": "^10.2.0",
"prettier": "^3.1.1"
"husky": "^9.1.5",
"lint-staged": "^15.2.9",
"mocha": "^10.7.3",
"prettier": "^3.3.3"
},
"files": [
"lib",
"LICENSE",
"README.md"
],
"license": "MIT"
}
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
node-version: ['18.x', '20.x']
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install
run: npm ci
- name: Lint
if: matrix.node-version == '20.x'
run: npm run lint
- name: Prettier
if: matrix.node-version == '20.x'
run: npm run prettier:check
- name: Test
run: npm test

Sorry, the diff of this file is not supported yet

'use strict';
module.exports = {
'*.js': 'eslint --fix',
'*.{js,md}': 'prettier --write'
};

Sorry, the diff of this file is not supported yet

{
"singleQuote": true,
"trailingComma": "none"
}
# Changes
## 5.0.1
- 🐛 [`446db5c`](https://github.com/javascript-studio/studio-lambda/commit/446db5c1925c71c71a9d46b8a2594483fe4dab25)
Fix max idle overtaking lambda timeout
- 📚 [`16953a0`](https://github.com/javascript-studio/studio-lambda/commit/16953a0c3d91773c58aa26930e985217010ce962)
Add missing documentation for timeout option
- 📚 [`e582183`](https://github.com/javascript-studio/studio-lambda/commit/e582183c319d65dd66abe5e1a7ee007c6184c541)
Remove bad link to apex
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2024-08-28._
## 5.0.0
- [`50359f9`](https://github.com/javascript-studio/studio-lambda/commit/50359f942e13f5ff4f2f958ce66ecf4d119c69f1)
Use husky and lint-staged
- [`7c72184`](https://github.com/javascript-studio/studio-lambda/commit/7c721842ffe07ca9dd2ae7507b24267cdbbf1dfb)
Drop node 16
- [`abb5df7`](https://github.com/javascript-studio/studio-lambda/commit/abb5df7ab3400ad5f41511425c3c0c1a13d302a0)
Rename master to main
- [`d0be735`](https://github.com/javascript-studio/studio-lambda/commit/d0be735b756ad7189a9abcabf4e7cf09a50441ee)
State MIT license in package.json
- [`af1f621`](https://github.com/javascript-studio/studio-lambda/commit/af1f6217d7cdd75ad23e12a1abd16469a867eccd)
Add LICENSE
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2024-01-17._
## 4.0.1
- ✨ [`cca2e48`](https://github.com/javascript-studio/studio-lambda/commit/cca2e48693b8b900aa40b296b8ee4bb276be470b)
Support node 20 and update GitHub action
- ✨ [`d64d9ff`](https://github.com/javascript-studio/studio-lambda/commit/d64d9ff20a4f8fec8eb72b49ec0aeef3d75e4d42)
Upgrade prettier to v3
- ✨ [`2670491`](https://github.com/javascript-studio/studio-lambda/commit/267049180cead33e746b75e7c88098b22613a68a)
Update eslint config and eslint
- ✨ [`c27072b`](https://github.com/javascript-studio/studio-lambda/commit/c27072b6a51d755f3c5fec354199a7638f2c0fbf)
Update Studio ndjson
- ✨ [`06c894f`](https://github.com/javascript-studio/studio-lambda/commit/06c894f05c291feb730a86cea9c730bd7312e987)
Upgrade Studio Changes
- ✨ [`4e929c7`](https://github.com/javascript-studio/studio-lambda/commit/4e929c703cd47ab1e1ff8f37145a62c9f459260b)
Upgrade @sinonjs/referee-sinon
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2024-01-11._
## 4.0.0
- 💥 [`8be1398`](https://github.com/javascript-studio/studio-lambda/commit/8be1398865ee2b415d8fd04436068d495ac586ea)
Require node 16
- 💥 [`496cc52`](https://github.com/javascript-studio/studio-lambda/commit/496cc525838552e0284f740d07d9c5620233366e)
Use dynamic import to support loading es modules
- 💥 [`d458059`](https://github.com/javascript-studio/studio-lambda/commit/d4580593c68ef845a236c03a7be02a6bffbb181a)
Change generic destroy error code to `E_FAILED`
- 🛡 [`8a6a3b3`](https://github.com/javascript-studio/studio-lambda/commit/8a6a3b35a5d28e21502cdbad8140039b8341196f)
npm audit
- ✨ [`6814ed2`](https://github.com/javascript-studio/studio-lambda/commit/6814ed26f83fdec1807e14b42d2a5c8a12afe973)
Update Studio Changes
- ✨ [`0d942b9`](https://github.com/javascript-studio/studio-lambda/commit/0d942b9a8733808958dfec9d6f2071901dc8b597)
Upgrade referee-sinon
- ✨ [`673cc02`](https://github.com/javascript-studio/studio-lambda/commit/673cc0266bdaf961fd8d688d96f0bb6b0221f469)
Update prettier
- ✨ [`5c264ff`](https://github.com/javascript-studio/studio-lambda/commit/5c264ff5560fc7eb4b518f8f3dfd455f9438d6da)
Upgrade mocha
- ✨ [`bf9897b`](https://github.com/javascript-studio/studio-lambda/commit/bf9897bc9a2317d3224b0cf4a0abff5e82872c0c)
Upgrade eslint-config and eslint
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2023-01-19._
## 3.0.2
- 🐛 [`5b7e59e`](https://github.com/javascript-studio/studio-lambda/commit/5b7e59e36d5b041b729ebc41e1b6edf40ad3d390)
Fix region and account config via env
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2021-06-23._
## 3.0.1
- 🐛 [`e1be0d4`](https://github.com/javascript-studio/studio-lambda/commit/e1be0d43cc69645cd7f9a10b7a51d3358af96b28)
Handler `JSON.stringify` error in lambda response
- ✨ [`526574b`](https://github.com/javascript-studio/studio-lambda/commit/526574b5dbf616747079a012f5075384b25d92ab)
Upgrade @sinonjs/referee-sinon to latest (Morgan Roderick)
- ✨ [`2f25018`](https://github.com/javascript-studio/studio-lambda/commit/2f25018dec371373b33c8245549135cb3c79aeb9)
Set required node version in package-lock.json (Morgan Roderick)
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2021-05-21._
## 3.0.0
- 💥 [`b5a53ad`](https://github.com/javascript-studio/studio-lambda/commit/b5a53ad96c6e915d35015923c5055895c84d7352)
Require node 12
- 🍏 [`21b2d2c`](https://github.com/javascript-studio/studio-lambda/commit/21b2d2c4b5b6551935335f7df5d2c36e9306add7)
Return promise in lambda.invoke if no callback is given
- 🍏 [`c37577a`](https://github.com/javascript-studio/studio-lambda/commit/c37577a4beee285f3c6543d30b8b8fa323a0069b)
Support async lambda functions
- 📚 [`4de3f74`](https://github.com/javascript-studio/studio-lambda/commit/4de3f74669cf682d0bb962e25416b0d4d1611535)
Document usage with async await
- 🐛 [`2111a8f`](https://github.com/javascript-studio/studio-lambda/commit/2111a8f14f153c703ba2ce8989e56b08c7c4f07d)
Fix error handling if lambda throws while launching
- ✨ [`957d2e6`](https://github.com/javascript-studio/studio-lambda/commit/957d2e6f7b78fee49db17405cebaf320d049ed71)
Avoid else by returning early
- ✨ [`e6879f2`](https://github.com/javascript-studio/studio-lambda/commit/e6879f2575c639d9b3feb1915062a73e1c935169)
Configure GitHub actions
- ✨ [`64de260`](https://github.com/javascript-studio/studio-lambda/commit/64de2605cc4875bd1a66cb067a2f066e6ae8977c)
Run lint, test and prettier checks separately
- ✨ [`b9db826`](https://github.com/javascript-studio/studio-lambda/commit/b9db8264ebc97c92e21b55489e6015fe90394d6d)
Update Studio Changes
- ✨ [`0af1d4e`](https://github.com/javascript-studio/studio-lambda/commit/0af1d4e3005cfe863af46b770cfc9d7502738238)
Upgrade referee-sinon to latest
- ✨ [`f2000dd`](https://github.com/javascript-studio/studio-lambda/commit/f2000dd28b2ca00a987c1404034ad5b687b02ad0)
Upgrade mocha to latest
- ✨ [`c466a9d`](https://github.com/javascript-studio/studio-lambda/commit/c466a9de0e8954498fe9ef537450db0b891e222e)
Setup prettier
- ✨ [`e0469bb`](https://github.com/javascript-studio/studio-lambda/commit/e0469bb7d7514d208d67a5de9c1729f71c8cb92b)
Upgrade eslint and eslint-config
- ✨ [`94c71c6`](https://github.com/javascript-studio/studio-lambda/commit/94c71c6ce1338604b61312731dddc06036317de5)
Use npm 7
- ✨ [`b121728`](https://github.com/javascript-studio/studio-lambda/commit/b121728669ce120cb737ea750e1a739170ceac70)
Add .gitignore
- ✨ [`883ba54`](https://github.com/javascript-studio/studio-lambda/commit/883ba544640776b0d24d6adafc1fe449dfe2d56e)
Fix memory limit tests for node 12
- ✨ [`0914a38`](https://github.com/javascript-studio/studio-lambda/commit/0914a384b6afcfc3c92e977ee3779347cc6e15ab)
Upgrade referee-sinon to v6
- ✨ [`eb41ede`](https://github.com/javascript-studio/studio-lambda/commit/eb41ede3c2e0abae743d7412c455212b03ccc8b7)
Upgrade Studio Changes to v2
- ✨ [`570c069`](https://github.com/javascript-studio/studio-lambda/commit/570c069af8cddb1cb65d6ec345b3f17ba9d21384)
Make eslint-config a dev dependency
_Released by [Maximilian Antoni](https://github.com/mantoni) on 2021-04-21._
## 2.0.2
- 🐛 [`6a70c3c`](https://github.com/javascript-studio/studio-lambda/commit/6a70c3cae4640e68d6dd62196aeac5bfaaf62737)
Revert "Do not prepend forwarded log namespace with name"
> This reverts commit 7082da34f64146db43402d40835bfb2ffbb934f7.
## 2.0.1
- 🐛 [`7082da3`](https://github.com/javascript-studio/studio-lambda/commit/7082da34f64146db43402d40835bfb2ffbb934f7)
Do not prepend forwarded log namespace with name
## 2.0.0
- 💥 [`82e917a`](https://github.com/javascript-studio/studio-lambda/commit/82e917a83d8536e778ffa18aa461c301d5d656e5)
__BREAKING:__ Upgrade Studio Log to v2
- ✨ [`e0669ea`](https://github.com/javascript-studio/studio-lambda/commit/e0669ea81256c489608fc43c6d8304a5d28293af)
Use Sinon + Referee and use Sinon default sandbox
- 📚 [`8362816`](https://github.com/javascript-studio/studio-lambda/commit/83628163b14ca0cd937c8ee6ca01fb3b27c36ae0)
Add commit links with `--commits`
## 1.14.1
- 🐛 Pass error cause in logs through to lambda logger
## 1.14.0
- 🍏 Implement more AWS environment variables for lambda functions
- 🍏 Change log topic for new lambdas from "launch" to "spawn"
- ✨ Add tests for AWS_REGION and AWS_PROFILE environment variables
- ✨ Refactor setting `getRemainingTimeInMillis` on context
## 1.13.0
- 🍏 Add `shutdown` and `stats` APIs
- 🐛 Improve lambda function error handling
- 🐛 Handle ndjson transform errors
## 1.12.0
- 🍏 Use configured memory and improve process exit handling
## 1.11.1
- 🐛 Improve generated `awsRequestId`
## 1.11.0
- 🍏 Add support for `functionName`.
- 🍏 Add support for `awsRequestId`.
- 🍏 Add support for `memoryLimitInMB`.
- 🍏 Add support for `getRemainingTimeInMillis()`.
- 🐛 Build the function ARN within the Lambda function instead of injecting it.
The `STUDIO_AWS_ACCOUNT` environment variable was not picket up correctly.
## 1.10.1
- ✨ Use `@studio/ndjson` to parse log output from Lambdas
## 1.10.0
- ⚠️ Remove hack to filter log data
> Log data should be filtered where it's created. With the new
> `@studio/log-x` module and the updated `@studio/wrap` this hack can be
> removed.
- ✨ Add `package-lock.json`
## 1.9.1
- 🐛 Add `invokedFunctionArn` to given context if missing
## 1.9.0
- 🍏 Generate `invokedFunctionArn` in default context
- 📚 Use upper case `Lambda` module name in examples
## 1.8.2
- 🐛 Fix entries cleanup in kill handler
> The entries array is not necessarily cleaned up in reverse insertion
> order. With this patch, the kill handler does not expect the entry at a
> fixed position, but queries the entry to remove by ID instead.
## 1.8.1
- 🐛 Fail on missing environment variables
> - Throw an error if a variable replacement fails.
> - Handle launch exceptions by logging an error and invoking the callback
> with an error message.
## 1.8.0
- 🍏 Support environment variable replacement in Lambda config files
> If a config file contains placeholders in the form `${ENV_VAR}`, they are
> replaced with the corresponding environment variable. This feature is not
> supported by Apex. Use it to configure secret variables in local configs.
## 1.7.0
- 🙊 Do not log Lambda input and output
> The new `@studio/wrap` implementation already do logging within the
> Lambda function. Log filtering should also be applied there.
## 1.6.2
- 🐛 Pass on parent process `execArgv`
> This allows to invoke the parent process with v8 options like
> `--stack-trace-limit=50` and have them passed on to the Lambda
> processes. This restores the default behavior for child processes.
## 1.6.1
Log output improvements:
- 🔢 Include the Lambda execution time in the stats log message
- 🙈 Set `config_file` to `"<defaults>"` if not found
- 🙈 Remove messages to streamline log output
## 1.6.0
- 🔢 Log Lambda process memory usage
## 1.5.0
- 🍏 Add Lamdba debugging option
## 1.4.0
- 🍏 Pass on `process.env.DEBUG`
## 1.3.3
- 🍏 Improve log output filtering
- 🍏 Handle Lambda logs on stdout
- 🙈 Do not log `authorizationToken`
- 🐛 Fix log message with string event
## 1.3.2
- 🐛 Reduce duplication in log messages
## 1.3.1
- 🐛 Rename logger
## 1.3.0
- 🍏 Use `@studio/log`
## 1.2.2
- 🐛 Handle invalid lambda response
## 1.2.1
- 🐛 Timeouts are defined in seconds
## 1.2.0
- 🍏 Add `base_dir` config option
## 1.1.2
- 🍏 Pass `HOME` environment variable for the `aws-sdk` module to find the
credentials file
- ✨ Change log emoji for send and receive events
## 1.1.1
A few bug fixes and minor improvements:
- 🐛 Preserve configured env if config file is loaded
- 🐛 Do not log authorization and token header values
- 🐛 Prefix log messages with "Lambda"
- 🐛 Log lambda error response
## 1.1.0
- 🍏 Allow to pass an optional `context` object to the Lambda handler
function. If no context is given, it defaults to an empty object, retaining
the previous behavior.
## 1.0.0
- ✨ Inception

Sorry, the diff of this file is not supported yet

'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, `Other dir ${event.name}`);
}, 1);
};
'use strict';
let count = 0;
exports.handle = function (event, context, callback) {
count++;
setTimeout(() => {
callback(null, `Count ${count}`);
}, 200);
};
'use strict';
exports.handle = function (event, context, callback) {
const ctx = Object.assign(
{
invokedFunctionArn: context.invokedFunctionArn // getter
},
context
);
setTimeout(() => {
callback(null, JSON.stringify(ctx));
}, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, `Debug ${process.env.DEBUG}`);
}, 1);
};
{
"environment": {
"STUDIO_ENV_VAR": "Hello",
"STUDIO_ENV_TPL": "${STUDIO_ENVIRONMENT_VARIABLE}"
}
}
'use strict';
exports.handle = function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
// eslint-disable-next-line prefer-promise-reject-errors
reject(`${process.env.STUDIO_ENV_VAR} ${process.env.STUDIO_ENV_TPL}`);
}, 1);
});
};
{
"environment": {
"STUDIO_ENV_VAR": "Hello",
"STUDIO_ENV_TPL": "${STUDIO_ENVIRONMENT_VARIABLE}"
}
}
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(
null,
`${process.env.STUDIO_ENV_VAR} ${process.env.STUDIO_ENV_TPL}`
);
}, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, `Hello ${process.env[event.env]}`);
}, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, context.getRemainingTimeInMillis());
}, 1);
};
'use strict';
exports.handle = function (event) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Hello ${event.name}`);
}, 1);
});
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, `Hello ${event.name}`);
}, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
const obj = {
toJSON() {
throw new Error('Ouch!');
}
};
callback(null, obj);
}, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
console.log('{"some":"incomplete json output');
setTimeout(callback, 1);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
callback(null, () => {});
}, 1);
};
'use strict';
const logger = require('@studio/log');
const Stringify = require('@studio/ndjson/stringify');
logger.pipe(new Stringify()).pipe(process.stdout);
const log = logger('Test');
exports.handle = function (event, context, callback) {
const err = new Error('Fail');
err.cause = new Error('Cause');
log.error('Failure', err);
callback();
};
'use strict';
const logger = require('@studio/log');
const Stringify = require('@studio/ndjson/stringify');
logger.pipe(new Stringify()).pipe(process.stdout);
const log = logger('Test');
exports.handle = function (event, context, callback) {
log.ok('Check');
log.warn({ event });
log.error({ event }, new Error());
log.wtf();
console.log('Raw log line');
callback();
};
'use strict';
const count = 1024 * 1024;
exports.handle = function (event, context, callback) {
const entries = [];
try {
for (let i = 0; i < count; i++) {
entries.push('a');
}
callback(null, 'Allocated');
} catch (e) {
callback(String(process.memoryUsage().rss));
}
};
'use strict';
let count = 0;
exports.handle = function (event, context, callback) {
count++;
setTimeout(() => {
callback(null, `Count ${count}`);
}, 1);
};
'use strict';
exports.handle = function () {
throw new Error('Oh noes!');
};
'use strict';
exports.handle = function (_event, _context, _callback) {
throw new Error('Oh noes!');
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
// eslint-disable-next-line n/no-callback-literal
callback('E_TOO_LATE');
}, 200);
};
'use strict';
exports.handle = function (event, context, callback) {
setTimeout(() => {
// eslint-disable-next-line n/no-callback-literal
callback('E_TOO_LATE');
}, 200);
};
'use strict';
process.env.AWS_PROFILE = 'studio-lambda-test';
const fs = require('fs');
const path = require('path');
const { assert, match, sinon } = require('@sinonjs/referee-sinon');
const logger = require('@studio/log');
const Lambda = require('..');
const log = logger('Lambda');
describe('lambda', () => {
let lambda;
before(() => {
process.chdir(`${__dirname}/fixture`);
});
afterEach(() => {
sinon.restore();
lambda.shutdown();
delete process.env.AWS_REGION;
delete process.env.STUDIO_AWS_ACCOUNT;
});
it('invokes lambda with event', (done) => {
lambda = Lambda.create();
lambda.invoke('hello', { name: 'JavaScript Studio' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello JavaScript Studio');
done();
});
});
it('invokes async lambda', (done) => {
lambda = Lambda.create();
lambda.invoke(
'hello-async',
{ name: 'JavaScript Studio' },
(err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello JavaScript Studio');
done();
}
);
});
it('resolves promise if no callback is given', async () => {
lambda = Lambda.create();
const promise = lambda.invoke('hello-async', {
name: 'JavaScript Studio'
});
await assert.resolves(promise, 'Hello JavaScript Studio');
});
function hasExpectedErrorMessage(err) {
return err.message.startsWith(
'Lambda invalid-response message parse error'
);
}
it('handles invalid response', (done) => {
lambda = Lambda.create();
lambda.invoke('invalid-response', {}, (err) => {
assert.equals(err.name, 'Error');
assert.isTrue(hasExpectedErrorMessage(err));
done();
});
});
it('rejects promise if no callback is given', async () => {
lambda = Lambda.create();
const promise = lambda.invoke('invalid-response');
await assert.rejects(promise, match(hasExpectedErrorMessage));
});
it('handles invalid JSON', (done) => {
lambda = Lambda.create();
lambda.invoke('invalid-json', {}, (err) => {
assert.equals(err.name, 'Error');
assert.equals(err.message, 'Ouch!');
assert.match(err.stack, 'Error: Ouch!\n at ');
done();
});
});
it('invokes lambda with default context', (done) => {
sinon.useFakeTimers(123);
lambda = Lambda.create();
lambda.invoke('context', {}, (err, value) => {
assert.isNull(err);
assert.json(value, {
functionName: 'context',
memoryLimitInMB: 128,
awsRequestId: '000000123_context_1',
invokedFunctionArn:
'arn:aws:lambda:us-east-1:000000000000:function:context'
});
done();
});
});
it('uses AWS_REGION environment variable in function ARN', (done) => {
process.env.AWS_REGION = 'eu-central-1';
lambda = Lambda.create();
lambda.invoke('context', {}, (err, value) => {
assert.isNull(err);
assert.matchJson(value, {
invokedFunctionArn:
'arn:aws:lambda:eu-central-1:000000000000:function:context'
});
done();
});
});
it('uses STUDIO_AWS_ACCOUNT environment variable in function ARN', (done) => {
process.env.STUDIO_AWS_ACCOUNT = '12345678';
lambda = Lambda.create();
lambda.invoke('context', {}, (err, value) => {
assert.isNull(err);
assert.matchJson(value, {
invokedFunctionArn: 'arn:aws:lambda:us-east-1:12345678:function:context'
});
done();
});
});
it('invokes lambda with given awsRequestId', (done) => {
lambda = Lambda.create();
lambda.invoke('context', {}, { awsRequestId: '666' }, (err, value) => {
assert.isNull(err);
assert.matchJson(value, {
awsRequestId: '666',
invokedFunctionArn:
'arn:aws:lambda:us-east-1:000000000000:function:context'
});
done();
});
});
it('implements getRemainingTimeInMillis() (default timeout)', (done) => {
lambda = Lambda.create();
lambda.invoke('getRemainingTimeInMillis', {}, (err, value) => {
assert.isNull(err);
assert.greater(value, 4950);
assert.less(value, 5000);
done();
});
});
it('implements getRemainingTimeInMillis() (configured timeout)', (done) => {
lambda = Lambda.create({
timeout: 1
});
lambda.invoke('getRemainingTimeInMillis', {}, (err, value) => {
assert.isNull(err);
assert.greater(value, 950);
assert.less(value, 1050);
done();
});
});
it('invokes lambda with environment variables from options', (done) => {
lambda = Lambda.create({
env: {
STUDIO_ENV_VAR: 'JavaScript Studio'
}
});
lambda.invoke('env', { env: 'STUDIO_ENV_VAR' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello JavaScript Studio');
done();
});
});
it('passes AWS_PROFILE to lambda', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_PROFILE' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello studio-lambda-test');
done();
});
});
it('defaults AWS_REGION to us-east-1', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_REGION' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello us-east-1');
done();
});
});
it('passes AWS_REGION to lambda', (done) => {
process.env.AWS_REGION = 'eu-central-1';
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_REGION' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello eu-central-1');
done();
});
});
it('defaults AWS_DEFAULT_REGION to us-east-1', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_DEFAULT_REGION' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello us-east-1');
done();
});
});
it('set AWS_DEFAULT_REGION to AWS_REGION', (done) => {
process.env.AWS_REGION = 'eu-central-1';
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_DEFAULT_REGION' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello eu-central-1');
done();
});
});
it('sets LAMBDA_TASK_ROOT to lambda source dir', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'LAMBDA_TASK_ROOT' }, (err, value) => {
assert.isNull(err);
assert.equals(
value,
`Hello ${path.resolve(__dirname, 'fixture/functions/env')}`
);
done();
});
});
it('sets AWS_EXECUTION_ENV to AWS_Lambda_nodejs6.10', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_EXECUTION_ENV' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello AWS_Lambda_nodejs6.10');
done();
});
});
it('sets AWS_LAMBDA_FUNCTION_NAME to lambda name', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'AWS_LAMBDA_FUNCTION_NAME' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello env');
done();
});
});
it('sets AWS_LAMBDA_FUNCTION_MEMORY_SIZE to lambda name', (done) => {
lambda = Lambda.create();
lambda.invoke(
'env',
{ env: 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' },
(err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello 128');
done();
}
);
});
it('sets AWS_LAMBDA_FUNCTION_VERSION to 1', (done) => {
lambda = Lambda.create();
lambda.invoke(
'env',
{ env: 'AWS_LAMBDA_FUNCTION_VERSION' },
(err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello 1');
done();
}
);
});
it('sets LANG to en_US.UTF-8', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'LANG' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello en_US.UTF-8');
done();
});
});
it('sets PATH to /usr/local/bin', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'PATH' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello /usr/local/bin');
done();
});
});
it('sets TZ to UTC', (done) => {
lambda = Lambda.create();
lambda.invoke('env', { env: 'TZ' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Hello UTC');
done();
});
});
it('invokes lambda with environment variables from file', (done) => {
process.env.STUDIO_ENVIRONMENT_VARIABLE = 'JavaScript Studio';
lambda = Lambda.create({
env: {
AWS_PROFILE: 'local'
}
});
lambda.invoke('env-file', {}, (err, value) => {
delete process.env.STUDIO_ENVIRONMENT_VARIABLE;
assert.isNull(err);
assert.equals(value, 'Hello JavaScript Studio');
done();
});
});
it('fails to launch lambda if environment variable is missing', (done) => {
lambda = Lambda.create({
env: {
AWS_PROFILE: 'local'
}
});
lambda.invoke('env-file', {}, (err) => {
assert.json(err, {
message: 'Failed to launch "env-file"'
});
done();
});
});
it('fails to launch async lambda if environment variable is missing', async () => {
lambda = Lambda.create({
env: {
AWS_PROFILE: 'local'
}
});
const promise = lambda.invoke('env-file-async');
await assert.rejects(
promise,
match.json({
message: 'Failed to launch "env-file-async"'
})
);
});
it('invokes lambda with DEBUG environment variables', (done) => {
process.env.DEBUG = 'ON';
lambda = Lambda.create({});
lambda.invoke('debug', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Debug ON');
done();
});
});
it('reuses lambda process', (done) => {
lambda = Lambda.create();
lambda.invoke('reuse', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 1');
// eslint-disable-next-line no-shadow
lambda.invoke('reuse', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 2');
done();
});
});
});
it('runs lambda concurrently', (done) => {
lambda = Lambda.create();
let invokes = 0;
lambda.invoke('concurrent', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 1');
if (++invokes === 2) {
done();
}
});
lambda.invoke('concurrent', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 1');
if (++invokes === 2) {
done();
}
});
});
it('shuts down lambda after max_idle', (done) => {
lambda = Lambda.create({
max_idle: 200
});
lambda.invoke('reuse', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 1');
setTimeout(() => {
// eslint-disable-next-line no-shadow
lambda.invoke('reuse', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Count 1');
done();
});
}, 201);
});
});
it('kills lambda after default timeout', (done) => {
sinon.stub(log, 'warn');
lambda = Lambda.create({
timeout: 0.1
});
lambda.invoke('timeout', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
assert.calledOnce(log.warn);
done();
});
});
it('kills lambda after configured timeout', (done) => {
sinon.stub(log, 'warn');
lambda = Lambda.create({
env: {
AWS_PROFILE: 'local'
}
});
lambda.invoke('timeout-file', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
assert.calledOnce(log.warn);
done();
});
});
it('kills lambda after configured timeout if larger than max_idle', (done) => {
sinon.stub(log, 'warn');
lambda = Lambda.create({
max_idle: 25,
env: {
AWS_PROFILE: 'local'
}
});
lambda.invoke('timeout-file', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
assert.calledOnce(log.warn);
done();
});
});
it('does not fail to talk to lambda after two where killed', (done) => {
sinon.stub(log, 'warn');
lambda = Lambda.create({
timeout: 0.1
});
lambda.invoke('timeout', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
});
lambda.invoke('timeout', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
// eslint-disable-next-line no-shadow
lambda.invoke('timeout', {}, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
done();
});
});
});
it('uses given base_dir', (done) => {
lambda = Lambda.create({
base_dir: `${__dirname}/fixture/cwd`
});
lambda.invoke('hello', { name: 'works' }, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Other dir works');
done();
});
});
it('handles log output', (done) => {
const lambda_log = logger('Lambda log');
const lambda_test_log = logger('Lambda log Test');
lambda = Lambda.create();
sinon.stub(lambda_log, 'ignore');
sinon.stub(lambda_test_log, 'ok');
sinon.stub(lambda_test_log, 'warn');
sinon.stub(lambda_test_log, 'error');
sinon.stub(lambda_test_log, 'wtf');
lambda.invoke('log', { is: 42 }, (err) => {
assert.isNull(err);
assert.calledOnceWith(lambda_log.ignore, 'Raw log line');
assert.calledOnceWith(lambda_test_log.ok, 'Check');
assert.calledOnceWith(lambda_test_log.warn, { event: { is: 42 } });
assert.calledOnceWith(
lambda_test_log.error,
{ event: { is: 42 } },
match({ stack: match.string })
);
assert.calledOnce(lambda_test_log.wtf);
assert.calledWithExactly(lambda_test_log.wtf);
done();
});
});
it('handles error log output with cause', (done) => {
const lambda_test_log = logger('Lambda log-error-cause Test');
lambda = Lambda.create();
sinon.stub(lambda_test_log, 'error');
lambda.invoke('log-error-cause', {}, (err) => {
assert.isNull(err);
assert.calledOnceWith(
lambda_test_log.error,
'Failure',
match({
stack: match('Fail'),
cause: match('Cause')
})
);
done();
});
});
it('handles invalid log output', (done) => {
const lambda_log = logger('Lambda invalid-log');
sinon.stub(lambda_log, 'ignore');
lambda = Lambda.create();
lambda.invoke('invalid-log', {}, (err) => {
assert.isNull(err);
assert.calledOnceWith(
lambda_log.ignore,
'{"some":"incomplete json output'
);
done();
});
});
it('handles throwing lambda', (done) => {
sinon.stub(process.stderr, 'write');
lambda = Lambda.create();
lambda.invoke('throw', {}, (err) => {
assert.json(err, { code: 'E_FAILED' });
done();
});
});
it('handles throwing async lambda', (done) => {
sinon.stub(process.stderr, 'write');
lambda = Lambda.create();
lambda.invoke('throw-async', {}, (err) => {
assert.json(err, { code: 'E_FAILED' });
done();
});
});
it('runs node process with default memory', (done) => {
lambda = Lambda.create({});
lambda.invoke('memory', {}, (err, value) => {
assert.isNull(err);
assert.equals(value, 'Allocated');
done();
});
});
context('with memory limit', () => {
after(() => {
// Remove the node memory report.
const dir = `${__dirname}/fixture/functions/memory`;
// eslint-disable-next-line n/no-sync
fs.readdirSync(dir)
.filter((file) => file.startsWith('report.'))
.forEach((file) => {
// eslint-disable-next-line n/no-sync
fs.unlinkSync(`${dir}/${file}`);
});
});
it('runs node process with memory from config file', (done) => {
sinon.stub(process.stderr, 'write');
lambda = Lambda.create({
env: {
AWS_PROFILE: 'local'
}
});
lambda.invoke('memory', {}, (err) => {
assert.json(err, { code: 'E_FAILED' });
done();
});
});
it('runs node process with memory from property', (done) => {
sinon.stub(process.stderr, 'write');
lambda = Lambda.create({
memory: 8
});
lambda.invoke('memory', {}, (err) => {
assert.json(err, { code: 'E_FAILED' });
done();
});
});
});
});
'use strict';
process.env.AWS_PROFILE = 'studio-lambda-test';
const { assert } = require('@sinonjs/referee-sinon');
const Lambda = require('..');
describe('shutdown', () => {
before(() => {
process.chdir(`${__dirname}/fixture`);
});
it('removes any running instance', (done) => {
const lambda = Lambda.create();
lambda.invoke('hello', { name: 'X' }, () => {
lambda.shutdown();
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 0);
assert.equals(stats.hello.requests, 1);
assert.equals(stats.hello.active, 0);
done();
});
});
it('fails pending instance', (done) => {
const lambda = Lambda.create();
lambda.invoke('hello', { name: 'X' }, (err) => {
assert.json(err, { code: 'E_FAILED' });
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 0);
assert.equals(stats.hello.requests, 1);
assert.equals(stats.hello.active, 0);
done();
});
lambda.shutdown();
});
it('waits for pending instance if graceful options is true', (done) => {
const lambda = Lambda.create();
lambda.invoke('hello', { name: 'X' }, (err) => {
assert.isNull(err);
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 0);
assert.equals(stats.hello.requests, 1);
assert.equals(stats.hello.active, 0);
done();
});
lambda.shutdown({ graceful: true });
});
it('waits for timeout instance if graceful options is true', (done) => {
const lambda = Lambda.create({
timeout: 0.1
});
lambda.invoke('timeout', { name: 'X' }, (err) => {
assert.json(err, { code: 'E_TIMEOUT' });
const stats = lambda.stats();
assert.isObject(stats.timeout);
assert.equals(stats.timeout.instances, 0);
assert.equals(stats.timeout.requests, 1);
assert.equals(stats.timeout.active, 0);
done();
});
lambda.shutdown({ graceful: true });
});
});
'use strict';
process.env.AWS_PROFILE = 'studio-lambda-test';
const { assert } = require('@sinonjs/referee-sinon');
const Lambda = require('..');
describe('stats', () => {
let lambda;
before(() => {
process.chdir(`${__dirname}/fixture`);
});
beforeEach(() => {
lambda = Lambda.create();
});
afterEach(() => {
lambda.shutdown();
});
it('counts first instance and request', (done) => {
lambda.invoke('hello', { name: 'Stats' }, () => {
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 1);
assert.equals(stats.hello.requests, 1);
assert.equals(stats.hello.active, 0);
done();
});
});
it('counts sequential requests', (done) => {
lambda.invoke('hello', { name: 'A' }, () => {
lambda.invoke('hello', { name: 'B' }, () => {
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 1);
assert.equals(stats.hello.requests, 2);
assert.equals(stats.hello.active, 0);
done();
});
});
});
it('counts concurrent requests', (done) => {
let count = 2;
const latch = () => {
if (--count === 0) {
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 2);
assert.equals(stats.hello.requests, 2);
assert.equals(stats.hello.active, 0);
done();
}
};
lambda.invoke('hello', { name: 'A' }, latch);
lambda.invoke('hello', { name: 'B' }, latch);
});
it('counts active instances', (done) => {
lambda.invoke('hello', {}, () => {});
lambda.invoke('hello', {}, () => {});
const stats = lambda.stats();
assert.isObject(stats.hello);
assert.equals(stats.hello.instances, 2);
assert.equals(stats.hello.requests, 2);
assert.equals(stats.hello.active, 2);
done();
});
});