serverless-spy 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.jsii CHANGED
@@ -3347,7 +3347,7 @@
3347
3347
  },
3348
3348
  "name": "serverless-spy",
3349
3349
  "readme": {
3350
- "markdown": "![ServerlessSpy](./logo/full_logo.svg)\n\nCDK-based library for writing elegant, fast-executing integration tests on AWS serverless architecture and an additional web console to monitor events in real time.\n\n# How it works\n\n**ServerlessSpy CDK construct creates infrastructure to intercept events in Lambda, SNS, SQS, EventBridge, DynamoDB, S3... and sends it to a testing library or your local web console via API Gateway WebSocket. The testing library subscribes to events so tests can be executed fast without checking/retrying if the process has finished. The testing library is integrated with Jest but can also be used with any other testing library. The web console can be used to see and inspect events in real time.**\n\n[![Concept](./doc/concept.svg)](https://serverlessspy.com/)\n\n**Your test for the example above would look something like this:**\n```typescript\n(\n await serverlessSpyListener.waitForEventBridgeMyEventBus<TestData>({\n condition: (d) => d.detail.id === id,\n })\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForSnsTopicMyTopic<TestData>({\n condition: (d) => d.message.id === id,\n })\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForSqsMyQueue<TestData>({\n condition: (d) => d.body.id === id,\n })\n).toMatchObject(...);\n\n(\n await (\n await serverlessSpyListener.waitForFunctionMyLambdaRequest<TestData>({\n condition: (d) => d.request.id === id,\n })\n ).followedByResponse();\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForDynamoDBMyTable<TestData>({\n condition: (d) => d.keys.pk === id,\n })\n).toMatchObject({\n eventName: 'INSERT',\n newImage: ...,\n});\n```\n\n**You can see all the events in the local web console:**\n![Web console](./doc/web_console.gif)\n\n# Key benefits\n - **Easy** to write tests that are strongly typed thanks to TypeScript ❤️.\n - **Tests are executed much FASTER** 🏎️💨 No need to write tests in a way to periodically check if the process has finished because all events are pushed to the testing library.\n - **Tests can run in parallel** if you use conditions and filter events specific to your test. This drastically reduces the execution time of the CI/CD process.\n - **Web console** enables you to see all events in real time. Debugging 🕵 has never been easier. Search is supported (with regular expression).\n\n# What ServerlessSpy is not\n - ServerlessSpy can not be used if your infrastructure is not created with CDK.\n - The solution is meant only for the development and (automatic) testing environment. You should **EXCLUDE** ServerlessSpy CDK construct in any other environment, especially a production or a high-load environment. ServerlessSpy is not meant for those; it just induces costs and could contribute to hitting AWS quotas (Lambda concurrent executions, ...).\n - Only Node.js stack is supported. There are no plans to support Python or any other. Use of TypeScript is deeply encouraged.\n - Web console only runs on your local computer. No cloud hosting of any kind (for now).\n\n# Documentation\n - [Quick Start](doc/quick_start.md)\n - [CDK Construct](doc/CDK_construct.md)\n - [Writing Tests](doc/writing_tests.md)\n - [Lambda](doc/Lambda.md)\n - [SQS](doc/SQS.md)\n - [SNS](doc/SNS.md)\n - [EventBridge](doc/EventBridge.md)\n - [DynamoDB](doc/DynamoDB.md)\n - [S3](doc/S3.md)\n - Kinesis (work in progress)\n - Step Functions (work in progress)\n - [Web Console](doc/web_console.md)\n - [Frequently Asked Questions (FAQ)](doc/FAQ.md)\n - [Sample Application](https://github.com/ServerlessLife/serverless-spy-example){:target=\"_blank\"}\n - [Roadmap](doc/roadmap.md)\n - [Code of Conduct](doc/CODE_OF_CONDUCT.md)\n - [Contributing Guide](doc/CONTRIBUTING.md)\n - [Contributors](doc/Contributors.md)\n - [License](./LICENSE.md)"
3350
+ "markdown": "![ServerlessSpy](./logo/full_logo.svg)\n\nCDK-based library for writing elegant, fast-executing integration tests on AWS serverless architecture and an additional web console to monitor events in real time.\n\n# How it works\n\n**ServerlessSpy CDK construct creates infrastructure to intercept events in Lambda, SNS, SQS, EventBridge, DynamoDB, S3... and sends it to a testing library or your local web console via API Gateway WebSocket. The testing library subscribes to events so tests can be executed fast without checking/retrying if the process has finished. The testing library is integrated with Jest but can also be used with any other testing library. The web console can be used to see and inspect events in real time.**\n\n[![Concept](./doc/concept.svg)](https://serverlessspy.com/)\n\n**Your test for the example above would look something like this:**\n```typescript\n(\n await serverlessSpyListener.waitForEventBridgeMyEventBus<TestData>({\n condition: (d) => d.detail.id === id,\n })\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForSnsTopicMyTopic<TestData>({\n condition: (d) => d.message.id === id,\n })\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForSqsMyQueue<TestData>({\n condition: (d) => d.body.id === id,\n })\n).toMatchObject(...);\n\n(\n await (\n await serverlessSpyListener.waitForFunctionMyLambdaRequest<TestData>({\n condition: (d) => d.request.id === id,\n })\n ).followedByResponse();\n).toMatchObject(...);\n\n(\n await serverlessSpyListener.waitForDynamoDBMyTable<TestData>({\n condition: (d) => d.keys.pk === id,\n })\n).toMatchObject({\n eventName: 'INSERT',\n newImage: ...,\n});\n```\n\n**You can see all the events in the local web console:**\n![Web console](./doc/web_console.gif)\n\n# Key benefits\n - **Easy** to write tests that are strongly typed thanks to TypeScript ❤️.\n - **Tests are executed much FASTER** 🏎️💨 No need to write tests in a way to periodically check if the process has finished because all events are pushed to the testing library.\n - **Tests can run in parallel** if you use conditions and filter events specific to your test. This drastically reduces the execution time of the CI/CD process.\n - **Web console** enables you to see all events in real time. Debugging 🕵 has never been easier. Search is supported (with regular expression).\n\n# What ServerlessSpy is not\n - ServerlessSpy can not be used if your infrastructure is not created with CDK.\n - The solution is meant only for the development and (automatic) testing environment. You should **EXCLUDE** ServerlessSpy CDK construct in any other environment, especially a production or a high-load environment. ServerlessSpy is not meant for those; it just induces costs and could contribute to hitting AWS quotas (Lambda concurrent executions, ...).\n - Only Node.js stack is supported. There are no plans to support Python or any other. Use of TypeScript is deeply encouraged.\n - Web console only runs on your local computer. No cloud hosting of any kind (for now).\n\n# Documentation\n - [Quick Start](doc/quick_start.md)\n - [CDK Construct](doc/CDK_construct.md)\n - [Writing Tests](doc/writing_tests.md)\n - [Lambda](doc/Lambda.md)\n - [SQS](doc/SQS.md)\n - [SNS](doc/SNS.md)\n - [EventBridge](doc/EventBridge.md)\n - [DynamoDB](doc/DynamoDB.md)\n - [S3](doc/S3.md)\n - Kinesis (work in progress)\n - Step Functions (work in progress)\n - [Web Console](doc/web_console.md)\n - [Frequently Asked Questions (FAQ)](doc/FAQ.md)\n - [Sample Application](https://github.com/ServerlessLife/serverless-spy-example){:target=\"_blank\"}\n - [Roadmap](doc/roadmap.md)\n - [Code of Conduct](doc/CODE_OF_CONDUCT.md)\n - [Contributing Guide](doc/CONTRIBUTING.md)\n - [License](./LICENSE.md)\n\n# Contributors\n - [Hugo Lewenhaupt](https://github.com/Lewenhaupt)\n - ⭐⭐⭐ place for your name ⭐⭐⭐"
3351
3351
  },
