@fluojs/cli 1.0.0-beta.4 → 1.0.0-beta.5

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 (35) hide show
  1. package/README.ko.md +63 -0
  2. package/README.md +63 -0
  3. package/dist/cli.d.ts +8 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +171 -4
  6. package/dist/commands/diagnostics.d.ts +15 -0
  7. package/dist/commands/diagnostics.d.ts.map +1 -0
  8. package/dist/commands/diagnostics.js +163 -0
  9. package/dist/commands/new.js +2 -2
  10. package/dist/commands/package-manager.d.ts +9 -0
  11. package/dist/commands/package-manager.d.ts.map +1 -0
  12. package/dist/commands/package-manager.js +63 -0
  13. package/dist/commands/package-workflow.d.ts +20 -0
  14. package/dist/commands/package-workflow.d.ts.map +1 -0
  15. package/dist/commands/package-workflow.js +137 -0
  16. package/dist/commands/scripts.d.ts +38 -0
  17. package/dist/commands/scripts.d.ts.map +1 -0
  18. package/dist/commands/scripts.js +418 -0
  19. package/dist/dev-runner/node-restart-runner.d.ts +50 -0
  20. package/dist/dev-runner/node-restart-runner.d.ts.map +1 -0
  21. package/dist/dev-runner/node-restart-runner.js +248 -0
  22. package/dist/generators/manifest.d.ts +24 -0
  23. package/dist/generators/manifest.d.ts.map +1 -1
  24. package/dist/generators/manifest.js +9 -0
  25. package/dist/generators/resource.d.ts +10 -0
  26. package/dist/generators/resource.d.ts.map +1 -0
  27. package/dist/generators/resource.js +23 -0
  28. package/dist/generators/templates/controller.ts.ejs +5 -1
  29. package/dist/generators/templates/request-dto.ts.ejs +3 -0
  30. package/dist/new/scaffold.d.ts.map +1 -1
  31. package/dist/new/scaffold.js +62 -13
  32. package/dist/update-check.d.ts +1 -0
  33. package/dist/update-check.d.ts.map +1 -1
  34. package/dist/update-check.js +7 -5
  35. package/package.json +2 -2
