create-moost 0.5.33 → 0.6.1

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/README.md CHANGED
@@ -21,7 +21,7 @@ To create a Moost HTTP app, use the following command in your terminal:
21
21
  npm create moost -- --http
22
22
  ```
23
23
 
24
- This command will initiate the creation of a new Moost HTTP app. It will prompt you with a series of questions to configure your app, such as enabling eslint and prettier, and choosing between the esbuild and rollup bundlers.
24
+ This command will initiate the creation of a new Moost HTTP app. It will prompt you to configure the project name, whether to include a Moost Workflows example, and whether to add do-me-lint (smart eslint installer).
25
25
 
26
26
  ### Creating a CLI App
27
27
 
@@ -31,6 +31,6 @@ To create a Moost CLI app, use the following command in your terminal:
31
31
  npm create moost -- --cli
32
32
  ```
33
33
 
34
- This command will start the creation process for a new Moost CLI app. It will ask you questions to customize your app, including eslint and prettier preferences, as well as the choice between the esbuild and rollup bundlers.
34
+ This command will start the creation process for a new Moost CLI app. It will prompt you to configure the project name and whether to add do-me-lint (smart eslint installer).
35
35
 
36
36
  ## [Official Documentation](https://moost.org/)
package/dist/index.cjs CHANGED
@@ -63,24 +63,35 @@ async function getPrompts(inputs) {
63
63
  {
64
64
  name: "type",
65
65
  type: () => {
66
- if (inputs.cli && !inputs.http) {
66
+ if (inputs.ws && !inputs.cli && !inputs.http) {
67
+ predefined.type = "ws";
68
+ return null;
69
+ }
70
+ if (inputs.cli && !inputs.http && !inputs.ws) {
67
71
  predefined.type = "cli";
68
72
  return null;
69
73
  }
70
- if (!inputs.cli && inputs.http) {
74
+ if (inputs.http && !inputs.cli && !inputs.ws) {
71
75
  predefined.type = "http";
72
76
  return null;
73
77
  }
74
78
  return "select";
75
79
  },
76
80
  message: "Moost Adapter:",
77
- choices: [{
78
- title: "HTTP (Web) Application",
79
- value: "http"
80
- }, {
81
- title: "CLI Application",
82
- value: "cli"
83
- }]
81
+ choices: [
82
+ {
83
+ title: "HTTP (Web) Application",
84
+ value: "http"
85
+ },
86
+ {
87
+ title: "WebSocket Application",
88
+ value: "ws"
89
+ },
90
+ {
91
+ title: "CLI Application",
92
+ value: "cli"
93
+ }
94
+ ]
84
95
  },
85
96
  {
86
97
  name: "packageName",
@@ -89,18 +100,34 @@ async function getPrompts(inputs) {
89
100
  initial: () => toValidPackageName(predefined.targetDir),
90
101
  validate: (dir) => isValidPackageName(dir) || "Invalid package.json name"
91
102
  },
103
+ {
104
+ name: "ws",
105
+ type: (prev, values) => {
106
+ const type = values.type || predefined.type;
107
+ if (type !== "http" || inputs.ws) return null;
108
+ return "toggle";
109
+ },
110
+ message: "Add WebSockets?",
111
+ initial: false,
112
+ active: "Yes",
113
+ inactive: "No"
114
+ },
92
115
  {
93
116
  name: "wf",
94
- type: (prev, values) => inputs.wf || values.type === "cli" || inputs.cli ? null : "toggle",
117
+ type: (prev, values) => {
118
+ const type = values.type || predefined.type;
119
+ if (inputs.wf || type === "cli" || type === "ws") return null;
120
+ return "toggle";
121
+ },
95
122
  message: "Add Moost Workflows Example?",
96
123
  initial: false,
97
124
  active: "Yes",
98
125
  inactive: "No"
99
126
  },
100
127
  {
101
- name: "domelint",
102
- type: () => inputs.domelint ? null : "toggle",
103
- message: "Add do-me-lint (smart eslint installer)?",
128
+ name: "oxc",
129
+ type: () => inputs.oxc ? null : "toggle",
130
+ message: "Add OXC lint and formatter (oxlint + oxfmt)?",
104
131
  initial: false,
105
132
  active: "Yes",
106
133
  inactive: "No"
@@ -129,7 +156,7 @@ function isValidPackageName(projectName) {
129
156
  return /^(?:@[\d*a-z~-][\d*._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*$/.test(projectName);
130
157
  }
131
158
  function toValidPackageName(projectName) {
132
- return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^\da-z~-]+/g, "-");
159
+ return projectName.trim().toLowerCase().replaceAll(/\s+/g, "-").replace(/^[._]/, "").replaceAll(/[^\da-z~-]+/g, "-");
133
160
  }
134
161
 
135
162
  //#endregion
@@ -139,9 +166,8 @@ const rw = new __prostojs_rewrite.ProstoRewrite({ textPattern: [
139
166
  "Dockerfile",
140
167
  "*config",
141
168
  ".gitignore",
142
- ".eslintrc.json",
143
- ".prettierignore",
144
- ".prettierrc"
169
+ ".oxlintrc.json",
170
+ ".oxfmtrc.json"
145
171
  ] });
146
172
  const root = process.cwd();
147
173
  const { version } = JSON.parse((0, fs.readFileSync)((0, path.join)(__dirname, "../package.json")).toString());
@@ -150,39 +176,43 @@ async function scaffold(data) {
150
176
  if ((0, fs.existsSync)(projectDir)) {
151
177
  if (data.overwrite) emptyDirectorySync(projectDir);
152
178
  } else (0, fs.mkdirSync)(projectDir);
153
- const templatePath = (0, path.join)(__dirname, "../templates", data.type);
179
+ const templatePath = (0, path.join)(__dirname, "../templates", data.type === "ws" ? "ws" : data.type);
154
180
  const commonPath = (0, path.join)(__dirname, "../templates/common");
155
181
  const wfPath = (0, path.join)(__dirname, "../templates/wf");
182
+ const wsAddonPath = (0, path.join)(__dirname, "../templates/ws-addon");
156
183
  const context = {
157
184
  ...data,
158
185
  version
159
186
  };
160
187
  const excludeCommon = [];
161
- if (!data.domelint) excludeCommon.push(".domelintrc.yml");
188
+ if (!data.oxc) {
189
+ excludeCommon.push(".oxlintrc.json");
190
+ excludeCommon.push(".oxfmtrc.json");
191
+ }
192
+ const renameFile = (filename) => {
193
+ if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
194
+ return filename;
195
+ };
162
196
  await rw.rewriteDir({
163
197
  baseDir: templatePath,
164
198
  output: projectDir,
165
- renameFile(filename) {
166
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
167
- return filename;
168
- }
199
+ renameFile
169
200
  }, context);
170
201
  await rw.rewriteDir({
171
202
  baseDir: commonPath,
172
203
  output: projectDir,
173
204
  exclude: excludeCommon,
174
- renameFile(filename) {
175
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
176
- return filename;
177
- }
205
+ renameFile
178
206
  }, context);
179
- if (data.wf) await rw.rewriteDir({
207
+ if (data.wf && data.type === "http") await rw.rewriteDir({
180
208
  baseDir: wfPath,
181
209
  output: projectDir,
182
- renameFile(filename) {
183
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
184
- return filename;
185
- }
210
+ renameFile
211
+ }, context);
212
+ if (data.ws && data.type === "http") await rw.rewriteDir({
213
+ baseDir: wsAddonPath,
214
+ output: projectDir,
215
+ renameFile
186
216
  }, context);
187
217
  }
188
218
  function emptyDirectorySync(directory) {
@@ -219,18 +249,20 @@ let Commands = class Commands$1 extends __moostjs_event_cli.CliApp {
219
249
  return this.execute(name);
220
250
  }
221
251
  async execute(name) {
222
- (0, __wooksjs_event_cli.useAutoHelp)() && process.exit(0);
252
+ if ((0, __wooksjs_event_cli.useAutoHelp)()) process.exit(0);
223
253
  const prompts$2 = await getPrompts({
224
254
  name,
225
255
  http: !!(0, __wooksjs_event_cli.useCliOption)("http"),
226
256
  cli: !!(0, __wooksjs_event_cli.useCliOption)("cli"),
257
+ ws: !!(0, __wooksjs_event_cli.useCliOption)("ws"),
227
258
  wf: !!(0, __wooksjs_event_cli.useCliOption)("wf"),
228
- domelint: !!(0, __wooksjs_event_cli.useCliOption)("domelint"),
259
+ oxc: !!(0, __wooksjs_event_cli.useCliOption)("oxc"),
229
260
  force: !!(0, __wooksjs_event_cli.useCliOption)("force")
230
261
  });
231
262
  console.log("\nScaffolding a new project...");
232
263
  await scaffold(prompts$2);
233
264
  const cli = prompts$2.type === "cli";
265
+ const ws = prompts$2.type === "ws";
234
266
  return `
235
267
  Success! Your new "${prompts$2.projectName}" project has been created successfully. 
236
268
 
@@ -245,10 +277,10 @@ ${cli ? `
245
277
  3. Make bin.js executable:
246
278
  chmod +x ./bin.js 
247
279
  ` : ""}
248
- ${cli ? "4" : "3"}. Start the development server:
280
+ ${cli ? "4" : "3"}. ${ws ? "Build and start" : "Start the development server"}:
249
281
  npm run dev${cli ? " -- hello World" : ""}
250
282
 
251
- You're all set! The development server will help you in building your application.
283
+ You're all set!${ws ? "" : " The development server will help you in building your application."}
252
284
  Enjoy coding, and build something amazing!
253
285
  `;
254
286
  }
@@ -278,14 +310,19 @@ function run() {
278
310
  description: "Use Moost CLI",
279
311
  type: Boolean
280
312
  },
313
+ {
314
+ keys: ["ws"],
315
+ description: "Use Moost WebSocket",
316
+ type: Boolean
317
+ },
281
318
  {
282
319
  keys: ["wf"],
283
320
  description: "Add Workflow Adapter",
284
321
  type: Boolean
285
322
  },
286
323
  {
287
- keys: ["domelint"],
288
- description: "Add do-me-lint (smart eslint installer)",
324
+ keys: ["oxc"],
325
+ description: "Add OXC lint and formatter (oxlint + oxfmt)",
289
326
  type: Boolean
290
327
  },
291
328
  {
package/dist/index.mjs CHANGED
@@ -40,24 +40,35 @@ async function getPrompts(inputs) {
40
40
  {
41
41
  name: "type",
42
42
  type: () => {
43
- if (inputs.cli && !inputs.http) {
43
+ if (inputs.ws && !inputs.cli && !inputs.http) {
44
+ predefined.type = "ws";
45
+ return null;
46
+ }
47
+ if (inputs.cli && !inputs.http && !inputs.ws) {
44
48
  predefined.type = "cli";
45
49
  return null;
46
50
  }
47
- if (!inputs.cli && inputs.http) {
51
+ if (inputs.http && !inputs.cli && !inputs.ws) {
48
52
  predefined.type = "http";
49
53
  return null;
50
54
  }
51
55
  return "select";
52
56
  },
53
57
  message: "Moost Adapter:",
54
- choices: [{
55
- title: "HTTP (Web) Application",
56
- value: "http"
57
- }, {
58
- title: "CLI Application",
59
- value: "cli"
60
- }]
58
+ choices: [
59
+ {
60
+ title: "HTTP (Web) Application",
61
+ value: "http"
62
+ },
63
+ {
64
+ title: "WebSocket Application",
65
+ value: "ws"
66
+ },
67
+ {
68
+ title: "CLI Application",
69
+ value: "cli"
70
+ }
71
+ ]
61
72
  },
62
73
  {
63
74
  name: "packageName",
@@ -66,18 +77,34 @@ async function getPrompts(inputs) {
66
77
  initial: () => toValidPackageName(predefined.targetDir),
67
78
  validate: (dir) => isValidPackageName(dir) || "Invalid package.json name"
68
79
  },
80
+ {
81
+ name: "ws",
82
+ type: (prev, values) => {
83
+ const type = values.type || predefined.type;
84
+ if (type !== "http" || inputs.ws) return null;
85
+ return "toggle";
86
+ },
87
+ message: "Add WebSockets?",
88
+ initial: false,
89
+ active: "Yes",
90
+ inactive: "No"
91
+ },
69
92
  {
70
93
  name: "wf",
71
- type: (prev, values) => inputs.wf || values.type === "cli" || inputs.cli ? null : "toggle",
94
+ type: (prev, values) => {
95
+ const type = values.type || predefined.type;
96
+ if (inputs.wf || type === "cli" || type === "ws") return null;
97
+ return "toggle";
98
+ },
72
99
  message: "Add Moost Workflows Example?",
73
100
  initial: false,
74
101
  active: "Yes",
75
102
  inactive: "No"
76
103
  },
77
104
  {
78
- name: "domelint",
79
- type: () => inputs.domelint ? null : "toggle",
80
- message: "Add do-me-lint (smart eslint installer)?",
105
+ name: "oxc",
106
+ type: () => inputs.oxc ? null : "toggle",
107
+ message: "Add OXC lint and formatter (oxlint + oxfmt)?",
81
108
  initial: false,
82
109
  active: "Yes",
83
110
  inactive: "No"
@@ -106,7 +133,7 @@ function isValidPackageName(projectName) {
106
133
  return /^(?:@[\d*a-z~-][\d*._a-z~-]*\/)?[\da-z~-][\d._a-z~-]*$/.test(projectName);
107
134
  }
108
135
  function toValidPackageName(projectName) {
109
- return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^\da-z~-]+/g, "-");
136
+ return projectName.trim().toLowerCase().replaceAll(/\s+/g, "-").replace(/^[._]/, "").replaceAll(/[^\da-z~-]+/g, "-");
110
137
  }
111
138
 
112
139
  //#endregion
@@ -116,9 +143,8 @@ const rw = new ProstoRewrite({ textPattern: [
116
143
  "Dockerfile",
117
144
  "*config",
118
145
  ".gitignore",
119
- ".eslintrc.json",
120
- ".prettierignore",
121
- ".prettierrc"
146
+ ".oxlintrc.json",
147
+ ".oxfmtrc.json"
122
148
  ] });
123
149
  const root = process.cwd();
124
150
  const { version } = JSON.parse(readFileSync(join(__dirname, "../package.json")).toString());
@@ -127,39 +153,43 @@ async function scaffold(data) {
127
153
  if (existsSync(projectDir)) {
128
154
  if (data.overwrite) emptyDirectorySync(projectDir);
129
155
  } else mkdirSync(projectDir);
130
- const templatePath = join(__dirname, "../templates", data.type);
156
+ const templatePath = join(__dirname, "../templates", data.type === "ws" ? "ws" : data.type);
131
157
  const commonPath = join(__dirname, "../templates/common");
132
158
  const wfPath = join(__dirname, "../templates/wf");
159
+ const wsAddonPath = join(__dirname, "../templates/ws-addon");
133
160
  const context = {
134
161
  ...data,
135
162
  version
136
163
  };
137
164
  const excludeCommon = [];
138
- if (!data.domelint) excludeCommon.push(".domelintrc.yml");
165
+ if (!data.oxc) {
166
+ excludeCommon.push(".oxlintrc.json");
167
+ excludeCommon.push(".oxfmtrc.json");
168
+ }
169
+ const renameFile = (filename) => {
170
+ if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
171
+ return filename;
172
+ };
139
173
  await rw.rewriteDir({
140
174
  baseDir: templatePath,
141
175
  output: projectDir,
142
- renameFile(filename) {
143
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
144
- return filename;
145
- }
176
+ renameFile
146
177
  }, context);
147
178
  await rw.rewriteDir({
148
179
  baseDir: commonPath,
149
180
  output: projectDir,
150
181
  exclude: excludeCommon,
151
- renameFile(filename) {
152
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
153
- return filename;
154
- }
182
+ renameFile
155
183
  }, context);
156
- if (data.wf) await rw.rewriteDir({
184
+ if (data.wf && data.type === "http") await rw.rewriteDir({
157
185
  baseDir: wfPath,
158
186
  output: projectDir,
159
- renameFile(filename) {
160
- if (filename.endsWith(".jsonc")) return filename.replace(/c$/, "");
161
- return filename;
162
- }
187
+ renameFile
188
+ }, context);
189
+ if (data.ws && data.type === "http") await rw.rewriteDir({
190
+ baseDir: wsAddonPath,
191
+ output: projectDir,
192
+ renameFile
163
193
  }, context);
164
194
  }
165
195
  function emptyDirectorySync(directory) {
@@ -196,18 +226,20 @@ let Commands = class Commands$1 extends CliApp {
196
226
  return this.execute(name);
197
227
  }
198
228
  async execute(name) {
199
- useAutoHelp() && process.exit(0);
229
+ if (useAutoHelp()) process.exit(0);
200
230
  const prompts$1 = await getPrompts({
201
231
  name,
202
232
  http: !!useCliOption("http"),
203
233
  cli: !!useCliOption("cli"),
234
+ ws: !!useCliOption("ws"),
204
235
  wf: !!useCliOption("wf"),
205
- domelint: !!useCliOption("domelint"),
236
+ oxc: !!useCliOption("oxc"),
206
237
  force: !!useCliOption("force")
207
238
  });
208
239
  console.log("\nScaffolding a new project...");
209
240
  await scaffold(prompts$1);
210
241
  const cli = prompts$1.type === "cli";
242
+ const ws = prompts$1.type === "ws";
211
243
  return `
212
244
  Success! Your new "${prompts$1.projectName}" project has been created successfully. 
213
245
 
@@ -222,10 +254,10 @@ ${cli ? `
222
254
  3. Make bin.js executable:
223
255
  chmod +x ./bin.js 
224
256
  ` : ""}
225
- ${cli ? "4" : "3"}. Start the development server:
257
+ ${cli ? "4" : "3"}. ${ws ? "Build and start" : "Start the development server"}:
226
258
  npm run dev${cli ? " -- hello World" : ""}
227
259
 
228
- You're all set! The development server will help you in building your application.
260
+ You're all set!${ws ? "" : " The development server will help you in building your application."}
229
261
  Enjoy coding, and build something amazing!
230
262
  `;
231
263
  }
@@ -255,14 +287,19 @@ function run() {
255
287
  description: "Use Moost CLI",
256
288
  type: Boolean
257
289
  },
290
+ {
291
+ keys: ["ws"],
292
+ description: "Use Moost WebSocket",
293
+ type: Boolean
294
+ },
258
295
  {
259
296
  keys: ["wf"],
260
297
  description: "Add Workflow Adapter",
261
298
  type: Boolean
262
299
  },
263
300
  {
264
- keys: ["domelint"],
265
- description: "Add do-me-lint (smart eslint installer)",
301
+ keys: ["oxc"],
302
+ description: "Add OXC lint and formatter (oxlint + oxfmt)",
266
303
  type: Boolean
267
304
  },
268
305
  {
package/package.json CHANGED
@@ -1,12 +1,39 @@
1
1
  {
2
2
  "name": "create-moost",
3
- "version": "0.5.33",
3
+ "version": "0.6.1",
4
4
  "description": "create-moost",
5
+ "keywords": [
6
+ "composables",
7
+ "framework",
8
+ "moost",
9
+ "moostjs",
10
+ "prostojs",
11
+ "wooksjs"
12
+ ],
13
+ "homepage": "https://github.com/moostjs/moostjs/tree/main/packages/create-moost#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/moostjs/moostjs/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Artem Maltsev",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/moostjs/moostjs.git",
22
+ "directory": "packages/create-moost"
23
+ },
24
+ "bin": {
25
+ "create-moost": "./bin.cjs"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "templates",
30
+ "bin.js"
31
+ ],
32
+ "type": "module",
33
+ "sideEffects": false,
5
34
  "main": "dist/index.cjs",
6
35
  "module": "dist/index.mjs",
7
36
  "types": "dist/index.d.ts",
8
- "sideEffects": false,
9
- "type": "module",
10
37
  "exports": {
11
38
  "./package.json": "./package.json",
12
39
  ".": {
@@ -15,47 +42,20 @@
15
42
  "require": "./dist/index.cjs"
16
43
  }
17
44
  },
18
- "bin": {
19
- "create-moost": "./bin.cjs"
20
- },
21
- "files": [
22
- "dist",
23
- "templates",
24
- "bin.js"
25
- ],
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/moostjs/moostjs.git",
29
- "directory": "packages/create-moost"
30
- },
31
- "keywords": [
32
- "moost",
33
- "moostjs",
34
- "composables",
35
- "framework",
36
- "wooksjs",
37
- "prostojs"
38
- ],
39
- "author": "Artem Maltsev",
40
- "license": "MIT",
41
- "bugs": {
42
- "url": "https://github.com/moostjs/moostjs/issues"
43
- },
44
- "homepage": "https://github.com/moostjs/moostjs/tree/main/packages/create-moost#readme",
45
45
  "dependencies": {
46
46
  "@prostojs/rewrite": "^0.1.1",
47
- "@wooksjs/event-cli": "^0.6.2",
47
+ "@wooksjs/event-cli": "^0.7.3",
48
48
  "prompts": "^2.4.2",
49
- "@moostjs/event-cli": "^0.5.33",
50
- "moost": "^0.5.33"
49
+ "moost": "^0.6.1",
50
+ "@moostjs/event-cli": "^0.6.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/prompts": "^2.4.9",
54
54
  "rolldown": "1.0.0-beta.19",
55
- "unplugin-swc": "^1.5.5",
56
- "vite": "^6.1.0",
55
+ "unplugin-swc": "^1.5.9",
56
+ "vite": "^6.4.1",
57
57
  "vitest": "3.2.4",
58
- "@moostjs/vite": "^0.5.33"
58
+ "@moostjs/vite": "^0.6.1"
59
59
  },
60
60
  "scripts": {
61
61
  "pub": "pnpm publish --access public",
@@ -12,11 +12,13 @@
12
12
  "bin.js"
13
13
  ],
14
14
  "scripts": {
15
- //=IF (domelint)
16
- "postinstall": "npx do-me-lint",
17
- //=END IF
18
15
  "dev": "pnpm build && node ./dist/main.js",
19
16
  "build": "rolldown build -c rolldown.config.ts",
17
+ //=IF (oxc)
18
+ "lint": "oxlint",
19
+ "format": "oxfmt .",
20
+ "format:check": "oxfmt --check .",
21
+ //=END IF
20
22
  "test": "echo \"Error: no test specified\" && exit 1"
21
23
  },
22
24
  "keywords": [],
@@ -26,8 +28,12 @@
26
28
  "@moostjs/event-cli": "^{{ version }}"
27
29
  },
28
30
  "devDependencies": {
31
+ //=IF (oxc)
32
+ "oxlint": "^1.49.0",
33
+ "oxfmt": "^0.34.0",
34
+ //=END IF
29
35
  "typescript": "^5.7.2",
30
- "unplugin-swc": "^1.5.1",
31
- "rolldown": "1.0.0-beta.3"
36
+ "unplugin-swc": "^1.5.9",
37
+ "rolldown": "1.0.0-beta.19"
32
38
  }
33
39
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
+ "printWidth": 100,
4
+ "tabWidth": 2,
5
+ "useTabs": false,
6
+ "semi": false,
7
+ "singleQuote": true,
8
+ "trailingComma": "all",
9
+ "insertFinalNewline": true
10
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["import", "typescript"],
4
+ "env": {
5
+ "es2021": true,
6
+ "node": true
7
+ },
8
+ "categories": {
9
+ "correctness": "error",
10
+ "suspicious": "warn"
11
+ },
12
+ "ignorePatterns": ["node_modules", "dist"],
13
+ "rules": {
14
+ "no-unused-vars": "off",
15
+ "no-use-before-define": "off",
16
+ "no-inline-comments": "off",
17
+ "no-warning-comments": "off",
18
+ "no-negated-condition": "off",
19
+ "no-shadow": "off",
20
+
21
+ "eqeqeq": "error",
22
+ "prefer-const": "error",
23
+ "prefer-template": "error",
24
+ "no-var": "error",
25
+ "curly": "error",
26
+ "no-self-compare": "error",
27
+ "no-template-curly-in-string": "error",
28
+ "array-callback-return": "error",
29
+
30
+ "import/first": "error",
31
+ "import/no-duplicates": "error",
32
+ "import/no-self-import": "error",
33
+ "import/no-default-export": "error",
34
+ "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
35
+
36
+ "typescript/consistent-type-imports": "error",
37
+ "typescript/no-extraneous-class": "off",
38
+ "typescript/no-explicit-any": "warn",
39
+ "typescript/no-non-null-assertion": "warn",
40
+
41
+ "max-params": ["warn", { "max": 4 }],
42
+ "complexity": "off",
43
+ "max-lines-per-function": "off",
44
+ "max-statements": "off"
45
+ },
46
+ "overrides": [
47
+ {
48
+ "files": ["**/*.config.*"],
49
+ "rules": {
50
+ "import/no-default-export": "off"
51
+ }
52
+ }
53
+ ]
54
+ }
@@ -5,31 +5,38 @@
5
5
  "main": "dist/main.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
- //=IF (domelint)
9
- "postinstall": "npx do-me-lint",
10
- //=END IF
11
8
  "dev": "vite dev",
12
9
  "build": "vite build",
10
+ //=IF (oxc)
11
+ "lint": "oxlint",
12
+ "format": "oxfmt .",
13
+ "format:check": "oxfmt --check .",
14
+ //=END IF
13
15
  "test": "echo \"Error: no test specified\" && exit 1"
14
16
  },
15
17
  "keywords": [],
16
18
  "author": "",
17
19
  "license": "ISC",
18
20
  "dependencies": {
19
- //=IF (type === 'http')
20
21
  "@moostjs/event-http": "^{{ version }}",
22
+ //=IF (ws)
23
+ "@moostjs/event-ws": "^{{ version }}",
21
24
  //=END IF
22
25
  //=IF (wf)
23
26
  "@moostjs/event-wf": "^{{ version }}",
24
- "@wooksjs/http-static": "^0.6.0",
27
+ "@wooksjs/http-static": "^0.7.3",
25
28
  //=END IF
26
29
  "moost": "^{{ version }}"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@moostjs/vite": "^{{ version }}",
30
33
  "@types/node": "^22.10.2",
34
+ //=IF (oxc)
35
+ "oxlint": "^1.49.0",
36
+ "oxfmt": "^0.34.0",
37
+ //=END IF
31
38
  "typescript": "^5.7.2",
32
- "unplugin-swc": "^1.5.1",
33
- "vite": "^6.1.0"
39
+ "unplugin-swc": "^1.5.9",
40
+ "vite": "^7.0.0"
34
41
  }
35
42
  }
@@ -1,49 +1,48 @@
1
- /**
2
- * @file Main Entry Point for Moost Application Template
3
- *
4
- * This file serves as the primary entry point for the Moost application.
5
- *
6
- * For more details on Moost Webapp and Moost Workflows, visit:
7
- * - Moost Webapp: https://moost.org/webapp/
8
- * - Moost Workflows: https://moost.org/wf/
9
- */
10
-
11
1
  import { MoostHttp } from "@moostjs/event-http";
2
+ //=IF (ws)
3
+ import { MoostWs } from "@moostjs/event-ws";
4
+ //=END IF
12
5
  //=IF (wf)
13
6
  import { MoostWf } from "@moostjs/event-wf";
14
7
  //=END IF
15
8
  import { Moost } from "moost";
16
9
 
17
10
  import { AppController } from "./controllers/app.controller";
11
+ //=IF (ws)
12
+ import { WsController } from "./controllers/ws.controller";
13
+ //=END IF
18
14
  //=IF (wf)
19
15
  import { WfController } from "./workflow/wf.controller";
20
16
  //=END IF
21
17
 
22
- /**
23
- * Initializes and configures the Moost application.
24
- *
25
- * The application performs the following steps:
26
- * 1. Creates a new instance of the Moost application.
27
- * 2. Sets up the moost adapters.
28
- * 3. Registers controllers.
29
- * 4. Initializes the application to start handling incoming events.
30
- */
31
18
  const app = new Moost();
32
19
 
33
20
  // Configure the HTTP adapter and start listening on port 3000
34
- app.adapter(new MoostHttp()).listen(3000, () => {
21
+ const http = new MoostHttp();
22
+ app.adapter(http).listen(3000, () => {
35
23
  app.getLogger("[{{ projectName }}]").info("Server started on port 3000");
36
24
  });
37
25
 
26
+ //=IF (ws)
27
+ // Configure the WebSocket adapter integrated with the HTTP server
28
+ app.adapter(new MoostWs({ httpApp: http }));
29
+ //=END IF
30
+
38
31
  //=IF (wf)
39
32
  // Configure the Workflow adapter to handle workflow-related events
40
33
  app.adapter(new MoostWf());
41
34
  //=END IF
42
35
 
43
36
  // Register application controllers
44
- //=IF (wf)
37
+ //=IF (wf && ws)
38
+ app.registerControllers(AppController, WsController, WfController).init();
39
+ //=END IF
40
+ //=IF (wf && !ws)
45
41
  app.registerControllers(AppController, WfController).init();
46
42
  //=END IF
47
- //=IF (!wf)
43
+ //=IF (!wf && ws)
44
+ app.registerControllers(AppController, WsController).init();
45
+ //=END IF
46
+ //=IF (!wf && !ws)
48
47
  app.registerControllers(AppController).init();
49
48
  //=END IF
@@ -20,8 +20,8 @@
20
20
 
21
21
  import { Body } from "@moostjs/event-http";
22
22
  import type { TFlowOutput } from "@moostjs/event-wf";
23
- import type { TInterceptorClass, TInterceptorFn } from "moost";
24
- import { Injectable, Intercept } from "moost";
23
+ import type { TOvertakeFn } from "moost";
24
+ import { After, Before, Intercept, Interceptor, Overtake, Response } from "moost";
25
25
 
26
26
  import { decryptState, encryptState } from "./wf.encrypt";
27
27
  import type {
@@ -38,65 +38,45 @@ import type {
38
38
  * This interceptor handles the encryption and decryption of the workflow state to ensure
39
39
  * secure transmission between the backend and frontend.
40
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
- */
41
+ @Interceptor(undefined, "FOR_EVENT")
42
+ class Wf2HtmlPageInterceptor {
48
43
  @Body()
49
44
  input?: TWfExampleInput & { wfState: string | TWfState | undefined };
50
45
 
51
46
  /**
52
- * The interceptor handler function that processes workflow events.
53
- *
54
- * @type {TInterceptorFn}
47
+ * Pre-processing: decrypts the workflow state if it is a string,
48
+ * or clears it if it is not.
55
49
  */
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
- });
50
+ @Before()
51
+ decryptState() {
52
+ if (typeof this.input?.wfState === "string") {
53
+ this.input.wfState = decryptState(this.input.wfState);
54
+ } else if (this.input) {
55
+ this.input.wfState = undefined;
56
+ }
57
+ }
72
58
 
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
- >;
59
+ /**
60
+ * Post-processing: renders an HTML input form or final output page
61
+ * based on the workflow response.
62
+ */
63
+ @After()
64
+ renderOutput(@Response() response: unknown, @Overtake() reply: TOvertakeFn) {
65
+ const wfOutput = response as TFlowOutput<
66
+ TWfExampleContext,
67
+ TWfExampleInput,
68
+ TWfExampleInputSchema
69
+ >;
89
70
 
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
- };
71
+ if (wfOutput.inputRequired) {
72
+ reply(
73
+ renderInputPage(wfOutput.inputRequired, encryptState(wfOutput.state))
74
+ );
75
+ }
76
+ if (wfOutput.finished) {
77
+ reply(renderOutputPage(wfOutput.state.context));
78
+ }
79
+ }
100
80
  }
101
81
 
102
82
  /**
@@ -0,0 +1,74 @@
1
+ # {{ projectName }}
2
+
3
+ ## Moost WebSocket Template
4
+
5
+ This template provides a standalone WebSocket application using Moost WS. It demonstrates how to handle WebSocket connections and messages with decorators.
6
+
7
+ ## Prerequisites
8
+
9
+ - Node.js (v18 or later)
10
+ - A package manager like [npm](https://www.npmjs.com/)
11
+
12
+ ## Installation
13
+
14
+ This template is installed via:
15
+
16
+ ```bash
17
+ npm create moost@latest -- --ws
18
+ ```
19
+
20
+ After the setup, navigate into the project folder and install dependencies:
21
+
22
+ ```bash
23
+ cd {{ packageName }}
24
+ npm install
25
+ ```
26
+
27
+ ## Running Locally
28
+
29
+ ### Development Mode
30
+
31
+ ```bash
32
+ npm run dev
33
+ ```
34
+
35
+ This command builds the project and starts the WebSocket server on port 3000.
36
+
37
+ ### Build for Production
38
+
39
+ ```bash
40
+ npm run build
41
+ node ./dist/main.js
42
+ ```
43
+
44
+ ## WebSocket Protocol
45
+
46
+ The server uses a JSON-based message protocol. Send messages in this format:
47
+
48
+ ```json
49
+ {
50
+ "event": "message",
51
+ "data": { "text": "Hello!" }
52
+ }
53
+ ```
54
+
55
+ The server will reply with:
56
+
57
+ ```json
58
+ {
59
+ "event": "message",
60
+ "data": { "echo": { "text": "Hello!" } }
61
+ }
62
+ ```
63
+
64
+ ## Files Overview
65
+
66
+ - **`src/main.ts`** - Defines WebSocket handlers using decorators.
67
+ - **`rolldown.config.ts`** - Build configuration (including SWC for decorators).
68
+ - **`package.json`** - Contains scripts, dependencies, and configuration.
69
+
70
+ ## Customization
71
+
72
+ - **Add New Message Handlers:** Use `@Message(event, path?)` to handle different event types.
73
+ - **Access Connection Info:** Use `@ConnectionId()` to get the connection UUID.
74
+ - **Use Rooms:** Import `useWsRooms()` from `@moostjs/event-ws` for room-based messaging.
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "{{ packageName }}",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "dist/main.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "npm run build && node ./dist/main.js",
9
+ "build": "rolldown build -c rolldown.config.ts",
10
+ //=IF (oxc)
11
+ "lint": "oxlint",
12
+ "format": "oxfmt .",
13
+ "format:check": "oxfmt --check .",
14
+ //=END IF
15
+ "test": "echo \"Error: no test specified\" && exit 1"
16
+ },
17
+ "keywords": [],
18
+ "author": "",
19
+ "license": "ISC",
20
+ "dependencies": {
21
+ "@moostjs/event-ws": "^{{ version }}",
22
+ "moost": "^{{ version }}"
23
+ },
24
+ "devDependencies": {
25
+ //=IF (oxc)
26
+ "oxlint": "^1.49.0",
27
+ "oxfmt": "^0.34.0",
28
+ //=END IF
29
+ "typescript": "^5.7.2",
30
+ "unplugin-swc": "^1.5.9",
31
+ "rolldown": "1.0.0-beta.19"
32
+ }
33
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'rolldown'
2
+ import swc from 'unplugin-swc';
3
+
4
+ export default defineConfig({
5
+ input: 'src/main.ts',
6
+ output: {
7
+ format: 'esm',
8
+ file: 'dist/main.js',
9
+ },
10
+ external: ['@moostjs/event-ws', 'moost'],
11
+ plugins: [
12
+ swc.rolldown(),
13
+ ]
14
+ })
@@ -0,0 +1,34 @@
1
+ import {
2
+ WsApp,
3
+ Connect,
4
+ Disconnect,
5
+ Message,
6
+ MessageData,
7
+ ConnectionId,
8
+ Controller,
9
+ } from '@moostjs/event-ws'
10
+
11
+ @Controller()
12
+ class ChatController {
13
+ @Connect()
14
+ onConnect(@ConnectionId() id: string) {
15
+ console.log(`Client connected: ${id}`)
16
+ }
17
+
18
+ @Message('message')
19
+ onMessage(@MessageData() data: unknown) {
20
+ return { echo: data }
21
+ }
22
+
23
+ @Disconnect()
24
+ onDisconnect(@ConnectionId() id: string) {
25
+ console.log(`Client disconnected: ${id}`)
26
+ }
27
+ }
28
+
29
+ new WsApp()
30
+ .controllers(ChatController)
31
+ .start(3000)
32
+ .then(() => {
33
+ console.log('WebSocket server started on port 3000')
34
+ })
@@ -0,0 +1,43 @@
1
+ import { Upgrade } from "@moostjs/event-http";
2
+ import {
3
+ Connect,
4
+ ConnectionId,
5
+ Disconnect,
6
+ Message,
7
+ MessageData,
8
+ WooksWs,
9
+ } from "@moostjs/event-ws";
10
+ import { Controller, Param } from "moost";
11
+
12
+ /**
13
+ * `WsController` handles the WebSocket upgrade route
14
+ * and WebSocket message/connection events.
15
+ *
16
+ * The @Upgrade decorator registers an HTTP route that upgrades
17
+ * the connection to WebSocket. Message handlers use the
18
+ * @Message decorator to handle incoming WebSocket messages.
19
+ */
20
+ @Controller()
21
+ export class WsController {
22
+ constructor(private ws: WooksWs) {}
23
+
24
+ @Upgrade("ws")
25
+ handleUpgrade() {
26
+ return this.ws.upgrade();
27
+ }
28
+
29
+ @Connect()
30
+ onConnect(@ConnectionId() id: string) {
31
+ console.log(`WS client connected: ${id}`);
32
+ }
33
+
34
+ @Message("message")
35
+ onMessage(@MessageData() data: unknown) {
36
+ return { echo: data };
37
+ }
38
+
39
+ @Disconnect()
40
+ onDisconnect(@ConnectionId() id: string) {
41
+ console.log(`WS client disconnected: ${id}`);
42
+ }
43
+ }
@@ -1,14 +0,0 @@
1
- ignoredRules:
2
- # for Moost controllers: they don't typically use 'this'
3
- - '@typescript-eslint/class-methods-use-this'
4
-
5
- # For Moost decorators: it's common to pull url route params from the url
6
- - max-params
7
-
8
- # Too strict, hinders readability
9
- - '@typescript-eslint/strict-boolean-expressions'
10
-
11
- # Empty class is a valid use case for Moost's controller composition
12
- - '@typescript-eslint/no-extraneous-class'
13
-
14
- - '@typescript-eslint/consistent-type-imports'