3352
3352
  "repository": {
3353
3353
  "type": "git",
@@ -3724,6 +3724,6 @@
3724
3724
  "symbolId": "src/ServerlessSpy:SpyFilter"
3725
3725
  }
3726
3726
  },
3727
- "version": "1.2.0",
3728
- "fingerprint": "vDOcVERbIQZ52jsl4PmUkXsW3onZ3JEZPcUjhtdYO6U="
3727
+ "version": "1.3.0",
3728
+ "fingerprint": "/tOVhx2e5Ip4D6CT7XIt1RHqG1j8UZLIPKMImPI2U/g="
3729
3729
  }
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ![ServerlessSpy](./logo/full_logo.svg)
2
2
 
3
- CDK-based library for writing elegant, fast-executing integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
3
+ CDK-based library for writing elegant, fast-executing integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
4
4
 
5
5
  # How it works
6
6
 
@@ -56,7 +56,7 @@ CDK-based library for writing elegant, fast-executing integration tests on AWS s
56
56
  - **Web console** enables you to see all events in real time. Debugging 🕵 has never been easier. Search is supported (with regular expression).
57
57
 
58
58
  # What ServerlessSpy is not
59
- - ServerlessSpy can not be used if your infrastructure is not created with CDK.
59
+ - ServerlessSpy can not be used if your infrastructure is not created with CDK.
60
60
  - The solution is meant only for the development and (automatic) testing environment. You should **EXCLUDE** ServerlessSpy CDK construct in any other environment, especially a production or a high-load environment. ServerlessSpy is not meant for those; it just induces costs and could contribute to hitting AWS quotas (Lambda concurrent executions, ...).
