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.
- package/.devcontainer/devcontainer.json +4 -0
- package/.devcontainer/setup.sh +11 -0
- package/.dockerignore +2 -0
- package/.github/CONTRIBUTING.md +52 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
- package/.github/dependabot.yml +17 -0
- package/.github/workflows/codeql.yml +76 -0
- package/.github/workflows/publish_docs.yml +25 -0
- package/.github/workflows/test.yml +78 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +1 -0
- package/.vscode/launch.json +42 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/Dockerfile +17 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/SECURITY.md +5 -0
- package/__tests__/actions/cacheTest.ts +58 -0
- package/__tests__/actions/randomNumber.ts +26 -0
- package/__tests__/actions/recursiveAction.ts +16 -0
- package/__tests__/actions/sleepTest.ts +24 -0
- package/__tests__/actions/status.ts +17 -0
- package/__tests__/actions/swagger.ts +76 -0
- package/__tests__/actions/validationTest.ts +63 -0
- package/__tests__/cli/cli.ts +126 -0
- package/__tests__/core/api.ts +632 -0
- package/__tests__/core/cache.ts +400 -0
- package/__tests__/core/chatRoom.ts +589 -0
- package/__tests__/core/cli.ts +349 -0
- package/__tests__/core/cluster.ts +132 -0
- package/__tests__/core/config.ts +78 -0
- package/__tests__/core/errors.ts +112 -0
- package/__tests__/core/log.ts +23 -0
- package/__tests__/core/middleware.ts +427 -0
- package/__tests__/core/plugins/partialPlugin.ts +94 -0
- package/__tests__/core/plugins/withPlugin.ts +88 -0
- package/__tests__/core/plugins/withoutPlugin.ts +81 -0
- package/__tests__/core/process.ts +42 -0
- package/__tests__/core/specHelper.ts +330 -0
- package/__tests__/core/staticFile/compression.ts +99 -0
- package/__tests__/core/staticFile/staticFile.ts +180 -0
- package/__tests__/core/tasks/customQueueFunction.ts +67 -0
- package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
- package/__tests__/core/tasks/tasks.ts +605 -0
- package/__tests__/integration/browser.ts +133 -0
- package/__tests__/integration/ioredis-mock.ts +194 -0
- package/__tests__/integration/sendBuffer.ts +97 -0
- package/__tests__/integration/sendFile.ts +24 -0
- package/__tests__/integration/sharedFingerprint.ts +82 -0
- package/__tests__/integration/taskFlow.ts +110 -0
- package/__tests__/jest.ts +5 -0
- package/__tests__/modules/action.ts +103 -0
- package/__tests__/modules/config.ts +19 -0
- package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
- package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
- package/__tests__/servers/web/enableMultiples.ts +83 -0
- package/__tests__/servers/web/fileUpload.ts +79 -0
- package/__tests__/servers/web/jsonp.ts +57 -0
- package/__tests__/servers/web/nonMultiples.ts +83 -0
- package/__tests__/servers/web/rawBody.ts +208 -0
- package/__tests__/servers/web/returnErrorCodes.ts +55 -0
- package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
- package/__tests__/servers/web/routes/routes.ts +579 -0
- package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
- package/__tests__/servers/web/web.ts +1031 -0
- package/__tests__/servers/websocket.ts +795 -0
- package/__tests__/tasks/runAction.ts +37 -0
- package/__tests__/template.ts.example +20 -0
- package/__tests__/testCliCommands/hello.ts +44 -0
- package/__tests__/testPlugin/public/plugin.html +1 -0
- package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
- package/__tests__/testPlugin/src/bin/hello.ts +22 -0
- package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
- package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
- package/__tests__/testPlugin/tsconfig.json +10 -0
- package/__tests__/utils/utils.ts +492 -0
- package/app.json +23 -0
- package/bin/deploy-docs +39 -0
- package/client/ActionheroWebsocketClient.js +277 -0
- package/docker-compose.yml +73 -0
- package/package.json +24 -0
- package/public/chat.html +194 -0
- package/public/css/cosmo.css +12 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +115 -0
- package/public/javascript/.gitkeep +0 -0
- package/public/linkedSession.html +80 -0
- package/public/logo/actionhero-small.png +0 -0
- package/public/logo/actionhero.png +0 -0
- package/public/pixel.gif +0 -0
- package/public/simple.html +2 -0
- package/public/swagger.html +32 -0
- package/public/websocketLoadTest.html +322 -0
- package/src/actions/cacheTest.ts +58 -0
- package/src/actions/createChatRoom.ts +20 -0
- package/src/actions/randomNumber.ts +17 -0
- package/src/actions/recursiveAction.ts +13 -0
- package/src/actions/sendFile.ts +12 -0
- package/src/actions/sleepTest.ts +40 -0
- package/src/actions/status.ts +73 -0
- package/src/actions/swagger.ts +155 -0
- package/src/actions/validationTest.ts +36 -0
- package/src/bin/actionhero.ts +225 -0
- package/src/bin/methods/actions/list.ts +30 -0
- package/src/bin/methods/console.ts +26 -0
- package/src/bin/methods/generate/action.ts +58 -0
- package/src/bin/methods/generate/cli.ts +51 -0
- package/src/bin/methods/generate/initializer.ts +54 -0
- package/src/bin/methods/generate/plugin.ts +57 -0
- package/src/bin/methods/generate/server.ts +38 -0
- package/src/bin/methods/generate/task.ts +68 -0
- package/src/bin/methods/generate.ts +176 -0
- package/src/bin/methods/task/enqueue.ts +35 -0
- package/src/classes/action.ts +98 -0
- package/src/classes/actionProcessor.ts +463 -0
- package/src/classes/api.ts +51 -0
- package/src/classes/cli.ts +67 -0
- package/src/classes/config.ts +15 -0
- package/src/classes/connection.ts +321 -0
- package/src/classes/exceptionReporter.ts +9 -0
- package/src/classes/initializer.ts +59 -0
- package/src/classes/initializers.ts +5 -0
- package/src/classes/input.ts +9 -0
- package/src/classes/inputs.ts +34 -0
- package/src/classes/process/actionheroVersion.ts +15 -0
- package/src/classes/process/env.ts +16 -0
- package/src/classes/process/id.ts +34 -0
- package/src/classes/process/pid.ts +32 -0
- package/src/classes/process/projectRoot.ts +16 -0
- package/src/classes/process/typescript.ts +47 -0
- package/src/classes/process.ts +479 -0
- package/src/classes/server.ts +251 -0
- package/src/classes/task.ts +87 -0
- package/src/config/api.ts +107 -0
- package/src/config/errors.ts +162 -0
- package/src/config/logger.ts +113 -0
- package/src/config/plugins.ts +37 -0
- package/src/config/redis.ts +78 -0
- package/src/config/routes.ts +44 -0
- package/src/config/tasks.ts +84 -0
- package/src/config/web.ts +136 -0
- package/src/config/websocket.ts +62 -0
- package/src/index.ts +46 -0
- package/src/initializers/actions.ts +125 -0
- package/src/initializers/chatRoom.ts +214 -0
- package/src/initializers/connections.ts +124 -0
- package/src/initializers/exceptions.ts +155 -0
- package/src/initializers/params.ts +52 -0
- package/src/initializers/redis.ts +191 -0
- package/src/initializers/resque.ts +248 -0
- package/src/initializers/routes.ts +229 -0
- package/src/initializers/servers.ts +134 -0
- package/src/initializers/specHelper.ts +195 -0
- package/src/initializers/staticFile.ts +253 -0
- package/src/initializers/tasks.ts +188 -0
- package/src/modules/action.ts +89 -0
- package/src/modules/cache.ts +326 -0
- package/src/modules/chatRoom.ts +321 -0
- package/src/modules/config.ts +246 -0
- package/src/modules/log.ts +62 -0
- package/src/modules/redis.ts +93 -0
- package/src/modules/route.ts +59 -0
- package/src/modules/specHelper.ts +182 -0
- package/src/modules/task.ts +527 -0
- package/src/modules/utils/argv.ts +3 -0
- package/src/modules/utils/arrayStartingMatch.ts +21 -0
- package/src/modules/utils/arrayUnique.ts +15 -0
- package/src/modules/utils/collapseObjectToArray.ts +33 -0
- package/src/modules/utils/deepCopy.ts +3 -0
- package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
- package/src/modules/utils/eventLoopDelay.ts +34 -0
- package/src/modules/utils/fileUtils.ts +119 -0
- package/src/modules/utils/filterObjectForLogging.ts +51 -0
- package/src/modules/utils/filterResponseForLogging.ts +53 -0
- package/src/modules/utils/getExternalIPAddress.ts +17 -0
- package/src/modules/utils/hashMerge.ts +63 -0
- package/src/modules/utils/isPlainObject.ts +45 -0
- package/src/modules/utils/isRunning.ts +7 -0
- package/src/modules/utils/parseCookies.ts +20 -0
- package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
- package/src/modules/utils/parseIPv6URI.ts +24 -0
- package/src/modules/utils/replaceDistWithSrc.ts +9 -0
- package/src/modules/utils/safeGlob.ts +6 -0
- package/src/modules/utils/sleep.ts +8 -0
- package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
- package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
- package/src/modules/utils.ts +66 -0
- package/src/server.ts +20 -0
- package/src/servers/web.ts +894 -0
- package/src/servers/websocket.ts +304 -0
- package/src/tasks/runAction.ts +29 -0
- package/tea.yaml +9 -0
- package/templates/README.md.template +17 -0
- package/templates/action.ts.template +15 -0
- package/templates/boot.js.template +9 -0
- package/templates/cli.ts.template +15 -0
- package/templates/gitignore.template +23 -0
- package/templates/initializer.ts.template +17 -0
- package/templates/package-plugin.json.template +12 -0
- package/templates/package.json.template +45 -0
- package/templates/projectMap.txt +39 -0
- package/templates/projectServer.ts.template +20 -0
- package/templates/server.ts.template +37 -0
- package/templates/task.ts.template +16 -0
- package/templates/test/action.ts.template +13 -0
- package/templates/test/task.ts.template +20 -0
- package/tsconfig.json +11 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
import { Action, config, api, RouteType } from "./../index";
|
2
|
+
import * as fs from "fs";
|
3
|
+
import * as path from "path";
|
4
|
+
import { PackageJson } from "type-fest";
|
5
|
+
|
6
|
+
const SWAGGER_VERSION = "2.0";
|
7
|
+
const API_VERSION = ""; // if you need a prefix to your API routes, like `v1`
|
8
|
+
const parentPackageJSON: PackageJson = JSON.parse(
|
9
|
+
fs.readFileSync(path.join(__dirname, "..", "..", "package.json")).toString(),
|
10
|
+
);
|
11
|
+
|
12
|
+
const responses = {
|
13
|
+
200: {
|
14
|
+
description: "successful operation",
|
15
|
+
},
|
16
|
+
400: {
|
17
|
+
description: "Invalid input",
|
18
|
+
},
|
19
|
+
404: {
|
20
|
+
description: "Not Found",
|
21
|
+
},
|
22
|
+
422: {
|
23
|
+
description: "Missing or invalid params",
|
24
|
+
},
|
25
|
+
500: {
|
26
|
+
description: "Server error",
|
27
|
+
},
|
28
|
+
};
|
29
|
+
|
30
|
+
export class Swagger extends Action {
|
31
|
+
name = "swagger";
|
32
|
+
description = "return API documentation in the OpenAPI specification";
|
33
|
+
outputExample = {};
|
34
|
+
|
35
|
+
getLatestAction(route: RouteType) {
|
36
|
+
let matchedAction: Action;
|
37
|
+
Object.keys(api.actions.actions).forEach((actionName) => {
|
38
|
+
Object.keys(api.actions.actions[actionName]).forEach((version) => {
|
39
|
+
const action = api.actions.actions[actionName][version];
|
40
|
+
if (action.name === route.action) {
|
41
|
+
matchedAction = action;
|
42
|
+
}
|
43
|
+
});
|
44
|
+
});
|
45
|
+
|
46
|
+
return matchedAction;
|
47
|
+
}
|
48
|
+
|
49
|
+
buildSwaggerPaths() {
|
50
|
+
const swaggerPaths: {
|
51
|
+
[path: string]: {
|
52
|
+
[method: string]: {
|
53
|
+
tags: string[];
|
54
|
+
summary: string;
|
55
|
+
consumes: string[];
|
56
|
+
produces: string[];
|
57
|
+
parameters: Array<{
|
58
|
+
in: string;
|
59
|
+
name: string;
|
60
|
+
type: string;
|
61
|
+
required: boolean;
|
62
|
+
default: string | number | boolean;
|
63
|
+
}>;
|
64
|
+
responses: typeof responses;
|
65
|
+
security: string[];
|
66
|
+
};
|
67
|
+
};
|
68
|
+
} = {};
|
69
|
+
const tags: string[] = [];
|
70
|
+
|
71
|
+
for (const [method, routes] of Object.entries(api.routes.routes)) {
|
72
|
+
routes.map((route) => {
|
73
|
+
const action = this.getLatestAction(route);
|
74
|
+
if (!action) return;
|
75
|
+
|
76
|
+
const tag = action.name.split(":")[0];
|
77
|
+
const formattedPath = route.path
|
78
|
+
.replace("/v:apiVersion", "")
|
79
|
+
.replace(/\/:(\w*)/g, "/{$1}");
|
80
|
+
|
81
|
+
swaggerPaths[formattedPath] = swaggerPaths[formattedPath] || {};
|
82
|
+
swaggerPaths[formattedPath][method] = {
|
83
|
+
tags: [tag],
|
84
|
+
summary: action.description,
|
85
|
+
// description: action.description, // this is redundant
|
86
|
+
consumes: ["application/json"],
|
87
|
+
produces: ["application/json"],
|
88
|
+
parameters: Object.keys(action.inputs)
|
89
|
+
.sort()
|
90
|
+
.map((inputName) => {
|
91
|
+
return {
|
92
|
+
in: route.path.includes(`:${inputName}`) ? "path" : "query",
|
93
|
+
name: inputName,
|
94
|
+
type: "string", // not really true, but helps the swagger validator
|
95
|
+
required:
|
96
|
+
action.inputs[inputName].required ||
|
97
|
+
route.path.includes(`:${inputName}`)
|
98
|
+
? true
|
99
|
+
: false,
|
100
|
+
default:
|
101
|
+
action.inputs[inputName].default !== null &&
|
102
|
+
action.inputs[inputName].default !== undefined
|
103
|
+
? typeof action.inputs[inputName].default === "object"
|
104
|
+
? JSON.stringify(action.inputs[inputName].default)
|
105
|
+
: typeof action.inputs[inputName].default === "function"
|
106
|
+
? action.inputs[inputName].default()
|
107
|
+
: `${action.inputs[inputName].default}`
|
108
|
+
: undefined,
|
109
|
+
};
|
110
|
+
}),
|
111
|
+
responses,
|
112
|
+
security: [],
|
113
|
+
};
|
114
|
+
|
115
|
+
if (!tags.includes(tag)) {
|
116
|
+
tags.push(tag);
|
117
|
+
}
|
118
|
+
});
|
119
|
+
}
|
120
|
+
|
121
|
+
return { swaggerPaths, tags };
|
122
|
+
}
|
123
|
+
|
124
|
+
async run() {
|
125
|
+
const { swaggerPaths, tags } = this.buildSwaggerPaths();
|
126
|
+
|
127
|
+
return {
|
128
|
+
swagger: SWAGGER_VERSION,
|
129
|
+
info: {
|
130
|
+
description: parentPackageJSON.description,
|
131
|
+
version: parentPackageJSON.version,
|
132
|
+
title: parentPackageJSON.name,
|
133
|
+
license: { name: parentPackageJSON.license },
|
134
|
+
},
|
135
|
+
host:
|
136
|
+
config.web.allowedRequestHosts[0]
|
137
|
+
?.replace("https://", "")
|
138
|
+
.replace("https://", "") ?? `localhost:${config.web.port}`,
|
139
|
+
basePath: `/api/${API_VERSION}`,
|
140
|
+
// tags: tags.map((tag) => {
|
141
|
+
// return { name: tag, description: `topic: ${tag}` };
|
142
|
+
// }),
|
143
|
+
schemes: config.web.allowedRequestHosts[0] ? ["https", "http"] : ["http"],
|
144
|
+
paths: swaggerPaths,
|
145
|
+
|
146
|
+
securityDefinitions: {
|
147
|
+
// TODO (custom)?
|
148
|
+
},
|
149
|
+
externalDocs: {
|
150
|
+
description: "Learn more about Actionhero",
|
151
|
+
url: "https://www.actionherojs.com",
|
152
|
+
},
|
153
|
+
};
|
154
|
+
}
|
155
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import { Action } from "./../index";
|
2
|
+
|
3
|
+
export class ValidationTest extends Action {
|
4
|
+
name = "validationTest";
|
5
|
+
description = "I will test action input validators.";
|
6
|
+
inputs = {
|
7
|
+
string: {
|
8
|
+
required: true as true,
|
9
|
+
validator: (param: string) => {
|
10
|
+
return typeof param === "string";
|
11
|
+
},
|
12
|
+
},
|
13
|
+
number: {
|
14
|
+
required: false,
|
15
|
+
formatter: (param: string, name: string) => {
|
16
|
+
if (parseInt(param) == 123) {
|
17
|
+
throw new Error(`Failed formatting ${name} correctly!`);
|
18
|
+
}
|
19
|
+
return parseInt(param);
|
20
|
+
},
|
21
|
+
validator: (param: number, name: string) => {
|
22
|
+
if (typeof param === "number") {
|
23
|
+
throw new Error(`Param ${name} is not a valid number!`);
|
24
|
+
}
|
25
|
+
},
|
26
|
+
},
|
27
|
+
};
|
28
|
+
outputExample = {
|
29
|
+
string: "imAString!",
|
30
|
+
number: 2,
|
31
|
+
};
|
32
|
+
|
33
|
+
async run({ params }: { params: { string: string; number: number } }) {
|
34
|
+
return { string: params.string, number: params.number };
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import * as path from "path";
|
4
|
+
import * as fs from "fs";
|
5
|
+
import { program, InvalidArgumentError } from "commander";
|
6
|
+
import { typescript } from "../classes/process/typescript";
|
7
|
+
import { projectRoot } from "../classes/process/projectRoot";
|
8
|
+
import { ensureNoTsHeaderOrSpecFiles } from "../modules/utils/ensureNoTsHeaderOrSpecFiles";
|
9
|
+
import { CLI } from "../classes/cli";
|
10
|
+
import { PackageJson } from "type-fest";
|
11
|
+
|
12
|
+
// load explicitly to find the type changes for the config module
|
13
|
+
import "../config/api";
|
14
|
+
import "../config/plugins";
|
15
|
+
import "../config/logger";
|
16
|
+
import "../config/routes";
|
17
|
+
import { safeGlobSync } from "../modules/utils/safeGlob";
|
18
|
+
|
19
|
+
export namespace ActionheroCLIRunner {
|
20
|
+
export async function run() {
|
21
|
+
program.storeOptionsAsProperties(false);
|
22
|
+
program.version(getVersion());
|
23
|
+
|
24
|
+
let pathsLoaded: string[] = [];
|
25
|
+
try {
|
26
|
+
const { config } = await import("../index");
|
27
|
+
|
28
|
+
// this project
|
29
|
+
for (const p of config.general.paths.cli) {
|
30
|
+
await loadDirectory(path.join(p), pathsLoaded);
|
31
|
+
}
|
32
|
+
|
33
|
+
// plugins
|
34
|
+
for (const plugin of Object.values(config.plugins)) {
|
35
|
+
if (plugin.cli !== false) {
|
36
|
+
// old plugins
|
37
|
+
await loadDirectory(path.join(plugin.path, "bin"), pathsLoaded);
|
38
|
+
// new plugins
|
39
|
+
await loadDirectory(
|
40
|
+
path.join(plugin.path, "dist", "bin"),
|
41
|
+
pathsLoaded,
|
42
|
+
);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
// core
|
47
|
+
if (config.general.cliIncludeInternal !== false) {
|
48
|
+
await loadDirectory(__dirname, pathsLoaded);
|
49
|
+
}
|
50
|
+
} catch (e) {
|
51
|
+
// we are trying to build a new project, only load the generate command
|
52
|
+
await loadDirectory(path.join(__dirname), pathsLoaded, "generate");
|
53
|
+
}
|
54
|
+
|
55
|
+
program.parse(process.argv);
|
56
|
+
}
|
57
|
+
|
58
|
+
// --- Utils --- //
|
59
|
+
|
60
|
+
export async function loadDirectory(
|
61
|
+
dir: string,
|
62
|
+
pathsLoaded: string[],
|
63
|
+
match = "*",
|
64
|
+
) {
|
65
|
+
if (!fs.existsSync(dir)) return;
|
66
|
+
const realpath = fs.realpathSync(dir);
|
67
|
+
if (pathsLoaded.includes(realpath)) return;
|
68
|
+
pathsLoaded.push(realpath);
|
69
|
+
|
70
|
+
const matcher = `${realpath}/**/+(${
|
71
|
+
typescript ? `${match}.js|*.ts` : `${match}.js`
|
72
|
+
})`;
|
73
|
+
const files = ensureNoTsHeaderOrSpecFiles(safeGlobSync(matcher));
|
74
|
+
for (const i in files) {
|
75
|
+
const collection = await import(files[i]);
|
76
|
+
for (const j in collection) {
|
77
|
+
const command = collection[j];
|
78
|
+
convertCLIToCommanderAction(command);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
export async function convertCLIToCommanderAction(
|
84
|
+
cliConstructor: new () => CLI,
|
85
|
+
) {
|
86
|
+
if (
|
87
|
+
Object.getPrototypeOf(cliConstructor?.prototype?.constructor || {})
|
88
|
+
.name !== "CLI"
|
89
|
+
) {
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
|
93
|
+
const instance: CLI = new cliConstructor();
|
94
|
+
const command = program
|
95
|
+
.command(instance.name)
|
96
|
+
.description(instance.description)
|
97
|
+
.action(async (_arg1, _arg2, _arg3, _arg4, _arg5) => {
|
98
|
+
await runCommand(instance, _arg1, _arg2, _arg3, _arg4, _arg5);
|
99
|
+
})
|
100
|
+
.on("--help", () => {
|
101
|
+
if (instance.example) {
|
102
|
+
console.log("");
|
103
|
+
console.log("Example: \r\n" + " " + instance.example);
|
104
|
+
}
|
105
|
+
if (typeof instance.help === "function") instance.help();
|
106
|
+
});
|
107
|
+
|
108
|
+
for (const key in instance.inputs) {
|
109
|
+
const input = instance.inputs[key];
|
110
|
+
|
111
|
+
if (input.flag && !input.letter) {
|
112
|
+
throw new Error(
|
113
|
+
`flag inputs require a short letter (${JSON.stringify(input)})`,
|
114
|
+
);
|
115
|
+
}
|
116
|
+
|
117
|
+
const separators =
|
118
|
+
input.required || input.requiredValue ? ["<", ">"] : ["[", "]"];
|
119
|
+
const methodName = input.required ? "requiredOption" : "option";
|
120
|
+
const argString = `${input.letter ? `-${input.letter}, ` : ""}--${key} ${
|
121
|
+
input.flag
|
122
|
+
? ""
|
123
|
+
: `${separators[0]}${input.placeholder || key}${
|
124
|
+
input.variadic ? "..." : ""
|
125
|
+
}${separators[1]}`
|
126
|
+
}`;
|
127
|
+
|
128
|
+
const argProcessor = (
|
129
|
+
value: string,
|
130
|
+
accumulator?: unknown[],
|
131
|
+
): unknown => {
|
132
|
+
try {
|
133
|
+
if (typeof input.formatter === "function") {
|
134
|
+
value = input.formatter(value);
|
135
|
+
}
|
136
|
+
|
137
|
+
if (typeof input.validator === "function") {
|
138
|
+
input.validator(value);
|
139
|
+
}
|
140
|
+
|
141
|
+
if (input.variadic) {
|
142
|
+
if (!Array.isArray(accumulator)) accumulator = [];
|
143
|
+
accumulator.push(value);
|
144
|
+
return accumulator;
|
145
|
+
}
|
146
|
+
|
147
|
+
return value;
|
148
|
+
} catch (error) {
|
149
|
+
throw new InvalidArgumentError(error?.message ?? error);
|
150
|
+
}
|
151
|
+
};
|
152
|
+
|
153
|
+
command[methodName](
|
154
|
+
argString,
|
155
|
+
input.description,
|
156
|
+
argProcessor,
|
157
|
+
input.default,
|
158
|
+
);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
export async function runCommand(
|
163
|
+
instance: CLI,
|
164
|
+
_arg1: any,
|
165
|
+
_arg2: any,
|
166
|
+
_arg3: any,
|
167
|
+
_arg4: any,
|
168
|
+
_arg5: any,
|
169
|
+
) {
|
170
|
+
let toStop = false;
|
171
|
+
|
172
|
+
let _arguments: string[] = [];
|
173
|
+
let params: Record<string, string[]> = {};
|
174
|
+
[_arg1, _arg2, _arg3, _arg4, _arg5].forEach((arg) => {
|
175
|
+
if (typeof arg?.opts === "function") {
|
176
|
+
params = arg.opts();
|
177
|
+
} else if (arg !== null && arg !== undefined && typeof arg !== "object") {
|
178
|
+
_arguments.push(arg);
|
179
|
+
}
|
180
|
+
});
|
181
|
+
|
182
|
+
params["_arguments"] = _arguments;
|
183
|
+
|
184
|
+
if (instance.initialize === false && instance.start === false) {
|
185
|
+
toStop = await instance.run({ params });
|
186
|
+
} else {
|
187
|
+
try {
|
188
|
+
const { Process } = await import("../index");
|
189
|
+
const actionHeroProcess = new Process();
|
190
|
+
|
191
|
+
if (instance.initialize) await actionHeroProcess.initialize();
|
192
|
+
if (instance.start) await actionHeroProcess.start();
|
193
|
+
|
194
|
+
toStop = await instance.run({ params });
|
195
|
+
} catch (error) {
|
196
|
+
console.error(error.toString());
|
197
|
+
process.exit(1);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
if (toStop || toStop === null || toStop === undefined) {
|
202
|
+
setTimeout(process.exit, 500, 0);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
export function readPackageJSON(file: string) {
|
207
|
+
return JSON.parse(fs.readFileSync(file).toString());
|
208
|
+
}
|
209
|
+
|
210
|
+
export function getVersion(): string {
|
211
|
+
const parentPackageJSON = path.join(projectRoot, "package.json");
|
212
|
+
|
213
|
+
if (fs.existsSync(parentPackageJSON)) {
|
214
|
+
const pkg: PackageJson = readPackageJSON(parentPackageJSON);
|
215
|
+
return pkg.version;
|
216
|
+
} else {
|
217
|
+
const pkg: PackageJson = readPackageJSON(
|
218
|
+
path.join(__dirname, "..", "..", "package.json"),
|
219
|
+
);
|
220
|
+
return pkg.version;
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
ActionheroCLIRunner.run();
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { api, CLI } from "./../../../index";
|
2
|
+
|
3
|
+
export class ActionsListCLI extends CLI {
|
4
|
+
constructor() {
|
5
|
+
super();
|
6
|
+
this.name = "actions-list";
|
7
|
+
this.description = "List the actions defined on this server";
|
8
|
+
}
|
9
|
+
|
10
|
+
async run() {
|
11
|
+
for (const actionName in api.actions.actions) {
|
12
|
+
console.log(`\r\n--- ${actionName} ---`);
|
13
|
+
const collection = api.actions.actions[actionName];
|
14
|
+
|
15
|
+
for (const version in collection) {
|
16
|
+
const action = collection[version];
|
17
|
+
console.info(` version: ${version}`);
|
18
|
+
console.info(` ${action.description}`);
|
19
|
+
console.info(" inputs:");
|
20
|
+
for (const input in action.inputs) {
|
21
|
+
console.info(
|
22
|
+
` ${input}: ${JSON.stringify(action.inputs[input])}`,
|
23
|
+
);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
return true;
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import * as REPL from "repl";
|
2
|
+
import { api, env, CLI } from "./../../index";
|
3
|
+
|
4
|
+
export class ConsoleCLI extends CLI {
|
5
|
+
name = "console";
|
6
|
+
description =
|
7
|
+
"Start an interactive REPL session with the api object in-scope";
|
8
|
+
|
9
|
+
async run() {
|
10
|
+
await new Promise((resolve, reject) => {
|
11
|
+
const repl = REPL.start({
|
12
|
+
prompt: "[ AH::" + env + " ] >> ",
|
13
|
+
input: process.stdin,
|
14
|
+
output: process.stdout,
|
15
|
+
useGlobal: false,
|
16
|
+
});
|
17
|
+
|
18
|
+
repl.context.api = api;
|
19
|
+
|
20
|
+
repl.on("exit", resolve);
|
21
|
+
repl.on("error", reject);
|
22
|
+
});
|
23
|
+
|
24
|
+
return true;
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import * as fs from "fs";
|
2
|
+
import * as path from "path";
|
3
|
+
import { config, utils, CLI, ParamsFrom } from "./../../../index";
|
4
|
+
|
5
|
+
export class GenerateActionCLI extends CLI {
|
6
|
+
name = "generate-action";
|
7
|
+
description = "Generate a new Action";
|
8
|
+
example =
|
9
|
+
"actionhero generate action --name=<name> --description=[description]";
|
10
|
+
inputs = {
|
11
|
+
name: {
|
12
|
+
required: true as true,
|
13
|
+
description: "The name of the Action to Generate",
|
14
|
+
letter: "n",
|
15
|
+
},
|
16
|
+
description: {
|
17
|
+
required: false,
|
18
|
+
description: "The description of the Action",
|
19
|
+
default: "an actionhero action",
|
20
|
+
letter: "d",
|
21
|
+
},
|
22
|
+
};
|
23
|
+
|
24
|
+
async run({ params }: { params: ParamsFrom<GenerateActionCLI> }) {
|
25
|
+
let actionTemplateBuffer = fs.readFileSync(
|
26
|
+
path.join(__dirname, "../../../../templates/action.ts.template"),
|
27
|
+
);
|
28
|
+
let actionTemplate = actionTemplateBuffer.toString();
|
29
|
+
|
30
|
+
let testTemplateBuffer = fs.readFileSync(
|
31
|
+
path.join(__dirname, "/../../../../templates/test/action.ts.template"),
|
32
|
+
);
|
33
|
+
let testTemplate = testTemplateBuffer.toString();
|
34
|
+
|
35
|
+
for (const [k, v] of Object.entries(params)) {
|
36
|
+
const regex = new RegExp("%%" + k + "%%", "g");
|
37
|
+
actionTemplate = actionTemplate.replace(regex, v);
|
38
|
+
testTemplate = testTemplate.replace(regex, v);
|
39
|
+
}
|
40
|
+
|
41
|
+
let message = utils.fileUtils.createFileSafely(
|
42
|
+
utils.replaceDistWithSrc(config.general.paths.action[0]) +
|
43
|
+
"/" +
|
44
|
+
params.name +
|
45
|
+
".ts",
|
46
|
+
actionTemplate,
|
47
|
+
);
|
48
|
+
console.info(message);
|
49
|
+
|
50
|
+
message = utils.fileUtils.createFileSafely(
|
51
|
+
config.general.paths.test[0] + "/actions/" + params.name + ".ts",
|
52
|
+
testTemplate,
|
53
|
+
);
|
54
|
+
console.info(message);
|
55
|
+
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import * as fs from "fs";
|
2
|
+
import * as path from "path";
|
3
|
+
import { config, utils, ParamsFrom, CLI } from "./../../../index";
|
4
|
+
|
5
|
+
export class GenerateCLICLI extends CLI {
|
6
|
+
name = "generate-cli";
|
7
|
+
description = "Generate a new cli command";
|
8
|
+
example = "actionhero generate cli --name=<name>";
|
9
|
+
inputs = {
|
10
|
+
name: {
|
11
|
+
required: true as true as true,
|
12
|
+
description: "The name of the CLI Command to generate",
|
13
|
+
letter: "n",
|
14
|
+
},
|
15
|
+
description: {
|
16
|
+
required: false,
|
17
|
+
description: "The name of the CLI Command",
|
18
|
+
default: "an actionhero cli command",
|
19
|
+
letter: "d",
|
20
|
+
},
|
21
|
+
example: {
|
22
|
+
required: false,
|
23
|
+
description: "An example to include for the CLI Command's help",
|
24
|
+
default: "actionhero command --option=yes",
|
25
|
+
letter: "e",
|
26
|
+
},
|
27
|
+
};
|
28
|
+
|
29
|
+
async run({ params }: { params: ParamsFrom<GenerateCLICLI> }) {
|
30
|
+
let templateBuffer = fs.readFileSync(
|
31
|
+
path.join(__dirname, "/../../../../templates/cli.ts.template"),
|
32
|
+
);
|
33
|
+
|
34
|
+
let template = templateBuffer.toString();
|
35
|
+
|
36
|
+
for (const [k, v] of Object.entries(params)) {
|
37
|
+
const regex = new RegExp("%%" + k + "%%", "g");
|
38
|
+
template = template.replace(regex, v);
|
39
|
+
}
|
40
|
+
|
41
|
+
const message = utils.fileUtils.createFileSafely(
|
42
|
+
utils.replaceDistWithSrc(
|
43
|
+
config.general.paths.cli[0] + "/" + params.name + ".ts",
|
44
|
+
),
|
45
|
+
template,
|
46
|
+
);
|
47
|
+
console.log(message);
|
48
|
+
|
49
|
+
return true;
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import * as fs from "fs";
|
2
|
+
import * as path from "path";
|
3
|
+
import { config, utils, CLI, ParamsFrom } from "./../../../index";
|
4
|
+
|
5
|
+
export class GenerateInitializerCLI extends CLI {
|
6
|
+
name = "generate-initializer";
|
7
|
+
description = "Generate a new Initializer";
|
8
|
+
example =
|
9
|
+
"actionhero generate initializer --name=<name> --loadPriority=[p] --startPriority=[p] --stopPriority=[p]";
|
10
|
+
inputs = {
|
11
|
+
name: {
|
12
|
+
required: true as true,
|
13
|
+
description: "The name of the Initializer to generate",
|
14
|
+
letter: "n",
|
15
|
+
},
|
16
|
+
loadPriority: {
|
17
|
+
required: true as true,
|
18
|
+
description: "The order that this Initializer will initialize",
|
19
|
+
default: "1000",
|
20
|
+
},
|
21
|
+
startPriority: {
|
22
|
+
required: true as true,
|
23
|
+
description: "The order that this Initializer will start",
|
24
|
+
default: "1000",
|
25
|
+
},
|
26
|
+
stopPriority: {
|
27
|
+
required: true as true,
|
28
|
+
description: "The order that this Initializer will stop",
|
29
|
+
default: "1000",
|
30
|
+
},
|
31
|
+
};
|
32
|
+
|
33
|
+
async run({ params }: { params: ParamsFrom<GenerateInitializerCLI> }) {
|
34
|
+
let templateBuffer = fs.readFileSync(
|
35
|
+
path.join(__dirname, "/../../../../templates/initializer.ts.template"),
|
36
|
+
);
|
37
|
+
let template = String(templateBuffer);
|
38
|
+
|
39
|
+
for (const [k, v] of Object.entries(params)) {
|
40
|
+
const regex = new RegExp("%%" + k + "%%", "g");
|
41
|
+
template = template.replace(regex, v);
|
42
|
+
}
|
43
|
+
|
44
|
+
const message = utils.fileUtils.createFileSafely(
|
45
|
+
utils.replaceDistWithSrc(
|
46
|
+
config.general.paths.initializer[0] + "/" + params.name + ".ts",
|
47
|
+
),
|
48
|
+
template,
|
49
|
+
);
|
50
|
+
console.log(message);
|
51
|
+
|
52
|
+
return true;
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import * as fs from "fs";
|
2
|
+
import * as path from "path";
|
3
|
+
import { PackageJson } from "type-fest";
|
4
|
+
import { CLI, utils } from "./../../../index";
|
5
|
+
|
6
|
+
const PackageJSON: PackageJson = JSON.parse(
|
7
|
+
fs
|
8
|
+
.readFileSync(path.join(__dirname, "..", "..", "..", "..", "package.json"))
|
9
|
+
.toString(),
|
10
|
+
);
|
11
|
+
|
12
|
+
export class GeneratePluginCLI extends CLI {
|
13
|
+
name = "generate-plugin";
|
14
|
+
description =
|
15
|
+
"Generate the structure of a new actionhero plugin in an empty directory";
|
16
|
+
example = "actionhero generate plugin";
|
17
|
+
|
18
|
+
async run() {
|
19
|
+
let templateBuffer = fs.readFileSync(
|
20
|
+
path.join(
|
21
|
+
__dirname,
|
22
|
+
"/../../../../templates/package-plugin.json.template",
|
23
|
+
),
|
24
|
+
);
|
25
|
+
let template = String(templateBuffer);
|
26
|
+
|
27
|
+
const regex = new RegExp("%%versionNumber%%", "g");
|
28
|
+
template = template.replace(regex, PackageJSON.version);
|
29
|
+
|
30
|
+
[
|
31
|
+
"actions",
|
32
|
+
"tasks",
|
33
|
+
"initializers",
|
34
|
+
"servers",
|
35
|
+
"config",
|
36
|
+
"bin",
|
37
|
+
"public",
|
38
|
+
].forEach((type) => {
|
39
|
+
try {
|
40
|
+
const message = utils.fileUtils.createDirSafely(
|
41
|
+
path.join(process.cwd(), type),
|
42
|
+
);
|
43
|
+
console.info(message);
|
44
|
+
} catch (error) {
|
45
|
+
console.log(error.toString());
|
46
|
+
}
|
47
|
+
});
|
48
|
+
|
49
|
+
const message = utils.fileUtils.createFileSafely(
|
50
|
+
path.join(process.cwd(), "package.json"),
|
51
|
+
template,
|
52
|
+
);
|
53
|
+
console.info(message);
|
54
|
+
|
55
|
+
return true;
|
56
|
+
}
|
57
|
+
}
|