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,119 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+
4
+ /**
5
+ * Check if a directory exists.
6
+ */
7
+ export function dirExists(dir: string): boolean {
8
+ try {
9
+ const stats = fs.lstatSync(dir);
10
+ return stats.isDirectory() || stats.isSymbolicLink();
11
+ } catch (e) {
12
+ return false;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Check if a file exists.
18
+ */
19
+ export function fileExists(file: string): boolean {
20
+ try {
21
+ const stats = fs.lstatSync(file);
22
+ return stats.isFile() || stats.isSymbolicLink();
23
+ } catch (e) {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Create a directory, only if it doesn't exist yet.
30
+ * Throws an error if the directory already exists, or encounters a filesystem problem.
31
+ */
32
+ export function createDirSafely(dir: string): string {
33
+ if (dirExists(dir)) {
34
+ const error = new Error(
35
+ `directory '${path.normalize(dir)}' already exists`,
36
+ );
37
+ // @ts-ignore
38
+ error.code = "EEXIST";
39
+ throw error;
40
+ } else {
41
+ fs.mkdirSync(path.normalize(dir), "0766");
42
+ return `created directory '${path.normalize(dir)}'`;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Create a file, only if it doesn't exist yet.
48
+ * Throws an error if the file already exists, or encounters a filesystem problem.
49
+ */
50
+ export function createFileSafely(
51
+ file: string,
52
+ data: string | NodeJS.ArrayBufferView,
53
+ overwrite: boolean = false,
54
+ ): string {
55
+ if (fileExists(file) && !overwrite) {
56
+ const error = new Error(`file '${path.normalize(file)}' already exists`);
57
+ // @ts-ignore
58
+ error.code = "EEXIST";
59
+ throw error;
60
+ } else {
61
+ let message = `wrote file '${path.normalize(file)}'`;
62
+ if (overwrite && fileExists(file)) {
63
+ message = ` - overwritten file '${path.normalize(file)}'`;
64
+ }
65
+ fs.writeFileSync(path.normalize(file), data);
66
+ return message;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Create an Actionhero LinkFile, only if it doesn't exist yet.
72
+ * Throws an error if the file already exists, or encounters a filesystem problem.
73
+ */
74
+ export function createLinkfileSafely(filePath: string, type: string): string {
75
+ if (fileExists(filePath)) {
76
+ const error = new Error(`link file '${filePath}' already exists`);
77
+ // @ts-ignore
78
+ error.code = "EEXIST";
79
+ throw error;
80
+ } else {
81
+ fs.writeFileSync(filePath, type);
82
+ return `creating linkfile '${filePath}'`;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Remove an Actionhero LinkFile, only if it exists.
88
+ * Throws an error if the file does not exist, or encounters a filesystem problem.
89
+ */
90
+ export function removeLinkfileSafely(filePath: string): string {
91
+ if (!fileExists(filePath)) {
92
+ const error = new Error(`link file '${filePath}' doesn't exist`);
93
+ // @ts-ignore
94
+ error.code = "ENOEXIST";
95
+ throw error;
96
+ } else {
97
+ fs.unlinkSync(filePath);
98
+ return `removing linkfile '${filePath}'`;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Create a system symbolic link.
104
+ * Throws an error if it encounters a filesystem problem.
105
+ */
106
+ export function createSymlinkSafely(
107
+ destination: string,
108
+ source: string,
109
+ ): string {
110
+ if (dirExists(destination)) {
111
+ const error = new Error(`symbolic link '${destination}' already exists`);
112
+ // @ts-ignore
113
+ error.code = "EEXIST";
114
+ throw error;
115
+ } else {
116
+ fs.symlinkSync(source, destination, "dir");
117
+ return `creating symbolic link '${destination}' => '${source}'`;
118
+ }
119
+ }
@@ -0,0 +1,51 @@
1
+ import { isPlainObject } from "./isPlainObject";
2
+ import { deepCopy } from "./deepCopy";
3
+ import { config } from "../config";
4
+ import * as dotProp from "dot-prop";
5
+
6
+ /**
7
+ * Prepares acton params for logging.
8
+ * Hides any sensitive data as defined by `api.config.general.filteredParams`
9
+ * Truncates long strings via `api.config.logger.maxLogStringLength`
10
+ */
11
+ export function filterObjectForLogging(
12
+ params: Record<string, any>,
13
+ ): Record<string, any> {
14
+ params = deepCopy(params);
15
+ const sanitizedParams: Record<string, any> = {};
16
+
17
+ for (const i in params) {
18
+ if (
19
+ Array.isArray(params[i]) &&
20
+ params[i].length > (config.logger?.maxLogArrayLength ?? 10)
21
+ ) {
22
+ params[i] = `${params[i].length} items`;
23
+ }
24
+
25
+ if (isPlainObject(params[i])) {
26
+ sanitizedParams[i] = params[i];
27
+ } else if (typeof params[i] === "string") {
28
+ sanitizedParams[i] = params[i].substring(
29
+ 0,
30
+ config.logger.maxLogStringLength,
31
+ );
32
+ } else {
33
+ sanitizedParams[i] = params[i];
34
+ }
35
+ }
36
+
37
+ let filteredParams: string[];
38
+ if (typeof config.general.filteredParams === "function") {
39
+ filteredParams = config.general.filteredParams();
40
+ } else {
41
+ filteredParams = config.general.filteredParams;
42
+ }
43
+
44
+ filteredParams.forEach((configParam) => {
45
+ if (dotProp.get(params, configParam) !== undefined) {
46
+ dotProp.set(sanitizedParams, configParam, "[FILTERED]");
47
+ }
48
+ });
49
+
50
+ return sanitizedParams;
51
+ }
@@ -0,0 +1,53 @@
1
+ import { isPlainObject } from "./isPlainObject";
2
+ import { deepCopy } from "./deepCopy";
3
+ import { config } from "../config";
4
+ import * as dotProp from "dot-prop";
5
+
6
+ /**
7
+ * Prepares acton response for logging.
8
+ * Hides any sensitive data as defined by `api.config.general.filteredResponse`
9
+ * Truncates long strings via `api.config.logger.maxLogStringLength`
10
+ */
11
+ export function filterResponseForLogging(response: Record<string, any>): {
12
+ [key: string]: any;
13
+ } {
14
+ response = deepCopy(response);
15
+ const sanitizedResponse: Record<string, any> = {};
16
+
17
+ for (const i in response) {
18
+ if (
19
+ Array.isArray(response[i]) &&
20
+ response[i].length > (config.logger?.maxLogArrayLength ?? 10)
21
+ ) {
22
+ response[i] = `${response[i].length} items`;
23
+ }
24
+
25
+ if (isPlainObject(response[i])) {
26
+ sanitizedResponse[i] = response[i];
27
+ } else if (typeof response[i] === "string") {
28
+ sanitizedResponse[i] = response[i].substring(
29
+ 0,
30
+ config.logger.maxLogStringLength,
31
+ );
32
+ } else if (response[i] instanceof Error) {
33
+ sanitizedResponse[i] = response[i].message ?? String(response[i]);
34
+ } else {
35
+ sanitizedResponse[i] = response[i];
36
+ }
37
+ }
38
+
39
+ let filteredResponse: string[];
40
+ if (typeof config.general.filteredResponse === "function") {
41
+ filteredResponse = config.general.filteredResponse();
42
+ } else {
43
+ filteredResponse = config.general.filteredResponse;
44
+ }
45
+
46
+ filteredResponse.forEach((configParam) => {
47
+ if (dotProp.get(response, configParam) !== undefined) {
48
+ dotProp.set(sanitizedResponse, configParam, "[FILTERED]");
49
+ }
50
+ });
51
+
52
+ return sanitizedResponse;
53
+ }
@@ -0,0 +1,17 @@
1
+ import * as os from "os";
2
+
3
+ /**
4
+ * Returns this server's external/public IP address
5
+ */
6
+ export function getExternalIPAddress(): string {
7
+ const interfaces = os.networkInterfaces();
8
+ let ip = null;
9
+ for (const dev in interfaces) {
10
+ interfaces[dev].forEach((details) => {
11
+ if (details.family === "IPv4" && details.address !== "127.0.0.1") {
12
+ ip = details.address;
13
+ }
14
+ });
15
+ }
16
+ return ip;
17
+ }
@@ -0,0 +1,63 @@
1
+ import { isPlainObject } from "./isPlainObject";
2
+
3
+ /**
4
+ * Recursively merge 2 Objects together. Will resolve functions if they are present, unless the parent Object has the property `_toExpand = false`.
5
+ * Actionhero uses this internally to construct and resolve the config.
6
+ * Matching keys in B override A.
7
+ */
8
+ export function hashMerge(
9
+ a: Record<string, any>,
10
+ b: Record<string, any>,
11
+ arg?: Record<string, any>,
12
+ ): { [key: string]: any } {
13
+ const c: Record<string, any> = {};
14
+ let i: string;
15
+ let response: object;
16
+
17
+ for (i in a) {
18
+ if (isPlainObject(a[i])) {
19
+ // can't be added into above condition, or empty objects will overwrite and not merge
20
+ // also make sure empty objects are created
21
+ c[i] = Object.keys(a[i]).length > 0 ? hashMerge(c[i], a[i], arg) : {};
22
+ } else {
23
+ if (typeof a[i] === "function") {
24
+ response = a[i](arg);
25
+ if (isPlainObject(response)) {
26
+ c[i] = hashMerge(c[i], response, arg);
27
+ } else {
28
+ c[i] = response;
29
+ }
30
+ } else {
31
+ // don't create first term if it is undefined or null
32
+ if (a[i] === undefined || a[i] === null) {
33
+ } else c[i] = a[i];
34
+ }
35
+ }
36
+ }
37
+ for (i in b) {
38
+ if (isPlainObject(b[i])) {
39
+ // can't be added into above condition, or empty objects will overwrite and not merge
40
+ if (Object.keys(b[i]).length > 0) c[i] = hashMerge(c[i], b[i], arg);
41
+ // make sure empty objects are only created, when no key exists yet
42
+ else if (!(i in c)) c[i] = {};
43
+ } else {
44
+ if (typeof b[i] === "function") {
45
+ response = b[i](arg);
46
+ if (isPlainObject(response)) {
47
+ c[i] = hashMerge(c[i], response, arg);
48
+ } else {
49
+ c[i] = response;
50
+ }
51
+ } else {
52
+ // ignore second term if it is undefined
53
+ if (b[i] === undefined) {
54
+ } else if (b[i] == null && i in c)
55
+ // delete second term/key if value is null and it already exists
56
+ delete c[i];
57
+ // normal assignments for everything else
58
+ else c[i] = b[i];
59
+ }
60
+ }
61
+ }
62
+ return c;
63
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Is the JS Object passed in truly just an object?
3
+ */
4
+ export function isPlainObject(o: any) {
5
+ const safeTypes = [
6
+ Boolean,
7
+ Number,
8
+ String,
9
+ Function,
10
+ Array,
11
+ Date,
12
+ RegExp,
13
+ Buffer,
14
+ ];
15
+ const safeInstances = ["boolean", "number", "string", "function"];
16
+ const expandPreventMatchKey = "_toExpand"; // set `_toExpand = false` within an object if you don't want to expand it
17
+ let i;
18
+
19
+ if (!o) {
20
+ return false;
21
+ }
22
+ if (o instanceof Object === false) {
23
+ return false;
24
+ }
25
+ for (i in safeTypes) {
26
+ if (o instanceof safeTypes[i]) {
27
+ return false;
28
+ }
29
+ }
30
+ for (i in safeInstances) {
31
+ if (typeof o === safeInstances[i]) {
32
+ return false;
33
+ }
34
+ }
35
+ if (o[expandPreventMatchKey] === false) {
36
+ return false;
37
+ }
38
+
39
+ try {
40
+ // sometimes objects cannot be successfully stringified (https://github.com/node-formidable/formidable/pull/796)
41
+ return o.toString() === "[object Object]";
42
+ } catch (error) {
43
+ return true;
44
+ }
45
+ }
@@ -0,0 +1,7 @@
1
+ export function isRunning(pid: number) {
2
+ try {
3
+ return process.kill(pid, 0);
4
+ } catch (e) {
5
+ return e.code === "EPERM";
6
+ }
7
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Transform the cookie headers of a node HTTP `req` Object into a hash.
3
+ */
4
+ export function parseCookies(req: {
5
+ headers: { [key: string]: string | string[] };
6
+ }): object {
7
+ const cookies: Record<string, string> = {};
8
+ if (req.headers.cookie) {
9
+ (Array.isArray(req.headers.cookie)
10
+ ? req.headers.cookie.join("")
11
+ : req.headers.cookie
12
+ )
13
+ .split(";")
14
+ .forEach((cookie) => {
15
+ const parts = cookie.split("=");
16
+ cookies[parts[0].trim()] = (parts[1] || "").trim();
17
+ });
18
+ }
19
+ return cookies;
20
+ }
@@ -0,0 +1,53 @@
1
+ import { parseIPv6URI } from "./parseIPv6URI";
2
+
3
+ /**
4
+ * Return ip and port information if defined in the header
5
+ */
6
+ export function parseHeadersForClientAddress(
7
+ headers: Record<string, string | string[]>,
8
+ ) {
9
+ let ip: string;
10
+ let port: number | string;
11
+
12
+ if (headers["x-forwarded-for"]) {
13
+ let parts;
14
+ let forwardedIp = Array.isArray(headers["x-forwarded-for"])
15
+ ? headers["x-forwarded-for"][0].split(",")[0]
16
+ : headers["x-forwarded-for"].split(",")[0];
17
+ if (
18
+ forwardedIp.indexOf(".") >= 0 ||
19
+ (forwardedIp.indexOf(".") < 0 && forwardedIp.indexOf(":") < 0)
20
+ ) {
21
+ // IPv4
22
+ forwardedIp = forwardedIp.replace("::ffff:", ""); // remove any IPv6 information, ie: '::ffff:127.0.0.1'
23
+ parts = forwardedIp.split(":");
24
+ if (parts[0]) {
25
+ ip = parts[0];
26
+ }
27
+ if (parts[1]) {
28
+ port = parts[1];
29
+ }
30
+ } else {
31
+ // IPv6
32
+ parts = parseIPv6URI(forwardedIp);
33
+ if (parts.host) {
34
+ ip = parts.host;
35
+ }
36
+ if (parts.port) {
37
+ port = parts.port;
38
+ }
39
+ }
40
+ }
41
+ if (headers["x-forwarded-port"]) {
42
+ port = Array.isArray(headers["x-forwarded-port"])
43
+ ? headers["x-forwarded-port"][0]
44
+ : headers["x-forwarded-port"];
45
+ }
46
+ if (headers["x-real-ip"]) {
47
+ // https://distinctplace.com/2014/04/23/story-behind-x-forwarded-for-and-x-real-ip-headers/
48
+ ip = Array.isArray(headers["x-real-ip"])
49
+ ? headers["x-real-ip"][0]
50
+ : headers["x-real-ip"];
51
+ }
52
+ return { ip, port };
53
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Parse an IPv6 address, returning both host and port.
3
+ * see https://github.com/actionhero/actionhero/issues/275
4
+ */
5
+ export function parseIPv6URI(addr: string): {
6
+ host: string;
7
+ port: number;
8
+ } {
9
+ let host = "::1";
10
+ let port = "80";
11
+ const regexp = new RegExp(/\[([0-9a-f:]+(?:%.+)?)]:([0-9]{1,5})/);
12
+ // if we have brackets parse them and find a port
13
+ if (addr.indexOf("[") > -1 && addr.indexOf("]") > -1) {
14
+ const res = regexp.exec(addr);
15
+ if (res === null) {
16
+ throw new Error("failed to parse address");
17
+ }
18
+ host = res[1];
19
+ port = res[2];
20
+ } else {
21
+ host = addr;
22
+ }
23
+ return { host: host, port: parseInt(port, 10) };
24
+ }
@@ -0,0 +1,9 @@
1
+ import { config } from "../config";
2
+
3
+ /**
4
+ * Used by generator functions running from your `dist`, it replaces the path with your `src`
5
+ * Relies on api.config.general.paths
6
+ */
7
+ export function replaceDistWithSrc(f: string) {
8
+ return f.replace(config.general.paths.dist, config.general.paths.src);
9
+ }
@@ -0,0 +1,6 @@
1
+ import * as glob from "glob";
2
+
3
+ export function safeGlobSync(match: string, args: glob.IOptions = {}) {
4
+ const isWindows = process.platform === "win32";
5
+ return glob.sync(match, { ...args, windowsPathsNoEscape: isWindows });
6
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ Sleep with a Promise
3
+ */
4
+ export function sleep(time: number): Promise<void> {
5
+ return new Promise((resolve) => {
6
+ setTimeout(resolve, time);
7
+ });
8
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Sorts an Array of Objects with a priority key
3
+ */
4
+ export function sortGlobalMiddleware(
5
+ globalMiddlewareList: Array<any>,
6
+ middleware: {
7
+ [key: string]: any;
8
+ },
9
+ ) {
10
+ globalMiddlewareList.sort((a, b) => {
11
+ if (middleware[a].priority > middleware[b].priority) {
12
+ return 1;
13
+ } else {
14
+ return -1;
15
+ }
16
+ });
17
+ }
@@ -0,0 +1,29 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+
4
+ export function sourceRelativeLinkPath(
5
+ linkFile: string,
6
+ pluginPaths: Array<string>,
7
+ ): string | boolean {
8
+ const type = fs.readFileSync(linkFile).toString();
9
+ const pathParts = linkFile.split(path.sep);
10
+ const name = pathParts[pathParts.length - 1].split(".")[0];
11
+ const pathsToTry = pluginPaths.slice(0);
12
+ let pluginRoot: string;
13
+
14
+ pathsToTry.forEach((pluginPath) => {
15
+ const pluginPathAttempt = path.normalize(pluginPath + path.sep + name);
16
+ try {
17
+ const stats = fs.lstatSync(pluginPathAttempt);
18
+ if (!pluginRoot && (stats.isDirectory() || stats.isSymbolicLink())) {
19
+ pluginRoot = pluginPathAttempt;
20
+ }
21
+ } catch (e) {}
22
+ });
23
+
24
+ if (!pluginRoot) {
25
+ return false;
26
+ }
27
+ const pluginSection = path.normalize(pluginRoot + path.sep + type);
28
+ return pluginSection;
29
+ }
@@ -0,0 +1,66 @@
1
+ import { argv } from "./utils/argv";
2
+ import { arrayStartingMatch } from "./utils/arrayStartingMatch";
3
+ import { arrayUnique } from "./utils/arrayUnique";
4
+ import { collapseObjectToArray } from "./utils/collapseObjectToArray";
5
+ import { deepCopy } from "./utils/deepCopy";
6
+ import { ensureNoTsHeaderOrSpecFiles } from "./utils/ensureNoTsHeaderOrSpecFiles";
7
+ import { eventLoopDelay } from "./utils/eventLoopDelay";
8
+ import { filterObjectForLogging } from "./utils/filterObjectForLogging";
9
+ import { filterResponseForLogging } from "./utils/filterResponseForLogging";
10
+ import { getExternalIPAddress } from "./utils/getExternalIPAddress";
11
+ import { hashMerge } from "./utils/hashMerge";
12
+ import { isPlainObject } from "./utils/isPlainObject";
13
+ import { parseHeadersForClientAddress } from "./utils/parseHeadersForClientAddress";
14
+ import { parseCookies } from "./utils/parseCookies";
15
+ import { parseIPv6URI } from "./utils/parseIPv6URI";
16
+ import { replaceDistWithSrc } from "./utils/replaceDistWithSrc";
17
+ import { sleep } from "./utils/sleep";
18
+ import { sortGlobalMiddleware } from "./utils/sortGlobalMiddleware";
19
+ import { sourceRelativeLinkPath } from "./utils/sourceRelativeLinkPath";
20
+ import {
21
+ dirExists,
22
+ fileExists,
23
+ createDirSafely,
24
+ createFileSafely,
25
+ createLinkfileSafely,
26
+ removeLinkfileSafely,
27
+ createSymlinkSafely,
28
+ } from "./utils/fileUtils";
29
+ import { isRunning } from "./utils/isRunning";
30
+ import { safeGlobSync } from "./utils/safeGlob";
31
+
32
+ /**
33
+ * Utility functions for Actionhero
34
+ */
35
+ export const utils = {
36
+ argv,
37
+ arrayStartingMatch,
38
+ arrayUnique,
39
+ collapseObjectToArray,
40
+ deepCopy,
41
+ ensureNoTsHeaderOrSpecFiles,
42
+ eventLoopDelay,
43
+ filterObjectForLogging,
44
+ filterResponseForLogging,
45
+ getExternalIPAddress,
46
+ hashMerge,
47
+ isPlainObject,
48
+ parseHeadersForClientAddress,
49
+ parseCookies,
50
+ parseIPv6URI,
51
+ replaceDistWithSrc,
52
+ sleep,
53
+ sortGlobalMiddleware,
54
+ sourceRelativeLinkPath,
55
+ fileUtils: {
56
+ dirExists,
57
+ fileExists,
58
+ createDirSafely,
59
+ createFileSafely,
60
+ createLinkfileSafely,
61
+ removeLinkfileSafely,
62
+ createSymlinkSafely,
63
+ },
64
+ safeGlobSync,
65
+ isRunning,
66
+ };
package/src/server.ts ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ // load any custom code, configure the env, as needed
4
+
5
+ async function main() {
6
+ // create a new actionhero process
7
+ const { Process } = await import("./index");
8
+ const app = new Process();
9
+
10
+ // handle unix signals and uncaught exceptions & rejections
11
+ app.registerProcessSignals((exitCode) => {
12
+ process.exit(exitCode);
13
+ });
14
+
15
+ // start the app!
16
+ // you can pass custom configuration to the process as needed
17
+ await app.start();
18
+ }
19
+
20
+ main();