61
61
  - Only Node.js stack is supported. There are no plans to support Python or any other. Use of TypeScript is deeply encouraged.
62
62
  - Web console only runs on your local computer. No cloud hosting of any kind (for now).
@@ -64,20 +64,23 @@ CDK-based library for writing elegant, fast-executing integration tests on AWS s
64
64
  # Documentation
65
65
  - [Quick Start](doc/quick_start.md)
66
66
  - [CDK Construct](doc/CDK_construct.md)
67
- - [Writing Tests](doc/writing_tests.md)
68
- - [Lambda](doc/Lambda.md)
67
+ - [Writing Tests](doc/writing_tests.md)
68
+ - [Lambda](doc/Lambda.md)
69
69
  - [SQS](doc/SQS.md)
70
70
  - [SNS](doc/SNS.md)
71
71
  - [EventBridge](doc/EventBridge.md)
72
72
  - [DynamoDB](doc/DynamoDB.md)
73
- - [S3](doc/S3.md)
73
+ - [S3](doc/S3.md)
74
74
  - Kinesis (work in progress)
75
75
  - Step Functions (work in progress)
76
- - [Web Console](doc/web_console.md)
77
- - [Frequently Asked Questions (FAQ)](doc/FAQ.md)
76
+ - [Web Console](doc/web_console.md)
77
+ - [Frequently Asked Questions (FAQ)](doc/FAQ.md)
78
78
  - [Sample Application](https://github.com/ServerlessLife/serverless-spy-example){:target="_blank"}
79
- - [Roadmap](doc/roadmap.md)
80
- - [Code of Conduct](doc/CODE_OF_CONDUCT.md)
81
- - [Contributing Guide](doc/CONTRIBUTING.md)
82
- - [Contributors](doc/Contributors.md)
83
- - [License](./LICENSE.md)
79
+ - [Roadmap](doc/roadmap.md)
80
+ - [Code of Conduct](doc/CODE_OF_CONDUCT.md)
81
+ - [Contributing Guide](doc/CONTRIBUTING.md)
82
+ - [License](./LICENSE.md)
83
+
84
+ # Contributors
85
+ - [Hugo Lewenhaupt](https://github.com/Lewenhaupt)
86
+ - ⭐⭐⭐ place for your name ⭐⭐⭐
package/cli/cli.ts CHANGED
@@ -46,7 +46,7 @@ async function run() {
46
46
  progam.parse(process.argv);
47
47
 
48
48
  if (!options.ws && !options.cdkoutput) {
49
- throw new Error('--ws or --cdkstack parameter not specified');
49
+ throw new Error('--ws or --cdkoutput parameter not specified');
50
50
  }
51
51
 
52
52
  if (options.cdkoutput) {
@@ -195,6 +195,13 @@ function getNpmModuleInstalledPath(npm: string) {
195
195
  return folderAsPackage;
196
196
  }
197
197
 
198
+ // When boostrap ends up in importing projects root node_modules and not in serverless-spys node_modules
199
+ folderAsPackage = path.join(__dirname, '../../../../', 'node_modules', npm);
200
+
201
+ if (fs.existsSync(folderAsPackage)) {
202
+ return folderAsPackage;
203
+ }
204
+
198
205
  throw new Error(
199
206
  `Can not find package in folder ${folder} and ${folderAsPackage}`
200
207
  );
@@ -1 +1 @@
1
- v1.2.0
1
+ v1.3.0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * This code was copied from:
5
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client
6
+ *
7
+ * Defines custom error types throwable by the runtime.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const util = require('util');
13
+
14
+ function _isError(obj) {
15
+ return (
16
+ obj &&
17
+ obj.name &&
18
+ obj.message &&
19
+ obj.stack &&
20
+ typeof obj.name === 'string' &&
21
+ typeof obj.message === 'string' &&
22
+ typeof obj.stack === 'string'
23
+ );
24
+ }
25
+
26
+ function intoError(err) {
27
+ if (err instanceof Error) {
28
+ return err;
29
+ } else {
30
+ return new Error(err);
31
+ }
32
+ }
33
+
34
+ module.exports.intoError = intoError;
35
+
36
+ /**
37
+ * Attempt to convert an object into a response object.
38
+ * This method accounts for failures when serializing the error object.
39
+ */
40
+ function toRapidResponse(error) {
41
+ try {
42
+ if (util.types.isNativeError(error) || _isError(error)) {
43
+ return {
44
+ errorType: error.name,
45
+ errorMessage: error.message,
46
+ trace: error.stack.split('\n'),
47
+ };
48
+ } else {
49
+ return {
50
+ errorType: typeof error,
51
+ errorMessage: error.toString(),
52
+ trace: [],
53
+ };
54
+ }
55
+ } catch (_err) {
56
+ return {
57
+ errorType: 'handled',
58
+ errorMessage:
59
+ 'callback called with Error argument, but there was a problem while retrieving one or more of its message, name, and stack',
60
+ };
61
+ }
62
+ }
63
+
64
+ module.exports.toRapidResponse = toRapidResponse;
65
+
66
+ /**
67
+ * Format an error with the expected properties.
68
+ * For compatability, the error string always starts with a tab.
69
+ */
70
+ module.exports.toFormatted = (error) => {
71
+ try {
72
+ return (
73
+ '\t' + JSON.stringify(error, (_k, v) => _withEnumerableProperties(v))
74
+ );
75
+ } catch (err) {
76
+ return '\t' + JSON.stringify(toRapidResponse(error));
77
+ }
78
+ };
79
+
80
+ /**
81
+ * Error name, message, code, and stack are all members of the superclass, which
82
+ * means they aren't enumerable and don't normally show up in JSON.stringify.
83
+ * This method ensures those interesting properties are available along with any
84
+ * user-provided enumerable properties.
85
+ */
86
+ function _withEnumerableProperties(error) {
87
+ if (error instanceof Error) {
88
+ let ret = Object.assign(
89
+ {
90
+ errorType: error.name,
91
+ errorMessage: error.message,
92
+ code: error.code,
93
+ },
94
+ error
95
+ );
96
+ if (typeof error.stack == 'string') {
97
+ ret.stack = error.stack.split('\n');
98
+ }
99
+ return ret;
100
+ } else {
101
+ return error;
102
+ }
103
+ }
104
+
105
+ const errorClasses = [
106
+ class ImportModuleError extends Error {},
107
+ class HandlerNotFound extends Error {},
108
+ class MalformedHandlerName extends Error {},
109
+ class UserCodeSyntaxError extends Error {},
110
+ class MalformedStreamingHandler extends Error {},
111
+ class InvalidStreamingOperation extends Error {},
112
+ class UnhandledPromiseRejection extends Error {
113
+ constructor(reason, promise) {
114
+ super(reason);
115
+ this.reason = reason;
116
+ this.promise = promise;
117
+ }
118
+ },
119
+ ];
120
+
121
+ errorClasses.forEach((e) => {
122
+ module.exports[e.name] = e;
123
+ e.prototype.name = `Runtime.${e.name}`;
124
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * This code was copied from:
5
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client
6
+ *
7
+ * HttpResponseStream is NOT used by the runtime.
8
+ * It is only exposed in the `awslambda` variable for customers to use.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const METADATA_PRELUDE_CONTENT_TYPE =
14
+ 'application/vnd.awslambda.http-integration-response';
15
+ const DELIMITER_LEN = 8;
16
+
17
+ // Implements the application/vnd.awslambda.http-integration-response content type.
18
+ class HttpResponseStream {
19
+ static from(underlyingStream, prelude) {
20
+ underlyingStream.setContentType(METADATA_PRELUDE_CONTENT_TYPE);
21
+
22
+ // JSON.stringify is required. NULL byte is not allowed in metadataPrelude.
23
+ const metadataPrelude = JSON.stringify(prelude);
24
+
25
+ underlyingStream._onBeforeFirstWrite = (write) => {
26
+ write(metadataPrelude);
27
+
28
+ // Write 8 null bytes after the JSON prelude.
29
+ write(new Uint8Array(DELIMITER_LEN));
30
+ };
31
+
32
+ return underlyingStream;
33
+ }
34
+ }
35
+
36
+ module.exports.HttpResponseStream = HttpResponseStream;
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * This code was copied from:
5
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client
6
+ *
7
+ * This module defines the functions for loading the user's code as specified
8
+ * in a handler string.
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+ const {
16
+ HandlerNotFound,
17
+ MalformedHandlerName,
18
+ ImportModuleError,
19
+ UserCodeSyntaxError,
20
+ } = require('./Errors.js');
21
+ const { verbose } = require('./VerboseLog.js').logger('LOADER');
22
+ const { HttpResponseStream } = require('./HttpResponseStream');
23
+
24
+ const FUNCTION_EXPR = /^([^.]*)\.(.*)$/;
25
+ const RELATIVE_PATH_SUBSTRING = '..';
26
+ const HANDLER_STREAMING = Symbol.for('aws.lambda.runtime.handler.streaming');
27
+ const HANDLER_HIGHWATERMARK = Symbol.for(
28
+ 'aws.lambda.runtime.handler.streaming.highWaterMark'
29
+ );
30
+ const STREAM_RESPONSE = 'response';
31
+
32
+ // `awslambda.streamifyResponse` function is provided by default.
33
+ const NoGlobalAwsLambda =
34
+ process.env['AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA'] === '1' ||
35
+ process.env['AWS_LAMBDA_NODEJS_NO_GLOBAL_AWSLAMBDA'] === 'true';
36
+
37
+ /**
38
+ * Break the full handler string into two pieces, the module root and the actual
39
+ * handler string.
40
+ * Given './somepath/something/module.nestedobj.handler' this returns
41
+ * ['./somepath/something', 'module.nestedobj.handler']
42
+ */
43
+ function _moduleRootAndHandler(fullHandlerString) {
44
+ let handlerString = path.basename(fullHandlerString);
45
+ let moduleRoot = fullHandlerString.substring(
46
+ 0,
47
+ fullHandlerString.indexOf(handlerString)
48
+ );
49
+ return [moduleRoot, handlerString];
50
+ }
51
+
52
+ /**
53
+ * Split the handler string into two pieces: the module name and the path to
54
+ * the handler function.
55
+ */
56
+ function _splitHandlerString(handler) {
57
+ let match = handler.match(FUNCTION_EXPR);
58
+ if (!match || match.length != 3) {
59
+ throw new MalformedHandlerName('Bad handler');
60
+ }
61
+ return [match[1], match[2]]; // [module, function-path]
62
+ }
63
+
64
+ /**
65
+ * Resolve the user's handler function from the module.
66
+ */
67
+ function _resolveHandler(object, nestedProperty) {
68
+ return nestedProperty.split('.').reduce((nested, key) => {
69
+ return nested && nested[key];
70
+ }, object);
71
+ }
72
+
73
+ function _tryRequireFile(file, extension) {
74
+ const path = file + (extension || '');
75
+ verbose('Try loading as commonjs:', path);
76
+ return fs.existsSync(path) ? require(path) : undefined;
77
+ }
78
+
79
+ async function _tryAwaitImport(file, extension) {
80
+ const path = file + (extension || '');
81
+ verbose('Try loading as esmodule:', path);
82
+
83
+ if (fs.existsSync(path)) {
84
+ return await import(path);
85
+ }
86
+
87
+ return undefined;
88
+ }
89
+
90
+ function _hasFolderPackageJsonTypeModule(folder) {
91
+ // Check if package.json exists, return true if type === "module" in package json.
92
+ // If there is no package.json, and there is a node_modules, return false.
93
+ // Check parent folder otherwise, if there is one.
94
+ if (folder.endsWith('/node_modules')) {
95
+ return false;
96
+ }
97
+
98
+ const pj = path.join(folder, '/package.json');
99
+ if (fs.existsSync(pj)) {
100
+ try {
101
+ const pkg = JSON.parse(fs.readFileSync(pj));
102
+ if (pkg) {
103
+ if (pkg.type === 'module') {
104
+ verbose('type: module detected in', pj);
105
+ return true;
106
+ } else {
107
+ verbose('type: module not detected in', pj);
108
+ return false;
109
+ }
110
+ }
111
+ } catch (e) {
112
+ console.warn(
113
+ `${pj} cannot be read, it will be ignored for ES module detection purposes.`,
114
+ e
115
+ );
116
+ return false;
117
+ }
118
+ }
119
+
120
+ if (folder === '/') {
121
+ // We have reached root without finding either a package.json or a node_modules.
122
+ return false;
123
+ }
124
+
125
+ return _hasFolderPackageJsonTypeModule(path.resolve(folder, '..'));
126
+ }
127
+
128
+ function _hasPackageJsonTypeModule(file) {
129
+ // File must have a .js extension
130
+ const jsPath = file + '.js';
131
+ return fs.existsSync(jsPath)
132
+ ? _hasFolderPackageJsonTypeModule(path.resolve(path.dirname(jsPath)))
133
+ : false;
134
+ }
135
+
136
+ /**
137
+ * Attempt to load the user's module.
138
+ * Attempts to directly resolve the module relative to the application root,
139
+ * then falls back to the more general require().
140
+ */
141
+ async function _tryRequire(appRoot, moduleRoot, module) {
142
+ verbose(
143
+ 'Try loading as commonjs: ',
144
+ module,
145
+ ' with paths: ,',
146
+ appRoot,
147
+ moduleRoot
148
+ );
149
+
150
+ const lambdaStylePath = path.resolve(appRoot, moduleRoot, module);
151
+
152
+ // Extensionless files are loaded via require.
153
+ const extensionless = _tryRequireFile(lambdaStylePath);
154
+ if (extensionless) {
155
+ return extensionless;
156
+ }
157
+
158
+ // If package.json type != module, .js files are loaded via require.
159
+ const pjHasModule = _hasPackageJsonTypeModule(lambdaStylePath);
160
+ if (!pjHasModule) {
161
+ const loaded = _tryRequireFile(lambdaStylePath, '.js');
162
+ if (loaded) {
163
+ return loaded;
164
+ }
165
+ }
166
+
167
+ // If still not loaded, try .js, .mjs, and .cjs in that order.
168
+ // Files ending with .js are loaded as ES modules when the nearest parent package.json
169
+ // file contains a top-level field "type" with a value of "module".
170
+ // https://nodejs.org/api/packages.html#packages_type
171
+ const loaded =
172
+ (pjHasModule && (await _tryAwaitImport(lambdaStylePath, '.js'))) ||
173
+ (await _tryAwaitImport(lambdaStylePath, '.mjs')) ||
174
+ _tryRequireFile(lambdaStylePath, '.cjs');
175
+ if (loaded) {
176
+ return loaded;
177
+ }
178
+
179
+ verbose(
180
+ 'Try loading as commonjs: ',
181
+ module,
182
+ ' with path(s): ',
183
+ appRoot,
184
+ moduleRoot
185
+ );
186
+
187
+ // Why not just require(module)?
188
+ // Because require() is relative to __dirname, not process.cwd(). And the
189
+ // runtime implementation is not located in /var/task
190
+ // This won't work (yet) for esModules as import.meta.resolve is still experimental
191
+ // See: https://nodejs.org/api/esm.html#esm_import_meta_resolve_specifier_parent
192
+ const nodeStylePath = require.resolve(module, {
193
+ paths: [appRoot, moduleRoot],
194
+ });
195
+
196
+ return require(nodeStylePath);
197
+ }
198
+
199
+ /**
200
+ * Load the user's application or throw a descriptive error.
201
+ * @throws Runtime errors in two cases
202
+ * 1 - UserCodeSyntaxError if there's a syntax error while loading the module
203
+ * 2 - ImportModuleError if the module cannot be found
204
+ */
205
+ async function _loadUserApp(appRoot, moduleRoot, module) {
206
+ if (!NoGlobalAwsLambda) {
207
+ globalThis.awslambda = {
208
+ streamifyResponse: (handler, options) => {
209
+ handler[HANDLER_STREAMING] = STREAM_RESPONSE;
210
+ if (typeof options?.highWaterMark === 'number') {
211
+ handler[HANDLER_HIGHWATERMARK] = parseInt(options.highWaterMark);
212
+ }
213
+ return handler;
214
+ },
215
+ HttpResponseStream: HttpResponseStream,
216
+ };
217
+ }
218
+
219
+ try {
220
+ return await _tryRequire(appRoot, moduleRoot, module);
221
+ } catch (e) {
222
+ if (e instanceof SyntaxError) {
223
+ throw new UserCodeSyntaxError(e);
224
+ } else if (e.code !== undefined && e.code === 'MODULE_NOT_FOUND') {
225
+ verbose('globalPaths', JSON.stringify(require('module').globalPaths));
226
+ throw new ImportModuleError(e);
227
+ } else {
228
+ throw e;
229
+ }
230
+ }
231
+ }
232
+
233
+ function _throwIfInvalidHandler(fullHandlerString) {
234
+ if (fullHandlerString.includes(RELATIVE_PATH_SUBSTRING)) {
235
+ throw new MalformedHandlerName(
236
+ `'${fullHandlerString}' is not a valid handler name. Use absolute paths when specifying root directories in handler names.`
237
+ );
238
+ }
239
+ }
240
+
241
+ function _isHandlerStreaming(handler) {
242
+ if (
243
+ typeof handler[HANDLER_STREAMING] === 'undefined' ||
244
+ handler[HANDLER_STREAMING] === null ||
245
+ handler[HANDLER_STREAMING] === false
246
+ ) {
247
+ return false;
248
+ }
249
+
250
+ if (handler[HANDLER_STREAMING] === STREAM_RESPONSE) {
251
+ return STREAM_RESPONSE;
252
+ } else {
253
+ throw new MalformedStreamingHandler(
254
+ 'Only response streaming is supported.'
255
+ );
256
+ }
257
+ }
258
+
259
+ function _highWaterMark(handler) {
260
+ if (
261
+ typeof handler[HANDLER_HIGHWATERMARK] === 'undefined' ||
262
+ handler[HANDLER_HIGHWATERMARK] === null ||
263
+ handler[HANDLER_HIGHWATERMARK] === false
264
+ ) {
265
+ return undefined;
266
+ }
267
+
268
+ const hwm = parseInt(handler[HANDLER_HIGHWATERMARK]);
269
+ return isNaN(hwm) ? undefined : hwm;
270
+ }
271
+
272
+ /**
273
+ * Load the user's function with the approot and the handler string.
274
+ * @param appRoot {string}
275
+ * The path to the application root.
276
+ * @param handlerString {string}
277
+ * The user-provided handler function in the form 'module.function'.
278
+ * @return userFuction {function}
279
+ * The user's handler function. This function will be passed the event body,
280
+ * the context object, and the callback function.
281
+ * @throws In five cases:-
282
+ * 1 - if the handler string is incorrectly formatted an error is thrown
283
+ * 2 - if the module referenced by the handler cannot be loaded
284
+ * 3 - if the function in the handler does not exist in the module
285
+ * 4 - if a property with the same name, but isn't a function, exists on the
286
+ * module
287
+ * 5 - the handler includes illegal character sequences (like relative paths
288
+ * for traversing up the filesystem '..')
289
+ * Errors for scenarios known by the runtime, will be wrapped by Runtime.* errors.
290
+ */
291
+ module.exports.load = async function (appRoot, fullHandlerString) {
292
+ _throwIfInvalidHandler(fullHandlerString);
293
+
294
+ let [moduleRoot, moduleAndHandler] = _moduleRootAndHandler(fullHandlerString);
295
+ let [module, handlerPath] = _splitHandlerString(moduleAndHandler);
296
+
297
+ let userApp = await _loadUserApp(appRoot, moduleRoot, module);
298
+ let handlerFunc = _resolveHandler(userApp, handlerPath);
299
+
300
+ if (!handlerFunc) {
301
+ throw new HandlerNotFound(
302
+ `${fullHandlerString} is undefined or not exported`
303
+ );
304
+ }
305
+
306
+ if (typeof handlerFunc !== 'function') {
307
+ throw new HandlerNotFound(`${fullHandlerString} is not a function`);
308
+ }
309
+
310
+ return handlerFunc;
311
+ };
312
+
313
+ module.exports.isHandlerFunction = function (value) {
314
+ return typeof value === 'function';
315
+ };
316
+
317
+ module.exports.getHandlerMetadata = function (handlerFunc) {
318
+ return {
319
+ streaming: _isHandlerStreaming(handlerFunc),
320
+ highWaterMark: _highWaterMark(handlerFunc),
321
+ };
322
+ };
323
+
324
+ module.exports.STREAM_RESPONSE = STREAM_RESPONSE;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * This code was copied from:
5
+ * https://github.com/aws/aws-lambda-nodejs-runtime-interface-client
6
+ *
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const EnvVarName = 'AWS_LAMBDA_RUNTIME_VERBOSE';
12
+ const Tag = 'RUNTIME';
13
+ const Verbosity = (() => {
14
+ if (!process.env[EnvVarName]) {
15
+ return 0;
16
+ }
17
+
18
+ try {
19
+ const verbosity = parseInt(process.env[EnvVarName]);
20
+ return verbosity < 0 ? 0 : verbosity > 3 ? 3 : verbosity;
21
+ } catch (_) {
22
+ return 0;
23
+ }
24
+ })();
25
+
26
+ exports.logger = function (category) {
27
+ return {
28
+ verbose: function () {
29
+ if (Verbosity >= 1) {
30
+ const args = [...arguments].map((arg) =>
31
+ typeof arg === 'function' ? arg() : arg
32
+ );
33
+ console.log.apply(null, [Tag, category, ...args]);
34
+ }
35
+ },
36
+ vverbose: function () {
37
+ if (Verbosity >= 2) {
38
+ const args = [...arguments].map((arg) =>
39
+ typeof arg === 'function' ? arg() : arg
40
+ );
41
+ console.log.apply(null, [Tag, category, ...args]);
42
+ }
43
+ },
44
+ vvverbose: function () {
45
+ if (Verbosity >= 3) {
46
+ const args = [...arguments].map((arg) =>
47
+ typeof arg === 'function' ? arg() : arg
48
+ );
49
+ console.log.apply(null, [Tag, category, ...args]);
50
+ }
51
+ },
52
+ };
53
+ };