0z2i6v3u5t 1.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 (211) hide show
  1. package/.devcontainer/devcontainer.json +4 -0
  2. package/.devcontainer/setup.sh +11 -0
  3. package/.dockerignore +2 -0
  4. package/.github/CONTRIBUTING.md +52 -0
  5. package/.github/FUNDING.yml +3 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
  9. package/.github/dependabot.yml +17 -0
  10. package/.github/workflows/codeql.yml +76 -0
  11. package/.github/workflows/publish_docs.yml +25 -0
  12. package/.github/workflows/test.yml +78 -0
  13. package/.nvmrc +1 -0
  14. package/.prettierignore +1 -0
  15. package/.prettierrc +1 -0
  16. package/.vscode/launch.json +42 -0
  17. package/CODE_OF_CONDUCT.md +76 -0
  18. package/Dockerfile +17 -0
  19. package/LICENSE +21 -0
  20. package/README.md +3 -0
  21. package/SECURITY.md +5 -0
  22. package/__tests__/actions/cacheTest.ts +58 -0
  23. package/__tests__/actions/randomNumber.ts +26 -0
  24. package/__tests__/actions/recursiveAction.ts +16 -0
  25. package/__tests__/actions/sleepTest.ts +24 -0
  26. package/__tests__/actions/status.ts +17 -0
  27. package/__tests__/actions/swagger.ts +76 -0
  28. package/__tests__/actions/validationTest.ts +63 -0
  29. package/__tests__/cli/cli.ts +126 -0
  30. package/__tests__/core/api.ts +632 -0
  31. package/__tests__/core/cache.ts +400 -0
  32. package/__tests__/core/chatRoom.ts +589 -0
  33. package/__tests__/core/cli.ts +349 -0
  34. package/__tests__/core/cluster.ts +132 -0
  35. package/__tests__/core/config.ts +78 -0
  36. package/__tests__/core/errors.ts +112 -0
  37. package/__tests__/core/log.ts +23 -0
  38. package/__tests__/core/middleware.ts +427 -0
  39. package/__tests__/core/plugins/partialPlugin.ts +94 -0
  40. package/__tests__/core/plugins/withPlugin.ts +88 -0
  41. package/__tests__/core/plugins/withoutPlugin.ts +81 -0
  42. package/__tests__/core/process.ts +42 -0
  43. package/__tests__/core/specHelper.ts +330 -0
  44. package/__tests__/core/staticFile/compression.ts +99 -0
  45. package/__tests__/core/staticFile/staticFile.ts +180 -0
  46. package/__tests__/core/tasks/customQueueFunction.ts +67 -0
  47. package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
  48. package/__tests__/core/tasks/tasks.ts +605 -0
  49. package/__tests__/integration/browser.ts +133 -0
  50. package/__tests__/integration/ioredis-mock.ts +194 -0
  51. package/__tests__/integration/sendBuffer.ts +97 -0
  52. package/__tests__/integration/sendFile.ts +24 -0
  53. package/__tests__/integration/sharedFingerprint.ts +82 -0
  54. package/__tests__/integration/taskFlow.ts +110 -0
  55. package/__tests__/jest.ts +5 -0
  56. package/__tests__/modules/action.ts +103 -0
  57. package/__tests__/modules/config.ts +19 -0
  58. package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
  59. package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
  60. package/__tests__/servers/web/enableMultiples.ts +83 -0
  61. package/__tests__/servers/web/fileUpload.ts +79 -0
  62. package/__tests__/servers/web/jsonp.ts +57 -0
  63. package/__tests__/servers/web/nonMultiples.ts +83 -0
  64. package/__tests__/servers/web/rawBody.ts +208 -0
  65. package/__tests__/servers/web/returnErrorCodes.ts +55 -0
  66. package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
  67. package/__tests__/servers/web/routes/routes.ts +579 -0
  68. package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
  69. package/__tests__/servers/web/web.ts +1031 -0
  70. package/__tests__/servers/websocket.ts +795 -0
  71. package/__tests__/tasks/runAction.ts +37 -0
  72. package/__tests__/template.ts.example +20 -0
  73. package/__tests__/testCliCommands/hello.ts +44 -0
  74. package/__tests__/testPlugin/public/plugin.html +1 -0
  75. package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
  76. package/__tests__/testPlugin/src/bin/hello.ts +22 -0
  77. package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
  78. package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
  79. package/__tests__/testPlugin/tsconfig.json +10 -0
  80. package/__tests__/utils/utils.ts +492 -0
  81. package/app.json +23 -0
  82. package/bin/deploy-docs +39 -0
  83. package/client/ActionheroWebsocketClient.js +277 -0
  84. package/docker-compose.yml +73 -0
  85. package/package.json +24 -0
  86. package/public/chat.html +194 -0
  87. package/public/css/cosmo.css +12 -0
  88. package/public/favicon.ico +0 -0
  89. package/public/index.html +115 -0
  90. package/public/javascript/.gitkeep +0 -0
  91. package/public/linkedSession.html +80 -0
  92. package/public/logo/actionhero-small.png +0 -0
  93. package/public/logo/actionhero.png +0 -0
  94. package/public/pixel.gif +0 -0
  95. package/public/simple.html +2 -0
  96. package/public/swagger.html +32 -0
  97. package/public/websocketLoadTest.html +322 -0
  98. package/src/actions/cacheTest.ts +58 -0
  99. package/src/actions/createChatRoom.ts +20 -0
  100. package/src/actions/randomNumber.ts +17 -0
  101. package/src/actions/recursiveAction.ts +13 -0
  102. package/src/actions/sendFile.ts +12 -0
  103. package/src/actions/sleepTest.ts +40 -0
  104. package/src/actions/status.ts +73 -0
  105. package/src/actions/swagger.ts +155 -0
  106. package/src/actions/validationTest.ts +36 -0
  107. package/src/bin/actionhero.ts +225 -0
  108. package/src/bin/methods/actions/list.ts +30 -0
  109. package/src/bin/methods/console.ts +26 -0
  110. package/src/bin/methods/generate/action.ts +58 -0
  111. package/src/bin/methods/generate/cli.ts +51 -0
  112. package/src/bin/methods/generate/initializer.ts +54 -0
  113. package/src/bin/methods/generate/plugin.ts +57 -0
  114. package/src/bin/methods/generate/server.ts +38 -0
  115. package/src/bin/methods/generate/task.ts +68 -0
  116. package/src/bin/methods/generate.ts +176 -0
  117. package/src/bin/methods/task/enqueue.ts +35 -0
  118. package/src/classes/action.ts +98 -0
  119. package/src/classes/actionProcessor.ts +463 -0
  120. package/src/classes/api.ts +51 -0
  121. package/src/classes/cli.ts +67 -0
  122. package/src/classes/config.ts +15 -0
  123. package/src/classes/connection.ts +321 -0
  124. package/src/classes/exceptionReporter.ts +9 -0
  125. package/src/classes/initializer.ts +59 -0
  126. package/src/classes/initializers.ts +5 -0
  127. package/src/classes/input.ts +9 -0
  128. package/src/classes/inputs.ts +34 -0
  129. package/src/classes/process/actionheroVersion.ts +15 -0
  130. package/src/classes/process/env.ts +16 -0
  131. package/src/classes/process/id.ts +34 -0
  132. package/src/classes/process/pid.ts +32 -0
  133. package/src/classes/process/projectRoot.ts +16 -0
  134. package/src/classes/process/typescript.ts +47 -0
  135. package/src/classes/process.ts +479 -0
  136. package/src/classes/server.ts +251 -0
  137. package/src/classes/task.ts +87 -0
  138. package/src/config/api.ts +107 -0
  139. package/src/config/errors.ts +162 -0
  140. package/src/config/logger.ts +113 -0
  141. package/src/config/plugins.ts +37 -0
  142. package/src/config/redis.ts +78 -0
  143. package/src/config/routes.ts +44 -0
  144. package/src/config/tasks.ts +84 -0
  145. package/src/config/web.ts +136 -0
  146. package/src/config/websocket.ts +62 -0
  147. package/src/index.ts +46 -0
  148. package/src/initializers/actions.ts +125 -0
  149. package/src/initializers/chatRoom.ts +214 -0
  150. package/src/initializers/connections.ts +124 -0
  151. package/src/initializers/exceptions.ts +155 -0
  152. package/src/initializers/params.ts +52 -0
  153. package/src/initializers/redis.ts +191 -0
  154. package/src/initializers/resque.ts +248 -0
  155. package/src/initializers/routes.ts +229 -0
  156. package/src/initializers/servers.ts +134 -0
  157. package/src/initializers/specHelper.ts +195 -0
  158. package/src/initializers/staticFile.ts +253 -0
  159. package/src/initializers/tasks.ts +188 -0
  160. package/src/modules/action.ts +89 -0
  161. package/src/modules/cache.ts +326 -0
  162. package/src/modules/chatRoom.ts +321 -0
  163. package/src/modules/config.ts +246 -0
  164. package/src/modules/log.ts +62 -0
  165. package/src/modules/redis.ts +93 -0
  166. package/src/modules/route.ts +59 -0
  167. package/src/modules/specHelper.ts +182 -0
  168. package/src/modules/task.ts +527 -0
  169. package/src/modules/utils/argv.ts +3 -0
  170. package/src/modules/utils/arrayStartingMatch.ts +21 -0
  171. package/src/modules/utils/arrayUnique.ts +15 -0
  172. package/src/modules/utils/collapseObjectToArray.ts +33 -0
  173. package/src/modules/utils/deepCopy.ts +3 -0
  174. package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
  175. package/src/modules/utils/eventLoopDelay.ts +34 -0
  176. package/src/modules/utils/fileUtils.ts +119 -0
  177. package/src/modules/utils/filterObjectForLogging.ts +51 -0
  178. package/src/modules/utils/filterResponseForLogging.ts +53 -0
  179. package/src/modules/utils/getExternalIPAddress.ts +17 -0
  180. package/src/modules/utils/hashMerge.ts +63 -0
  181. package/src/modules/utils/isPlainObject.ts +45 -0
  182. package/src/modules/utils/isRunning.ts +7 -0
  183. package/src/modules/utils/parseCookies.ts +20 -0
  184. package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
  185. package/src/modules/utils/parseIPv6URI.ts +24 -0
  186. package/src/modules/utils/replaceDistWithSrc.ts +9 -0
  187. package/src/modules/utils/safeGlob.ts +6 -0
  188. package/src/modules/utils/sleep.ts +8 -0
  189. package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
  190. package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
  191. package/src/modules/utils.ts +66 -0
  192. package/src/server.ts +20 -0
  193. package/src/servers/web.ts +894 -0
  194. package/src/servers/websocket.ts +304 -0
  195. package/src/tasks/runAction.ts +29 -0
  196. package/tea.yaml +9 -0
  197. package/templates/README.md.template +17 -0
  198. package/templates/action.ts.template +15 -0
  199. package/templates/boot.js.template +9 -0
  200. package/templates/cli.ts.template +15 -0
  201. package/templates/gitignore.template +23 -0
  202. package/templates/initializer.ts.template +17 -0
  203. package/templates/package-plugin.json.template +12 -0
  204. package/templates/package.json.template +45 -0
  205. package/templates/projectMap.txt +39 -0
  206. package/templates/projectServer.ts.template +20 -0
  207. package/templates/server.ts.template +37 -0
  208. package/templates/task.ts.template +16 -0
  209. package/templates/test/action.ts.template +13 -0
  210. package/templates/test/task.ts.template +20 -0
  211. package/tsconfig.json +11 -0
