@ikonintegration/ikapi 4.0.1 → 5.0.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.
Files changed (236) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +81 -0
  3. package/.github/workflows/npmpublish.yml +8 -19
  4. package/.github/workflows/prs.yml +12 -0
  5. package/README.md +89 -99
  6. package/dist/index.d.ts +16 -0
  7. package/dist/index.js +27 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/package-lock.json +11881 -0
  10. package/dist/package.json +81 -0
  11. package/dist/src/API/Request.d.ts +125 -0
  12. package/dist/src/API/Request.js +185 -0
  13. package/dist/src/API/Request.js.map +1 -0
  14. package/dist/src/API/Response.d.ts +188 -0
  15. package/dist/src/API/Response.js +270 -0
  16. package/dist/src/API/Response.js.map +1 -0
  17. package/dist/src/BaseEvent/DynamoTransaction.d.ts +70 -0
  18. package/dist/src/BaseEvent/DynamoTransaction.js +104 -0
  19. package/dist/src/BaseEvent/DynamoTransaction.js.map +1 -0
  20. package/dist/src/BaseEvent/EventProcessor.d.ts +58 -0
  21. package/dist/src/BaseEvent/EventProcessor.js +101 -0
  22. package/dist/src/BaseEvent/EventProcessor.js.map +1 -0
  23. package/dist/src/BaseEvent/Process.d.ts +50 -0
  24. package/dist/src/BaseEvent/Process.js +64 -0
  25. package/dist/src/BaseEvent/Process.js.map +1 -0
  26. package/dist/src/BaseEvent/StepTransaction.d.ts +23 -0
  27. package/dist/src/BaseEvent/StepTransaction.js +27 -0
  28. package/dist/src/BaseEvent/StepTransaction.js.map +1 -0
  29. package/dist/src/BaseEvent/Transaction.d.ts +149 -0
  30. package/dist/src/BaseEvent/Transaction.js +224 -0
  31. package/dist/src/BaseEvent/Transaction.js.map +1 -0
  32. package/dist/src/Cache/Redis.d.ts +29 -0
  33. package/dist/src/Cache/Redis.js +80 -0
  34. package/dist/src/Cache/Redis.js.map +1 -0
  35. package/dist/src/Cache/types.d.ts +31 -0
  36. package/dist/src/Cache/types.js +2 -0
  37. package/dist/src/Cache/types.js.map +1 -0
  38. package/dist/src/Config/Configuration.d.ts +123 -0
  39. package/dist/src/Config/Configuration.js +109 -0
  40. package/dist/src/Config/Configuration.js.map +1 -0
  41. package/dist/src/Config/EnvironmentVar.d.ts +74 -0
  42. package/dist/src/Config/EnvironmentVar.js +138 -0
  43. package/dist/src/Config/EnvironmentVar.js.map +1 -0
  44. package/dist/src/Crypto/Crypto.d.ts +45 -0
  45. package/dist/src/Crypto/Crypto.js +72 -0
  46. package/dist/src/Crypto/Crypto.js.map +1 -0
  47. package/dist/src/Database/Database.d.ts +21 -0
  48. package/dist/src/Database/Database.js +15 -0
  49. package/dist/src/Database/Database.js.map +1 -0
  50. package/dist/src/Database/DatabaseManager.d.ts +47 -0
  51. package/dist/src/Database/DatabaseManager.js +60 -0
  52. package/dist/src/Database/DatabaseManager.js.map +1 -0
  53. package/dist/src/Database/DatabaseTransaction.d.ts +101 -0
  54. package/dist/src/Database/DatabaseTransaction.js +126 -0
  55. package/dist/src/Database/DatabaseTransaction.js.map +1 -0
  56. package/dist/src/Database/index.d.ts +10 -0
  57. package/dist/src/Database/index.js +15 -0
  58. package/dist/src/Database/index.js.map +1 -0
  59. package/dist/src/Database/integrations/dynamo/DynamoDatabase.d.ts +35 -0
  60. package/dist/src/Database/integrations/dynamo/DynamoDatabase.js +59 -0
  61. package/dist/src/Database/integrations/dynamo/DynamoDatabase.js.map +1 -0
  62. package/dist/src/Database/integrations/kysely/KyselyDatabase.d.ts +66 -0
  63. package/dist/src/Database/integrations/kysely/KyselyDatabase.js +86 -0
  64. package/dist/src/Database/integrations/kysely/KyselyDatabase.js.map +1 -0
  65. package/dist/src/Database/integrations/kysely/KyselyTransaction.d.ts +70 -0
  66. package/dist/src/Database/integrations/kysely/KyselyTransaction.js +118 -0
  67. package/dist/src/Database/integrations/kysely/KyselyTransaction.js.map +1 -0
  68. package/dist/src/Database/integrations/pgsql/PostgresDatabase.d.ts +36 -0
  69. package/dist/src/Database/integrations/pgsql/PostgresDatabase.js +54 -0
  70. package/dist/src/Database/integrations/pgsql/PostgresDatabase.js.map +1 -0
  71. package/dist/src/Database/integrations/pgsql/PostgresTransaction.d.ts +63 -0
  72. package/dist/src/Database/integrations/pgsql/PostgresTransaction.js +61 -0
  73. package/dist/src/Database/integrations/pgsql/PostgresTransaction.js.map +1 -0
  74. package/dist/src/Database/types.d.ts +76 -0
  75. package/dist/src/Database/types.js +2 -0
  76. package/dist/src/Database/types.js.map +1 -0
  77. package/dist/src/Globals.d.ts +93 -0
  78. package/dist/src/Globals.js +99 -0
  79. package/dist/src/Globals.js.map +1 -0
  80. package/dist/src/Logger/Logger.d.ts +161 -0
  81. package/dist/src/Logger/Logger.js +299 -0
  82. package/dist/src/Logger/Logger.js.map +1 -0
  83. package/dist/src/Mailer/Mailer.d.ts +78 -0
  84. package/dist/src/Mailer/Mailer.js +182 -0
  85. package/dist/src/Mailer/Mailer.js.map +1 -0
  86. package/dist/src/Publisher/Publisher.d.ts +39 -0
  87. package/dist/src/Publisher/Publisher.js +77 -0
  88. package/dist/src/Publisher/Publisher.js.map +1 -0
  89. package/dist/src/Server/RouteResolver.d.ts +33 -0
  90. package/dist/src/Server/RouteResolver.js +100 -0
  91. package/dist/src/Server/RouteResolver.js.map +1 -0
  92. package/dist/src/Server/Router.d.ts +157 -0
  93. package/dist/src/Server/Router.js +32 -0
  94. package/dist/src/Server/Router.js.map +1 -0
  95. package/dist/src/Server/lib/ContainerServer.d.ts +42 -0
  96. package/dist/src/Server/lib/ContainerServer.js +66 -0
  97. package/dist/src/Server/lib/ContainerServer.js.map +1 -0
  98. package/dist/src/Server/lib/Server.d.ts +45 -0
  99. package/dist/src/Server/lib/Server.js +93 -0
  100. package/dist/src/Server/lib/Server.js.map +1 -0
  101. package/dist/src/Server/lib/container/GenericHandler.d.ts +9 -0
  102. package/dist/src/Server/lib/container/GenericHandler.js +82 -0
  103. package/dist/src/Server/lib/container/GenericHandler.js.map +1 -0
  104. package/dist/src/Server/lib/container/GenericHandlerEvent.d.ts +52 -0
  105. package/dist/src/Server/lib/container/GenericHandlerEvent.js +132 -0
  106. package/dist/src/Server/lib/container/GenericHandlerEvent.js.map +1 -0
  107. package/dist/src/Server/lib/container/HealthHandler.d.ts +9 -0
  108. package/dist/src/Server/lib/container/HealthHandler.js +19 -0
  109. package/dist/src/Server/lib/container/HealthHandler.js.map +1 -0
  110. package/dist/src/Server/lib/container/Proxy.d.ts +67 -0
  111. package/dist/src/Server/lib/container/Proxy.js +143 -0
  112. package/dist/src/Server/lib/container/Proxy.js.map +1 -0
  113. package/dist/src/Server/lib/container/Utils.d.ts +14 -0
  114. package/dist/src/Server/lib/container/Utils.js +37 -0
  115. package/dist/src/Server/lib/container/Utils.js.map +1 -0
  116. package/dist/src/Util/AsyncSingleton.d.ts +31 -0
  117. package/dist/src/Util/AsyncSingleton.js +83 -0
  118. package/dist/src/Util/AsyncSingleton.js.map +1 -0
  119. package/dist/src/Util/Utils.d.ts +61 -0
  120. package/dist/src/Util/Utils.js +147 -0
  121. package/dist/src/Util/Utils.js.map +1 -0
  122. package/dist/src/Validation/Validator.d.ts +17 -0
  123. package/dist/src/Validation/Validator.js +39 -0
  124. package/dist/src/Validation/Validator.js.map +1 -0
  125. package/dist/tsconfig.tsbuildinfo +1 -0
  126. package/index.ts +41 -0
  127. package/jest.config.ts +37 -0
  128. package/jest.smoke.config.ts +34 -0
  129. package/package.json +66 -22
  130. package/src/API/Request.ts +214 -0
  131. package/src/API/Response.ts +370 -0
  132. package/src/BaseEvent/DynamoTransaction.ts +175 -0
  133. package/src/BaseEvent/EventProcessor.ts +140 -0
  134. package/src/BaseEvent/Process.ts +78 -0
  135. package/src/BaseEvent/StepTransaction.ts +35 -0
  136. package/src/BaseEvent/Transaction.ts +323 -0
  137. package/src/Cache/Redis.ts +89 -0
  138. package/src/Cache/types.ts +33 -0
  139. package/src/Config/Configuration.ts +199 -0
  140. package/src/Config/EnvironmentVar.ts +142 -0
  141. package/src/Crypto/Crypto.ts +89 -0
  142. package/src/Database/Database.ts +22 -0
  143. package/src/Database/DatabaseManager.ts +67 -0
  144. package/src/Database/DatabaseTransaction.ts +170 -0
  145. package/src/Database/index.ts +27 -0
  146. package/src/Database/integrations/dynamo/DynamoDatabase.ts +58 -0
  147. package/src/Database/integrations/kysely/KyselyDatabase.ts +99 -0
  148. package/src/Database/integrations/kysely/KyselyTransaction.ts +172 -0
  149. package/src/Database/integrations/pgsql/PostgresDatabase.ts +56 -0
  150. package/src/Database/integrations/pgsql/PostgresTransaction.ts +87 -0
  151. package/src/Database/types.ts +85 -0
  152. package/src/Globals.ts +103 -0
  153. package/src/Logger/Logger.ts +363 -0
  154. package/src/Mailer/Mailer.ts +217 -0
  155. package/src/Publisher/Publisher.ts +96 -0
  156. package/src/Server/RouteResolver.ts +124 -0
  157. package/src/Server/Router.ts +200 -0
  158. package/src/Server/lib/ContainerServer.ts +65 -0
  159. package/src/Server/lib/Server.ts +109 -0
  160. package/src/Server/lib/container/GenericHandler.ts +76 -0
  161. package/src/Server/lib/container/GenericHandlerEvent.ts +154 -0
  162. package/src/Server/lib/container/HealthHandler.ts +11 -0
  163. package/src/Server/lib/container/Proxy.ts +172 -0
  164. package/src/Server/lib/container/Utils.ts +33 -0
  165. package/src/Util/AsyncSingleton.ts +86 -0
  166. package/src/Util/Utils.ts +131 -0
  167. package/src/Validation/Validator.ts +45 -0
  168. package/tests/API/Request.test.ts +273 -0
  169. package/tests/API/Response.test.ts +367 -0
  170. package/tests/BaseEvent/DynamoTransaction.test.ts +272 -0
  171. package/tests/BaseEvent/EventProcessor.test.ts +263 -0
  172. package/tests/BaseEvent/Process.test.ts +47 -0
  173. package/tests/BaseEvent/StepTransaction.test.ts +44 -0
  174. package/tests/BaseEvent/Transaction.test.ts +402 -0
  175. package/tests/Cache/Redis-client.test.ts +90 -0
  176. package/tests/Cache/Redis-cluster.test.ts +100 -0
  177. package/tests/Config/Config.test.ts +205 -0
  178. package/tests/Config/EnvironmentVar.test.ts +251 -0
  179. package/tests/Crypto/Crypto.test.ts +88 -0
  180. package/tests/Database/DatabaseManager.test.ts +79 -0
  181. package/tests/Database/integrations/dynamo/DynamoDatabase.test.ts +44 -0
  182. package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
  183. package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
  184. package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
  185. package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
  186. package/tests/Logger/Logger.test.ts +215 -0
  187. package/tests/Mailer/Mailer.test.ts +59 -0
  188. package/tests/Publisher/Publisher.test.ts +60 -0
  189. package/tests/Server/RouteResolver.test.ts +116 -0
  190. package/tests/Server/Router.test.ts +39 -0
  191. package/tests/Server/lib/ContainerServer.test.ts +531 -0
  192. package/tests/Server/lib/Server.test.ts +12 -0
  193. package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
  194. package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
  195. package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
  196. package/tests/Server/lib/container/Proxy.test.ts +268 -0
  197. package/tests/Server/lib/container/Utils.test.ts +47 -0
  198. package/tests/Test.utils.ts +78 -0
  199. package/tests/Utils/Utils.test.ts +229 -0
  200. package/tests/Validation/Validator.test.ts +82 -0
  201. package/tsconfig.json +26 -0
  202. package/tsconfig.smoke.json +26 -0
  203. package/index.js +0 -88
  204. package/src/API/IKRequest.js +0 -52
  205. package/src/API/IKResponse.js +0 -119
  206. package/src/API/IKUtils.js +0 -51
  207. package/src/BaseEvent/IKProcess.js +0 -77
  208. package/src/BaseEvent/IKTransaction.js +0 -139
  209. package/src/Cache/Prototype/IKCache.js +0 -17
  210. package/src/Cache/Redis/IKRedis.js +0 -148
  211. package/src/Database/DDB/IKDB.js +0 -56
  212. package/src/Database/DDB/IKDBBaseExpression.js +0 -130
  213. package/src/Database/DDB/IKDBBaseQuery.js +0 -151
  214. package/src/Database/DDB/IKDBQueryBatchGet.js +0 -37
  215. package/src/Database/DDB/IKDBQueryBatchWrite.js +0 -64
  216. package/src/Database/DDB/IKDBQueryDelete.js +0 -34
  217. package/src/Database/DDB/IKDBQueryGet.js +0 -48
  218. package/src/Database/DDB/IKDBQueryPut.js +0 -87
  219. package/src/Database/DDB/IKDBQueryScan.js +0 -45
  220. package/src/Database/DDB/IKDBQueryTransactionalWrite.js +0 -69
  221. package/src/Database/DDB/IKDBQueryUpdate.js +0 -221
  222. package/src/Database/DDB/_IKDBQueryTransactionalRead.js +0 -46
  223. package/src/Database/PSQL/IKDB.js +0 -41
  224. package/src/Database/PSQL/IKDBBaseQuery.js +0 -26
  225. package/src/Database/Prototype/IKDB.js +0 -21
  226. package/src/Database/Prototype/IKDBBaseQuery.js +0 -14
  227. package/src/IKDynamoStream.js +0 -42
  228. package/src/IKEventProcessor.js +0 -42
  229. package/src/IKGlobals.js +0 -24
  230. package/src/IKRouter.js +0 -47
  231. package/src/IKStepTransaction.js +0 -14
  232. package/src/Logger/IKLogger.js +0 -136
  233. package/src/Mailer/IKMailer.js +0 -69
  234. package/src/Publisher/IKPublisher.js +0 -44
  235. package/src/Tracker/IKExecutionTracker.js +0 -79
  236. package/src/Validation/IKValidation.js +0 -76