@@ -0,0 +1,248 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { existsSync, readdirSync, readFileSync, statSync, watch } from 'node:fs';
4
+ import { basename, join, relative, sep } from 'node:path';
5
+ const DEFAULT_DEBOUNCE_MS = 100;
6
+ const DEFAULT_IGNORES = ['.cache', '.fluo', '.git', '.turbo', 'coverage', 'dist', 'node_modules', '*.swp', '*.swo', '*~', '.#*'];
7
+ const WATCH_FILES = ['.env', 'package.json', 'tsconfig.json', 'tsconfig.build.json'];
8
+ function normalizeIgnorePatterns(patterns) {
9
+ return patterns.map(pattern => pattern.trim()).filter(pattern => pattern.length > 0);
10
+ }
11
+ function parseIgnorePatterns(env) {
12
+ const configured = env.FLUO_DEV_WATCH_IGNORE?.split(',') ?? [];
13
+ return normalizeIgnorePatterns([...DEFAULT_IGNORES, ...configured]);
14
+ }
15
+ function matchesSegmentPattern(segment, pattern) {
16
+ if (pattern.startsWith('*.')) {
17
+ return segment.endsWith(pattern.slice(1));
18
+ }
19
+ if (pattern.endsWith('*')) {
20
+ return segment.startsWith(pattern.slice(0, -1));
21
+ }
22
+ return segment === pattern;
23
+ }
24
+ function shouldIgnorePath(filePath, projectDirectory, ignorePatterns) {
25
+ const relativePath = relative(projectDirectory, filePath);
26
+ if (relativePath.startsWith('..')) {
27
+ return true;
28
+ }
29
+ const segments = relativePath.split(sep).filter(Boolean);
30
+ return segments.some(segment => ignorePatterns.some(pattern => matchesSegmentPattern(segment, pattern)));
31
+ }
32
+ function hashFileContent(filePath) {
33
+ try {
34
+ const stats = statSync(filePath);
35
+ if (!stats.isFile()) {
36
+ return undefined;
37
+ }
38
+ return createHash('sha256').update(readFileSync(filePath)).digest('hex');
39
+ } catch (_error) {
40
+ return undefined;
41
+ }
42
+ }
43
+ function collectContentPaths(filePath, projectDirectory, ignorePatterns, paths) {
44
+ if (shouldIgnorePath(filePath, projectDirectory, ignorePatterns)) {
45
+ return;
46
+ }
47
+ try {
48
+ const stats = statSync(filePath);
49
+ if (stats.isDirectory()) {
50
+ for (const entry of readdirSync(filePath)) {
51
+ collectContentPaths(join(filePath, entry), projectDirectory, ignorePatterns, paths);
52
+ }
53
+ return;
54
+ }
55
+ if (stats.isFile()) {
56
+ paths.add(filePath);
57
+ }
58
+ } catch (_error) {
59
+ paths.add(filePath);
60
+ }
61
+ }
62
+ function collectWatchedContentPaths(paths, projectDirectory, ignorePatterns) {
63
+ const collected = new Set();
64
+ for (const filePath of paths) {
65
+ collectContentPaths(filePath, projectDirectory, ignorePatterns, collected);
66
+ }
67
+ return collected;
68
+ }
69
+
70
+ /**
71
+ * Creates a content-diff gate for fluo-owned dev restarts.
72
+ *
73
+ * @param projectDirectory Project root used for ignore matching.
74
+ * @param ignorePatterns Additional or default ignore patterns to apply before hashing.
75
+ * @returns A gate that reports whether watched paths changed by content rather than by filesystem event alone.
76
+ */
77
+ export function createContentChangeGate(projectDirectory, ignorePatterns = DEFAULT_IGNORES) {
78
+ const normalizedIgnores = normalizeIgnorePatterns(ignorePatterns);
79
+ const hashes = new Map();
80
+ return {
81
+ commitBaseline(paths) {
82
+ for (const filePath of collectWatchedContentPaths(paths, projectDirectory, normalizedIgnores)) {
83
+ hashes.set(filePath, hashFileContent(filePath));
84
+ }
85
+ },
86
+ hasMeaningfulChange(paths) {
87
+ for (const filePath of collectWatchedContentPaths(paths, projectDirectory, normalizedIgnores)) {
88
+ const nextHash = hashFileContent(filePath);
89
+ const previousHash = hashes.get(filePath);
90
+ if (previousHash !== nextHash) {
91
+ return true;
92
+ }
93
+ }
94
+ return false;
95
+ }
96
+ };
97
+ }
98
+ function getWatchTargets(projectDirectory) {
99
+ return [join(projectDirectory, 'src'), ...WATCH_FILES.map(fileName => join(projectDirectory, fileName))].filter(target => existsSync(target));
100
+ }
101
+ function stopChild(child) {
102
+ if (child && child.exitCode === null && !child.killed) {
103
+ child.kill('SIGTERM');
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Runs the Node.js development lifecycle through fluo-owned restart supervision.
109
+ *
110
+ * @param options Runner dependencies and project settings.
111
+ * @returns A promise that resolves with the final child exit code when the runner stops.
112
+ */
113
+ export async function runNodeRestartRunner(options) {
114
+ const projectDirectory = options.projectDirectory ?? process.cwd();
115
+ const env = options.env;
116
+ const signalTarget = options.signalTarget ?? process;
117
+ const spawnChild = options.spawnChild ?? spawn;
118
+ const stdout = options.stdout ?? process.stdout;
119
+ const stderr = options.stderr ?? process.stderr;
120
+ const watchTarget = options.watchTarget ?? watch;
121
+ const appArgs = options.appArgs ?? [];
122
+ const debounceMs = options.debounceMs ?? Number(env.FLUO_DEV_RELOAD_DEBOUNCE_MS ?? DEFAULT_DEBOUNCE_MS);
123
+ const gate = createContentChangeGate(projectDirectory, parseIgnorePatterns(env));
124
+ const watchTargets = getWatchTargets(projectDirectory);
125
+ let child;
126
+ const pendingRestartPaths = new Set();
127
+ const restartAfterClosePaths = new Set();
128
+ let restartTimer;
129
+ let restarting = false;
130
+ let stopping = false;
131
+ const startChild = (resolveExitCode, cleanup) => {
132
+ child = spawnChild(process.execPath, ['--env-file=.env', '--import', 'tsx', 'src/main.ts', ...appArgs], {
133
+ cwd: projectDirectory,
134
+ env,
135
+ stdio: 'inherit'
136
+ });
137
+ child.once('close', code => {
138
+ if (restarting) {
139
+ return;
140
+ }
141
+ if (stopping) {
142
+ cleanup();
143
+ resolveExitCode(code ?? 0);
144
+ return;
145
+ }
146
+ cleanup();
147
+ resolveExitCode(code ?? 1);
148
+ });
149
+ };
150
+ const scheduleRestart = (filePath, resolveExitCode, cleanup) => {
151
+ pendingRestartPaths.add(filePath);
152
+ if (restartTimer) {
153
+ clearTimeout(restartTimer);
154
+ }
155
+ restartTimer = setTimeout(() => {
156
+ const restartPaths = [...pendingRestartPaths];
157
+ pendingRestartPaths.clear();
158
+ restartTimer = undefined;
159
+ if (!gate.hasMeaningfulChange(restartPaths)) {
160
+ return;
161
+ }
162
+ stdout.write(`[fluo] restarting after content change: ${relative(projectDirectory, restartPaths[restartPaths.length - 1] ?? projectDirectory)}\n`);
163
+ const previousChild = child;
164
+ const startReplacementChild = () => {
165
+ startChild(resolveExitCode, cleanup);
166
+ gate.commitBaseline(restartPaths);
167
+ };
168
+ if (previousChild) {
169
+ for (const restartPath of restartPaths) {
170
+ restartAfterClosePaths.add(restartPath);
171
+ }
172
+ if (restarting) {
173
+ return;
174
+ }
175
+ restarting = true;
176
+ previousChild.once('close', () => {
177
+ const committedRestartPaths = [...restartAfterClosePaths];
178
+ restartAfterClosePaths.clear();
179
+ restarting = false;
180
+ if (stopping) {
181
+ return;
182
+ }
183
+ startChild(resolveExitCode, cleanup);
184
+ gate.commitBaseline(committedRestartPaths);
185
+ });
186
+ stopChild(previousChild);
187
+ return;
188
+ }
189
+ try {
190
+ startReplacementChild();
191
+ } finally {
192
+ restarting = false;
193
+ }
194
+ }, debounceMs);
195
+ };
196
+ gate.commitBaseline(watchTargets);
197
+ return new Promise(resolveExitCode => {
198
+ const watchers = [];
199
+ let cleanedUp = false;
200
+ const stop = () => {
201
+ stopping = true;
202
+ cleanup();
203
+ stopChild(child);
204
+ };
205
+ const cleanup = () => {
206
+ if (cleanedUp) {
207
+ return;
208
+ }
209
+ cleanedUp = true;
210
+ if (restartTimer) {
211
+ clearTimeout(restartTimer);
212
+ restartTimer = undefined;
213
+ }
214
+ pendingRestartPaths.clear();
215
+ restartAfterClosePaths.clear();
216
+ for (const watcher of watchers.splice(0)) {
217
+ watcher.close();
218
+ }
219
+ signalTarget.off('SIGINT', stop);
220
+ signalTarget.off('SIGTERM', stop);
221
+ };
222
+ startChild(resolveExitCode, cleanup);
223
+ for (const target of watchTargets) {
224
+ try {
225
+ const stats = statSync(target);
226
+ const watchOptions = {
227
+ recursive: stats.isDirectory()
228
+ };
229
+ const listener = (_event, filename) => {
230
+ const fileName = filename ? String(filename) : basename(target);
231
+ scheduleRestart(stats.isDirectory() ? join(target, fileName) : target, resolveExitCode, cleanup);
232
+ };
233
+ try {
234
+ watchers.push(watchTarget(target, watchOptions, listener));
235
+ } catch (error) {
236
+ if (!stats.isDirectory()) {
237
+ throw error;
238
+ }
239
+ watchers.push(watchTarget(target, listener));
240
+ }
241
+ } catch (error) {
242
+ stderr.write(`[fluo] unable to watch ${target}: ${error instanceof Error ? error.message : String(error)}\n`);
243
+ }
244
+ }
245
+ signalTarget.once('SIGINT', stop);
246
+ signalTarget.once('SIGTERM', stop);
247
+ });
248
+ }
@@ -126,6 +126,14 @@ export declare const builtInGeneratorCollection: {
126
126
  readonly nextStepHint: "Import the DTO in a controller and add it as a parameter with @FromBody or @FromQuery.";
127
127
  readonly schematic: "request-dto";
128
128
  readonly wiringBehavior: "files-only";
129
+ }, {
130
+ readonly aliases: readonly ["resrc"];
131
+ readonly description: "Generate a full resource slice with module, controller, service, repository, and DTO stubs.";
132
+ readonly factory: (name: string, options: import("../generator-types.js").GenerateOptions | undefined) => import("../generator-types.js").GeneratedFile[];
133
+ readonly kind: "resource";
134
+ readonly nextStepHint: "Run 'pnpm typecheck' and wire the resource module into a parent module when ready.";
135
+ readonly schematic: "resource";
136
+ readonly wiringBehavior: "files-only";
129
137
  }, {
130
138
  readonly aliases: readonly ["res"];
131
139
  readonly description: "Generate a response DTO for typed response payloads (files only — use it as a controller return type).";
@@ -230,6 +238,14 @@ export declare const generatorCollections: readonly [{
230
238
  readonly nextStepHint: "Import the DTO in a controller and add it as a parameter with @FromBody or @FromQuery.";
231
239
  readonly schematic: "request-dto";
232
240
  readonly wiringBehavior: "files-only";
241
+ }, {
242
+ readonly aliases: readonly ["resrc"];
243
+ readonly description: "Generate a full resource slice with module, controller, service, repository, and DTO stubs.";
244
+ readonly factory: (name: string, options: import("../generator-types.js").GenerateOptions | undefined) => import("../generator-types.js").GeneratedFile[];
245
+ readonly kind: "resource";
246
+ readonly nextStepHint: "Run 'pnpm typecheck' and wire the resource module into a parent module when ready.";
247
+ readonly schematic: "resource";
248
+ readonly wiringBehavior: "files-only";
233
249
  }, {
234
250
  readonly aliases: readonly ["res"];
235
251
  readonly description: "Generate a response DTO for typed response payloads (files only — use it as a controller return type).";
@@ -332,6 +348,14 @@ export declare const generatorManifest: readonly [{
332
348
  readonly nextStepHint: "Import the DTO in a controller and add it as a parameter with @FromBody or @FromQuery.";
333
349
  readonly schematic: "request-dto";
334
350
  readonly wiringBehavior: "files-only";
351
+ }, {
352
+ readonly aliases: readonly ["resrc"];
353
+ readonly description: "Generate a full resource slice with module, controller, service, repository, and DTO stubs.";
354
+ readonly factory: (name: string, options: import("../generator-types.js").GenerateOptions | undefined) => import("../generator-types.js").GeneratedFile[];
355
+ readonly kind: "resource";
356
+ readonly nextStepHint: "Run 'pnpm typecheck' and wire the resource module into a parent module when ready.";
357
+ readonly schematic: "resource";
358
+ readonly wiringBehavior: "files-only";
335
359
  }, {
336
360
  readonly aliases: readonly ["res"];
337
361
  readonly description: "Generate a response DTO for typed response payloads (files only — use it as a controller return type).";
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/generators/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,uBAAuB,CAAC;AAYrF,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAExE,KAAK,4BAA4B,GAAG;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,4BAA4B,CAAC;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,iBAAiB,GAAG,YAAY,CAAC;CAClD,CAAC;AAEF,4FAA4F;AAC5F,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAKkB,CAAC;AA6FtD,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKC,CAAC;AAEzC,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAiF,CAAC;AAEnH,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAwC,CAAC;AAEvE,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,uEAAuE;AACvE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAcrE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,GAAG,mBAAmB,CAOhF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,SAAS,mBAAmB,EAAE,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,GAAE,MAAsC,GAAG,SAAS,mBAAmB,EAAE,CAO7H;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAMzF"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/generators/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,uBAAuB,CAAC;AAarF,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAExE,KAAK,4BAA4B,GAAG;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,4BAA4B,CAAC;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,iBAAiB,GAAG,YAAY,CAAC;CAClD,CAAC;AAEF,4FAA4F;AAC5F,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,sBAAsB,EAAE,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;EAKkB,CAAC;AAsGtD,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKC,CAAC;AAEzC,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAiF,CAAC;AAEnH,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAwC,CAAC;AAEvE,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,uEAAuE;AACvE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAcrE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,GAAG,mBAAmB,CAOhF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,SAAS,mBAAmB,EAAE,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,GAAE,MAAsC,GAAG,SAAS,mBAAmB,EAAE,CAO7H;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAMzF"}
@@ -5,6 +5,7 @@ import { generateMiddlewareFiles } from './middleware.js';
5
5
  import { generateModuleFiles } from './module.js';
6
6
  import { generateRepoFiles } from './repository.js';
7
7
  import { generateRequestDtoFiles } from './request-dto.js';
8
+ import { generateResourceFiles } from './resource.js';
8
9
  import { generateResponseDtoFiles } from './response-dto.js';
9
10
  import { generateServiceFiles } from './service.js';
10
11
 
@@ -113,6 +114,14 @@ const builtInGeneratorDefinitions = [{
113
114
  nextStepHint: 'Import the DTO in a controller and add it as a parameter with @FromBody or @FromQuery.',
114
115
  schematic: 'request-dto',
115
116
  wiringBehavior: 'files-only'
117
+ }, {
118
+ aliases: ['resrc'],
119
+ description: 'Generate a full resource slice with module, controller, service, repository, and DTO stubs.',
120
+ factory: (name, options) => generateResourceFiles(name, options),
121
+ kind: 'resource',
122
+ nextStepHint: "Run 'pnpm typecheck' and wire the resource module into a parent module when ready.",
123
+ schematic: 'resource',
124
+ wiringBehavior: 'files-only'
116
125
  }, {
117
126
  aliases: ['res'],
118
127
  description: 'Generate a response DTO for typed response payloads (files only — use it as a controller return type).',
@@ -0,0 +1,10 @@
1
+ import type { GenerateOptions, GeneratedFile } from '../types.js';
2
+ /**
3
+ * Generate a complete feature resource slice.
4
+ *
5
+ * @param name The resource name.
6
+ * @param options The generation options.
7
+ * @returns The generated resource files.
8
+ */
9
+ export declare function generateResourceFiles(name: string, options?: GenerateOptions): GeneratedFile[];
10
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/generators/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASlE;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,aAAa,EAAE,CASlG"}
@@ -0,0 +1,23 @@
1
+ import { generateControllerFiles } from './controller.js';
2
+ import { generateModuleFiles } from './module.js';
3
+ import { generateRepoFiles } from './repository.js';
4
+ import { generateRequestDtoFiles } from './request-dto.js';
5
+ import { generateResponseDtoFiles } from './response-dto.js';
6
+ import { generateServiceFiles } from './service.js';
7
+
8
+ /**
9
+ * Generate a complete feature resource slice.
10
+ *
11
+ * @param name The resource name.
12
+ * @param options The generation options.
13
+ * @returns The generated resource files.
14
+ */
15
+ export function generateResourceFiles(name, options = {}) {
16
+ return [...generateModuleFiles(name), ...generateRepoFiles(name, options), ...generateServiceFiles(name, {
17
+ ...options,
18
+ hasRepo: true
19
+ }), ...generateControllerFiles(name, {
20
+ ...options,
21
+ hasService: true
22
+ }), ...generateRequestDtoFiles(`Create ${name}`), ...generateResponseDtoFiles(name)];
23
+ }
@@ -1,5 +1,7 @@
1
1
  <% if (hasService) { %>
2
- import { Inject } from '@fluojs/core';
2
+ import { ensureMetadataSymbol, Inject } from '@fluojs/core';
3
+ <% } else { %>
4
+ import { ensureMetadataSymbol } from '@fluojs/core';
3
5
  <% } %>
4
6
  import { Controller, Get } from '@fluojs/http';
5
7
 
@@ -7,6 +9,8 @@ import { Controller, Get } from '@fluojs/http';
7
9
  import { <%- service %> } from './<%- kebab %>.service';
8
10
  <% } %>
9
11
 
12
+ ensureMetadataSymbol();
13
+
10
14
  @Controller('/<%- kebab %>')
11
15
  <% if (hasService) { %>
12
16
  @Inject(<%- service %>)
@@ -1,6 +1,9 @@
1
+ import { ensureMetadataSymbol } from '@fluojs/core';
1
2
  import { IsString, MinLength } from '@fluojs/validation';
2
3
  import { FromBody } from '@fluojs/http';
3
4
 
5
+ ensureMetadataSymbol();
6
+
4
7
  export class <%- pascal %> {
5
8
  @FromBody('<%- bodyField %>')
6
9
  @IsString()
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/new/scaffold.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAokEnE;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAkB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,6BAAuB,CAAC"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/new/scaffold.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAqnEnE;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAkB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,6BAAuB,CAAC"}
@@ -147,32 +147,36 @@ function createProjectScripts(bootstrapPlan) {
147
147
  switch (bootstrapPlan.profile.id) {
148
148
  case 'application-bun-bun-http':
149
149
  return {
150
- build: 'bun build ./src/main.ts --outdir ./dist --target bun',
151
- dev: 'bun --watch src/main.ts',
150
+ build: 'fluo build',
151
+ dev: 'fluo dev',
152
+ start: 'fluo start',
152
153
  test: 'vitest run',
153
154
  'test:watch': 'vitest',
154
155
  typecheck: 'tsc -p tsconfig.json --noEmit'
155
156
  };
156
157
  case 'application-deno-deno-http':
157
158
  return {
158
- build: 'mkdir -p dist && deno compile --allow-env --allow-net --output dist/app src/main.ts',
159
- dev: 'deno run --allow-env --allow-net --watch src/main.ts',
159
+ build: 'fluo build',
160
+ dev: 'fluo dev',
161
+ start: 'fluo start',
160
162
  test: 'deno test --allow-env --allow-net',
161
163
  'test:watch': 'deno test --allow-env --allow-net --watch',
162
164
  typecheck: 'deno check src/main.ts src/app.test.ts'
163
165
  };
164
166
  case 'application-cloudflare-workers-cloudflare-workers-http':
165
167
  return {
166
- build: 'wrangler deploy --dry-run',
167
- dev: 'wrangler dev',
168
+ build: 'fluo build',
169
+ dev: 'fluo dev',
170
+ start: 'fluo start',
168
171
  test: 'vitest run',
169
172
  'test:watch': 'vitest',
170
173
  typecheck: 'tsc -p tsconfig.json --noEmit'
171
174
  };
172
175
  default:
173
176
  return {
174
- build: 'babel src --extensions .ts --out-dir dist --config-file ./babel.config.cjs && tsc -p tsconfig.build.json',
175
- dev: 'node --env-file=.env --watch --watch-preserve-output --import tsx src/main.ts',
177
+ build: 'fluo build',
178
+ dev: 'fluo dev',
179
+ start: 'fluo start',
176
180
  test: 'vitest run',
177
181
  'test:watch': 'vitest',
178
182
  typecheck: 'tsc -p tsconfig.json --noEmit'
@@ -281,9 +285,49 @@ function createBabelConfig() {
281
285
  `;
282
286
  }
283
287
  function createViteConfig() {
284
- return `import { defineConfig } from 'vite';
288
+ return `import { transformAsync } from '@babel/core';
289
+ import type { Plugin } from 'vite';
290
+ import { defineConfig } from 'vite';
291
+
292
+ function fluoDecoratorsPlugin(): Plugin {
293
+ return {
294
+ name: 'fluo-babel-decorators',
295
+ async transform(code: string, id: string) {
296
+ if (!id.endsWith('.ts') || id.includes('.test.')) {
297
+ return null;
298
+ }
299
+
300
+ const result = await transformAsync(code, {
301
+ babelrc: false,
302
+ configFile: false,
303
+ filename: id,
304
+ plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]],
305
+ presets: [['@babel/preset-typescript', { allowDeclareFields: true }]],
306
+ sourceMaps: true,
307
+ });
308
+
309
+ if (!result?.code) {
310
+ return null;
311
+ }
312
+
313
+ return { code: result.code, map: result.map };
314
+ },
315
+ };
316
+ }
285
317
 
286
318
  export default defineConfig({
319
+ plugins: [fluoDecoratorsPlugin()],
320
+ build: {
321
+ emptyOutDir: true,
322
+ outDir: 'dist',
323
+ rollupOptions: {
324
+ output: {
325
+ entryFileNames: 'main.js',
326
+ },
327
+ },
328
+ ssr: 'src/main.ts',
329
+ target: 'node20',
330
+ },
287
331
  server: {
288
332
  port: 5173,
289
333
  },
@@ -328,7 +372,7 @@ Generated by @fluojs/cli.
328
372
  - Starter contract: ${starterContract}
329
373
  - Default baseline: when you omit \`--platform\`, \`fluo new\` still generates the Node.js + Fastify HTTP starter by default
330
374
  - Broader runtime/adapter package coverage is documented in the fluo docs and package READMEs; this generated starter intentionally describes only the wired starter path above
331
- - Package manager: install/run commands can use ${options.packageManager}; runtime choice stays explicit and is independent from the package manager you picked
375
+ - Package manager: install/run commands can use ${options.packageManager}; ${createRunCommand(options.packageManager, 'dev')}, ${createRunCommand(options.packageManager, 'build')}, and ${createRunCommand(options.packageManager, 'start')} delegate to \`fluo dev\`, \`fluo build\`, and \`fluo start\`, which own the generated runtime lifecycle and default \`NODE_ENV\` when unset
332
376
  - Runtime dependency set: generated manifest entries match the selected runtime contract instead of inheriting the Node-only starter recipe
333
377
  ${corsLine}
334
378
  - Observability: /health and /ready endpoints are included by default
@@ -338,6 +382,7 @@ ${corsLine}
338
382
  ## Commands
339
383
 
340
384
  - Dev: ${createRunCommand(options.packageManager, 'dev')}
385
+ - Start: ${createRunCommand(options.packageManager, 'start')}
341
386
  - Build: ${createRunCommand(options.packageManager, 'build')}
342
387
  - Typecheck: ${createRunCommand(options.packageManager, 'typecheck')}
343
388
  - Test: ${createRunCommand(options.packageManager, 'test')}
@@ -452,7 +497,7 @@ Generated by @fluojs/cli.
452
497
  - Transport: \`${starter.readmeTransportLabel}\` is the generated runnable starter contract for this project
453
498
  - Runtime: \`node\`
454
499
  - Platform: \`none\` because the microservice starter boots through \`@fluojs/microservices\`, not an HTTP adapter
455
- - Package manager: install/run commands can use ${options.packageManager}; ${starter.packageManagerNote}
500
+ - Package manager: install/run commands can use ${options.packageManager}; ${createRunCommand(options.packageManager, 'dev')}, ${createRunCommand(options.packageManager, 'build')}, and ${createRunCommand(options.packageManager, 'start')} delegate to \`fluo dev\`, \`fluo build\`, and \`fluo start\`, which own the generated runtime lifecycle and default \`NODE_ENV\` when unset; ${starter.packageManagerNote}
456
501
  - Messaging contract: \`src/math/math.handler.ts\` exposes a \`${starter.pattern}\` message pattern and the generated tests verify it through an in-memory transport so the starter stays testable without external brokers
457
502
  - Entrypoint contract: ${starter.entrypointNote}
458
503
  - Starter contract note: ${starter.starterNote}
@@ -461,6 +506,7 @@ Generated by @fluojs/cli.
461
506
  ## Commands
462
507
 
463
508
  - Dev: ${createRunCommand(options.packageManager, 'dev')}
509
+ - Start: ${createRunCommand(options.packageManager, 'start')}
464
510
  - Build: ${createRunCommand(options.packageManager, 'build')}
465
511
  - Typecheck: ${createRunCommand(options.packageManager, 'typecheck')}
466
512
  - Test: ${createRunCommand(options.packageManager, 'test')}
@@ -611,12 +657,14 @@ describe('HealthService', () => {
611
657
  `;
612
658
  }
613
659
  function createHealthControllerFile(importSuffix = '') {
614
- return `import { Inject } from '@fluojs/core';
660
+ return `import { ensureMetadataSymbol, Inject } from '@fluojs/core';
615
661
  import { Controller, Get } from '@fluojs/http';
616
662
 
617
663
  import { HealthService } from './health.service${importSuffix}';
618
664
  import { HealthResponseDto } from './health.response.dto${importSuffix}';
619
665
 
666
+ ensureMetadataSymbol();
667
+
620
668
  @Inject(HealthService)
621
669
  @Controller('/health-info')
622
670
  export class HealthController {
@@ -1365,12 +1413,13 @@ Generated by @fluojs/cli.
1365
1413
 
1366
1414
  - Shape: \`mixed\`
1367
1415
  - Supported topology: \`single-package\` generates one shared Node.js package where the Fastify HTTP application also starts an attached TCP microservice from the same \`src/main.ts\`
1368
- - Runtime contract: \`src/app.ts\` owns one shared module graph, and \`src/main.ts\` bootstraps the HTTP API before calling \`connectMicroservice()\` and \`startAllMicroservices()\`
1416
+ - Runtime contract: \`src/app.ts\` owns one shared module graph, and \`src/main.ts\` bootstraps the HTTP API before calling \`connectMicroservice()\` and \`startAllMicroservices()\`; ${createRunCommand(options.packageManager, 'dev')}, ${createRunCommand(options.packageManager, 'build')}, and ${createRunCommand(options.packageManager, 'start')} delegate to \`fluo dev\`, \`fluo build\`, and \`fluo start\`, which default \`NODE_ENV\` when unset
1369
1417
  - Intentional limitation: the first mixed release supports only the explicit Fastify HTTP + attached TCP microservice contract; other mixed transports or separate multi-entrypoint layouts fail validation instead of generating partial scaffolds
1370
1418
 
1371
1419
  ## Commands
1372
1420
 
1373
1421
  - Dev: ${createRunCommand(options.packageManager, 'dev')}
1422
+ - Start: ${createRunCommand(options.packageManager, 'start')}
1374
1423
  - Build: ${createRunCommand(options.packageManager, 'build')}
1375
1424
  - Typecheck: ${createRunCommand(options.packageManager, 'typecheck')}
1376
1425
  - Test: ${createRunCommand(options.packageManager, 'test')}
@@ -41,6 +41,7 @@ export type UpdatePrompter = {
41
41
  * Describes the cli update check runtime options contract.
42
42
  */
43
43
  export interface CliUpdateCheckRuntimeOptions {
44
+ bypassCache?: boolean;
44
45
  cacheFile?: string;
45
46
  cacheTtlMs?: number;
46
47
  ci?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAOA,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAcF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE,MAAM,EAAE,UAAU,CAAC;CACpB,GACD;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1E,cAAc,CAAC,EAAE,CAAC,cAAc,EAAE,oBAAoB,EAAE,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1G,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAscD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAcnG;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkEjI"}
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAOA,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAcF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IACE,MAAM,EAAE,UAAU,CAAC;CACpB,GACD;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1E,cAAc,CAAC,EAAE,CAAC,cAAc,EAAE,oBAAoB,EAAE,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1G,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAycD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAcnG;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmEjI"}
@@ -204,10 +204,12 @@ async function fetchLatestDistTag(packageName) {
204
204
  clearTimeout(timeout);
205
205
  }
206
206
  }
207
- async function resolveLatestVersion(packageName, cacheFile, cacheTtlMs, nowMs, fetchLatestVersion) {
208
- const cachedLatestVersion = await readCachedLatestVersion(cacheFile, nowMs, cacheTtlMs);
209
- if (cachedLatestVersion) {
210
- return cachedLatestVersion;
207
+ async function resolveLatestVersion(packageName, cacheFile, cacheTtlMs, nowMs, fetchLatestVersion, bypassCache) {
208
+ if (!bypassCache) {
209
+ const cachedLatestVersion = await readCachedLatestVersion(cacheFile, nowMs, cacheTtlMs);
210
+ if (cachedLatestVersion) {
211
+ return cachedLatestVersion;
212
+ }
211
213
  }
212
214
  let latestVersion;
213
215
  try {
@@ -429,7 +431,7 @@ export async function runCliUpdateCheck(argv, options = {}) {
429
431
  action: 'continue'
430
432
  };
431
433
  }
432
- const latestVersion = await resolveLatestVersion(packageName, cacheFile, cacheTtlMs, nowMs, options.fetchLatestVersion ?? fetchLatestDistTag);
434
+ const latestVersion = await resolveLatestVersion(packageName, cacheFile, cacheTtlMs, nowMs, options.fetchLatestVersion ?? fetchLatestDistTag, options.bypassCache === true);
433
435
  if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
434
436
  return {
435
437
  action: 'continue'
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "migration",
10
10
  "diagnostics"
11
11
  ],
12
- "version": "1.0.0-beta.4",
12
+ "version": "1.0.0-beta.5",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -44,7 +44,7 @@
44
44
  "ejs": "^3.1.10",
45
45
  "tsx": "^4.20.4",
46
46
  "typescript": "^6.0.2",
47
- "@fluojs/runtime": "^1.0.0-beta.9"
47
+ "@fluojs/runtime": "^1.0.0-beta.10"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@fluojs/studio": "^1.0.0-beta.3"