@@ -0,0 +1,321 @@
1
+ import * as uuid from "uuid";
2
+ import { RouteType } from "../modules/route";
3
+ import { api, chatRoom } from "./../index";
4
+ import { config } from "./../modules/config";
5
+
6
+ export type ConnectionData = {
7
+ id?: string;
8
+ fingerprint?: string;
9
+ messageId?: string;
10
+ type: string;
11
+ rawConnection: any;
12
+ remotePort: number | string;
13
+ remoteIP: string;
14
+ canChat: boolean;
15
+ };
16
+
17
+ export const connectionVerbs = [
18
+ "quit",
19
+ "exit",
20
+ "paramAdd",
21
+ "paramDelete",
22
+ "paramView",
23
+ "paramsView",
24
+ "paramsDelete",
25
+ "roomAdd",
26
+ "roomLeave",
27
+ "roomView",
28
+ "detailsView",
29
+ "say",
30
+ ] as const;
31
+ export type ConnectionVerb = (typeof connectionVerbs)[number];
32
+
33
+ /**
34
+ * The generic representation of a connection for all server types is an Actionhero.Connection. You will never be creating these yourself via an action or task, but you will find them in your Actions and Action Middleware.
35
+ */
36
+ export class Connection {
37
+ /**A unique string identifer for this connection. */
38
+ id: string;
39
+ /** A unique string identifer for this connection, but common among subsequent requests. For example, all web requests from the same client have the same fingerprint, but not the same id */
40
+ fingerprint: string;
41
+ /**The type of this connection (web, websocket, etc) as defined by the name of the server which created it */
42
+ type: string;
43
+ /**Any rooms this connection is a member of, it it can chat */
44
+ rooms: Array<string>;
45
+ /**Can this connection use the chat system? */
46
+ canChat: boolean;
47
+ /**Any params this connection has saved for use in subsequent Actions. */
48
+ params: ConnectionParams;
49
+ /**How many actions are currently running for this connection? Most server types have a limit */
50
+ pendingActions: number;
51
+ /**How many actions has this connection run since it connected. */
52
+ totalActions: number;
53
+ /**The Id of the latest message this connection has sent to the server. */
54
+ messageId: string;
55
+ /**The timestamp of when this connection was created */
56
+ connectedAt: number;
57
+ /**The remote connection's IP address (as best as we can tell). May be either IPv4 or IPv6. */
58
+ remoteIP: string;
59
+ /**The remote connection's port. Related to connection.remoteIP */
60
+ remotePort: string | number;
61
+ /**Any connection-specific properties. For, example, the HTTP res and req objects for `web` connections are here */
62
+ rawConnection: any;
63
+ /**If there's a local error */
64
+ error?: NodeJS.ErrnoException;
65
+ /**If there's a local extension to the request*/
66
+ extension?: string;
67
+ destroyed: boolean;
68
+ /** storage for a response payload */
69
+ response?: Record<string, unknown>;
70
+ /** storage for session data */
71
+ session?: Record<string, any>;
72
+
73
+ // --- custom methods ---
74
+
75
+ /** for specHelper */
76
+ messages?: Array<{ message: string; [key: string]: any }>;
77
+
78
+ //** for web connections */
79
+ setHeader?: (key: string, value: string) => {};
80
+ setStatusCode?: (code: number) => {};
81
+ matchedRoute?: RouteType;
82
+ pipe?: Function;
83
+
84
+ /**
85
+ * @param data The specifics of this connection
86
+ * @param callCreateMethods The specifics of this connection will calls create methods in the constructor. This property will exist for backward compatibility. If you want to construct connection and call create methods within async, you can use `await Actionhero.Connection.createAsync(details)` for construction.
87
+ */
88
+ constructor(data: ConnectionData, callCreateMethods = true) {
89
+ this.setup(data);
90
+ if (callCreateMethods) Connection.callConnectionCreateMethods(this);
91
+ api.connections.connections[this.id] = this;
92
+ }
93
+
94
+ /**
95
+ * @param data The specifics of this connection
96
+ */
97
+ static async createAsync(data: ConnectionData) {
98
+ const connection = new this(data, false);
99
+ await this.callConnectionCreateMethods(connection);
100
+ return connection;
101
+ }
102
+
103
+ private static async callConnectionCreateMethods(connection: Connection) {
104
+ for (const i in api.connections.globalMiddleware) {
105
+ const middlewareName = api.connections.globalMiddleware[i];
106
+ if (
107
+ typeof api.connections.middleware[middlewareName].create === "function"
108
+ ) {
109
+ await api.connections.middleware[middlewareName].create(connection);
110
+ }
111
+ }
112
+ }
113
+
114
+ private setup(data: ConnectionData) {
115
+ (["type", "rawConnection"] as const).forEach((req) => {
116
+ if (!data[req]) {
117
+ throw new Error(`${req} is required to create a new connection object`);
118
+ }
119
+ });
120
+
121
+ if (config.general.enforceConnectionProperties) {
122
+ if (!data.remotePort && data.remotePort?.toString() !== "0")
123
+ throw new Error(
124
+ "remotePort is required to create a new connection object",
125
+ );
126
+ if (!data.remoteIP && data.remoteIP?.toString() !== "0")
127
+ throw new Error(
128
+ "remoteIP is required to create a new connection object",
129
+ );
130
+
131
+ this.type = data.type;
132
+ this.rawConnection = data.rawConnection;
133
+ this.id = data.id ?? this.generateID();
134
+ this.fingerprint = data.fingerprint ?? this.id;
135
+ this.remotePort = data.remotePort ?? 0;
136
+ this.remoteIP = data.remoteIP ?? "0";
137
+ this.messageId = data.messageId ?? "0";
138
+
139
+ this.connectedAt = new Date().getTime();
140
+ this.error = null;
141
+ this.rooms = [];
142
+ this.params = {};
143
+ this.session = {};
144
+ this.pendingActions = 0;
145
+ this.totalActions = 0;
146
+ this.canChat = data["canChat"];
147
+ this.destroyed = false;
148
+
149
+ const server = api.servers.servers[this.type];
150
+ if (server && server.connectionCustomMethods) {
151
+ for (const [name] of Object.entries(server.connectionCustomMethods)) {
152
+ //@ts-ignore
153
+ this.set(name, async (...args) => {
154
+ args.unshift(this);
155
+ return server.connectionCustomMethods[name].apply(null, args);
156
+ });
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Send a file to a connection (usually in the context of an Action). Be sure to set `data.toRender = false` in the action!
164
+ * Uses Server#processFile and will set `connection.params.file = path`
165
+ */
166
+ async sendFile(path: string) {
167
+ throw new Error("not implemented");
168
+ }
169
+
170
+ /**
171
+ * Send a message to a connection. Uses Server#sendMessage.
172
+ */
173
+ async sendMessage(message: string | object | Array<any>, verb?: string) {
174
+ throw new Error("not implemented");
175
+ }
176
+
177
+ private generateID() {
178
+ return uuid.v4();
179
+ }
180
+
181
+ /**
182
+ * Destroys the connection. If the type/sever of the connection has a goodbye message, it will be sent. The connection will be removed from all rooms. The connection's socket will be closed when possible.
183
+ */
184
+ async destroy() {
185
+ this.destroyed = true;
186
+
187
+ for (const i in api.connections.globalMiddleware) {
188
+ const middlewareName = api.connections.globalMiddleware[i];
189
+ if (
190
+ typeof api.connections.middleware[middlewareName].destroy === "function"
191
+ ) {
192
+ await api.connections.middleware[middlewareName].destroy(this);
193
+ }
194
+ }
195
+
196
+ if (this.canChat === true) {
197
+ const promises = [];
198
+ for (const i in this.rooms) {
199
+ const room = this.rooms[i];
200
+ promises.push(chatRoom.removeMember(this.id, room));
201
+ }
202
+ await Promise.all(promises);
203
+ }
204
+
205
+ const server = api.servers.servers[this.type];
206
+
207
+ if (server) {
208
+ if (server.attributes.logExits === true) {
209
+ server.log("connection closed", "info", { to: this.remoteIP });
210
+ }
211
+ if (typeof server.goodbye === "function") {
212
+ server.goodbye(this);
213
+ }
214
+ }
215
+
216
+ delete api.connections.connections[this.id];
217
+ }
218
+
219
+ private set(key: keyof typeof this, value: any) {
220
+ this[key] = value;
221
+ }
222
+
223
+ /**
224
+ * Try to run a verb command for a connection
225
+ */
226
+ async verbs(verb: string, words: string[] | string) {
227
+ let key: string;
228
+ let value: string;
229
+ let room: string;
230
+ const server = api.servers.servers[this.type];
231
+ const allowedVerbs = server.attributes.verbs;
232
+
233
+ if (!Array.isArray(words)) words = [words];
234
+
235
+ if (server && allowedVerbs.indexOf(verb) >= 0) {
236
+ server.log("verb", "debug", {
237
+ verb: verb,
238
+ to: this.remoteIP,
239
+ params: JSON.stringify(words),
240
+ });
241
+
242
+ // TODO: investigate allowedVerbs being an array of Constants or Symbols
243
+
244
+ switch (verb) {
245
+ case "quit":
246
+ case "exit":
247
+ return this.destroy();
248
+ case "paramAdd":
249
+ key = words[0];
250
+ value = words[1];
251
+ if (words[0] && words[0].indexOf("=") >= 0) {
252
+ const parts = words[0].split("=");
253
+ key = parts[0];
254
+ value = parts[1];
255
+ }
256
+
257
+ if (
258
+ config.general.disableParamScrubbing ||
259
+ api.params.postVariables.indexOf(key) >= 0
260
+ ) {
261
+ this.params[key] = value;
262
+ }
263
+ return;
264
+ case "paramDelete":
265
+ key = words[0];
266
+ delete this.params[key];
267
+ return;
268
+ case "paramView":
269
+ key = words[0];
270
+ return this.params[key];
271
+ case "paramsView":
272
+ return this.params;
273
+ case "paramsDelete":
274
+ for (const i in this.params) {
275
+ delete this.params[i];
276
+ }
277
+ return;
278
+ case "roomAdd":
279
+ room = words[0];
280
+ return chatRoom.addMember(this.id, room);
281
+ case "roomLeave":
282
+ room = words[0];
283
+ return chatRoom.removeMember(this.id, room);
284
+ case "roomView":
285
+ room = words[0];
286
+ if (this.rooms.indexOf(room) >= 0) {
287
+ return chatRoom.roomStatus(room);
288
+ }
289
+ throw new Error(await config.errors.connectionNotInRoom(this, room));
290
+ case "detailsView":
291
+ return {
292
+ id: this.id,
293
+ fingerprint: this.fingerprint,
294
+ remoteIP: this.remoteIP,
295
+ remotePort: this.remotePort,
296
+ params: this.params,
297
+ connectedAt: this.connectedAt,
298
+ rooms: this.rooms,
299
+ totalActions: this.totalActions,
300
+ pendingActions: this.pendingActions,
301
+ };
302
+ case "documentation":
303
+ return api.documentation.documentation;
304
+ case "say":
305
+ room = words.shift();
306
+ await api.chatRoom.broadcast(this, room, words.join(" "));
307
+ return;
308
+ }
309
+
310
+ const error = new Error(await config.errors.verbNotFound(this, verb));
311
+ throw error;
312
+ } else {
313
+ const error = new Error(await config.errors.verbNotAllowed(this, verb));
314
+ throw error;
315
+ }
316
+ }
317
+ }
318
+
319
+ export interface ConnectionParams {
320
+ [key: string]: any;
321
+ }
@@ -0,0 +1,9 @@
1
+ import type { ActionheroLogLevel } from "../modules/log";
2
+
3
+ export type ExceptionReporter = (
4
+ error: NodeJS.ErrnoException,
5
+ type: string,
6
+ name: string,
7
+ objects?: any,
8
+ severity?: ActionheroLogLevel,
9
+ ) => void;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Create a new Actionhero Initializer. The required properties of an initializer. These can be defined statically (this.name) or as methods which return a value.
3
+ */
4
+ export abstract class Initializer {
5
+ /**The name of the Initializer. */
6
+ name: string;
7
+ /**What order should this Initializer load at (Default: 1000, Actionhero core methods are < 1000) */
8
+ loadPriority?: number;
9
+ /**What order should this Initializer start at (Default: 1000, Actionhero core methods are < 1000) */
10
+ startPriority?: number;
11
+ /**What order should this Initializer stop at (Default: 1000, Actionhero core methods are < 1000) */
12
+ stopPriority?: number;
13
+
14
+ constructor() {
15
+ this.name = null;
16
+ this.loadPriority = 1000;
17
+ this.startPriority = 1000;
18
+ this.stopPriority = 1000;
19
+ }
20
+
21
+ /**
22
+ * Method run as part of the `initialize` lifecycle of your server. Usually sets api['YourNamespace']
23
+ */
24
+ async initialize?(): Promise<void>;
25
+
26
+ /**
27
+ * Method run as part of the `start` lifecycle of your server. Usually connects to remote servers or processes..
28
+ */
29
+ async start?(): Promise<void>;
30
+
31
+ /**
32
+ * Method run as part of the `initialize` lifecycle of your server. Usually disconnects from remote servers or processes.
33
+ */
34
+ async stop?(): Promise<void>;
35
+
36
+ validate() {
37
+ if (!this.name) {
38
+ throw new Error("name is required for this initializer");
39
+ }
40
+
41
+ const priorities = [
42
+ "loadPriority",
43
+ "startPriority",
44
+ "stopPriority",
45
+ ] as const;
46
+
47
+ priorities.forEach((priority) => {
48
+ if (
49
+ !this[priority] ||
50
+ typeof this[priority] !== "number" ||
51
+ this[priority] < 0
52
+ ) {
53
+ throw new Error(
54
+ `${priority} is a required property for the initializer \`${this.name}\``,
55
+ );
56
+ }
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,5 @@
1
+ import { Initializer } from "./initializer";
2
+
3
+ export interface Initializers {
4
+ [key: string]: Initializer;
5
+ }
@@ -0,0 +1,9 @@
1
+ export interface Input {
2
+ default?: any;
3
+ required?: boolean;
4
+ formatter?: Function | string[] | Function[];
5
+ validator?: Function | string[] | Function[];
6
+ schema?: {
7
+ [key: string]: any;
8
+ };
9
+ }
@@ -0,0 +1,34 @@
1
+ import { Input } from "./input";
2
+ import { Action } from "../classes/action";
3
+ import { Task } from "../classes/task";
4
+ import { CLI } from "../classes/cli";
5
+
6
+ export interface Inputs {
7
+ [key: string]: Input;
8
+ }
9
+ type ActionheroWithParams = Action | Task | CLI;
10
+
11
+ type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];
12
+ type FormatterOrString<I extends Input> = I["formatter"] extends (
13
+ ...args: any[]
14
+ ) => any
15
+ ? ReturnType<I["formatter"]>
16
+ : string;
17
+ type RequiredParamsKeys<A extends ActionheroWithParams> = KeysOfType<
18
+ A["inputs"],
19
+ Required
20
+ >;
21
+ type Variadic = { variadic: true };
22
+ type Required = Readonly<{ required: true }> | { required: true };
23
+
24
+ type ParamsExtractor<A extends ActionheroWithParams> = {
25
+ [Input in keyof A["inputs"]]: A["inputs"][Input] extends Variadic
26
+ ? FormatterOrString<A["inputs"][Input]>[]
27
+ : FormatterOrString<A["inputs"][Input]>;
28
+ };
29
+
30
+ export type ParamsFrom<A extends ActionheroWithParams> = Pick<
31
+ ParamsExtractor<A>,
32
+ RequiredParamsKeys<A>
33
+ > &
34
+ Partial<ParamsExtractor<A>>;
@@ -0,0 +1,15 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs";
3
+ import { PackageJson } from "type-fest";
4
+
5
+ const getPackageJson: () => PackageJson = () => {
6
+ return JSON.parse(
7
+ fs
8
+ .readFileSync(path.join(__dirname, "..", "..", "..", "package.json"))
9
+ .toString(),
10
+ );
11
+ };
12
+
13
+ export let actionheroVersion = getPackageJson().version;
14
+ export const recalculateActionheroVersion = () =>
15
+ (actionheroVersion = getPackageJson().version);
@@ -0,0 +1,16 @@
1
+ import { utils } from "../../modules/utils";
2
+
3
+ function determineNodeEnv(): string {
4
+ let env = "development";
5
+
6
+ if (utils.argv.NODE_ENV) {
7
+ env = utils.argv.NODE_ENV.toString();
8
+ } else if (process.env.NODE_ENV) {
9
+ env = process.env.NODE_ENV;
10
+ }
11
+
12
+ return env;
13
+ }
14
+
15
+ export let env = determineNodeEnv();
16
+ export const recalculateEnv = () => (env = determineNodeEnv());
@@ -0,0 +1,34 @@
1
+ import * as cluster from "cluster";
2
+ import { config } from "./../../modules/config";
3
+ import { utils } from "../../modules/utils";
4
+
5
+ /**
6
+ * I build this server's ID from the external IP address of this server and pid.
7
+ */
8
+ function determineId() {
9
+ let id = "";
10
+
11
+ if (utils.argv.title) {
12
+ id = utils.argv.title.toString();
13
+ } else if (process.env.ACTIONHERO_TITLE) {
14
+ id = process.env.ACTIONHERO_TITLE;
15
+ } else if (process.env.JEST_WORKER_ID) {
16
+ id = `test-server-${process.env.JEST_WORKER_ID || 0}`;
17
+ } else if (!config || !config.general.id) {
18
+ let externalIP = utils.getExternalIPAddress();
19
+ if (!externalIP) {
20
+ externalIP = "actionhero";
21
+ }
22
+
23
+ id = externalIP;
24
+ // @ts-ignore - we need to load * as for node v16 support
25
+ if (cluster["isWorker"]) id += `:${process.pid}`;
26
+ } else {
27
+ id = config.general.id;
28
+ }
29
+
30
+ return id;
31
+ }
32
+
33
+ export let id = determineId();
34
+ export const recalcuateId = () => (id = determineId());
@@ -0,0 +1,32 @@
1
+ import * as fs from "fs";
2
+ import { log } from "../../modules/log";
3
+ import { config } from "./../../modules/config";
4
+ import { id } from "./id";
5
+
6
+ function sanitizeId() {
7
+ let pidfile = String(id).trim();
8
+ pidfile = pidfile.replace(new RegExp(":", "g"), "-");
9
+ pidfile = pidfile.replace(new RegExp(" ", "g"), "_");
10
+
11
+ return pidfile;
12
+ }
13
+
14
+ const path = config.general.paths.pid[0]; // it would be silly to have more than one pid
15
+ let title = `actionhero-${sanitizeId()}`;
16
+
17
+ try {
18
+ fs.mkdirSync(path);
19
+ } catch (e) {}
20
+
21
+ export function writePidFile() {
22
+ log(`pid: ${process.pid}`, "notice");
23
+ fs.writeFileSync(path + "/" + title, process.pid.toString(), "ascii");
24
+ }
25
+
26
+ export function clearPidFile() {
27
+ try {
28
+ fs.unlinkSync(path + "/" + title);
29
+ } catch (error) {
30
+ log("Unable to remove pidfile", "error", error);
31
+ }
32
+ }
@@ -0,0 +1,16 @@
1
+ function determineProjectRoot() {
2
+ let projectRoot = process.cwd();
3
+ if (process.env.project_root) {
4
+ projectRoot = process.env.project_root;
5
+ } else if (process.env.projectRoot) {
6
+ projectRoot = process.env.projectRoot;
7
+ } else if (process.env.PROJECT_ROOT) {
8
+ projectRoot = process.env.PROJECT_ROOT;
9
+ }
10
+
11
+ return projectRoot;
12
+ }
13
+
14
+ export let projectRoot = determineProjectRoot();
15
+ export const recalculateProjectRoot = () =>
16
+ (projectRoot = determineProjectRoot());
@@ -0,0 +1,47 @@
1
+ import * as path from "path";
2
+
3
+ function isTypescript(): boolean {
4
+ // do we have any flags?
5
+ if (process.env.ACTIONHERO_TYPESCRIPT_MODE?.length > 0) {
6
+ return process.env.ACTIONHERO_TYPESCRIPT_MODE.toLowerCase() === "true"
7
+ ? true
8
+ : false;
9
+ }
10
+
11
+ // if this file is typescript, we are running typescript :D
12
+ // this is the best check, but fails when actionhero is compiled to js though...
13
+ const extension = path.extname(__filename);
14
+ if (extension === ".ts") return true;
15
+
16
+ // is the script that was executed a TS script? Check for a *.ts filename somewhere in the process arguments
17
+ for (const arg of process.argv) {
18
+ if (arg.match(/.+\.ts$/)) return true;
19
+ }
20
+
21
+ // are we running via ts-jest?
22
+ if (process.env.TS_JEST) return true;
23
+
24
+ // are we running via a ts-node/ts-node-dev shim?
25
+ const lastArg = process.execArgv[process.execArgv.length - 1];
26
+ if (lastArg && path.parse(lastArg).name.indexOf("ts-node") >= 0) {
27
+ return true;
28
+ }
29
+ try {
30
+ /**
31
+ * Are we running in typescript at the moment?
32
+ * see https://github.com/TypeStrong/ts-node/pull/858 for more details
33
+ */
34
+
35
+ // @ts-ignore
36
+ if (process[Symbol.for("ts-node.register.instance")]) return true;
37
+ } catch (error) {
38
+ console.error(error);
39
+ return false;
40
+ }
41
+
42
+ // We didn't find a reason to suspect we are running TS, so return false
43
+ return false;
44
+ }
45
+
46
+ export let typescript = isTypescript();
47
+ export const recalculateIsTypescript = () => (typescript = isTypescript());