@fluojs/cli 1.1.0 → 3.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 (137) hide show
  1. package/README.ko.md +183 -22
  2. package/README.md +186 -22
  3. package/dist/cli.d.ts +5 -4
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +128 -35
  6. package/dist/commands/diagnostics.d.ts +35 -0
  7. package/dist/commands/diagnostics.d.ts.map +1 -1
  8. package/dist/commands/diagnostics.js +60 -0
  9. package/dist/commands/generate.d.ts +1 -49
  10. package/dist/commands/generate.d.ts.map +1 -1
  11. package/dist/commands/generate.js +1 -214
  12. package/dist/commands/inspect.d.ts +5 -7
  13. package/dist/commands/inspect.d.ts.map +1 -1
  14. package/dist/commands/inspect.js +85 -65
  15. package/dist/commands/migrate.d.ts.map +1 -1
  16. package/dist/commands/migrate.js +27 -15
  17. package/dist/commands/migration-transform-tokens.d.ts +15 -0
  18. package/dist/commands/migration-transform-tokens.d.ts.map +1 -0
  19. package/dist/commands/migration-transform-tokens.js +41 -0
  20. package/dist/commands/new.d.ts +0 -6
  21. package/dist/commands/new.d.ts.map +1 -1
  22. package/dist/commands/new.js +42 -96
  23. package/dist/commands/output-path-safety.d.ts +14 -0
  24. package/dist/commands/output-path-safety.d.ts.map +1 -0
  25. package/dist/commands/output-path-safety.js +34 -0
  26. package/dist/commands/scripts.d.ts +1 -1
  27. package/dist/commands/scripts.d.ts.map +1 -1
  28. package/dist/commands/scripts.js +13 -7
  29. package/dist/commands/typegen-artifact.d.ts +54 -0
  30. package/dist/commands/typegen-artifact.d.ts.map +1 -0
  31. package/dist/commands/typegen-artifact.js +117 -0
  32. package/dist/commands/typegen-generation-child.d.ts +2 -0
  33. package/dist/commands/typegen-generation-child.d.ts.map +1 -0
  34. package/dist/commands/typegen-generation-child.js +59 -0
  35. package/dist/commands/typegen-generation-process.d.ts +46 -0
  36. package/dist/commands/typegen-generation-process.d.ts.map +1 -0
  37. package/dist/commands/typegen-generation-process.js +131 -0
  38. package/dist/commands/typegen-generation-protocol.d.ts +16 -0
  39. package/dist/commands/typegen-generation-protocol.d.ts.map +1 -0
  40. package/dist/commands/typegen-generation-protocol.js +35 -0
  41. package/dist/commands/typegen-isolated-source.d.ts +10 -0
  42. package/dist/commands/typegen-isolated-source.d.ts.map +1 -0
  43. package/dist/commands/typegen-isolated-source.js +75 -0
  44. package/dist/commands/typegen-options.d.ts +20 -0
  45. package/dist/commands/typegen-options.d.ts.map +1 -0
  46. package/dist/commands/typegen-options.js +71 -0
  47. package/dist/commands/typegen-source.d.ts +59 -0
  48. package/dist/commands/typegen-source.d.ts.map +1 -0
  49. package/dist/commands/typegen-source.js +183 -0
  50. package/dist/commands/typegen-watch.d.ts +46 -0
  51. package/dist/commands/typegen-watch.d.ts.map +1 -0
  52. package/dist/commands/typegen-watch.js +212 -0
  53. package/dist/commands/typegen.d.ts +29 -0
  54. package/dist/commands/typegen.d.ts.map +1 -0
  55. package/dist/commands/typegen.js +119 -0
  56. package/dist/dev-runner/node-restart-runner.d.ts +6 -0
  57. package/dist/dev-runner/node-restart-runner.d.ts.map +1 -1
  58. package/dist/dev-runner/node-restart-runner.js +64 -13
  59. package/dist/fixtures/inspect-react-app.module.d.ts +4 -0
  60. package/dist/fixtures/inspect-react-app.module.d.ts.map +1 -0
  61. package/dist/fixtures/inspect-react-app.module.js +32 -0
  62. package/dist/fixtures/typegen-react-app.module.d.ts +4 -0
  63. package/dist/fixtures/typegen-react-app.module.d.ts.map +1 -0
  64. package/dist/fixtures/typegen-react-app.module.js +33 -0
  65. package/dist/generate-command.d.ts +50 -0
  66. package/dist/generate-command.d.ts.map +1 -0
  67. package/dist/generate-command.js +214 -0
  68. package/dist/index.d.ts +6 -5
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +6 -5
  71. package/dist/new/install.js +1 -1
  72. package/dist/new/package-spec-resolver.d.ts.map +1 -1
  73. package/dist/new/package-spec-resolver.js +50 -59
  74. package/dist/new/prompt.d.ts +1 -0
  75. package/dist/new/prompt.d.ts.map +1 -1
  76. package/dist/new/prompt.js +40 -12
  77. package/dist/new/published-internal-dependencies.d.ts +20 -0
  78. package/dist/new/published-internal-dependencies.d.ts.map +1 -0
  79. package/dist/new/published-internal-dependencies.js +20 -0
  80. package/dist/new/react-vite-ssr-scaffold.d.ts +19 -0
  81. package/dist/new/react-vite-ssr-scaffold.d.ts.map +1 -0
  82. package/dist/new/react-vite-ssr-scaffold.js +108 -0
  83. package/dist/new/resolver.d.ts +2 -2
  84. package/dist/new/resolver.d.ts.map +1 -1
  85. package/dist/new/resolver.js +5 -1
  86. package/dist/new/scaffold.d.ts.map +1 -1
  87. package/dist/new/scaffold.js +177 -54
  88. package/dist/new/starter-profiles.d.ts +8 -4
  89. package/dist/new/starter-profiles.d.ts.map +1 -1
  90. package/dist/new/starter-profiles.js +52 -18
  91. package/dist/new/templates/react-vite-ssr/README.md.ejs +74 -0
  92. package/dist/new/templates/react-vite-ssr/playwright.config.ts.ejs +26 -0
  93. package/dist/new/templates/react-vite-ssr/src/app.test.ts.ejs +82 -0
  94. package/dist/new/templates/react-vite-ssr/src/app.ts.ejs +91 -0
  95. package/dist/new/templates/react-vite-ssr/src/entry-client.tsx.ejs +29 -0
  96. package/dist/new/templates/react-vite-ssr/src/entry-server.tsx.ejs +52 -0
  97. package/dist/new/templates/react-vite-ssr/src/load-manifest.test.ts.ejs +55 -0
  98. package/dist/new/templates/react-vite-ssr/src/load-manifest.ts.ejs +53 -0
  99. package/dist/new/templates/react-vite-ssr/src/main.ts.ejs +18 -0
  100. package/dist/new/templates/react-vite-ssr/src/page.tsx.ejs +43 -0
  101. package/dist/new/templates/react-vite-ssr/src/react-app.test.tsx.ejs +67 -0
  102. package/dist/new/templates/react-vite-ssr/src/react-app.tsx.ejs +46 -0
  103. package/dist/new/templates/react-vite-ssr/src/styles.css.ejs +25 -0
  104. package/dist/new/templates/react-vite-ssr/src/styles.d.ts.ejs +1 -0
  105. package/dist/new/templates/react-vite-ssr/tests/production-hydration.spec.ts.ejs +54 -0
  106. package/dist/new/templates/react-vite-ssr/tsconfig.json.ejs +16 -0
  107. package/dist/new/templates/react-vite-ssr/vite.client.config.ts.ejs +24 -0
  108. package/dist/new/templates/react-vite-ssr/vite.server.config.ts.ejs +17 -0
  109. package/dist/new/templates/react-vite-ssr/vitest.config.ts.ejs +10 -0
  110. package/dist/new/types.d.ts +4 -2
  111. package/dist/new/types.d.ts.map +1 -1
  112. package/dist/public-generate.d.ts +2 -0
  113. package/dist/public-generate.d.ts.map +1 -0
  114. package/dist/public-generate.js +1 -0
  115. package/dist/public-inspect.d.ts +13 -0
  116. package/dist/public-inspect.d.ts.map +1 -0
  117. package/dist/public-inspect.js +16 -0
  118. package/dist/public-new.d.ts +13 -0
  119. package/dist/public-new.d.ts.map +1 -0
  120. package/dist/public-new.js +16 -0
  121. package/dist/public-typegen.d.ts +14 -0
  122. package/dist/public-typegen.d.ts.map +1 -0
  123. package/dist/public-typegen.js +17 -0
  124. package/dist/run-cli.d.ts +2 -1
  125. package/dist/run-cli.d.ts.map +1 -1
  126. package/dist/studio/sidecar.d.ts.map +1 -1
  127. package/dist/studio/sidecar.js +106 -14
  128. package/dist/transforms/nestjs-migrate.d.ts +4 -1
  129. package/dist/transforms/nestjs-migrate.d.ts.map +1 -1
  130. package/dist/transforms/nestjs-migrate.js +625 -65
  131. package/dist/typegen-contract.d.ts +10 -0
  132. package/dist/typegen-contract.d.ts.map +1 -0
  133. package/dist/typegen-contract.js +9 -0
  134. package/dist/usage.d.ts +19 -0
  135. package/dist/usage.d.ts.map +1 -0
  136. package/dist/usage.js +178 -0
  137. package/package.json +19 -10
