create-moost 0.4.22 → 0.5.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/dist/index.cjs +30 -52
- package/dist/index.mjs +30 -52
- package/package.json +4 -4
- package/templates/common/.domelintrc.yml +14 -0
- package/templates/common/package.jsonc +38 -0
- package/templates/common/tsconfig.json +19 -13
- package/templates/common/vite.config.ts +13 -0
- package/templates/http/src/controllers/app.controller.ts +98 -5
- package/templates/http/src/main.ts +46 -13
- package/templates/wf/src/static/wf.css +135 -0
- package/templates/wf/src/workflow/wf.controller.ts +230 -0
- package/templates/wf/src/workflow/wf.encrypt.ts +122 -0
- package/templates/wf/src/workflow/wf.types.ts +176 -0
- package/templates/wf/src/workflow/wf2html.interceptor.ts +233 -0
- package/templates/common/.eslintrc.json +0 -52
- package/templates/common/.prettierignore +0 -4
- package/templates/common/.prettierrc +0 -8
- package/templates/common/build.js +0 -28
- package/templates/common/package.json +0 -78
- package/templates/common/rollup.config.js +0 -23
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Workflow to HTML Page Interceptor for Moost Workflow Template
|
|
3
|
+
*
|
|
4
|
+
* This module defines the `Wf2HtmlPageInterceptor` class, which serves as an interceptor
|
|
5
|
+
* for MoostHttp handler. The interceptor is responsible for
|
|
6
|
+
* converting workflow outputs into HTML pages with forms, enabling workflows to execute,
|
|
7
|
+
* pause for user inputs, and maintain context in an encrypted state property.
|
|
8
|
+
*
|
|
9
|
+
* **Important:** This example is highly simplified and intended solely for demonstration
|
|
10
|
+
* purposes. A production implementation would require more robust error handling,
|
|
11
|
+
* secure key management, and comprehensive validation mechanisms.
|
|
12
|
+
*
|
|
13
|
+
* The interceptor performs the following key functions:
|
|
14
|
+
* - Decrypts the workflow state received from the frontend.
|
|
15
|
+
* - Processes the workflow response to render appropriate HTML pages for user interaction.
|
|
16
|
+
* - Encrypts the updated workflow state before sending it back to the frontend.
|
|
17
|
+
*
|
|
18
|
+
* For more information on Moost Workflows, visit https://moost.org/wf/
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Body } from "@moostjs/event-http";
|
|
22
|
+
import type { TFlowOutput } from "@moostjs/event-wf";
|
|
23
|
+
import type { TInterceptorClass, TInterceptorFn } from "moost";
|
|
24
|
+
import { Injectable, Intercept } from "moost";
|
|
25
|
+
|
|
26
|
+
import { decryptState, encryptState } from "./wf.encrypt";
|
|
27
|
+
import type {
|
|
28
|
+
TWfExampleContext,
|
|
29
|
+
TWfExampleInput,
|
|
30
|
+
TWfExampleInputSchema,
|
|
31
|
+
TWfState,
|
|
32
|
+
} from "./wf.types";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `Wf2HtmlPageInterceptor` intercepts http handlers to transform workflow outputs
|
|
36
|
+
* into HTML pages, facilitating user interactions through forms.
|
|
37
|
+
*
|
|
38
|
+
* This interceptor handles the encryption and decryption of the workflow state to ensure
|
|
39
|
+
* secure transmission between the backend and frontend.
|
|
40
|
+
*/
|
|
41
|
+
@Injectable("FOR_EVENT")
|
|
42
|
+
class Wf2HtmlPageInterceptor implements TInterceptorClass {
|
|
43
|
+
/**
|
|
44
|
+
* The input payload containing user inputs and the workflow state.
|
|
45
|
+
*
|
|
46
|
+
* @type {TWfExampleInput & { wfState: string | TWfState | undefined }}
|
|
47
|
+
*/
|
|
48
|
+
@Body()
|
|
49
|
+
input?: TWfExampleInput & { wfState: string | TWfState | undefined };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The interceptor handler function that processes workflow events.
|
|
53
|
+
*
|
|
54
|
+
* @type {TInterceptorFn}
|
|
55
|
+
*/
|
|
56
|
+
handler: TInterceptorFn = (before, after) => {
|
|
57
|
+
/**
|
|
58
|
+
* Pre-processing step executed before the main workflow logic.
|
|
59
|
+
*
|
|
60
|
+
* - Decrypts the workflow state if it is a string.
|
|
61
|
+
* - Clears the workflow state if it is not a string.
|
|
62
|
+
*
|
|
63
|
+
* @function before
|
|
64
|
+
*/
|
|
65
|
+
before(() => {
|
|
66
|
+
if (typeof this.input?.wfState === "string") {
|
|
67
|
+
this.input.wfState = decryptState(this.input.wfState);
|
|
68
|
+
} else if (this.input) {
|
|
69
|
+
this.input.wfState = undefined;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Post-processing step executed after the main workflow logic.
|
|
75
|
+
*
|
|
76
|
+
* - Renders an input form if additional inputs are required.
|
|
77
|
+
* - Renders the final output page if the workflow has finished.
|
|
78
|
+
*
|
|
79
|
+
* @function after
|
|
80
|
+
* @param {TFlowOutput<TWfExampleContext, TWfExampleInput, TWfExampleInputSchema>} response - The workflow response.
|
|
81
|
+
* @param {Function} reply - The function to send the HTTP response.
|
|
82
|
+
*/
|
|
83
|
+
after((response, reply) => {
|
|
84
|
+
const wfOutput = response as TFlowOutput<
|
|
85
|
+
TWfExampleContext,
|
|
86
|
+
TWfExampleInput,
|
|
87
|
+
TWfExampleInputSchema
|
|
88
|
+
>;
|
|
89
|
+
|
|
90
|
+
if (wfOutput.inputRequired) {
|
|
91
|
+
reply(
|
|
92
|
+
renderInputPage(wfOutput.inputRequired, encryptState(wfOutput.state))
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (wfOutput.finished) {
|
|
96
|
+
reply(renderOutputPage(wfOutput.state.context));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Factory function to create an interceptor decorator for the `Wf2HtmlPageInterceptor`.
|
|
104
|
+
*
|
|
105
|
+
* @returns {Function} The interceptor decorator.
|
|
106
|
+
*/
|
|
107
|
+
export const Wf2HtmlPage = () => Intercept(Wf2HtmlPageInterceptor);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* SVG path data for the Moost logo used in the HTML pages.
|
|
111
|
+
*
|
|
112
|
+
* @type {string}
|
|
113
|
+
*/
|
|
114
|
+
const moostLogo =
|
|
115
|
+
'<path fill-rule="evenodd" clip-rule="evenodd" d="M126 71H127.5C137.717 71 146 79.2827 146 89.5C146 99.7173 137.717 108 127.5 108H126C120.942 117.021 113.089 122 103.012 122H127.5C145.449 122 160 107.449 160 89.5C160 71.5507 145.449 57 127.5 57H103.012C113.089 57 120.942 61.9792 126 71ZM103.012 71L89 57H87.5C69.5507 57 55 71.5507 55 89.5C55 107.449 69.5507 122 87.5 122H89L103.012 108H87.5C77.2827 108 69 99.7173 69 89.5C69 79.2827 77.2827 71 87.5 71H103.012Z" fill="#FF269B"/>';
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Renders the HTML page for user inputs.
|
|
119
|
+
*
|
|
120
|
+
* This function generates an HTML form based on the required inputs from the workflow.
|
|
121
|
+
* It includes the necessary fields, messages, error messages, and the encrypted workflow state.
|
|
122
|
+
*
|
|
123
|
+
* **Note:** This implementation is simplified for demonstration purposes.
|
|
124
|
+
*
|
|
125
|
+
* @param {TWfExampleInputSchema} inputRequired - The schema defining required inputs.
|
|
126
|
+
* @param {string} [state=''] - The encrypted workflow state to be included as a hidden field.
|
|
127
|
+
* @returns {string} The rendered HTML string for the input page.
|
|
128
|
+
*/
|
|
129
|
+
function renderInputPage(
|
|
130
|
+
inputRequired: TWfExampleInputSchema,
|
|
131
|
+
state = ""
|
|
132
|
+
): string {
|
|
133
|
+
return `
|
|
134
|
+
<!DOCTYPE html>
|
|
135
|
+
<html>
|
|
136
|
+
<head>
|
|
137
|
+
<title>Moost Workflows Example</title>
|
|
138
|
+
<link rel="preload stylesheet" href="/static/wf.css" as="style">
|
|
139
|
+
</head>
|
|
140
|
+
<body>
|
|
141
|
+
<section>
|
|
142
|
+
<h1>
|
|
143
|
+
<svg viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
144
|
+
${moostLogo}
|
|
145
|
+
<rect x="27" y="64" width="91" height="51" rx="25.5" stroke="#FF269B" stroke-width="14"/>
|
|
146
|
+
</svg>
|
|
147
|
+
Workflows Example
|
|
148
|
+
</h1>
|
|
149
|
+
<form method="POST" action="/wf">
|
|
150
|
+
${renderMessage(inputRequired.message)}
|
|
151
|
+
<input type="hidden" name="wfState" value="${state}" />
|
|
152
|
+
${inputRequired.inputs.map((i) => renderInput(i)).join("\n")}
|
|
153
|
+
${renderError(inputRequired.errorMessage)}
|
|
154
|
+
<button type="submit">Submit</button>
|
|
155
|
+
<a href="https://moost.org/wf/" target="_blank">Moost WF Documentation</a>
|
|
156
|
+
</form>
|
|
157
|
+
</section>
|
|
158
|
+
</body>
|
|
159
|
+
</html>
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Renders an optional message within the HTML page.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} [message] - The message to be displayed.
|
|
167
|
+
* @returns {string} The rendered HTML string for the message.
|
|
168
|
+
*/
|
|
169
|
+
function renderMessage(message?: string): string {
|
|
170
|
+
return message ? `<p>${message}</p>` : "";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Renders an optional error message within the HTML page.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} [message] - The error message to be displayed.
|
|
177
|
+
* @returns {string} The rendered HTML string for the error message.
|
|
178
|
+
*/
|
|
179
|
+
function renderError(message?: string): string {
|
|
180
|
+
return message ? `<p class="error">${message}</p>` : "";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Renders the final output HTML page after workflow completion.
|
|
185
|
+
*
|
|
186
|
+
* This function generates an HTML page displaying the user's greeting, email, and age.
|
|
187
|
+
*
|
|
188
|
+
* **Note:** This implementation is simplified for demonstration purposes.
|
|
189
|
+
*
|
|
190
|
+
* @param {TWfExampleContext} ctx - The workflow context containing user data.
|
|
191
|
+
* @returns {string} The rendered HTML string for the output page.
|
|
192
|
+
*/
|
|
193
|
+
function renderOutputPage(ctx: TWfExampleContext): string {
|
|
194
|
+
return `
|
|
195
|
+
<!DOCTYPE html>
|
|
196
|
+
<html>
|
|
197
|
+
<head>
|
|
198
|
+
<title>Moost Workflows Example</title>
|
|
199
|
+
<link rel="preload stylesheet" href="/static/wf.css" as="style">
|
|
200
|
+
</head>
|
|
201
|
+
<body>
|
|
202
|
+
<section>
|
|
203
|
+
<h1>
|
|
204
|
+
<svg viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
205
|
+
${moostLogo}
|
|
206
|
+
<rect x="27" y="64" width="91" height="51" rx="25.5" stroke="#FF269B" stroke-width="14"/>
|
|
207
|
+
</svg>
|
|
208
|
+
Workflows Example
|
|
209
|
+
</h1>
|
|
210
|
+
<h2>${ctx.greeting}</h2>
|
|
211
|
+
<p>Your email is ${ctx.email}</p>
|
|
212
|
+
<p>Your age is ${ctx.age}</p>
|
|
213
|
+
|
|
214
|
+
<a href="https://moost.org/wf/" target="_blank">Moost WF Documentation</a>
|
|
215
|
+
</section>
|
|
216
|
+
</body>
|
|
217
|
+
</html>
|
|
218
|
+
`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Renders an individual input field within the HTML form.
|
|
223
|
+
*
|
|
224
|
+
* This function generates the HTML string for a single input field based on its schema.
|
|
225
|
+
*
|
|
226
|
+
* @param {TWfExampleInputSchema['inputs'][number]} input - The input schema item.
|
|
227
|
+
* @returns {string} The rendered HTML string for the input field.
|
|
228
|
+
*/
|
|
229
|
+
function renderInput(input: TWfExampleInputSchema["inputs"][number]): string {
|
|
230
|
+
return `<label>${input.label}<input type="${input.type}" name="${
|
|
231
|
+
input.name
|
|
232
|
+
}" value="${input.value || ""}" /></label>`;
|
|
233
|
+
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"parserOptions": {
|
|
4
|
-
"project": ["./tsconfig.json"],
|
|
5
|
-
"tsconfigRootDir": "./",
|
|
6
|
-
"sourceType": "module"
|
|
7
|
-
},
|
|
8
|
-
"ignorePatterns": [
|
|
9
|
-
//=IF (bundler === 'rollup')
|
|
10
|
-
"rollup.config.js",
|
|
11
|
-
//=END IF
|
|
12
|
-
//=IF (type === 'cli')
|
|
13
|
-
"bin.js",
|
|
14
|
-
//=END IF
|
|
15
|
-
".eslintrc.js",
|
|
16
|
-
"dist"
|
|
17
|
-
],
|
|
18
|
-
"plugins": ["@typescript-eslint/eslint-plugin"],
|
|
19
|
-
"extends": [
|
|
20
|
-
"plugin:@typescript-eslint/eslint-recommended",
|
|
21
|
-
"plugin:@typescript-eslint/recommended",
|
|
22
|
-
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
|
23
|
-
//=IF (prettier)
|
|
24
|
-
"prettier"
|
|
25
|
-
//=END IF
|
|
26
|
-
],
|
|
27
|
-
"root": true,
|
|
28
|
-
"env": {
|
|
29
|
-
"node": true,
|
|
30
|
-
"jest": true
|
|
31
|
-
},
|
|
32
|
-
"rules": {
|
|
33
|
-
"indent": ["error", 4, { "SwitchCase": 1 }],
|
|
34
|
-
"comma-dangle": ["error", "always-multiline"],
|
|
35
|
-
"no-multiple-empty-lines": ["error", { "max": 1 }],
|
|
36
|
-
"lines-between-class-members": ["error", "always"],
|
|
37
|
-
"padded-blocks": ["error", "never"],
|
|
38
|
-
"eol-last": ["error", "always"],
|
|
39
|
-
"quotes": ["error", "single"],
|
|
40
|
-
"semi": ["error", "never"],
|
|
41
|
-
"@typescript-eslint/explicit-function-return-type": "off",
|
|
42
|
-
"@typescript-eslint/no-empty-interface": "off",
|
|
43
|
-
"@typescript-eslint/no-empty-function": "off",
|
|
44
|
-
"@typescript-eslint/no-explicit-any": "warn",
|
|
45
|
-
"@typescript-eslint/no-inferrable-types": "off",
|
|
46
|
-
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
47
|
-
"@typescript-eslint/no-implied-eval": "off",
|
|
48
|
-
|
|
49
|
-
"@typescript-eslint/no-this-alias": "warn",
|
|
50
|
-
"no-debugger": "error"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const esbuild = require('esbuild')
|
|
2
|
-
const esbuildPluginTsc = require('esbuild-plugin-tsc')
|
|
3
|
-
//=IF (type === 'http')
|
|
4
|
-
const { spawn } = require('child_process')
|
|
5
|
-
//=END IF
|
|
6
|
-
|
|
7
|
-
const watch = process.argv[2] === 'watch'
|
|
8
|
-
|
|
9
|
-
esbuild[watch ? 'context' : 'build']({
|
|
10
|
-
entryPoints: [
|
|
11
|
-
'./src/main.ts',
|
|
12
|
-
],
|
|
13
|
-
logLevel: 'info',
|
|
14
|
-
bundle: true,
|
|
15
|
-
outdir: './dist',
|
|
16
|
-
platform: 'node',
|
|
17
|
-
packages: 'external',
|
|
18
|
-
sourcemap: watch,
|
|
19
|
-
plugins: [esbuildPluginTsc()],
|
|
20
|
-
}).then(async ctx => {
|
|
21
|
-
if (watch) {
|
|
22
|
-
//=IF (type === 'http')
|
|
23
|
-
const nodemon = spawn('nodemon', { stdio: 'inherit' })
|
|
24
|
-
process.on('SIGTERM', async () => { nodemon.kill(); await ctx.dispose() })
|
|
25
|
-
//=END IF
|
|
26
|
-
await ctx.watch()
|
|
27
|
-
}
|
|
28
|
-
})
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{ packageName }}",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "dist/main.js",
|
|
6
|
-
//=IF (type === 'cli')
|
|
7
|
-
"bin": {
|
|
8
|
-
"{{ packageName }}": "./bin.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"bin.js"
|
|
13
|
-
],
|
|
14
|
-
//=END IF
|
|
15
|
-
"scripts": {
|
|
16
|
-
//=IF (bundler === 'rollup')
|
|
17
|
-
//=IF (type === 'http')
|
|
18
|
-
"dev": "NODE_OPTIONS=--enable-source-maps rollup -c=./rollup.config.js --watch",
|
|
19
|
-
//=END IF
|
|
20
|
-
//=IF (type === 'cli')
|
|
21
|
-
"dev": "npm run build && ./bin.js",
|
|
22
|
-
//=END IF
|
|
23
|
-
"build": "rollup -c=./rollup.config.js",
|
|
24
|
-
//=END IF
|
|
25
|
-
//=IF (bundler === 'esbuild')
|
|
26
|
-
//=IF (type === 'http')
|
|
27
|
-
"dev": "npm run build:watch",
|
|
28
|
-
"nodemon": "NODE_OPTIONS=--enable-source-maps nodemon ./dist/main",
|
|
29
|
-
//=END IF
|
|
30
|
-
//=IF (type === 'cli')
|
|
31
|
-
"dev": "npm run build && ./bin.js",
|
|
32
|
-
//=END IF
|
|
33
|
-
"build": "node ./build",
|
|
34
|
-
"build:watch": "node ./build watch",
|
|
35
|
-
//=END IF
|
|
36
|
-
//=IF (prettier)
|
|
37
|
-
"prettify": "prettier . --write && lint --fix",
|
|
38
|
-
//=END IF
|
|
39
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
40
|
-
},
|
|
41
|
-
"author": "",
|
|
42
|
-
"license": "ISC",
|
|
43
|
-
"dependencies": {
|
|
44
|
-
//=IF (type === 'http')
|
|
45
|
-
"@moostjs/event-http": "^{{ version }}",
|
|
46
|
-
//=END IF
|
|
47
|
-
//=IF (type === 'cli')
|
|
48
|
-
"@moostjs/event-cli": "^{{ version }}",
|
|
49
|
-
//=END IF
|
|
50
|
-
"moost": "^{{ version }}"
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
//=IF (bundler === 'rollup')
|
|
54
|
-
//=IF (type === 'http')
|
|
55
|
-
"@rollup/plugin-run": "^3.0.1",
|
|
56
|
-
//=END IF
|
|
57
|
-
"@rollup/plugin-typescript": "^11.1.1",
|
|
58
|
-
"rollup": "^3.23.0",
|
|
59
|
-
"tslib": "^2.5.2",
|
|
60
|
-
//=END IF
|
|
61
|
-
//=IF (bundler === 'esbuild')
|
|
62
|
-
"esbuild": "^0.18.17",
|
|
63
|
-
"esbuild-plugin-tsc": "^0.4.0",
|
|
64
|
-
//=IF (type === 'http')
|
|
65
|
-
"nodemon": "^2.0.22",
|
|
66
|
-
//=END IF
|
|
67
|
-
//=END IF
|
|
68
|
-
//=IF (eslint)
|
|
69
|
-
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
|
70
|
-
"eslint": "^8.41.0",
|
|
71
|
-
"eslint-config-prettier": "^8.8.0",
|
|
72
|
-
//=END IF
|
|
73
|
-
//=IF (prettier)
|
|
74
|
-
"prettier": "^2.8.8",
|
|
75
|
-
//=END IF
|
|
76
|
-
"typescript": "^5.0.4"
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
//=IF (type === 'http')
|
|
2
|
-
const run = require('@rollup/plugin-run')
|
|
3
|
-
//=END IF
|
|
4
|
-
const ts = require('@rollup/plugin-typescript')
|
|
5
|
-
//=IF (type === 'http')
|
|
6
|
-
const dev = process.env.NODE_OPTIONS === '--enable-source-maps'
|
|
7
|
-
//=END IF
|
|
8
|
-
module.exports = {
|
|
9
|
-
input: './src/main.ts',
|
|
10
|
-
output: {
|
|
11
|
-
file: 'dist/main.js',
|
|
12
|
-
format: 'cjs',
|
|
13
|
-
//=IF (type === 'http')
|
|
14
|
-
sourcemap: dev,
|
|
15
|
-
//=END IF
|
|
16
|
-
},
|
|
17
|
-
plugins: [
|
|
18
|
-
ts(),
|
|
19
|
-
//=IF (type === 'http')
|
|
20
|
-
dev && run() || null,
|
|
21
|
-
//=END IF
|
|
22
|
-
],
|
|
23
|
-
}
|