serverless-offline 8.8.0 → 9.1.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/README.md +12 -11
- package/package.json +33 -58
- package/src/ServerlessOffline.js +409 -0
- package/src/config/commandOptions.js +159 -0
- package/src/config/constants.js +22 -0
- package/{dist → src}/config/defaultOptions.js +9 -17
- package/src/config/index.js +4 -0
- package/src/config/supportedRuntimes.js +47 -0
- package/src/events/authCanExecuteResource.js +35 -0
- package/src/events/authFunctionNameExtractor.js +75 -0
- package/src/events/authMatchPolicyResource.js +71 -0
- package/src/events/authValidateContext.js +51 -0
- package/src/events/http/Endpoint.js +135 -0
- package/src/events/http/Http.js +50 -0
- package/src/events/http/HttpEventDefinition.js +20 -0
- package/src/events/http/HttpServer.js +1242 -0
- package/src/events/http/OfflineEndpoint.js +33 -0
- package/src/events/http/authJWTSettingsExtractor.js +70 -0
- package/src/events/http/createAuthScheme.js +176 -0
- package/src/events/http/createJWTAuthScheme.js +106 -0
- package/src/events/http/index.js +1 -0
- package/src/events/http/javaHelpers.js +102 -0
- package/src/events/http/lambda-events/LambdaIntegrationEvent.js +57 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +233 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +190 -0
- package/src/events/http/lambda-events/VelocityContext.js +147 -0
- package/src/events/http/lambda-events/index.js +4 -0
- package/src/events/http/lambda-events/renderVelocityTemplateObject.js +93 -0
- package/{dist → src}/events/http/parseResources.js +73 -78
- package/src/events/http/payloadSchemaValidator.js +13 -0
- package/{dist → src}/events/http/templates/offline-default.req.vm +0 -0
- package/{dist → src}/events/http/templates/offline-default.res.vm +0 -0
- package/src/events/schedule/Schedule.js +131 -0
- package/src/events/schedule/ScheduleEvent.js +18 -0
- package/src/events/schedule/ScheduleEventDefinition.js +21 -0
- package/src/events/schedule/index.js +1 -0
- package/src/events/websocket/HttpServer.js +69 -0
- package/src/events/websocket/WebSocket.js +52 -0
- package/src/events/websocket/WebSocketClients.js +462 -0
- package/src/events/websocket/WebSocketEventDefinition.js +18 -0
- package/src/events/websocket/WebSocketServer.js +73 -0
- package/src/events/websocket/http-routes/_catchAll/catchAllRoute.js +16 -0
- package/src/events/websocket/http-routes/_catchAll/index.js +1 -0
- package/src/events/websocket/http-routes/connections/ConnectionsController.js +28 -0
- package/src/events/websocket/http-routes/connections/connectionsRoutes.js +70 -0
- package/src/events/websocket/http-routes/connections/index.js +1 -0
- package/src/events/websocket/http-routes/index.js +2 -0
- package/src/events/websocket/index.js +1 -0
- package/src/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +65 -0
- package/src/events/websocket/lambda-events/WebSocketConnectEvent.js +68 -0
- package/src/events/websocket/lambda-events/WebSocketDisconnectEvent.js +31 -0
- package/src/events/websocket/lambda-events/WebSocketEvent.js +29 -0
- package/src/events/websocket/lambda-events/WebSocketRequestContext.js +67 -0
- package/src/events/websocket/lambda-events/index.js +4 -0
- package/src/index.js +12 -0
- package/src/lambda/HttpServer.js +108 -0
- package/src/lambda/Lambda.js +68 -0
- package/src/lambda/LambdaContext.js +33 -0
- package/src/lambda/LambdaFunction.js +309 -0
- package/src/lambda/LambdaFunctionPool.js +109 -0
- package/src/lambda/__tests__/LambdaContext.test.js +30 -0
- package/src/lambda/__tests__/LambdaFunction.test.js +196 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js +47 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js +46 -0
- package/src/lambda/__tests__/fixtures/Lambda/package.json +3 -0
- package/src/lambda/__tests__/fixtures/lambdaFunction.fixture.js +145 -0
- package/src/lambda/__tests__/fixtures/package.json +3 -0
- package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +42 -0
- package/src/lambda/handler-runner/HandlerRunner.js +136 -0
- package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +72 -0
- package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +42 -0
- package/src/lambda/handler-runner/child-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/docker-runner/DockerContainer.js +417 -0
- package/src/lambda/handler-runner/docker-runner/DockerImage.js +35 -0
- package/src/lambda/handler-runner/docker-runner/DockerRunner.js +63 -0
- package/src/lambda/handler-runner/docker-runner/index.js +1 -0
- package/src/lambda/handler-runner/go-runner/GoRunner.js +167 -0
- package/src/lambda/handler-runner/go-runner/index.js +1 -0
- package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +125 -0
- package/src/lambda/handler-runner/in-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/index.js +1 -0
- package/src/lambda/handler-runner/java-runner/JavaRunner.js +114 -0
- package/src/lambda/handler-runner/java-runner/index.js +1 -0
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +138 -0
- package/src/lambda/handler-runner/python-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/python-runner/invoke.py +0 -0
- package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +107 -0
- package/src/lambda/handler-runner/ruby-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/ruby-runner/invoke.rb +0 -0
- package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +70 -0
- package/src/lambda/handler-runner/worker-thread-runner/index.js +1 -0
- package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +29 -0
- package/src/lambda/index.js +1 -0
- package/src/lambda/routes/index.js +2 -0
- package/src/lambda/routes/invocations/InvocationsController.js +102 -0
- package/src/lambda/routes/invocations/index.js +1 -0
- package/src/lambda/routes/invocations/invocationsRoute.js +77 -0
- package/src/lambda/routes/invoke-async/InvokeAsyncController.js +20 -0
- package/src/lambda/routes/invoke-async/index.js +1 -0
- package/src/lambda/routes/invoke-async/invokeAsyncRoute.js +33 -0
- package/src/utils/__tests__/createUniqueId.test.js +18 -0
- package/src/utils/__tests__/formatToClfTime.test.js +14 -0
- package/src/utils/__tests__/generateHapiPath.test.js +46 -0
- package/src/utils/__tests__/lowerCaseKeys.test.js +30 -0
- package/src/utils/__tests__/parseHeaders.test.js +13 -0
- package/src/utils/__tests__/parseMultiValueHeaders.test.js +24 -0
- package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +159 -0
- package/src/utils/__tests__/parseQueryStringParameters.test.js +15 -0
- package/src/utils/__tests__/splitHandlerPathAndName.test.js +54 -0
- package/src/utils/__tests__/unflatten.test.js +32 -0
- package/src/utils/checkDockerDaemon.js +19 -0
- package/src/utils/checkGoVersion.js +16 -0
- package/src/utils/createApiKey.js +5 -0
- package/src/utils/createUniqueId.js +5 -0
- package/src/utils/detectExecutable.js +11 -0
- package/{dist → src}/utils/formatToClfTime.js +6 -14
- package/src/utils/generateHapiPath.js +26 -0
- package/src/utils/getHttpApiCorsConfig.js +28 -0
- package/src/utils/index.js +42 -0
- package/src/utils/jsonPath.js +13 -0
- package/src/utils/logRoutes.js +64 -0
- package/src/utils/lowerCaseKeys.js +6 -0
- package/src/utils/parseHeaders.js +14 -0
- package/src/utils/parseMultiValueHeaders.js +27 -0
- package/src/utils/parseMultiValueQueryStringParameters.js +31 -0
- package/src/utils/parseQueryStringParameters.js +15 -0
- package/src/utils/splitHandlerPathAndName.js +31 -0
- package/src/utils/unflatten.js +11 -0
- package/dist/ServerlessOffline.js +0 -514
- package/dist/config/commandOptions.js +0 -149
- package/dist/config/constants.js +0 -30
- package/dist/config/index.js +0 -55
- package/dist/config/supportedRuntimes.js +0 -40
- package/dist/debugLog.js +0 -12
- package/dist/events/authCanExecuteResource.js +0 -35
- package/dist/events/authFunctionNameExtractor.js +0 -87
- package/dist/events/authMatchPolicyResource.js +0 -62
- package/dist/events/authValidateContext.js +0 -53
- package/dist/events/http/Endpoint.js +0 -173
- package/dist/events/http/Http.js +0 -77
- package/dist/events/http/HttpEventDefinition.js +0 -36
- package/dist/events/http/HttpServer.js +0 -1370
- package/dist/events/http/OfflineEndpoint.js +0 -38
- package/dist/events/http/authJWTSettingsExtractor.js +0 -76
- package/dist/events/http/createAuthScheme.js +0 -184
- package/dist/events/http/createJWTAuthScheme.js +0 -159
- package/dist/events/http/index.js +0 -15
- package/dist/events/http/javaHelpers.js +0 -99
- package/dist/events/http/lambda-events/LambdaIntegrationEvent.js +0 -87
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -246
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -225
- package/dist/events/http/lambda-events/VelocityContext.js +0 -170
- package/dist/events/http/lambda-events/index.js +0 -39
- package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -111
- package/dist/events/http/payloadSchemaValidator.js +0 -13
- package/dist/events/schedule/Schedule.js +0 -183
- package/dist/events/schedule/ScheduleEvent.js +0 -27
- package/dist/events/schedule/ScheduleEventDefinition.js +0 -36
- package/dist/events/schedule/index.js +0 -15
- package/dist/events/websocket/HttpServer.js +0 -114
- package/dist/events/websocket/WebSocket.js +0 -78
- package/dist/events/websocket/WebSocketClients.js +0 -577
- package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
- package/dist/events/websocket/WebSocketServer.js +0 -139
- package/dist/events/websocket/http-routes/_catchAll/catchAllRoute.js +0 -33
- package/dist/events/websocket/http-routes/_catchAll/index.js +0 -15
- package/dist/events/websocket/http-routes/connections/ConnectionsController.js +0 -45
- package/dist/events/websocket/http-routes/connections/connectionsRoutes.js +0 -95
- package/dist/events/websocket/http-routes/connections/index.js +0 -15
- package/dist/events/websocket/http-routes/index.js +0 -23
- package/dist/events/websocket/index.js +0 -15
- package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +0 -99
- package/dist/events/websocket/lambda-events/WebSocketConnectEvent.js +0 -101
- package/dist/events/websocket/lambda-events/WebSocketDisconnectEvent.js +0 -47
- package/dist/events/websocket/lambda-events/WebSocketEvent.js +0 -54
- package/dist/events/websocket/lambda-events/WebSocketRequestContext.js +0 -98
- package/dist/events/websocket/lambda-events/index.js +0 -39
- package/dist/index.js +0 -15
- package/dist/lambda/HttpServer.js +0 -124
- package/dist/lambda/Lambda.js +0 -117
- package/dist/lambda/LambdaContext.js +0 -53
- package/dist/lambda/LambdaFunction.js +0 -390
- package/dist/lambda/LambdaFunctionPool.js +0 -127
- package/dist/lambda/handler-runner/HandlerRunner.js +0 -195
- package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -124
- package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -49
- package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -515
- package/dist/lambda/handler-runner/docker-runner/DockerImage.js +0 -67
- package/dist/lambda/handler-runner/docker-runner/DockerRunner.js +0 -74
- package/dist/lambda/handler-runner/docker-runner/index.js +0 -15
- package/dist/lambda/handler-runner/go-runner/GoRunner.js +0 -230
- package/dist/lambda/handler-runner/go-runner/index.js +0 -15
- package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -228
- package/dist/lambda/handler-runner/in-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/index.js +0 -15
- package/dist/lambda/handler-runner/java-runner/JavaRunner.js +0 -153
- package/dist/lambda/handler-runner/java-runner/index.js +0 -15
- package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -185
- package/dist/lambda/handler-runner/python-runner/index.js +0 -15
- package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -147
- package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -92
- package/dist/lambda/handler-runner/worker-thread-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +0 -31
- package/dist/lambda/index.js +0 -15
- package/dist/lambda/routes/index.js +0 -23
- package/dist/lambda/routes/invocations/InvocationsController.js +0 -142
- package/dist/lambda/routes/invocations/index.js +0 -15
- package/dist/lambda/routes/invocations/invocationsRoute.js +0 -90
- package/dist/lambda/routes/invoke-async/InvokeAsyncController.js +0 -38
- package/dist/lambda/routes/invoke-async/index.js +0 -15
- package/dist/lambda/routes/invoke-async/invokeAsyncRoute.js +0 -43
- package/dist/main.js +0 -11
- package/dist/serverlessLog.js +0 -91
- package/dist/utils/checkDockerDaemon.js +0 -27
- package/dist/utils/checkGoVersion.js +0 -27
- package/dist/utils/createApiKey.js +0 -12
- package/dist/utils/createUniqueId.js +0 -14
- package/dist/utils/detectExecutable.js +0 -21
- package/dist/utils/generateHapiPath.js +0 -28
- package/dist/utils/getHttpApiCorsConfig.js +0 -40
- package/dist/utils/index.js +0 -165
- package/dist/utils/jsonPath.js +0 -21
- package/dist/utils/lowerCaseKeys.js +0 -14
- package/dist/utils/parseHeaders.js +0 -23
- package/dist/utils/parseMultiValueHeaders.js +0 -36
- package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
- package/dist/utils/parseQueryStringParameters.js +0 -26
- package/dist/utils/resolveJoins.js +0 -36
- package/dist/utils/satisfiesVersionRange.js +0 -20
- package/dist/utils/splitHandlerPathAndName.js +0 -37
- package/dist/utils/unflatten.js +0 -18
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { performance } from 'node:perf_hooks'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { log } from '@serverless/utils/log.js'
|
|
5
|
+
|
|
6
|
+
const { assign } = Object
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url)
|
|
9
|
+
|
|
10
|
+
export default class InProcessRunner {
|
|
11
|
+
#env = null
|
|
12
|
+
|
|
13
|
+
#functionKey = null
|
|
14
|
+
|
|
15
|
+
#handlerName = null
|
|
16
|
+
|
|
17
|
+
#handlerPath = null
|
|
18
|
+
|
|
19
|
+
#timeout = null
|
|
20
|
+
|
|
21
|
+
constructor(functionKey, handlerPath, handlerName, env, timeout) {
|
|
22
|
+
this.#env = env
|
|
23
|
+
this.#functionKey = functionKey
|
|
24
|
+
this.#handlerName = handlerName
|
|
25
|
+
this.#handlerPath = handlerPath
|
|
26
|
+
this.#timeout = timeout
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// no-op
|
|
30
|
+
// () => void
|
|
31
|
+
cleanup() {}
|
|
32
|
+
|
|
33
|
+
async run(event, context) {
|
|
34
|
+
// check if the handler module path exists
|
|
35
|
+
if (!require.resolve(this.#handlerPath)) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Could not find handler module '${this.#handlerPath}' for function '${
|
|
38
|
+
this.#functionKey
|
|
39
|
+
}'.`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// process.env should be available in the handler module scope as well as in the handler function scope
|
|
44
|
+
// NOTE: Don't use Object spread (...) here!
|
|
45
|
+
// otherwise the values of the attached props are not coerced to a string
|
|
46
|
+
// e.g. process.env.foo = 1 should be coerced to '1' (string)
|
|
47
|
+
assign(process.env, this.#env)
|
|
48
|
+
|
|
49
|
+
let handler
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// const { [this.#handlerName]: handler } = await import(this.#handlerPath)
|
|
53
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
54
|
+
;({ [this.#handlerName]: handler } = require(this.#handlerPath))
|
|
55
|
+
} catch (err) {
|
|
56
|
+
log.error(err)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof handler !== 'function') {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`offline: handler '${this.#handlerName}' in ${
|
|
62
|
+
this.#handlerPath
|
|
63
|
+
} is not a function`,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let callback
|
|
68
|
+
|
|
69
|
+
const callbackCalled = new Promise((res, rej) => {
|
|
70
|
+
callback = (err, data) => {
|
|
71
|
+
if (err === 'Unauthorized') {
|
|
72
|
+
res('Unauthorized')
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if (err) {
|
|
76
|
+
rej(err)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
res(data)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const executionTimeout = performance.now() + this.#timeout
|
|
84
|
+
|
|
85
|
+
// attach doc-deprecated functions
|
|
86
|
+
// create new immutable object
|
|
87
|
+
const lambdaContext = {
|
|
88
|
+
...context,
|
|
89
|
+
done: (err, data) => callback(err, data),
|
|
90
|
+
fail: (err) => callback(err),
|
|
91
|
+
getRemainingTimeInMillis: () => {
|
|
92
|
+
const timeLeft = executionTimeout - performance.now()
|
|
93
|
+
|
|
94
|
+
// just return 0 for now if we are beyond alotted time (timeout)
|
|
95
|
+
return timeLeft > 0 ? timeLeft : 0
|
|
96
|
+
},
|
|
97
|
+
succeed: (res) => callback(null, res),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let result
|
|
101
|
+
|
|
102
|
+
// execute (run) handler
|
|
103
|
+
try {
|
|
104
|
+
result = handler(event, lambdaContext, callback)
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(`Uncaught error in '${this.#functionKey}' handler.`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// // not a Promise, which is not supported by aws
|
|
110
|
+
// if (result == null || typeof result.then !== 'function') {
|
|
111
|
+
// throw new Error(`Synchronous function execution is not supported.`)
|
|
112
|
+
// }
|
|
113
|
+
|
|
114
|
+
const callbacks = [callbackCalled]
|
|
115
|
+
|
|
116
|
+
// Promise was returned
|
|
117
|
+
if (result != null && typeof result.then === 'function') {
|
|
118
|
+
callbacks.push(result)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const callbackResult = await Promise.race(callbacks)
|
|
122
|
+
|
|
123
|
+
return callbackResult
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './InProcessRunner.js'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './HandlerRunner.js'
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { EOL } from 'node:os'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { log } from '@serverless/utils/log.js'
|
|
4
|
+
import { invokeJavaLocal } from 'java-invoke-local'
|
|
5
|
+
|
|
6
|
+
const { parse, stringify } = JSON
|
|
7
|
+
const { has } = Reflect
|
|
8
|
+
|
|
9
|
+
export default class JavaRunner {
|
|
10
|
+
#deployPackage = null
|
|
11
|
+
|
|
12
|
+
#env = null
|
|
13
|
+
|
|
14
|
+
#functionName = null
|
|
15
|
+
|
|
16
|
+
#handler = null
|
|
17
|
+
|
|
18
|
+
constructor(funOptions, env) {
|
|
19
|
+
const { functionName, handler, servicePackage, functionPackage } =
|
|
20
|
+
funOptions
|
|
21
|
+
|
|
22
|
+
this.#deployPackage = functionPackage || servicePackage
|
|
23
|
+
this.#env = env
|
|
24
|
+
this.#functionName = functionName
|
|
25
|
+
this.#handler = handler
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// no-op
|
|
29
|
+
// () => void
|
|
30
|
+
cleanup() {}
|
|
31
|
+
|
|
32
|
+
#parsePayload(value) {
|
|
33
|
+
for (const item of value.split(EOL)) {
|
|
34
|
+
let json
|
|
35
|
+
|
|
36
|
+
// first check if it's JSON
|
|
37
|
+
try {
|
|
38
|
+
json = parse(item)
|
|
39
|
+
// nope, it's not JSON
|
|
40
|
+
} catch {
|
|
41
|
+
// no-op
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// now let's see if we have a property __offline_payload__
|
|
45
|
+
if (
|
|
46
|
+
json &&
|
|
47
|
+
typeof json === 'object' &&
|
|
48
|
+
has(json, '__offline_payload__')
|
|
49
|
+
) {
|
|
50
|
+
return json.__offline_payload__
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async run(event, context) {
|
|
58
|
+
const input = stringify({
|
|
59
|
+
context,
|
|
60
|
+
event,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const data = stringify({
|
|
64
|
+
artifact: this.#deployPackage,
|
|
65
|
+
data: input,
|
|
66
|
+
function: this.#functionName,
|
|
67
|
+
handler: this.#handler,
|
|
68
|
+
jsonOutput: true,
|
|
69
|
+
serverlessOffline: true,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const httpOptions = {
|
|
73
|
+
body: data,
|
|
74
|
+
method: 'POST',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const port = process.env.JAVA_OFFLINE_SERVER || 8080
|
|
78
|
+
|
|
79
|
+
let result
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Assume java-invoke-local server is running
|
|
83
|
+
|
|
84
|
+
const response = await fetch(
|
|
85
|
+
`http://localhost:${port}/invoke`,
|
|
86
|
+
httpOptions,
|
|
87
|
+
)
|
|
88
|
+
result = await response.text()
|
|
89
|
+
} catch {
|
|
90
|
+
log.notice(
|
|
91
|
+
'Local java server not running. For faster local invocations, run "java-invoke-local --server" in your project directory',
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
// Fallback invocation
|
|
95
|
+
const args = [
|
|
96
|
+
'-c',
|
|
97
|
+
this.#handler,
|
|
98
|
+
'-a',
|
|
99
|
+
this.#deployPackage,
|
|
100
|
+
'-f',
|
|
101
|
+
this.#functionName,
|
|
102
|
+
'-d',
|
|
103
|
+
input,
|
|
104
|
+
'--json-output',
|
|
105
|
+
'--serverless-offline',
|
|
106
|
+
]
|
|
107
|
+
result = invokeJavaLocal(args, this.#env)
|
|
108
|
+
|
|
109
|
+
log.notice(result)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.#parsePayload(result)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './JavaRunner.js'
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { EOL, platform } from 'node:os'
|
|
3
|
+
import { delimiter, dirname, join, relative, resolve } from 'node:path'
|
|
4
|
+
import process, { cwd } from 'node:process'
|
|
5
|
+
import readline from 'node:readline'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { log } from '@serverless/utils/log.js'
|
|
8
|
+
|
|
9
|
+
const { parse, stringify } = JSON
|
|
10
|
+
const { assign } = Object
|
|
11
|
+
const { has } = Reflect
|
|
12
|
+
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
14
|
+
|
|
15
|
+
export default class PythonRunner {
|
|
16
|
+
#env = null
|
|
17
|
+
|
|
18
|
+
#handlerName = null
|
|
19
|
+
|
|
20
|
+
#handlerPath = null
|
|
21
|
+
|
|
22
|
+
#runtime = null
|
|
23
|
+
|
|
24
|
+
constructor(funOptions, env) {
|
|
25
|
+
const { handlerName, handlerPath, runtime } = funOptions
|
|
26
|
+
|
|
27
|
+
this.#env = env
|
|
28
|
+
this.#handlerName = handlerName
|
|
29
|
+
this.#handlerPath = handlerPath
|
|
30
|
+
this.#runtime = platform() === 'win32' ? 'python.exe' : runtime
|
|
31
|
+
|
|
32
|
+
if (process.env.VIRTUAL_ENV) {
|
|
33
|
+
const runtimeDir = platform() === 'win32' ? 'Scripts' : 'bin'
|
|
34
|
+
|
|
35
|
+
process.env.PATH = [
|
|
36
|
+
join(process.env.VIRTUAL_ENV, runtimeDir),
|
|
37
|
+
delimiter,
|
|
38
|
+
process.env.PATH,
|
|
39
|
+
].join('')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [pythonExecutable] = this.#runtime.split('.')
|
|
43
|
+
|
|
44
|
+
this.handlerProcess = spawn(
|
|
45
|
+
pythonExecutable,
|
|
46
|
+
[
|
|
47
|
+
'-u',
|
|
48
|
+
resolve(__dirname, 'invoke.py'),
|
|
49
|
+
relative(cwd(), this.#handlerPath),
|
|
50
|
+
this.#handlerName,
|
|
51
|
+
],
|
|
52
|
+
{
|
|
53
|
+
env: assign(process.env, this.#env),
|
|
54
|
+
shell: true,
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
this.handlerProcess.stdout.readline = readline.createInterface({
|
|
59
|
+
input: this.handlerProcess.stdout,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// () => void
|
|
64
|
+
cleanup() {
|
|
65
|
+
this.handlerProcess.kill()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#parsePayload(value) {
|
|
69
|
+
let payload
|
|
70
|
+
|
|
71
|
+
for (const item of value.split(EOL)) {
|
|
72
|
+
let json
|
|
73
|
+
|
|
74
|
+
// first check if it's JSON
|
|
75
|
+
try {
|
|
76
|
+
json = parse(item)
|
|
77
|
+
// nope, it's not JSON
|
|
78
|
+
} catch {
|
|
79
|
+
// no-op
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// now let's see if we have a property __offline_payload__
|
|
83
|
+
if (
|
|
84
|
+
json &&
|
|
85
|
+
typeof json === 'object' &&
|
|
86
|
+
has(json, '__offline_payload__')
|
|
87
|
+
) {
|
|
88
|
+
payload = json.__offline_payload__
|
|
89
|
+
// everything else is print(), logging, ...
|
|
90
|
+
} else {
|
|
91
|
+
log.notice(item)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return payload
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// invokeLocalPython, loosely based on:
|
|
99
|
+
// https://github.com/serverless/serverless/blob/v1.50.0/lib/plugins/aws/invokeLocal/index.js#L410
|
|
100
|
+
// invoke.py, based on:
|
|
101
|
+
// https://github.com/serverless/serverless/blob/v1.50.0/lib/plugins/aws/invokeLocal/invoke.py
|
|
102
|
+
async run(event, context) {
|
|
103
|
+
return new Promise((accept, reject) => {
|
|
104
|
+
const input = stringify({
|
|
105
|
+
context,
|
|
106
|
+
event,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const onErr = (data) => {
|
|
110
|
+
// TODO
|
|
111
|
+
|
|
112
|
+
log.notice(data.toString())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const onLine = (line) => {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = this.#parsePayload(line.toString())
|
|
118
|
+
if (parsed) {
|
|
119
|
+
this.handlerProcess.stdout.readline.removeListener('line', onLine)
|
|
120
|
+
this.handlerProcess.stderr.removeListener('data', onErr)
|
|
121
|
+
return accept(parsed)
|
|
122
|
+
}
|
|
123
|
+
return null
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return reject(err)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.handlerProcess.stdout.readline.on('line', onLine)
|
|
130
|
+
this.handlerProcess.stderr.on('data', onErr)
|
|
131
|
+
|
|
132
|
+
process.nextTick(() => {
|
|
133
|
+
this.handlerProcess.stdin.write(input)
|
|
134
|
+
this.handlerProcess.stdin.write('\n')
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './PythonRunner.js'
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { EOL, platform } from 'node:os'
|
|
2
|
+
import { dirname, relative, resolve } from 'node:path'
|
|
3
|
+
import { cwd } from 'node:process'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { log } from '@serverless/utils/log.js'
|
|
6
|
+
import { execa } from 'execa'
|
|
7
|
+
|
|
8
|
+
const { parse, stringify } = JSON
|
|
9
|
+
const { has } = Reflect
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
|
|
13
|
+
export default class RubyRunner {
|
|
14
|
+
#env = null
|
|
15
|
+
|
|
16
|
+
#handlerName = null
|
|
17
|
+
|
|
18
|
+
#handlerPath = null
|
|
19
|
+
|
|
20
|
+
constructor(funOptions, env) {
|
|
21
|
+
const { handlerName, handlerPath } = funOptions
|
|
22
|
+
|
|
23
|
+
this.#env = env
|
|
24
|
+
this.#handlerName = handlerName
|
|
25
|
+
this.#handlerPath = handlerPath
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// no-op
|
|
29
|
+
// () => void
|
|
30
|
+
cleanup() {}
|
|
31
|
+
|
|
32
|
+
#parsePayload(value) {
|
|
33
|
+
let payload
|
|
34
|
+
|
|
35
|
+
for (const item of value.split(EOL)) {
|
|
36
|
+
let json
|
|
37
|
+
|
|
38
|
+
// first check if it's JSON
|
|
39
|
+
try {
|
|
40
|
+
json = parse(item)
|
|
41
|
+
// nope, it's not JSON
|
|
42
|
+
} catch {
|
|
43
|
+
// no-op
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// now let's see if we have a property __offline_payload__
|
|
47
|
+
if (
|
|
48
|
+
json &&
|
|
49
|
+
typeof json === 'object' &&
|
|
50
|
+
has(json, '__offline_payload__')
|
|
51
|
+
) {
|
|
52
|
+
payload = json.__offline_payload__
|
|
53
|
+
} else {
|
|
54
|
+
log.notice(item)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return payload
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// invokeLocalRuby, loosely based on:
|
|
62
|
+
// https://github.com/serverless/serverless/blob/v1.50.0/lib/plugins/aws/invokeLocal/index.js#L556
|
|
63
|
+
// invoke.rb, copy/pasted entirely as is:
|
|
64
|
+
// https://github.com/serverless/serverless/blob/v1.50.0/lib/plugins/aws/invokeLocal/invoke.rb
|
|
65
|
+
async run(event, context) {
|
|
66
|
+
const runtime = platform() === 'win32' ? 'ruby.exe' : 'ruby'
|
|
67
|
+
|
|
68
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/ruby-context.html
|
|
69
|
+
|
|
70
|
+
// https://docs.aws.amazon.com/lambda/latest/dg/ruby-context.html
|
|
71
|
+
// exclude callbackWaitsForEmptyEventLoop, don't mutate context
|
|
72
|
+
const { callbackWaitsForEmptyEventLoop, ..._context } = context
|
|
73
|
+
|
|
74
|
+
const input = stringify({
|
|
75
|
+
context: _context,
|
|
76
|
+
event,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// console.log(input)
|
|
80
|
+
|
|
81
|
+
const ruby = execa(
|
|
82
|
+
runtime,
|
|
83
|
+
[
|
|
84
|
+
resolve(__dirname, 'invoke.rb'),
|
|
85
|
+
relative(cwd(), this.#handlerPath),
|
|
86
|
+
this.#handlerName,
|
|
87
|
+
],
|
|
88
|
+
{
|
|
89
|
+
env: this.#env,
|
|
90
|
+
input,
|
|
91
|
+
// shell: true,
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
const result = await ruby
|
|
96
|
+
|
|
97
|
+
const { stderr, stdout } = result
|
|
98
|
+
|
|
99
|
+
if (stderr) {
|
|
100
|
+
// TODO
|
|
101
|
+
|
|
102
|
+
log.notice(stderr)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return this.#parsePayload(stdout)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './RubyRunner.js'
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { MessageChannel, Worker } from 'node:worker_threads'
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
|
+
const workerThreadHelperPath = resolve(__dirname, './workerThreadHelper.js')
|
|
7
|
+
|
|
8
|
+
export default class WorkerThreadRunner {
|
|
9
|
+
#workerThread = null
|
|
10
|
+
|
|
11
|
+
constructor(funOptions /* options */, env) {
|
|
12
|
+
// this._options = options
|
|
13
|
+
|
|
14
|
+
const { functionKey, handlerName, handlerPath, timeout } = funOptions
|
|
15
|
+
|
|
16
|
+
this.#workerThread = new Worker(workerThreadHelperPath, {
|
|
17
|
+
// don't pass process.env from the main process!
|
|
18
|
+
env,
|
|
19
|
+
workerData: {
|
|
20
|
+
functionKey,
|
|
21
|
+
handlerName,
|
|
22
|
+
handlerPath,
|
|
23
|
+
timeout,
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// () => Promise<number>
|
|
29
|
+
cleanup() {
|
|
30
|
+
// TODO console.log('worker thread cleanup')
|
|
31
|
+
|
|
32
|
+
return this.#workerThread.terminate()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
run(event, context) {
|
|
36
|
+
return new Promise((res, rej) => {
|
|
37
|
+
const { port1, port2 } = new MessageChannel()
|
|
38
|
+
|
|
39
|
+
port1
|
|
40
|
+
.on('message', (value) => {
|
|
41
|
+
if (value instanceof Error) {
|
|
42
|
+
rej(value)
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
res(value)
|
|
47
|
+
})
|
|
48
|
+
// emitted if the worker thread throws an uncaught exception.
|
|
49
|
+
// In that case, the worker will be terminated.
|
|
50
|
+
.on('error', rej)
|
|
51
|
+
// TODO
|
|
52
|
+
.on('exit', (code) => {
|
|
53
|
+
if (code !== 0) {
|
|
54
|
+
rej(new Error(`Worker stopped with exit code ${code}`))
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
this.#workerThread.postMessage(
|
|
59
|
+
{
|
|
60
|
+
context,
|
|
61
|
+
event,
|
|
62
|
+
// port2 is part of the payload, for the other side to answer messages
|
|
63
|
+
port: port2,
|
|
64
|
+
},
|
|
65
|
+
// port2 is also required to be part of the transfer list
|
|
66
|
+
[port2],
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './WorkerThreadRunner.js'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { env } from 'node:process'
|
|
2
|
+
import { parentPort, workerData } from 'node:worker_threads'
|
|
3
|
+
import InProcessRunner from '../in-process-runner/index.js'
|
|
4
|
+
|
|
5
|
+
const { functionKey, handlerName, handlerPath } = workerData
|
|
6
|
+
|
|
7
|
+
parentPort.on('message', async (messageData) => {
|
|
8
|
+
const { context, event, port, timeout } = messageData
|
|
9
|
+
|
|
10
|
+
// TODO we could probably cache this in the module scope?
|
|
11
|
+
const inProcessRunner = new InProcessRunner(
|
|
12
|
+
functionKey,
|
|
13
|
+
handlerPath,
|
|
14
|
+
handlerName,
|
|
15
|
+
env,
|
|
16
|
+
timeout,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
let result
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
result = await inProcessRunner.run(event, context)
|
|
23
|
+
} catch (err) {
|
|
24
|
+
port.postMessage(err)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// TODO check serializeability (contains function, symbol etc)
|
|
28
|
+
port.postMessage(result)
|
|
29
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Lambda.js'
|