@@ -3,6 +3,8 @@ import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { initializeGitRepository, installDependencies } from './install.js';
5
5
  import { resolvePackageSpecs } from './package-spec-resolver.js';
6
+ import { PUBLISHED_INTERNAL_DEPENDENCIES } from './published-internal-dependencies.js';
7
+ import { createReactViteSsrScaffoldFiles } from './react-vite-ssr-scaffold.js';
6
8
  import { resolveBootstrapPlan } from './resolver.js';
7
9
  const PUBLISHED_DEV_DEPENDENCIES = {
8
10
  '@babel/cli': '^7.26.4',
@@ -11,39 +13,29 @@ const PUBLISHED_DEV_DEPENDENCIES = {
11
13
  '@babel/preset-typescript': '^7.27.1',
12
14
  '@types/babel__core': '^7.20.5',
13
15
  '@types/node': '^22.13.10',
14
- '@vitest/coverage-v8': '^3.0.8',
15
- tsx: '^4.20.4',
16
+ '@vitest/coverage-v8': '^4.1.11',
17
+ tsx: '^4.23.1',
16
18
  typescript: '^6.0.2',
17
- vite: '^6.2.1',
18
- vitest: '^3.0.8'
19
+ vite: '^8.2.2',
20
+ vitest: '^4.1.11'
19
21
  };
20
22
  const PUBLISHED_RUNTIME_DEPENDENCIES = {
23
+ '@playwright/test': '^1.51.1',
24
+ '@types/react': '^19.2.14',
25
+ '@types/react-dom': '^19.2.3',
21
26
  '@types/amqplib': '^0.10.7',
22
- '@grpc/grpc-js': '^1.0.0',
27
+ '@grpc/grpc-js': '^1.14.4',
23
28
  '@grpc/proto-loader': '^0.8.0',
24
29
  amqplib: '^0.10.5',
25
30
  ioredis: '^5.0.0',
26
31
  kafkajs: '^2.2.4',
27
32
  mqtt: '^5.0.0',
28
- nats: '^2.29.3'
29
- };
30
- const PUBLISHED_INTERNAL_DEPENDENCIES = {
31
- '@fluojs/config': '^1.0.0',
32
- '@fluojs/core': '^1.0.0',
33
- '@fluojs/di': '^1.0.0',
34
- '@fluojs/http': '^1.0.0',
35
- '@fluojs/microservices': '^1.0.0',
36
- '@fluojs/platform-bun': '^1.0.0',
37
- '@fluojs/platform-cloudflare-workers': '^1.0.0',
38
- '@fluojs/platform-deno': '^1.0.0',
39
- '@fluojs/platform-express': '^1.0.0',
40
- '@fluojs/platform-fastify': '^1.0.0',
41
- '@fluojs/platform-nodejs': '^1.0.0',
42
- '@fluojs/runtime': '^1.0.0',
43
- '@fluojs/testing': '^1.0.0',
44
- '@fluojs/validation': '^1.0.0',
45
- '@fluojs/vite': '^1.0.0'
33
+ nats: '^2.29.3',
34
+ 'happy-dom': '^20.9.0',
35
+ react: '^19.2.6',
36
+ 'react-dom': '^19.2.6'
46
37
  };
38
+ const NODE_HTTP_LISTENER_ENGINE = '>=24.0.0 <27';
47
39
  function describeApplicationStarter(options) {
48
40
  if (options.runtime === 'bun') {
49
41
  return {
@@ -153,6 +145,16 @@ function createExecCommand(packageManager, command) {
153
145
  }
154
146
  function createProjectScripts(bootstrapPlan) {
155
147
  switch (bootstrapPlan.profile.id) {
148
+ case 'application-node-fastify-react-vite-ssr':
149
+ return {
150
+ build: 'vite build --config vite.client.config.ts && vite build --config vite.server.config.ts',
151
+ dev: 'vite build --config vite.client.config.ts && vite build --config vite.server.config.ts && node dist/server/main.js',
152
+ start: 'node dist/server/main.js',
153
+ test: 'vitest run',
154
+ 'test:browser': 'playwright test --config playwright.config.ts',
155
+ 'test:watch': 'vitest',
156
+ typecheck: 'tsc -p tsconfig.json --noEmit'
157
+ };
156
158
  case 'application-bun-bun-http':
157
159
  return {
158
160
  build: 'bun build ./src/main.ts --outdir ./dist --target bun',
@@ -210,9 +212,17 @@ function createProjectEngines(bootstrapPlan) {
210
212
  return {
211
213
  deno: '>=2.0.0'
212
214
  };
215
+ case 'application-node-fastify-http':
216
+ case 'application-node-fastify-react-vite-ssr':
217
+ case 'application-node-express-http':
218
+ case 'application-node-nodejs-http':
219
+ case 'mixed-node-fastify-tcp':
220
+ return {
221
+ node: NODE_HTTP_LISTENER_ENGINE
222
+ };
213
223
  default:
214
224
  return {
215
- node: '>=20.0.0'
225
+ node: NODE_HTTP_LISTENER_ENGINE
216
226
  };
217
227
  }
218
228
  }
@@ -233,7 +243,8 @@ function createPublishedDevDependencies(bootstrapPlan) {
233
243
  };
234
244
  }
235
245
  return {
236
- ...PUBLISHED_DEV_DEPENDENCIES
246
+ ...PUBLISHED_DEV_DEPENDENCIES,
247
+ '@types/node': '^24.0.0'
237
248
  };
238
249
  }
239
250
  function createProjectPackageJson(options, bootstrapPlan, releaseVersion, packageSpecs) {
@@ -302,13 +313,12 @@ function createProjectTsconfigBuild() {
302
313
  }
303
314
  function createBabelConfig() {
304
315
  return `module.exports = {
305
- ignore: ['src/**/*.test.ts'],
306
316
  presets: [['@babel/preset-typescript', { allowDeclareFields: true }]],
307
317
  plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]],
308
318
  };
309
319
  `;
310
320
  }
311
- function createViteConfig() {
321
+ function createViteConfig(bootstrapPlan) {
312
322
  return `import { fluoDecoratorsPlugin } from '@fluojs/vite';
313
323
  import { defineConfig } from 'vite';
314
324
 
@@ -317,13 +327,13 @@ export default defineConfig({
317
327
  build: {
318
328
  emptyOutDir: true,
319
329
  outDir: 'dist',
320
- rollupOptions: {
330
+ rolldownOptions: {
321
331
  output: {
322
332
  entryFileNames: 'main.js',
323
333
  },
324
334
  },
325
335
  ssr: 'src/main.ts',
326
- target: 'node20',
336
+ target: '${bootstrapPlan.schema.runtime === 'node' ? 'node24' : 'node20'}',
327
337
  },
328
338
  server: {
329
339
  port: 5173,
@@ -1050,13 +1060,36 @@ const codec = JSONCodec();
1050
1060
 
1051
1061
  class LazyNatsTransport implements MicroserviceTransport {
1052
1062
  private connection: NatsConnection | undefined;
1063
+ private initializing: Promise<NatsMicroserviceTransport> | undefined;
1053
1064
  private transport: NatsMicroserviceTransport | undefined;
1054
1065
 
1055
1066
  async close() {
1056
- await this.transport?.close();
1057
- await this.connection?.close();
1058
- this.transport = undefined;
1059
- this.connection = undefined;
1067
+ const transport = this.initializing ? await this.initializing.catch(() => undefined) : this.transport;
1068
+ let closeError: unknown;
1069
+ let closeFailed = false;
1070
+ try {
1071
+ await transport?.close();
1072
+ } catch (error) {
1073
+ closeError = error;
1074
+ closeFailed = true;
1075
+ } finally {
1076
+ try {
1077
+ await this.connection?.close();
1078
+ } catch (error) {
1079
+ if (!closeFailed) {
1080
+ closeError = error;
1081
+ closeFailed = true;
1082
+ }
1083
+ } finally {
1084
+ this.initializing = undefined;
1085
+ this.transport = undefined;
1086
+ this.connection = undefined;
1087
+ }
1088
+ }
1089
+
1090
+ if (closeFailed) {
1091
+ throw closeError;
1092
+ }
1060
1093
  }
1061
1094
 
1062
1095
  async emit(pattern: string, payload: unknown) {
@@ -1076,6 +1109,19 @@ class LazyNatsTransport implements MicroserviceTransport {
1076
1109
  return this.transport;
1077
1110
  }
1078
1111
 
1112
+ this.initializing ??= this.createTransport();
1113
+ try {
1114
+ return await this.initializing;
1115
+ } finally {
1116
+ this.initializing = undefined;
1117
+ }
1118
+ }
1119
+
1120
+ private async createTransport() {
1121
+ if (this.transport) {
1122
+ return this.transport;
1123
+ }
1124
+
1079
1125
  const connection = await connect({
1080
1126
  name: 'fluo-microservice-starter',
1081
1127
  servers,
@@ -1149,18 +1195,24 @@ const responseTopic = process.env.KAFKA_RESPONSE_TOPIC ?? 'fluo.microservices.re
1149
1195
 
1150
1196
  class LazyKafkaTransport implements MicroserviceTransport {
1151
1197
  private consumer: Consumer | undefined;
1198
+ private initializing: Promise<KafkaMicroserviceTransport> | undefined;
1152
1199
  private producer: Producer | undefined;
1153
1200
  private transport: KafkaMicroserviceTransport | undefined;
1154
1201
 
1155
1202
  async close() {
1156
- await this.transport?.close();
1157
- await Promise.all([
1158
- this.consumer?.disconnect().catch(() => undefined),
1159
- this.producer?.disconnect().catch(() => undefined),
1160
- ]);
1161
- this.consumer = undefined;
1162
- this.producer = undefined;
1163
- this.transport = undefined;
1203
+ const transport = this.initializing ? await this.initializing.catch(() => undefined) : this.transport;
1204
+ try {
1205
+ await transport?.close();
1206
+ } finally {
1207
+ await Promise.all([
1208
+ this.consumer?.disconnect().catch(() => undefined),
1209
+ this.producer?.disconnect().catch(() => undefined),
1210
+ ]);
1211
+ this.initializing = undefined;
1212
+ this.consumer = undefined;
1213
+ this.producer = undefined;
1214
+ this.transport = undefined;
1215
+ }
1164
1216
  }
1165
1217
 
1166
1218
  async emit(pattern: string, payload: unknown) {
@@ -1180,6 +1232,19 @@ class LazyKafkaTransport implements MicroserviceTransport {
1180
1232
  return this.transport;
1181
1233
  }
1182
1234
 
1235
+ this.initializing ??= this.createTransport();
1236
+ try {
1237
+ return await this.initializing;
1238
+ } finally {
1239
+ this.initializing = undefined;
1240
+ }
1241
+ }
1242
+
1243
+ private async createTransport() {
1244
+ if (this.transport) {
1245
+ return this.transport;
1246
+ }
1247
+
1183
1248
  const kafka = new Kafka({
1184
1249
  brokers,
1185
1250
  clientId,
@@ -1187,13 +1252,24 @@ class LazyKafkaTransport implements MicroserviceTransport {
1187
1252
  });
1188
1253
  const producer = kafka.producer();
1189
1254
  const consumer = kafka.consumer({ groupId: consumerGroup });
1190
- await Promise.all([producer.connect(), consumer.connect()]);
1255
+ this.producer = producer;
1256
+ this.consumer = consumer;
1257
+ try {
1258
+ await producer.connect();
1259
+ await consumer.connect();
1260
+ } catch (error) {
1261
+ await Promise.all([
1262
+ consumer.disconnect().catch(() => undefined),
1263
+ producer.disconnect().catch(() => undefined),
1264
+ ]);
1265
+ this.consumer = undefined;
1266
+ this.producer = undefined;
1267
+ throw error;
1268
+ }
1191
1269
 
1192
1270
  const handlers = new Map<string, (message: string) => Promise<void> | void>();
1193
1271
  let consumerRunning = false;
1194
1272
 
1195
- this.producer = producer;
1196
- this.consumer = consumer;
1197
1273
  this.transport = new KafkaMicroserviceTransport({
1198
1274
  consumer: {
1199
1275
  async subscribe(topic: string, handler: (message: string) => Promise<void> | void) {
@@ -1277,15 +1353,21 @@ const responseQueue = process.env.RABBITMQ_RESPONSE_QUEUE ?? 'fluo.microservices
1277
1353
  class LazyRabbitMqTransport implements MicroserviceTransport {
1278
1354
  private channel: Awaited<ReturnType<Awaited<ReturnType<typeof connect>>['createConfirmChannel']>> | undefined;
1279
1355
  private connection: Awaited<ReturnType<typeof connect>> | undefined;
1356
+ private initializing: Promise<RabbitMqMicroserviceTransport> | undefined;
1280
1357
  private transport: RabbitMqMicroserviceTransport | undefined;
1281
1358
 
1282
1359
  async close() {
1283
- await this.transport?.close();
1284
- await this.channel?.close().catch(() => undefined);
1285
- await this.connection?.close().catch(() => undefined);
1286
- this.channel = undefined;
1287
- this.connection = undefined;
1288
- this.transport = undefined;
1360
+ const transport = this.initializing ? await this.initializing.catch(() => undefined) : this.transport;
1361
+ try {
1362
+ await transport?.close();
1363
+ } finally {
1364
+ await this.channel?.close().catch(() => undefined);
1365
+ await this.connection?.close().catch(() => undefined);
1366
+ this.initializing = undefined;
1367
+ this.channel = undefined;
1368
+ this.connection = undefined;
1369
+ this.transport = undefined;
1370
+ }
1289
1371
  }
1290
1372
 
1291
1373
  async emit(pattern: string, payload: unknown) {
@@ -1305,11 +1387,31 @@ class LazyRabbitMqTransport implements MicroserviceTransport {
1305
1387
  return this.transport;
1306
1388
  }
1307
1389
 
1390
+ this.initializing ??= this.createTransport();
1391
+ try {
1392
+ return await this.initializing;
1393
+ } finally {
1394
+ this.initializing = undefined;
1395
+ }
1396
+ }
1397
+
1398
+ private async createTransport() {
1399
+ if (this.transport) {
1400
+ return this.transport;
1401
+ }
1402
+
1308
1403
  const connection = await connect(url);
1309
- const channel = await connection.createConfirmChannel();
1404
+ this.connection = connection;
1405
+ let channel: Awaited<ReturnType<typeof connection.createConfirmChannel>>;
1406
+ try {
1407
+ channel = await connection.createConfirmChannel();
1408
+ } catch (error) {
1409
+ await connection.close().catch(() => undefined);
1410
+ this.connection = undefined;
1411
+ throw error;
1412
+ }
1310
1413
  const consumerTags = new Map<string, string>();
1311
1414
 
1312
- this.connection = connection;
1313
1415
  this.channel = channel;
1314
1416
  this.transport = new RabbitMqMicroserviceTransport({
1315
1417
  consumer: {
@@ -1993,11 +2095,29 @@ function createWranglerConfig(projectName) {
1993
2095
  }
1994
2096
  function emitSharedScaffoldFiles(options, bootstrapPlan, releaseVersion, packageSpecs) {
1995
2097
  const envFile = createEnvFile(bootstrapPlan);
2098
+ if (bootstrapPlan.profile.id === 'application-node-fastify-react-vite-ssr') {
2099
+ return [{
2100
+ content: createProjectPackageJson(options, bootstrapPlan, releaseVersion, packageSpecs),
2101
+ path: 'package.json'
2102
+ }, ...createReactViteSsrScaffoldFiles({
2103
+ packageManager: options.packageManager,
2104
+ projectName: options.projectName
2105
+ }), {
2106
+ content: createBabelConfig(),
2107
+ path: 'babel.config.cjs'
2108
+ }, {
2109
+ content: createGitignore(),
2110
+ path: '.gitignore'
2111
+ }, ...(envFile ? [{
2112
+ content: envFile,
2113
+ path: '.env'
2114
+ }] : [])];
2115
+ }
1996
2116
  const sharedFiles = [{
1997
2117
  content: createProjectPackageJson(options, bootstrapPlan, releaseVersion, packageSpecs),
1998
2118
  path: 'package.json'
1999
2119
  }, {
2000
- content: createProjectReadme(options, bootstrapPlan),
2120
+ content: createProjectReadme(options, bootstrapPlan) + (bootstrapPlan.schema.runtime === 'node' ? '\n## Node.js support\n\nRequires Node.js `>=24.0.0 <27`. Use Node 24 LTS for local development, CI, and container images. Node builds target `node24` and use `@types/node@^24.0.0`.\n' : ''),
2001
2121
  path: 'README.md'
2002
2122
  }, {
2003
2123
  content: createGitignore(),
@@ -2014,7 +2134,7 @@ function emitSharedScaffoldFiles(options, bootstrapPlan, releaseVersion, package
2014
2134
  content: createBabelConfig(),
2015
2135
  path: 'babel.config.cjs'
2016
2136
  }, {
2017
- content: createViteConfig(),
2137
+ content: createViteConfig(bootstrapPlan),
2018
2138
  path: 'vite.config.ts'
2019
2139
  }, {
2020
2140
  content: createVitestConfig(),
@@ -2089,6 +2209,9 @@ function emitApplicationScaffoldFiles(options) {
2089
2209
  return files;
2090
2210
  }
2091
2211
  function emitScaffoldFilesForRecipe(options, recipeId) {
2212
+ if (recipeId === 'application-node-fastify-react-vite-ssr') {
2213
+ return [];
2214
+ }
2092
2215
  if (recipeId === 'application-bun-bun-http' || recipeId === 'application-cloudflare-workers-cloudflare-workers-http' || recipeId === 'application-deno-deno-http' || recipeId === 'application-node-fastify-http' || recipeId === 'application-node-express-http' || recipeId === 'application-node-nodejs-http') {
2093
2216
  return emitApplicationScaffoldFiles(options);
2094
2217
  }
@@ -1,8 +1,8 @@
1
- import type { BootstrapPlatform, BootstrapRuntime, BootstrapSchema, BootstrapShape, BootstrapToolingPreset, BootstrapTopology, BootstrapTransport } from './types.js';
1
+ import type { BootstrapPlatform, BootstrapRuntime, BootstrapSchema, BootstrapShape, BootstrapStarter, BootstrapToolingPreset, BootstrapTopology, BootstrapTransport } from './types.js';
2
2
  /** Emitter families that decide which scaffold template tree renders the starter. */
3
- export type StarterEmitterType = 'http' | 'microservice' | 'mixed';
3
+ export type StarterEmitterType = 'http' | 'microservice' | 'mixed' | 'react-vite-ssr';
4
4
  /** Stable IDs for the scaffold recipes currently shipped by `fluo new`. */
5
- export type StarterScaffoldRecipeId = 'application-bun-bun-http' | 'application-cloudflare-workers-cloudflare-workers-http' | 'application-deno-deno-http' | 'application-node-express-http' | 'application-node-fastify-http' | 'application-node-nodejs-http' | 'microservice-node-none-grpc' | 'microservice-node-none-kafka' | 'microservice-node-none-mqtt' | 'microservice-node-none-nats' | 'microservice-node-none-rabbitmq' | 'microservice-node-none-redis-streams' | 'microservice-node-none-tcp' | 'mixed-node-fastify-tcp';
5
+ export type StarterScaffoldRecipeId = 'application-bun-bun-http' | 'application-cloudflare-workers-cloudflare-workers-http' | 'application-deno-deno-http' | 'application-node-express-http' | 'application-node-fastify-http' | 'application-node-fastify-react-vite-ssr' | 'application-node-nodejs-http' | 'microservice-node-none-grpc' | 'microservice-node-none-kafka' | 'microservice-node-none-mqtt' | 'microservice-node-none-nats' | 'microservice-node-none-rabbitmq' | 'microservice-node-none-redis-streams' | 'microservice-node-none-tcp' | 'mixed-node-fastify-tcp';
6
6
  type StarterDependencies = {
7
7
  dependencies: readonly string[];
8
8
  devDependencies: readonly string[];
@@ -21,6 +21,7 @@ export interface StarterProfile {
21
21
  platformPromptLabel?: string;
22
22
  promptLabel: string;
23
23
  schema: BootstrapSchema;
24
+ starter: BootstrapStarter;
24
25
  }
25
26
  /** Supported microservice starter transports recognized by `fluo new`. */
26
27
  export declare const SUPPORTED_MICROSERVICE_STARTER_TRANSPORTS: readonly BootstrapTransport[];
@@ -40,6 +41,8 @@ export declare const SUPPORTED_BOOTSTRAP_TRANSPORTS: readonly BootstrapTransport
40
41
  export declare const SUPPORTED_BOOTSTRAP_TOOLING_PRESETS: readonly BootstrapToolingPreset[];
41
42
  /** Supported topology modes accepted by the current starter matrix. */
42
43
  export declare const SUPPORTED_BOOTSTRAP_TOPOLOGY_MODES: readonly BootstrapTopology['mode'][];
44
+ /** Named starter variants accepted by `fluo new`. */
45
+ export declare const SUPPORTED_BOOTSTRAP_STARTERS: readonly BootstrapStarter[];
43
46
  /** Default starter profile used for shape-less `fluo new` invocations. */
44
47
  export declare const DEFAULT_BOOTSTRAP_PROFILE: StarterProfile;
45
48
  /**
@@ -75,9 +78,10 @@ export declare function getDefaultBootstrapSchema(): BootstrapSchema;
75
78
  * Finds the starter profile that exactly matches a resolved bootstrap schema.
76
79
  *
77
80
  * @param schema Fully resolved bootstrap schema.
81
+ * @param starter Named starter variant selected by the caller.
78
82
  * @returns The matching starter profile, or `undefined` when the schema is validation-only.
79
83
  */
80
- export declare function getStarterProfileFromSchema(schema: BootstrapSchema): StarterProfile | undefined;
84
+ export declare function getStarterProfileFromSchema(schema: BootstrapSchema, starter?: BootstrapStarter): StarterProfile | undefined;
81
85
  /**
82
86
  * Checks whether one transport belongs to the supported microservice starter transport family.
83
87
  *
@@ -1 +1 @@
1
- {"version":3,"file":"starter-profiles.d.ts","sourceRoot":"","sources":["../../src/new/starter-profiles.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"starter-profiles.d.ts","sourceRoot":"","sources":["../../src/new/starter-profiles.ts"],"names":[],"mappings":"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"}
@@ -51,7 +51,8 @@ export const STARTER_PROFILE_REGISTRY = [{
51
51
  id: 'application-bun-bun-http',
52
52
  platformPromptLabel: 'Bun native HTTP',
53
53
  promptLabel: 'Application (HTTP starter)',
54
- schema: createSchema('application', 'bun', 'bun', 'http')
54
+ schema: createSchema('application', 'bun', 'bun', 'http'),
55
+ starter: 'standard'
55
56
  }, {
56
57
  dependencies: {
57
58
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/platform-deno', '@fluojs/runtime'],
@@ -67,7 +68,8 @@ export const STARTER_PROFILE_REGISTRY = [{
67
68
  id: 'application-deno-deno-http',
68
69
  platformPromptLabel: 'Deno native HTTP',
69
70
  promptLabel: 'Application (HTTP starter)',
70
- schema: createSchema('application', 'deno', 'deno', 'http')
71
+ schema: createSchema('application', 'deno', 'deno', 'http'),
72
+ starter: 'standard'
71
73
  }, {
72
74
  dependencies: {
73
75
  dependencies: ['@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/platform-cloudflare-workers', '@fluojs/runtime'],
@@ -83,7 +85,8 @@ export const STARTER_PROFILE_REGISTRY = [{
83
85
  id: 'application-cloudflare-workers-cloudflare-workers-http',
84
86
  platformPromptLabel: 'Cloudflare Workers',
85
87
  promptLabel: 'Application (HTTP starter)',
86
- schema: createSchema('application', 'cloudflare-workers', 'cloudflare-workers', 'http')
88
+ schema: createSchema('application', 'cloudflare-workers', 'cloudflare-workers', 'http'),
89
+ starter: 'standard'
87
90
  }, {
88
91
  dependencies: {
89
92
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/platform-fastify', '@fluojs/runtime'],
@@ -99,7 +102,25 @@ export const STARTER_PROFILE_REGISTRY = [{
99
102
  id: 'application-node-fastify-http',
100
103
  platformPromptLabel: 'Fastify',
101
104
  promptLabel: 'Application (HTTP starter)',
102
- schema: createSchema('application', 'node', 'fastify', 'http')
105
+ schema: createSchema('application', 'node', 'fastify', 'http'),
106
+ starter: 'standard'
107
+ }, {
108
+ dependencies: {
109
+ dependencies: ['@fluojs/core', '@fluojs/http', '@fluojs/platform-fastify', '@fluojs/react', '@fluojs/runtime', '@fluojs/validation', 'react', 'react-dom'],
110
+ devDependencies: ['@fluojs/cli', '@fluojs/testing', '@fluojs/vite', '@playwright/test', '@types/react', '@types/react-dom', 'happy-dom']
111
+ },
112
+ emitter: {
113
+ platform: 'fastify',
114
+ preset: 'standard',
115
+ runtime: 'node',
116
+ transport: 'http',
117
+ type: 'react-vite-ssr'
118
+ },
119
+ id: 'application-node-fastify-react-vite-ssr',
120
+ platformPromptLabel: 'Fastify',
121
+ promptLabel: 'React SSR + Vite',
122
+ schema: createSchema('application', 'node', 'fastify', 'http'),
123
+ starter: 'react-vite-ssr'
103
124
  }, {
104
125
  dependencies: {
105
126
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/platform-express', '@fluojs/runtime'],
@@ -115,7 +136,8 @@ export const STARTER_PROFILE_REGISTRY = [{
115
136
  id: 'application-node-express-http',
116
137
  platformPromptLabel: 'Express',
117
138
  promptLabel: 'Application (HTTP starter)',
118
- schema: createSchema('application', 'node', 'express', 'http')
139
+ schema: createSchema('application', 'node', 'express', 'http'),
140
+ starter: 'standard'
119
141
  }, {
120
142
  dependencies: {
121
143
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/platform-nodejs', '@fluojs/runtime'],
@@ -131,7 +153,8 @@ export const STARTER_PROFILE_REGISTRY = [{
131
153
  id: 'application-node-nodejs-http',
132
154
  platformPromptLabel: 'Raw Node.js HTTP',
133
155
  promptLabel: 'Application (HTTP starter)',
134
- schema: createSchema('application', 'node', 'nodejs', 'http')
156
+ schema: createSchema('application', 'node', 'nodejs', 'http'),
157
+ starter: 'standard'
135
158
  }, {
136
159
  dependencies: {
137
160
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime'],
@@ -146,7 +169,8 @@ export const STARTER_PROFILE_REGISTRY = [{
146
169
  },
147
170
  id: 'microservice-node-none-tcp',
148
171
  promptLabel: 'Microservice (transport-first starter)',
149
- schema: createSchema('microservice', 'node', 'none', 'tcp')
172
+ schema: createSchema('microservice', 'node', 'none', 'tcp'),
173
+ starter: 'standard'
150
174
  }, {
151
175
  dependencies: {
152
176
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', 'ioredis'],
@@ -161,7 +185,8 @@ export const STARTER_PROFILE_REGISTRY = [{
161
185
  },
162
186
  id: 'microservice-node-none-redis-streams',
163
187
  promptLabel: 'Microservice (Redis Streams starter)',
164
- schema: createSchema('microservice', 'node', 'none', 'redis-streams')
188
+ schema: createSchema('microservice', 'node', 'none', 'redis-streams'),
189
+ starter: 'standard'
165
190
  }, {
166
191
  dependencies: {
167
192
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', 'mqtt'],
@@ -176,7 +201,8 @@ export const STARTER_PROFILE_REGISTRY = [{
176
201
  },
177
202
  id: 'microservice-node-none-mqtt',
178
203
  promptLabel: 'Microservice (MQTT starter)',
179
- schema: createSchema('microservice', 'node', 'none', 'mqtt')
204
+ schema: createSchema('microservice', 'node', 'none', 'mqtt'),
205
+ starter: 'standard'
180
206
  }, {
181
207
  dependencies: {
182
208
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', '@grpc/grpc-js', '@grpc/proto-loader'],
@@ -191,7 +217,8 @@ export const STARTER_PROFILE_REGISTRY = [{
191
217
  },
192
218
  id: 'microservice-node-none-grpc',
193
219
  promptLabel: 'Microservice (gRPC starter)',
194
- schema: createSchema('microservice', 'node', 'none', 'grpc')
220
+ schema: createSchema('microservice', 'node', 'none', 'grpc'),
221
+ starter: 'standard'
195
222
  }, {
196
223
  dependencies: {
197
224
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', 'nats'],
@@ -206,7 +233,8 @@ export const STARTER_PROFILE_REGISTRY = [{
206
233
  },
207
234
  id: 'microservice-node-none-nats',
208
235
  promptLabel: 'Microservice (NATS starter)',
209
- schema: createSchema('microservice', 'node', 'none', 'nats')
236
+ schema: createSchema('microservice', 'node', 'none', 'nats'),
237
+ starter: 'standard'
210
238
  }, {
211
239
  dependencies: {
212
240
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', 'kafkajs'],
@@ -221,7 +249,8 @@ export const STARTER_PROFILE_REGISTRY = [{
221
249
  },
222
250
  id: 'microservice-node-none-kafka',
223
251
  promptLabel: 'Microservice (Kafka starter)',
224
- schema: createSchema('microservice', 'node', 'none', 'kafka')
252
+ schema: createSchema('microservice', 'node', 'none', 'kafka'),
253
+ starter: 'standard'
225
254
  }, {
226
255
  dependencies: {
227
256
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/di', '@fluojs/microservices', '@fluojs/runtime', 'amqplib'],
@@ -236,7 +265,8 @@ export const STARTER_PROFILE_REGISTRY = [{
236
265
  },
237
266
  id: 'microservice-node-none-rabbitmq',
238
267
  promptLabel: 'Microservice (RabbitMQ starter)',
239
- schema: createSchema('microservice', 'node', 'none', 'rabbitmq')
268
+ schema: createSchema('microservice', 'node', 'none', 'rabbitmq'),
269
+ starter: 'standard'
240
270
  }, {
241
271
  dependencies: {
242
272
  dependencies: ['@fluojs/config', '@fluojs/core', '@fluojs/validation', '@fluojs/di', '@fluojs/http', '@fluojs/microservices', '@fluojs/platform-fastify', '@fluojs/runtime'],
@@ -251,7 +281,8 @@ export const STARTER_PROFILE_REGISTRY = [{
251
281
  },
252
282
  id: 'mixed-node-fastify-tcp',
253
283
  promptLabel: 'Mixed (HTTP API + microservice starter)',
254
- schema: createSchema('mixed', 'node', 'fastify', 'tcp')
284
+ schema: createSchema('mixed', 'node', 'fastify', 'tcp'),
285
+ starter: 'standard'
255
286
  }];
256
287
 
257
288
  /** Supported scaffold shapes accepted by the current starter matrix. */
@@ -266,6 +297,8 @@ export const SUPPORTED_BOOTSTRAP_TRANSPORTS = ['http', ...SUPPORTED_MICROSERVICE
266
297
  export const SUPPORTED_BOOTSTRAP_TOOLING_PRESETS = ['standard'];
267
298
  /** Supported topology modes accepted by the current starter matrix. */
268
299
  export const SUPPORTED_BOOTSTRAP_TOPOLOGY_MODES = ['single-package'];
300
+ /** Named starter variants accepted by `fluo new`. */
301
+ export const SUPPORTED_BOOTSTRAP_STARTERS = ['standard', 'react-vite-ssr'];
269
302
 
270
303
  /** Default starter profile used for shape-less `fluo new` invocations. */
271
304
  export const DEFAULT_BOOTSTRAP_PROFILE = STARTER_PROFILE_REGISTRY.find(profile => profile.id === 'application-node-fastify-http');
@@ -279,7 +312,7 @@ export const DEFAULT_BOOTSTRAP_PROFILE = STARTER_PROFILE_REGISTRY.find(profile =
279
312
  */
280
313
  export function getStarterProfileForShape(shape, runtime) {
281
314
  if (runtime !== undefined) {
282
- return STARTER_PROFILE_REGISTRY.find(profile => profile.schema.shape === shape && profile.schema.runtime === runtime) ?? DEFAULT_BOOTSTRAP_PROFILE;
315
+ return STARTER_PROFILE_REGISTRY.find(profile => profile.starter === 'standard' && profile.schema.shape === shape && profile.schema.runtime === runtime) ?? DEFAULT_BOOTSTRAP_PROFILE;
283
316
  }
284
317
  if (shape === 'application') {
285
318
  return DEFAULT_BOOTSTRAP_PROFILE;
@@ -300,7 +333,7 @@ export function getStarterProfileForShape(shape, runtime) {
300
333
  * @returns Application starter profiles for the selected runtime, or all application starters when omitted.
301
334
  */
302
335
  export function getApplicationStarterProfiles(runtime) {
303
- return STARTER_PROFILE_REGISTRY.filter(profile => profile.schema.shape === 'application' && (runtime === undefined || profile.schema.runtime === runtime));
336
+ return STARTER_PROFILE_REGISTRY.filter(profile => profile.starter === 'standard' && profile.schema.shape === 'application' && (runtime === undefined || profile.schema.runtime === runtime));
304
337
  }
305
338
 
306
339
  /**
@@ -327,10 +360,11 @@ export function getDefaultBootstrapSchema() {
327
360
  * Finds the starter profile that exactly matches a resolved bootstrap schema.
328
361
  *
329
362
  * @param schema Fully resolved bootstrap schema.
363
+ * @param starter Named starter variant selected by the caller.
330
364
  * @returns The matching starter profile, or `undefined` when the schema is validation-only.
331
365
  */
332
- export function getStarterProfileFromSchema(schema) {
333
- return STARTER_PROFILE_REGISTRY.find(profile => profile.schema.shape === schema.shape && profile.schema.runtime === schema.runtime && profile.schema.platform === schema.platform && profile.schema.transport === schema.transport && profile.schema.tooling === schema.tooling && profile.schema.topology.deferred === schema.topology.deferred && profile.schema.topology.mode === schema.topology.mode);
366
+ export function getStarterProfileFromSchema(schema, starter = 'standard') {
367
+ return STARTER_PROFILE_REGISTRY.find(profile => profile.starter === starter && profile.schema.shape === schema.shape && profile.schema.runtime === schema.runtime && profile.schema.platform === schema.platform && profile.schema.transport === schema.transport && profile.schema.tooling === schema.tooling && profile.schema.topology.deferred === schema.topology.deferred && profile.schema.topology.mode === schema.topology.mode);
334
368
  }
335
369
 
336
370
  /**