@@ -0,0 +1,154 @@
1
+ import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
2
+ import cuid from 'cuid'
3
+ import { Request } from 'express'
4
+
5
+ import { parseMultiValueQueryStringParameters, parseQueryStringParameters } from './Utils.js'
6
+ import Globals from '../../../Globals.js'
7
+ import Server from '../Server.js'
8
+
9
+ /**
10
+ * Represents the response object returned by a generic event handler.
11
+ * @typedef {Object} GenericHandlerEventResponse
12
+ * @property {Error | string} [err] - An optional error object or error message.
13
+ * @property {*} [data] - An optional data object.
14
+ */
15
+ export type GenericHandlerEventResponse = { err?: Error | string; data?: any }
16
+
17
+ type HasRawBody = {
18
+ rawBody: Buffer
19
+ }
20
+
21
+ /**
22
+ * Represents a generic handler event for serverless functions.
23
+ */
24
+ export default class GenericHandlerEvent {
25
+ /**
26
+ * Represents an HTTP request.
27
+ * @property {Request} request - The HTTP request object.
28
+ */
29
+ public request: Request
30
+ /**
31
+ * The handler function for serverless events in a server.
32
+ * @param {Server['handleServerlessEvent']} serverlessHandler - The function that handles serverless events.
33
+ * @returns None
34
+ */
35
+ public serverlessHandler: Server['handleServerlessEvent']
36
+
37
+ /**
38
+ * Constructs a new instance of the class.
39
+ * @param {Request} request - The request object.
40
+ * @param {Server['handleServerlessEvent']} serverlessHandler - The serverless handler function.
41
+ * @returns None
42
+ */
43
+ constructor(request: Request, serverlessHandler: Server['handleServerlessEvent']) {
44
+ this.request = request
45
+ this.serverlessHandler = serverlessHandler
46
+ }
47
+
48
+ /**
49
+ * Invokes the handler function asynchronously and returns a promise that resolves to a GenericHandlerEventResponse.
50
+ * @returns {Promise<GenericHandlerEventResponse>} A promise that resolves to a GenericHandlerEventResponse.
51
+ */
52
+ public async invoke(): Promise<GenericHandlerEventResponse> {
53
+ // eslint-disable-next-line no-async-promise-executor
54
+ return new Promise(async (resolve, reject) => {
55
+ try {
56
+ // Build event and context
57
+ const event = this.buildEvent()
58
+ const context = this.buildContext(event, (err?: Error | string, data?: any) => {
59
+ resolve({ err, data })
60
+ })
61
+ // Invoke
62
+ await this.serverlessHandler(event, context)
63
+ } catch (e) {
64
+ reject(e) // forward rejection
65
+ }
66
+ })
67
+ }
68
+
69
+ /**
70
+ * Builds and returns an APIGatewayProxyEvent object based on the current request.
71
+ * @returns {APIGatewayProxyEvent} - The constructed APIGatewayProxyEvent object.
72
+ */
73
+ private buildEvent(): APIGatewayProxyEvent & HasRawBody {
74
+ return {
75
+ body: this.request.body || null, //enforce key
76
+ rawBody: this.request['rawBody'],
77
+ headers: <any>(this.request.headers || {}),
78
+ httpMethod: this.request.method?.toUpperCase(),
79
+ isBase64Encoded: false,
80
+ multiValueHeaders: <any>(this.request.headers || {}),
81
+ multiValueQueryStringParameters: parseMultiValueQueryStringParameters(this.request.url),
82
+ path: this.request.path,
83
+ pathParameters: null,
84
+ queryStringParameters: this.request.url ? parseQueryStringParameters(this.request.url) : {},
85
+ requestContext: {
86
+ accountId: process.env.AWS_ACCOUNT_ID || 'undefined',
87
+ apiId: '',
88
+ authorizer: null,
89
+ domainName: undefined,
90
+ domainPrefix: undefined,
91
+ extendedRequestId: cuid(),
92
+ httpMethod: this.request.method ? this.request.method.toUpperCase() : '',
93
+ identity: {
94
+ accessKey: null,
95
+ accountId: process.env.AWS_ACCOUNT_ID || null,
96
+ caller: null,
97
+ apiKey: null,
98
+ apiKeyId: null,
99
+ clientCert: null,
100
+ cognitoAuthenticationProvider: null,
101
+ cognitoAuthenticationType: null,
102
+ cognitoIdentityId: null,
103
+ cognitoIdentityPoolId: null,
104
+ principalOrgId: null,
105
+ sourceIp:
106
+ <string>this.request.headers?.['x-forwarded-for'] ||
107
+ this.request.socket?.remoteAddress ||
108
+ '',
109
+ user: null,
110
+ userAgent: this.request.headers?.['user-agent'] || null,
111
+ userArn: null,
112
+ },
113
+ path: this.request.path,
114
+ protocol: 'HTTP/1.1',
115
+ requestId: `${cuid()}-${cuid()}`,
116
+ requestTime: new Date().toISOString(),
117
+ requestTimeEpoch: Date.now(),
118
+ resourceId: '',
119
+ resourcePath: Globals.Listener_HTTP_ProxyRoute,
120
+ stage: process.env.STAGE || '',
121
+ },
122
+ resource: Globals.Listener_HTTP_ProxyRoute,
123
+ stageVariables: null,
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Builds and returns a context object for an AWS Lambda function.
129
+ * @param {APIGatewayProxyEvent} event - The event object passed to the Lambda function.
130
+ * @param {(err?: Error | string, data?: any) => void} callback - The callback function to be called when the Lambda function completes.
131
+ * @returns {Context} - The context object for the Lambda function.
132
+ */
133
+ private buildContext(
134
+ event: APIGatewayProxyEvent,
135
+ callback: (err?: Error | string, data?: any) => void
136
+ ): Context {
137
+ return {
138
+ awsRequestId: event.requestContext.requestId,
139
+ callbackWaitsForEmptyEventLoop: true,
140
+ getRemainingTimeInMillis: () => {
141
+ return 0
142
+ },
143
+ done: (err, data) => callback(err, data),
144
+ fail: err => callback(err),
145
+ succeed: res => callback(undefined, res),
146
+ functionName: 'container-function',
147
+ functionVersion: 'LATEST',
148
+ invokedFunctionArn: 'none',
149
+ memoryLimitInMB: '-1',
150
+ logGroupName: 'undefined',
151
+ logStreamName: 'undefined',
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,11 @@
1
+ import { Request, Response } from 'express'
2
+
3
+ /**
4
+ * Handles a request and sends a response with the message 'Healthy!'.
5
+ * @param {Request} _request - The request object.
6
+ * @param {Response} res - The response object.
7
+ * @returns None
8
+ */
9
+ export default async (_request: Request, res: Response) => {
10
+ res.send('Healthy!')
11
+ }
@@ -0,0 +1,172 @@
1
+ import fs from 'fs'
2
+ import { Server as HTTPServer, createServer } from 'http'
3
+
4
+ import cors from 'cors'
5
+ import express from 'express'
6
+
7
+ import Server from './../Server.js'
8
+ import GenericHandler from './GenericHandler.js'
9
+ import HealthHandler from './HealthHandler.js'
10
+ import Globals from '../../../Globals.js'
11
+ import Utils from '../../../Util/Utils.js'
12
+ import { RouterConfig } from '../../Router.js'
13
+
14
+ /* Get package.json version from IKApi on ESM */
15
+ const { version: appVersion } = JSON.parse(fs.readFileSync('package.json').toString())
16
+
17
+ /**
18
+ * Represents a Proxy class that handles routing and server functionality.
19
+ */
20
+ export default class Proxy {
21
+ /**
22
+ * A boolean flag indicating whether the process is currently stopping or not.
23
+ */
24
+ private stopping: boolean
25
+ /**
26
+ * The configuration object for the router.
27
+ */
28
+ private readonly config: RouterConfig
29
+ /**
30
+ * The Express application instance for the server.
31
+ * @readonly
32
+ * @type {express.Express}
33
+ */
34
+ private readonly app: express.Express
35
+ /**
36
+ * The handler function for serverless events in the Server class.
37
+ * @param {ServerlessEvent} event - The serverless event object.
38
+ * @returns None
39
+ */
40
+ private readonly serverlessHandler: Server['handleServerlessEvent']
41
+ /**
42
+ * Represents a listener for an HTTP server.
43
+ * @private
44
+ * @type {HTTPServer}
45
+ */
46
+ private listener: HTTPServer
47
+
48
+ /**
49
+ * Constructs a new instance of the Router class.
50
+ * @param {RouterConfig} config - The configuration object for the router.
51
+ * @param {Server['handleServerlessEvent']} serverlessHandler - The handler function for serverless events.
52
+ * @returns None
53
+ */
54
+ constructor(config: RouterConfig, serverlessHandler: Server['handleServerlessEvent']) {
55
+ this.stopping = false
56
+ this.config = config
57
+ this.serverlessHandler = serverlessHandler
58
+ this.app = express()
59
+ /* Opinionated Express configs */
60
+ this.app.use(
61
+ express.json({
62
+ verify(req, res, buf) {
63
+ req['rawBody'] = buf
64
+ },
65
+ })
66
+ )
67
+ // apply cors config
68
+ const corsConfig = this.config.cors || Utils.parseObjectNullIfEmpty(process.env.CORS)
69
+ this.app.use(
70
+ cors(
71
+ corsConfig
72
+ ? {
73
+ origin: corsConfig.origin,
74
+ allowedHeaders: corsConfig.headers,
75
+ credentials: !!corsConfig.allowCredentials,
76
+ }
77
+ : {}
78
+ )
79
+ )
80
+
81
+ // //This supposedly fix some 502 codes where nodejs socket would hang during
82
+ // //a request and if behind ALB, it would cause 502 codes. Had experiencied this
83
+ // //and 502 codes reduced dramastically, but still some appearances. Maybe this
84
+ // //is just a palliative work-around for the real issue; TODO: need to investigate
85
+ // //in the future.
86
+ // this.listener.listener.keepAliveTimeout = 120e3
87
+ // this.listener.listener.headersTimeout = 120e3
88
+ }
89
+
90
+ /**
91
+ * Loads the necessary components and initializes the application.
92
+ * @returns None
93
+ */
94
+ public async load() {
95
+ await this.startListeners()
96
+ this.installRoutes()
97
+ }
98
+
99
+ /**
100
+ * Unloads the current module, stopping any active listeners.
101
+ * @param {any} [err] - Optional error object to pass to the stopListeners method.
102
+ * @returns {Promise<void>} - A promise that resolves once the listeners have been stopped.
103
+ */
104
+ public async unload(err?: any) {
105
+ await this.stopListeners(err)
106
+ }
107
+
108
+ /**
109
+ * Starts the listeners for the proxy server.
110
+ * @returns {Promise<void>} A promise that resolves when the listeners have started.
111
+ */
112
+ private async startListeners(): Promise<void> {
113
+ // eslint-disable-next-line no-async-promise-executor
114
+ return new Promise(async resolve => {
115
+ const port = this.config.port || Globals.Listener_HTTP_DefaultPort
116
+ console.log(`[Proxy] - [STARTING] - v.${appVersion} - :${port}`)
117
+ // Create Server
118
+ this.listener = createServer(this.app)
119
+ // Set defaults
120
+ this.listener.setTimeout(this.config.timeout || Globals.Listener_HTTP_DefaultTimeout)
121
+ // Call hook if available
122
+ if (this.config.containerSetupHook)
123
+ await this.config.containerSetupHook(this.listener, this.app)
124
+ // Start Server
125
+ this.listener.listen(port, () => {
126
+ console.log(`[Proxy] - [STARTED]`)
127
+ resolve()
128
+ })
129
+ })
130
+ }
131
+
132
+ /**
133
+ * Stops the listeners and exits the process.
134
+ * @param {any} [err] - Optional error object.
135
+ * @returns {Promise<void>} - A promise that resolves when the listeners are stopped and the process is exited.
136
+ */
137
+ private async stopListeners(err?: any) {
138
+ if (this.stopping) return
139
+ this.stopping = true
140
+ console.debug('[Proxy] - [STOPPING]')
141
+ return new Promise(resolve => {
142
+ this.listener.close(_err => {
143
+ const err2 = err || _err
144
+ if (err2) console.log('[Proxy] - exit output:', err2)
145
+ console.log('[Proxy] - [STOPPED]')
146
+ process.exit(err2 ? 1 : 0)
147
+ resolve(null)
148
+ })
149
+ })
150
+ }
151
+
152
+ /**
153
+ * Installs the routes for the proxy server.
154
+ * @returns None
155
+ */
156
+ private installRoutes() {
157
+ //Health check route -- This is a bypass route to only check if
158
+ //runtime proxy is working and responding to calls.
159
+ console.log(
160
+ `[Proxy] - [HEALTH-ROUTE] - ${
161
+ this.config.healthCheckRoute || Globals.Listener_HTTP_DefaultHealthCheckRoute
162
+ }`
163
+ )
164
+ this.app
165
+ .route(this.config.healthCheckRoute || Globals.Listener_HTTP_DefaultHealthCheckRoute)
166
+ .get(HealthHandler)
167
+ //Main route -- We use a wildcard route because is not the job of the runtime and neither
168
+ //the task to deny/constrain routes that invoked this task; all the job is done by the
169
+ //load balancer and we just foward everything we have to the function.
170
+ this.app.route(Globals.Listener_HTTP_ProxyRoute).all(GenericHandler(this.serverlessHandler))
171
+ }
172
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Parses the multi-value query string parameters from the given URL.
3
+ * @param {string} url - The URL containing the query string parameters.
4
+ * @returns {Object} - An object representing the parsed query string parameters.
5
+ */
6
+ export function parseMultiValueQueryStringParameters(url: string) {
7
+ // dummy placeholder url for the WHATWG URL constructor
8
+ // https://github.com/nodejs/node/issues/12682
9
+ const { searchParams } = new URL(url, 'http://example')
10
+ //
11
+ if (Array.from(searchParams).length === 0) return {}
12
+ const map = new Map()
13
+ // eslint-disable-next-line no-restricted-syntax
14
+ for (const [key, value] of searchParams) {
15
+ const item = map.get(key)
16
+ if (item) item.push(value)
17
+ else map.set(key, [value])
18
+ }
19
+ return Object.fromEntries(map)
20
+ }
21
+
22
+ /**
23
+ * Parses the query string parameters from a given URL.
24
+ * @param {string} url - The URL to parse the query string parameters from.
25
+ * @returns {object | null} - An object containing the parsed query string parameters, or null if there are no parameters.
26
+ */
27
+ export function parseQueryStringParameters(url) {
28
+ // dummy placeholder url for the WHATWG URL constructor
29
+ // https://github.com/nodejs/node/issues/12682
30
+ const { searchParams } = new URL(url, 'http://example')
31
+ if (Array.from(searchParams).length === 0) return {}
32
+ return Object.fromEntries(searchParams)
33
+ }
@@ -0,0 +1,86 @@
1
+ type ResolveReject = [(value: void | PromiseLike<void>) => void, (reason?: any) => void]
2
+
3
+ /**
4
+ * Represents a class that can be used to create a singleton instance of a class that is created asynchronously.
5
+ */
6
+ export default class AsyncSingleton<T, R> {
7
+ private readonly factory: (r: R) => Promise<T>
8
+ private readonly checker: (value: T) => Promise<boolean>
9
+
10
+ private queue: ResolveReject[] = []
11
+ private instantiating: boolean = false
12
+
13
+ private value?: T
14
+
15
+ /**
16
+ * Creates a new instance of the AsyncSingleton class.
17
+ * @param factory The factory function that creates the instance.
18
+ * @param checker The function that checks if the instance is valid.
19
+ */
20
+ constructor(
21
+ factory: (r: R) => Promise<T>,
22
+ checker: (value: T) => Promise<boolean> = async () => true
23
+ ) {
24
+ this.factory = factory
25
+ this.checker = checker
26
+ }
27
+
28
+ /**
29
+ * Gets the instance of the class.
30
+ * @param r The configuration object for creating the instance.
31
+ */
32
+ public async instance(r: R): Promise<T> {
33
+ if (this.value && (await this.checker(this.value))) return this.value
34
+
35
+ if (this.instantiating) {
36
+ return await this.awaitOtherInit()
37
+ }
38
+
39
+ return await this.initHere(r)
40
+ }
41
+
42
+ /**
43
+ * Gets the current value, may be undefined or invalid.
44
+ */
45
+ public getValue(): T | undefined {
46
+ return this.value
47
+ }
48
+
49
+ /**
50
+ * Clears the current value and rejects all pending promises.
51
+ */
52
+ public clear() {
53
+ this.value = undefined
54
+ for (const [, rej] of this.queue) {
55
+ rej()
56
+ }
57
+ this.queue = []
58
+ }
59
+
60
+ private async initHere(r: R): Promise<T> {
61
+ try {
62
+ this.instantiating = true
63
+ this.value = await this.factory(r)
64
+
65
+ for (const [res] of this.queue) {
66
+ res()
67
+ }
68
+
69
+ this.queue = []
70
+
71
+ return this.value
72
+ } finally {
73
+ for (const [, rej] of this.queue) {
74
+ rej()
75
+ }
76
+ this.instantiating = false
77
+ }
78
+ }
79
+
80
+ private async awaitOtherInit(): Promise<T> {
81
+ await new Promise<void>((res, rej) => {
82
+ this.queue.push([res, rej])
83
+ })
84
+ return this.value!
85
+ }
86
+ }
@@ -0,0 +1,131 @@
1
+ import child_process from 'child_process'
2
+
3
+ import { convertToAttr, marshall, unmarshall } from '@aws-sdk/util-dynamodb'
4
+ /**
5
+ * Utility class containing various static methods for common operations.
6
+ */
7
+ export default class Utils {
8
+ /**
9
+ * Checks if the application is running in a hybridless container.
10
+ * @returns {boolean} - True if the application is running in a hybridless container, false otherwise.
11
+ */
12
+ public static isHybridlessContainer(): boolean {
13
+ return process.env.HYBRIDLESS_RUNTIME == 'true'
14
+ }
15
+
16
+ /**
17
+ * Checks if a given string is valid.
18
+ * @param {string} string - The string to check.
19
+ * @returns {boolean} - True if the string is valid, false otherwise.
20
+ */
21
+ public static isValidString(string: string): boolean {
22
+ return string?.length > 0 && !Array.isArray(string)
23
+ }
24
+
25
+ /**
26
+ * Parses a string into an integer and returns null if the string is not a valid number.
27
+ * @param {string} str - The string to parse into an integer.
28
+ * @returns {number | null} - The parsed integer or null if the string is not a valid number.
29
+ */
30
+ public static parseIntNullIfNaN(str?: string): number | null {
31
+ const n = parseInt(str || '')
32
+ return isNaN(n) ? null : n
33
+ }
34
+
35
+ /**
36
+ * Parses a JSON string and returns the resulting object. If the string is empty or
37
+ * cannot be parsed, null is returned.
38
+ * @param {string} string - The JSON string to parse.
39
+ * @returns {any | null} - The parsed object or null if the string is empty or invalid.
40
+ */
41
+ public static parseObjectNullIfEmpty(string: string | undefined): any | null {
42
+ let o = null
43
+ try {
44
+ o = string ? JSON.parse(string) : null
45
+ if (o && Object.keys(o).length <= 0) o = null
46
+ } catch (e) {
47
+ /* empty */
48
+ }
49
+ return o
50
+ }
51
+
52
+ /**
53
+ * Checks if a given value is a valid number.
54
+ * @param {string} number - The value to be checked.
55
+ * @returns {boolean} - True if the value is a valid number, false otherwise.
56
+ */
57
+ public static isValidNumber(number: string): boolean {
58
+ let validNumb = NaN
59
+ try {
60
+ validNumb = parseInt(number + '')
61
+ } catch (e) {
62
+ console.error('Error while validating number', e)
63
+ }
64
+ return !isNaN(validNumb) && !Array.isArray(number)
65
+ }
66
+
67
+ /**
68
+ * Retrieves the value from an object using a case-insensitive key lookup.
69
+ * @param {any} obj - The object to search for the key.
70
+ * @param {string} key - The key to search for in the object.
71
+ * @returns {any | null} The value associated with the key, or null if the key is not found.
72
+ */
73
+ public static caseInsensitiveObjectForKey(obj: any, key: string): any | null {
74
+ if (!obj) return null
75
+ const insensitiveKey = Object.keys(obj).find(k => k.toLowerCase() === key.toLowerCase())
76
+ if (insensitiveKey && insensitiveKey != '') return obj[insensitiveKey]
77
+ return null
78
+ }
79
+
80
+ /**
81
+ * Cleans out the /tmp directory asynchronously.
82
+ */
83
+ public static async cleanTemporaryFolder() {
84
+ return new Promise<void>(resolve => {
85
+ try {
86
+ child_process.execSync('rm -rf /tmp/*')
87
+ console.log('Cleaned tmp folder')
88
+ } catch (err) {
89
+ console.error('Error while cleaning tmp folder', err)
90
+ } finally {
91
+ resolve()
92
+ }
93
+ })
94
+ }
95
+
96
+ /**
97
+ * Marshalls the given item into a DynamoDB format.
98
+ * If the item is an array, it maps over each element and marshalls it recursively.
99
+ * If the item is an object, it marshalls the object using the marshall function with options to remove undefined values and convert class instances to maps.
100
+ * If the item is neither an array nor an object, it converts the item to an attribute.
101
+ * @param {any} item - The item to be marshalled.
102
+ * @returns The marshalled item in DynamoDB format.
103
+ */
104
+ public static ddbMarshall<T>(item: T, rec?: boolean) {
105
+ if (Array.isArray(item)) return { L: item.map(_i => this.ddbMarshall(_i, true)) }
106
+ else if (typeof item === 'object' && isNaN(parseInt(item as any))) {
107
+ const marshalled = marshall(item, {
108
+ removeUndefinedValues: true,
109
+ convertClassInstanceToMap: true,
110
+ })
111
+ if (rec) return { M: marshalled }
112
+ else return marshalled
113
+ } else
114
+ return convertToAttr(item, { removeUndefinedValues: true, convertClassInstanceToMap: true })
115
+ }
116
+
117
+ /**
118
+ * Recursively unmarshalls a DynamoDB item by converting it into a plain JavaScript object.
119
+ * @param {any} item - The DynamoDB item to unmarshall.
120
+ * @returns {any} The unmarshalled JavaScript object.
121
+ */
122
+ public static ddbUnmarshall(item) {
123
+ if (!item && item !== false) return null
124
+ if (Array.isArray(item)) {
125
+ return item.map(_item => this.ddbUnmarshall(_item))
126
+ } else if (typeof item === 'object') {
127
+ return unmarshall(item)
128
+ }
129
+ return item
130
+ }
131
+ }
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod'
2
+
3
+ import Response, { ResponseErrorType } from '../API/Response.js'
4
+ import Globals from '../Globals.js'
5
+
6
+ /**
7
+ * Validates the given data against the provided schema.
8
+ * @param {any} data - The data to be validated.
9
+ * @param {z.ZodObject<any>} schema - The schema to validate against.
10
+ * @returns {boolean | Response<ResponseErrorType>} - Returns true if the data is valid, otherwise returns a response object with an error message.
11
+ */
12
+ export default class Validator {
13
+ /**
14
+ * Validates the given data against the provided schema.
15
+ * @param {any} data - The data to be validated.
16
+ * @param {z.ZodObject<any> | z.ZodUnion<any>} schema - The schema to validate against.
17
+ * @returns {boolean | Response<ResponseErrorType>} - Returns either true if the data is valid or a Response object with an error message if validation fails.
18
+ */
19
+ public static validateSchema(
20
+ data: any,
21
+ schema: z.ZodObject<any> | z.ZodUnion<any> | z.ZodIntersection<any, any>
22
+ ): boolean | Response<ResponseErrorType> {
23
+ let error, validatedInput
24
+
25
+ // Validate body against known zod schema
26
+ try {
27
+ schema.parse(data)
28
+ validatedInput = true
29
+ } catch (err: z.ZodError | any) {
30
+ if (err instanceof z.ZodError) error = JSON.parse(err.message)
31
+ else error = 'Unknown validation error!' //unhandled case, hard to test
32
+ }
33
+
34
+ // Error validation
35
+ if (!validatedInput || error) {
36
+ return Response.BadRequestResponse(
37
+ Globals.ErrorResponseValidationFail,
38
+ Globals.ErrorCode_InvalidInput,
39
+ { validationFailure: error }
40
+ )
41
+ } else {
42
+ return true
43
+ }
44
+ }
45
+ }