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,37 @@
1
+ import { Process, specHelper } from "./../../src/index";
2
+ import { RunAction } from "../../src/tasks/runAction";
3
+
4
+ describe("Test: RunAction", () => {
5
+ const actionhero = new Process();
6
+
7
+ beforeAll(async () => await actionhero.start());
8
+ afterAll(async () => await actionhero.stop());
9
+
10
+ test("can run the task without params", async () => {
11
+ const { randomNumber } = await specHelper.runTask<RunAction>("runAction", {
12
+ action: "randomNumber",
13
+ });
14
+ expect(randomNumber).toBeGreaterThanOrEqual(0);
15
+ expect(randomNumber).toBeLessThan(1);
16
+ });
17
+
18
+ test("can run the task with params", async () => {
19
+ const { cacheTestResults } = await specHelper.runTask<RunAction>(
20
+ "runAction",
21
+ {
22
+ action: "cacheTest",
23
+ params: { key: "testKey", value: "testValue" },
24
+ },
25
+ );
26
+ expect(cacheTestResults.saveResp).toBe(true);
27
+ expect(cacheTestResults.deleteResp).toBe(true);
28
+ });
29
+
30
+ test("will throw with errors", async () => {
31
+ await expect(
32
+ specHelper.runTask<RunAction>("runAction", {
33
+ action: "cacheTest",
34
+ }),
35
+ ).rejects.toThrow(/key is a required parameter for this action/);
36
+ });
37
+ });
@@ -0,0 +1,20 @@
1
+ import { Process, env, id, specHelper } from "actionhero";
2
+ import { Status } from "../../src/actions/status";
3
+
4
+ describe("actionhero Tests", () => {
5
+ const actionhero = new Process();
6
+
7
+ beforeAll(async () => await actionhero.start());
8
+ afterAll(async () => await actionhero.stop());
9
+
10
+ test("should have booted into the test env", () => {
11
+ expect(process.env.NODE_ENV).toEqual("test");
12
+ expect(env).toEqual("test");
13
+ expect(id).toBeTruthy();
14
+ });
15
+
16
+ test("can retrieve server uptime via the status action", async () => {
17
+ const { uptime } = await specHelper.runAction<Status>("status");
18
+ expect(uptime).toBeGreaterThan(0);
19
+ });
20
+ });
@@ -0,0 +1,44 @@
1
+ import { CLI, ParamsFrom } from "./../../src/index";
2
+
3
+ export class HelloCliTest extends CLI {
4
+ name = "hello";
5
+ description = "I work";
6
+ inputs = {
7
+ name: {
8
+ required: false,
9
+ requiredValue: true,
10
+ default: "World",
11
+ },
12
+ title: {
13
+ letter: "t",
14
+ required: true,
15
+ default: "Dr.",
16
+ formatter: (val: string) => {
17
+ return `${val}.`;
18
+ },
19
+ validator: (val: string) => {
20
+ const parts = val.split(".");
21
+ if (parts.length > 2) throw new Error("too many periods");
22
+ },
23
+ },
24
+ countries: {
25
+ variadic: true as true,
26
+ formatter: (val: string) => `${val}!`,
27
+ validator: (val: string) => {
28
+ if (val.length > 0 && val[0].toUpperCase() !== val[0])
29
+ throw new Error("country not capitalized");
30
+ },
31
+ },
32
+ };
33
+
34
+ async run({ params }: { params: Partial<ParamsFrom<HelloCliTest>> }) {
35
+ const sayHello = (title: string, name: string, countries: string[]) =>
36
+ console.log(
37
+ `Hello, ${title} ${name} ${
38
+ countries ? `(${countries.join(" ")})` : ""
39
+ }`,
40
+ );
41
+ sayHello(params.title!, params.name!, params.countries!);
42
+ return true;
43
+ }
44
+ }
@@ -0,0 +1 @@
1
+ <h1>PLUGIN!<h1>
@@ -0,0 +1,14 @@
1
+ import { Action } from "../../../../src/index";
2
+
3
+ export default class PluginAction extends Action {
4
+ constructor() {
5
+ super();
6
+ this.name = "pluginAction";
7
+ this.description = "pluginAction";
8
+ this.outputExample = {};
9
+ }
10
+
11
+ async run({ response }) {
12
+ response.cool = true;
13
+ }
14
+ };
@@ -0,0 +1,22 @@
1
+ import { CLI } from "../../../../src/index";
2
+
3
+ export class Hello extends CLI {
4
+ constructor() {
5
+ super();
6
+ this.name = "hello";
7
+ this.description = "I say hello";
8
+ this.inputs={
9
+ name: {
10
+ required: true,
11
+ description: 'Who we are greeting',
12
+ letter: 'g',
13
+ default: 'Actionhero'
14
+ }
15
+ }
16
+ }
17
+
18
+ async run({params}) {
19
+ console.log(`Hello, ${params.name}`);
20
+ return true;
21
+ }
22
+ }
@@ -0,0 +1,17 @@
1
+ import { api, Initializer } from "../../../../src/index";
2
+
3
+ export class PluginInitializer extends Initializer {
4
+ constructor() {
5
+ super();
6
+ this.name = "pluginInitializer";
7
+ }
8
+
9
+ async initialize() {
10
+ api.pluginInitializer = { here: true };
11
+ }
12
+
13
+ async stop() {
14
+ // this seems silly, but is needed for testing, as we never clear properties on the API object
15
+ delete api.pluginInitializer;
16
+ }
17
+ }
@@ -0,0 +1,15 @@
1
+ import { Task } from "../../../../src/index";
2
+
3
+ export class PluginTask extends Task {
4
+ constructor() {
5
+ super();
6
+ this.name = "pluginTask";
7
+ this.description = "pluginTask";
8
+ this.frequency = 0;
9
+ this.queue = "default";
10
+ }
11
+
12
+ async run(params) {
13
+ return true;
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": ".",
4
+ "outDir": "./dist",
5
+ "allowJs": true,
6
+ "module": "commonjs",
7
+ "target": "es2018"
8
+ },
9
+ "include": ["./src/**/*"]
10
+ }
@@ -0,0 +1,492 @@
1
+ import * as glob from "glob";
2
+ import * as path from "path";
3
+ import { config, utils } from "./../../src/index";
4
+
5
+ describe("Utils", () => {
6
+ describe("util.sleep", () => {
7
+ test("it sleeps", async () => {
8
+ const start = new Date().getTime();
9
+ await utils.sleep(100);
10
+ const end = new Date().getTime();
11
+ expect(end - start).toBeGreaterThanOrEqual(99);
12
+ expect(end - start).toBeLessThan(200);
13
+ });
14
+ });
15
+
16
+ describe("utils.arrayUnique", () => {
17
+ test("works", () => {
18
+ const a = [1, 2, 3, 3, 4, 4, 4, 5, 5, 5];
19
+ expect(utils.arrayUnique(a)).toEqual([1, 2, 3, 4, 5]);
20
+ });
21
+ });
22
+
23
+ describe("utils.collapseObjectToArray", () => {
24
+ test("fails with numerical keys", () => {
25
+ const o = { 0: "a", 1: "b" };
26
+ const response = utils.collapseObjectToArray(o);
27
+ expect(response).toEqual(["a", "b"]);
28
+ });
29
+
30
+ test("fails with non-numerical keys", () => {
31
+ const o = { a: 1 };
32
+ const response = utils.collapseObjectToArray(o);
33
+ expect(response).toEqual(false);
34
+ });
35
+ });
36
+
37
+ describe("utils.hashMerge", () => {
38
+ const A = { a: 1, b: 2 };
39
+ const B = { b: -2, c: 3 };
40
+ const C = { a: 1, b: { m: 10, n: 11 } };
41
+ const D = { a: 1, b: { n: 111, o: 22, p: {} } };
42
+ const E = { b: {} };
43
+ const N = { b: null } as Record<string, any>;
44
+ const U = { b: undefined } as Record<string, any>;
45
+
46
+ test("simple", () => {
47
+ const Z = utils.hashMerge(A, B);
48
+ expect(Z.a).toEqual(1);
49
+ expect(Z.b).toEqual(-2);
50
+ expect(Z.c).toEqual(3);
51
+ });
52
+
53
+ test("directional", () => {
54
+ const Z = utils.hashMerge(B, A);
55
+ expect(Z.a).toEqual(1);
56
+ expect(Z.b).toEqual(2);
57
+ expect(Z.c).toEqual(3);
58
+ });
59
+
60
+ test("nested", () => {
61
+ const Z = utils.hashMerge(C, D);
62
+ expect(Z.a).toEqual(1);
63
+ expect(Z.b.m).toEqual(10);
64
+ expect(Z.b.n).toEqual(111);
65
+ expect(Z.b.o).toEqual(22);
66
+ expect(Z.b.p).toEqual({});
67
+ });
68
+
69
+ test("empty01", () => {
70
+ const Z = utils.hashMerge(E, D);
71
+ expect(Z.a).toEqual(1);
72
+ expect(Z.b.n).toEqual(111);
73
+ expect(Z.b.o).toEqual(22);
74
+ expect(Z.b.p).toEqual({});
75
+ });
76
+
77
+ test("empty10", () => {
78
+ const Z = utils.hashMerge(D, E);
79
+ expect(Z.a).toEqual(1);
80
+ expect(Z.b.n).toEqual(111);
81
+ expect(Z.b.o).toEqual(22);
82
+ expect(Z.b.p).toEqual({});
83
+ });
84
+
85
+ test("chained", () => {
86
+ const Z = utils.hashMerge(utils.hashMerge(C, E), D);
87
+ expect(Z.a).toEqual(1);
88
+ expect(Z.b.m).toEqual(10);
89
+ expect(Z.b.n).toEqual(111);
90
+ expect(Z.b.o).toEqual(22);
91
+ expect(Z.b.p).toEqual({});
92
+ });
93
+
94
+ test("null", () => {
95
+ const Z = utils.hashMerge(A, N);
96
+ expect(Z.a).toEqual(1);
97
+ expect(Z.b).toBeUndefined();
98
+ });
99
+
100
+ test("undefined", () => {
101
+ const Z = utils.hashMerge(A, U);
102
+ expect(Z.a).toEqual(1);
103
+ expect(Z.b).toEqual(2);
104
+ });
105
+ });
106
+
107
+ describe("eventLoopDelay", () => {
108
+ test("works", async () => {
109
+ const delay = await utils.eventLoopDelay(10000);
110
+ expect(delay).toBeGreaterThan(0);
111
+ expect(delay).toBeLessThan(1);
112
+ });
113
+ });
114
+
115
+ describe("#parseHeadersForClientAddress", () => {
116
+ test("only x-real-ip, port is null", () => {
117
+ const headers = {
118
+ "x-real-ip": "10.11.12.13",
119
+ };
120
+ const { ip, port } = utils.parseHeadersForClientAddress(headers);
121
+ expect(ip).toEqual("10.11.12.13");
122
+ expect(port).toBeFalsy();
123
+ });
124
+ test("load balancer, x-forwarded-for format", () => {
125
+ const headers = {
126
+ "x-forwarded-for": "35.36.37.38",
127
+ "x-forwarded-port": "80",
128
+ };
129
+ const { ip, port } = utils.parseHeadersForClientAddress(headers);
130
+ expect(ip).toEqual("35.36.37.38");
131
+ expect(port).toEqual("80");
132
+ });
133
+ });
134
+
135
+ describe("#parseIPv6URI", () => {
136
+ test("address and port", () => {
137
+ const uri = "[2604:4480::5]:8080";
138
+ const parts = utils.parseIPv6URI(uri);
139
+ expect(parts.host).toEqual("2604:4480::5");
140
+ expect(parts.port).toEqual(8080);
141
+ });
142
+
143
+ test("address without port", () => {
144
+ const uri = "2604:4480::5";
145
+ const parts = utils.parseIPv6URI(uri);
146
+ expect(parts.host).toEqual("2604:4480::5");
147
+ expect(parts.port).toEqual(80);
148
+ });
149
+
150
+ test("full uri", () => {
151
+ const uri = "http://[2604:4480::5]:8080/foo/bar";
152
+ const parts = utils.parseIPv6URI(uri);
153
+ expect(parts.host).toEqual("2604:4480::5");
154
+ expect(parts.port).toEqual(8080);
155
+ });
156
+
157
+ test("failing address", () => {
158
+ const uri = "[2604:4480:z:5]:80";
159
+ try {
160
+ const parts = utils.parseIPv6URI(uri);
161
+ console.log(parts);
162
+ } catch (e) {
163
+ expect(e.message).toEqual("failed to parse address");
164
+ }
165
+ });
166
+
167
+ test("should parse locally scoped ipv6 URIs without port", () => {
168
+ const uri = "fe80::1ff:fe23:4567:890a%eth2";
169
+ const parts = utils.parseIPv6URI(uri);
170
+ expect(parts.host).toEqual("fe80::1ff:fe23:4567:890a%eth2");
171
+ expect(parts.port).toEqual(80);
172
+ });
173
+
174
+ test("should parse locally scoped ipv6 URIs with port", () => {
175
+ const uri = "[fe80::1ff:fe23:4567:890a%eth2]:8080";
176
+ const parts = utils.parseIPv6URI(uri);
177
+ expect(parts.host).toEqual("fe80::1ff:fe23:4567:890a%eth2");
178
+ expect(parts.port).toEqual(8080);
179
+ });
180
+ });
181
+
182
+ describe("utils.arrayStartingMatch", () => {
183
+ test("finds matching arrays", () => {
184
+ const a = [1, 2, 3];
185
+ const b = [1, 2, 3, 4, 5];
186
+ const numberResult = utils.arrayStartingMatch(a, b);
187
+ expect(numberResult).toBe(true);
188
+
189
+ const c = ["a", "b", "c"];
190
+ const d = ["a", "b", "c", "d", "e"];
191
+ const stringResult = utils.arrayStartingMatch(c, d);
192
+ expect(stringResult).toBe(true);
193
+ });
194
+
195
+ test("finds non-matching arrays", () => {
196
+ const a = [1, 3];
197
+ const b = [1, 2, 3, 4, 5];
198
+ const numberResult = utils.arrayStartingMatch(a, b);
199
+ expect(numberResult).toBe(false);
200
+
201
+ const c = ["a", "b", "c"];
202
+ const d = ["a", "b", "d", "e"];
203
+ const stringResult = utils.arrayStartingMatch(c, d);
204
+ expect(stringResult).toBe(false);
205
+ });
206
+
207
+ test("does not pass with empty arrays; first", () => {
208
+ const a: number[] = [];
209
+ const b = [1, 2, 3, 4, 5];
210
+ const result = utils.arrayStartingMatch(a, b);
211
+ expect(result).toBe(false);
212
+ });
213
+
214
+ test("does not pass with empty arrays; second", () => {
215
+ const a = [1, 2, 3, 4, 5];
216
+ const b: number[] = [];
217
+ const result = utils.arrayStartingMatch(a, b);
218
+ expect(result).toBe(false);
219
+ });
220
+ });
221
+
222
+ describe("utils.replaceDistWithSrc", () => {
223
+ test("it replaces paths from dist to src", () => {
224
+ const p = `${config.general!.paths.action[0]}/new-actions/test.ts`;
225
+ const withDist = utils.replaceDistWithSrc(p);
226
+ expect(withDist).toMatch("/src/actions/new-actions/test.ts");
227
+ });
228
+ });
229
+
230
+ describe("utils.filterObjectForLogging", () => {
231
+ beforeEach(() => {
232
+ config.logger!.maxLogArrayLength = 100;
233
+ expect(config.general!.filteredParams.length).toEqual(0);
234
+ });
235
+
236
+ afterEach(() => {
237
+ // after each test, empty the array
238
+ config.general!.filteredParams = [];
239
+ config.logger!.maxLogArrayLength = 10;
240
+ });
241
+
242
+ const testInput = {
243
+ p1: 1,
244
+ p2: "s3cr3t",
245
+ o1: {
246
+ o1p1: 1,
247
+ o1p2: "also-s3cr3t",
248
+ o2: {
249
+ o2p1: "this is ok",
250
+ o2p2: "extremely-s3cr3t",
251
+ },
252
+ },
253
+ o2: {
254
+ name: "same as o1`s inner object!",
255
+ o2p1: "nothing secret",
256
+ },
257
+ a1: ["a", "b", "c"],
258
+ a2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
259
+ };
260
+
261
+ test("can filter top level params, no matter the type", () => {
262
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
263
+ (config.general!.filteredParams as string[]).push("p1", "p2", "o2");
264
+ const filteredParams = utils.filterObjectForLogging(inputs);
265
+ expect(filteredParams.p1).toEqual("[FILTERED]");
266
+ expect(filteredParams.p2).toEqual("[FILTERED]");
267
+ expect(filteredParams.o2).toEqual("[FILTERED]"); // entire object filtered
268
+ expect(filteredParams.o1).toEqual(testInput.o1); // unchanged
269
+ expect(filteredParams.a1).toEqual(testInput.a1); // unchanged
270
+ expect(filteredParams.a2).toEqual(testInput.a2); // unchanged
271
+ });
272
+
273
+ test("will not filter things that do not exist", () => {
274
+ // Identity
275
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
276
+ const filteredParams = utils.filterObjectForLogging(inputs);
277
+ expect(filteredParams).toEqual(testInput);
278
+
279
+ (config.general!.filteredParams as string[]).push(
280
+ "p3",
281
+ "p4",
282
+ "o1.o3",
283
+ "o1.o2.p1",
284
+ );
285
+ const filteredParams2 = utils.filterObjectForLogging(inputs);
286
+ expect(filteredParams2).toEqual(testInput);
287
+ expect(filteredParams.a1).toEqual(testInput.a1); // unchanged
288
+ expect(filteredParams.a2).toEqual(testInput.a2); // unchanged
289
+ });
290
+
291
+ test("can filter a single level dot notation", () => {
292
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
293
+ (config.general!.filteredParams as string[]).push(
294
+ "p1",
295
+ "o1.o1p1",
296
+ "somethingNotExist",
297
+ );
298
+ const filteredParams = utils.filterObjectForLogging(inputs);
299
+ expect(filteredParams.p1).toEqual("[FILTERED]");
300
+ expect(filteredParams.o1.o1p1).toEqual("[FILTERED]");
301
+ // Unchanged things
302
+ expect(filteredParams.p2).toEqual(testInput.p2);
303
+ expect(filteredParams.o1.o1p2).toEqual(testInput.o1.o1p2);
304
+ expect(filteredParams.o1.o2).toEqual(testInput.o1.o2);
305
+ expect(filteredParams.o2).toEqual(testInput.o2);
306
+ expect(filteredParams.a1).toEqual(testInput.a1);
307
+ expect(filteredParams.a2).toEqual(testInput.a2);
308
+ });
309
+
310
+ test("can filter two levels deep", () => {
311
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
312
+ (config.general!.filteredParams as string[]).push(
313
+ "p2",
314
+ "o1.o2.o2p1",
315
+ "o1.o2.notThere",
316
+ );
317
+ const filteredParams = utils.filterObjectForLogging(inputs);
318
+ expect(filteredParams.p2).toEqual("[FILTERED]");
319
+ expect(filteredParams.o1.o2.o2p1).toEqual("[FILTERED]");
320
+ // Unchanged things
321
+ expect(filteredParams.p1).toEqual(testInput.p1);
322
+ expect(filteredParams.o1.o1p1).toEqual(testInput.o1.o1p1);
323
+ expect(filteredParams.o1.o2.o2p2).toEqual(testInput.o1.o2.o2p2);
324
+ expect(filteredParams.a1).toEqual(testInput.a1);
325
+ expect(filteredParams.a2).toEqual(testInput.a2);
326
+ });
327
+
328
+ test("can filter with a function rather than an array", () => {
329
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
330
+ config.general!.filteredParams = () => {
331
+ return ["p1", "p2", "o2"];
332
+ };
333
+
334
+ const filteredParams = utils.filterObjectForLogging(inputs);
335
+ expect(filteredParams.p1).toEqual("[FILTERED]");
336
+ expect(filteredParams.p2).toEqual("[FILTERED]");
337
+ expect(filteredParams.o2).toEqual("[FILTERED]"); // entire object filtered
338
+ // Unchanged things
339
+ expect(filteredParams.o1).toEqual(testInput.o1);
340
+ expect(filteredParams.a1).toEqual(testInput.a1);
341
+ expect(filteredParams.a2).toEqual(testInput.a2);
342
+ });
343
+
344
+ test("short arrays will be displayed as-is", () => {
345
+ config.logger!.maxLogArrayLength = 100;
346
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
347
+ const filteredParams = utils.filterObjectForLogging(inputs);
348
+ expect(filteredParams.a1).toEqual(testInput.a1);
349
+ expect(filteredParams.a2).toEqual(testInput.a2);
350
+ });
351
+
352
+ test("long arrays will be collected", () => {
353
+ config.logger!.maxLogArrayLength = 10;
354
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
355
+ const filteredParams = utils.filterObjectForLogging(inputs);
356
+ expect(filteredParams.a1).toEqual(testInput.a1);
357
+ expect(filteredParams.a2).toEqual("11 items");
358
+ });
359
+ });
360
+
361
+ describe("utils.filterResponseForLogging", () => {
362
+ beforeEach(() => {
363
+ config.logger!.maxLogArrayLength = 100;
364
+ expect(config.general!.filteredResponse.length).toEqual(0);
365
+ });
366
+
367
+ afterEach(() => {
368
+ // after each test, empty the array
369
+ config.general!.filteredResponse = [];
370
+ config.logger!.maxLogArrayLength = 10;
371
+ });
372
+
373
+ const testInput = {
374
+ p1: 1,
375
+ p2: "s3cr3t",
376
+ o1: {
377
+ o1p1: 1,
378
+ o1p2: "also-s3cr3t",
379
+ o2: {
380
+ o2p1: "this is ok",
381
+ o2p2: "extremely-s3cr3t",
382
+ },
383
+ },
384
+ o2: {
385
+ name: "same as o1`s inner object!",
386
+ o2p1: "nothing secret",
387
+ },
388
+ a1: ["a", "b", "c"],
389
+ a2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
390
+ };
391
+
392
+ test("can filter top level params, no matter the type", () => {
393
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
394
+ (config.general!.filteredResponse as string[]).push("p1", "p2", "o2");
395
+ const filteredResponse = utils.filterResponseForLogging(inputs);
396
+ expect(filteredResponse.p1).toEqual("[FILTERED]");
397
+ expect(filteredResponse.p2).toEqual("[FILTERED]");
398
+ expect(filteredResponse.o2).toEqual("[FILTERED]"); // entire object filtered
399
+ expect(filteredResponse.o1).toEqual(testInput.o1); // unchanged
400
+ expect(filteredResponse.a1).toEqual(testInput.a1); // unchanged
401
+ expect(filteredResponse.a2).toEqual(testInput.a2); // unchanged
402
+ });
403
+
404
+ test("will not filter things that do not exist", () => {
405
+ // Identity
406
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
407
+ const filteredResponse = utils.filterResponseForLogging(inputs);
408
+ expect(filteredResponse).toEqual(testInput);
409
+
410
+ (config.general!.filteredResponse as string[]).push(
411
+ "p3",
412
+ "p4",
413
+ "o1.o3",
414
+ "o1.o2.p1",
415
+ );
416
+ const filteredResponse2 = utils.filterResponseForLogging(inputs);
417
+ expect(filteredResponse2).toEqual(testInput);
418
+ expect(filteredResponse2.a1).toEqual(testInput.a1); // unchanged
419
+ expect(filteredResponse2.a2).toEqual(testInput.a2); // unchanged
420
+ });
421
+
422
+ test("can filter a single level dot notation", () => {
423
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
424
+ (config.general!.filteredResponse as string[]).push(
425
+ "p1",
426
+ "o1.o1p1",
427
+ "somethingNotExist",
428
+ );
429
+ const filteredResponse = utils.filterResponseForLogging(inputs);
430
+ expect(filteredResponse.p1).toEqual("[FILTERED]");
431
+ expect(filteredResponse.o1.o1p1).toEqual("[FILTERED]");
432
+ // Unchanged things
433
+ expect(filteredResponse.p2).toEqual(testInput.p2);
434
+ expect(filteredResponse.o1.o1p2).toEqual(testInput.o1.o1p2);
435
+ expect(filteredResponse.o1.o2).toEqual(testInput.o1.o2);
436
+ expect(filteredResponse.o2).toEqual(testInput.o2);
437
+ expect(filteredResponse.a1).toEqual(testInput.a1);
438
+ expect(filteredResponse.a2).toEqual(testInput.a2);
439
+ });
440
+
441
+ test("can filter two levels deep", () => {
442
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
443
+ (config.general!.filteredResponse as string[]).push(
444
+ "p2",
445
+ "o1.o2.o2p1",
446
+ "o1.o2.notThere",
447
+ );
448
+ const filteredResponse = utils.filterResponseForLogging(inputs);
449
+ expect(filteredResponse.p2).toEqual("[FILTERED]");
450
+ expect(filteredResponse.o1.o2.o2p1).toEqual("[FILTERED]");
451
+ // Unchanged things
452
+ expect(filteredResponse.p1).toEqual(testInput.p1);
453
+ expect(filteredResponse.o1.o1p1).toEqual(testInput.o1.o1p1);
454
+ expect(filteredResponse.o1.o2.o2p2).toEqual(testInput.o1.o2.o2p2);
455
+ expect(filteredResponse.a1).toEqual(testInput.a1);
456
+ expect(filteredResponse.a2).toEqual(testInput.a2);
457
+ });
458
+
459
+ test("can filter with a function rather than an array", () => {
460
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
461
+ config.general!.filteredResponse = () => {
462
+ return ["p1", "p2", "o2"];
463
+ };
464
+
465
+ const filteredResponse = utils.filterResponseForLogging(inputs);
466
+ expect(filteredResponse.p1).toEqual("[FILTERED]");
467
+ expect(filteredResponse.p2).toEqual("[FILTERED]");
468
+ expect(filteredResponse.o2).toEqual("[FILTERED]"); // entire object filtered
469
+ expect(filteredResponse.o1).toEqual(testInput.o1); // unchanged
470
+ expect(filteredResponse.a1).toEqual(testInput.a1);
471
+ expect(filteredResponse.a2).toEqual(testInput.a2);
472
+ });
473
+
474
+ test("long arrays will be collected", () => {
475
+ config.logger!.maxLogArrayLength = 10;
476
+ const inputs = JSON.parse(JSON.stringify(testInput)); // quick deep Clone
477
+ const filteredResponse = utils.filterResponseForLogging(inputs);
478
+ expect(filteredResponse.a1).toEqual(testInput.a1);
479
+ expect(filteredResponse.a2).toEqual("11 items");
480
+ });
481
+
482
+ test("safeGlobSync to match normal glob.sync", () => {
483
+ const directory = __dirname; // if it is windows platform includes backslash (\\)
484
+ const pattern = "/**/*.ts";
485
+ const normalResult = glob.sync(directory.replace(/\\/g, "/") + pattern); // do not use path.join
486
+
487
+ const safeResult = utils.safeGlobSync(path.join(directory, pattern));
488
+
489
+ expect(normalResult).toEqual(safeResult);
490
+ });
491
+ });
492
+ });