@fluojs/cli 1.0.0-beta.1 → 1.0.0-beta.2

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.ko.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
4
 
5
- fluo 공식 CLI — 새 애플리케이션 부트스트랩, 컴포넌트 생성, 런타임 그래프 검사, 코드 변환을 지원합니다.
5
+ fluo 공식 CLI — 새 애플리케이션 부트스트랩, 컴포넌트 생성, 런타임 검사 데이터 내보내기, 코드 변환을 지원합니다.
6
6
 
7
7
  ## 목차
8
8
 
9
9
  - [설치](#설치)
10
+ - [업데이트 확인](#업데이트-확인)
10
11
  - [사용 시점](#사용-시점)
11
12
  - [빠른 시작](#빠른-시작)
12
13
  - [주요 패턴](#주요-패턴)
@@ -32,12 +33,18 @@ pnpm dlx @fluojs/cli new my-app
32
33
  - 지원되는 설치 경로는 전역 패키지(`pnpm add -g @fluojs/cli`)와 무설치 실행 경로(`pnpm dlx @fluojs/cli ...`)입니다.
33
34
  - 배포되는 `fluo` bin은 `package.json`에 선언된 dist 빌드 CLI 엔트리포인트를 기준으로 동작합니다.
34
35
 
36
+ ## 업데이트 확인
37
+
38
+ `fluo`가 interactive TTY에서 실행되면 로컬 캐시를 사용해 공개 npm `latest` dist-tag의 `@fluojs/cli` 버전을 확인하므로 매 invocation마다 registry를 호출하지 않습니다. 더 새로운 버전이 있으면 CLI가 설치 여부를 묻습니다. 거절하면 현재 설치된 버전으로 기존 명령을 계속 실행하고, 승인하면 `pnpm add -g @fluojs/cli@<latest>`를 실행한 뒤 같은 인자로 업데이트된 `fluo` 바이너리를 다시 시작합니다.
39
+
40
+ 업데이트 확인은 CI, non-TTY 출력, npm-script context, 업데이트 후 재실행 context, registry/network 실패, 명시적 opt-out 경로에서는 건너뜁니다. 한 번만 끄려면 `--no-update-check`(또는 compatibility alias `--no-update-notifier`)를 사용하고, 자동화에서 절대 prompt가 뜨면 안 되는 경우에는 `FLUO_NO_UPDATE_CHECK=1`을 설정하세요.
41
+
35
42
  ## 사용 시점
36
43
 
37
44
  - **부트스트랩**: 표준적이고 검증 가능한 구조로 새 프로젝트를 시작할 때.
38
45
  - **코드 생성**: 일관된 네이밍 규칙과 자동 연결 기능을 갖춘 모듈, 컨트롤러, 서비스, 레포지토리를 생성할 때.
39
46
  - **코드 변환**: 기존 코드베이스를 fluo의 표준 데코레이터 모델에 맞출 때.
40
- - **검사(Inspection)**: 런타임 의존성 그래프를 시각화하고 플랫폼 수준의 문제를 진단할 때.
47
+ - **검사(Inspection)**: 런타임 snapshot 데이터를 내보내고 그래프 보기 또는 렌더링은 Studio 소유 헬퍼에 위임할 때.
41
48
 
42
49
  ## 빠른 시작
43
50
 
@@ -90,6 +97,16 @@ fluo new my-mixed-app --shape mixed --transport tcp --runtime node --platform fa
90
97
 
91
98
  `fluo new`가 interactive TTY에서 실행되면 wizard는 기존 flags/config 모델을 그대로 사용합니다. wizard는 프로젝트 이름, shape-first 분기(`application` -> runtime + HTTP platform, `microservice` -> transport), 유지보수 가능한 tooling preset, package manager, 즉시 dependency를 설치할지 여부, git 저장소를 초기화할지 여부를 묻습니다. non-interactive 플래그 경로와 프로그래밍 방식의 `runNewCommand(...)` 호출도 동일한 resolved defaults를 사용합니다.
92
99
 
100
+ side effect 없이 완전히 resolved starter를 미리 확인하려면 `--print-plan`을 사용하세요:
101
+
102
+ ```bash
103
+ fluo new my-app --shape application --runtime node --platform fastify --print-plan
104
+ fluo new my-service --shape microservice --transport tcp --print-plan
105
+ fluo new my-mixed-app --shape mixed --print-plan
106
+ ```
107
+
108
+ plan preview 모드는 실제 scaffold와 같은 프로젝트 이름, shape, runtime, platform, transport, tooling preset, package manager, install 선택, git 선택을 resolve합니다. 선택된 starter recipe와 dependency 세트를 출력한 뒤 파일 생성, dependency 설치, git 저장소 초기화 없이 종료합니다.
109
+
93
110
  현재 제공되는 스타터 매트릭스(Node.js Fastify/Express/raw Node.js HTTP, Bun, Deno, Cloudflare Workers, TCP/Redis Streams/NATS/Kafka/RabbitMQ/MQTT/gRPC microservice, 그리고 mixed)와 남아 있는 더 넓은 어댑터 생태계를 문서 수준에서 구분한 표는 [fluo new 지원 매트릭스](../../docs/reference/fluo-new-support-matrix.ko.md)를 확인하세요. `@fluojs/redis` 같은 패키지 수준 통합은 더 넓은 생태계에 남아 있지만, 추가 `fluo new --transport` 스타터 플래그는 아닙니다.
94
111
 
95
112
  ### 2. 기능 추가
@@ -99,8 +116,16 @@ fluo new my-mixed-app --shape mixed --transport tcp --runtime node --platform fa
99
116
  fluo generate module users
100
117
  fluo generate controller users
101
118
  fluo generate service users
119
+ fluo generate request-dto users CreateUser
120
+ fluo generate service users --dry-run
102
121
  ```
103
122
 
123
+ Request DTO 생성은 feature 디렉터리와 DTO 클래스 이름을 분리해서 받습니다. 따라서 `CreateUser`, `UpdateUser` 같은 여러 입력 계약을 같은 `src/users/` 슬라이스 안에 둘 수 있습니다.
124
+
125
+ `--dry-run`을 추가하면 실제 실행과 같은 타깃 해석, 기존 파일 건너뛰기 또는 덮어쓰기 판단, 모듈 자동 등록 계획, 파일만 생성하는 wiring 상태, 다음 단계 힌트를 미리 볼 수 있습니다. 이 모드는 디렉터리 생성, 파일 쓰기, 모듈 갱신을 수행하지 않습니다. `--force`는 내용이 달라질 기존 파일의 계획 항목을 `SKIP`에서 `OVERWRITE`로 바꾸며, `--target-directory`는 실제 실행과 동일하게 지정한 소스 디렉터리 기준으로 preview 범위를 제한합니다.
126
+
127
+ Generator discovery는 의도적으로 built-in `@fluojs/cli/builtin` collection으로 제한됩니다. 외부 package-owned 또는 app-local generator collection은 보류되어 있습니다. `fluo generate`는 config file을 스캔하거나, 임의 package를 로드하거나, workspace-owned collection code를 실행하지 않습니다. 이 경계는 shipped generator 계약을 보존하면서 generator metadata, option schema, help output, file-write boundary를 결정적이고 테스트 가능하게 유지합니다.
128
+
104
129
  ## 주요 패턴
105
130
 
106
131
  ### 데코레이터 코드 변환
@@ -109,27 +134,48 @@ fluo generate service users
109
134
  ```bash
110
135
  # 변경 사항 미리보기 (dry-run)
111
136
  fluo migrate ./src
137
+ fluo migrate ./src --json
112
138
 
113
139
  # 변환 적용
114
140
  fluo migrate ./src --apply
141
+ fluo migrate ./src --apply --json
115
142
  ```
116
143
 
144
+ CI 작업, 대시보드, migration report에서 안정적인 machine-readable 결과가 필요하면 `--json`을 사용하세요. 사람을 위한 출력은 기본값으로 유지됩니다. JSON 모드는 성공 시 stdout에 structured report만 기록하고, parser 오류나 잘못된 flag 조합은 기존처럼 stderr에 메시지를 기록한 뒤 exit code `1`을 반환하며 partial JSON을 출력하지 않습니다. Report에는 `mode`(`dry-run` 또는 `apply`), `dryRun`, `apply`, 활성화된 `transforms`, `scannedFiles`, `changedFiles`, 전체 `warningCount`, 그리고 `filePath`, `changed`, `appliedTransforms`, `warningCount`, category label과 source line number가 포함된 warnings per-file metadata가 포함됩니다.
145
+
117
146
  **주요 변환 사항:**
118
147
  - `@nestjs/common` 임포트를 `@fluojs/core` 또는 `@fluojs/http`로 재작성합니다.
119
148
  - `@Injectable()`을 제거하고 스코프를 `@Scope()`로 매핑합니다.
120
149
  - `tsconfig.json`을 업데이트하여 `experimentalDecorators`를 비활성화하고 `baseUrl` 기반 경로 별칭을 TS6-safe `paths` 엔트리로 재작성합니다.
121
150
 
122
151
  ### 런타임 검사 (Inspection)
123
- 애플리케이션 구조를 시각화하고 초기화 문제를 해결합니다.
152
+ CLI가 그래프 렌더링을 소유하지 않으면서 애플리케이션 구조를 내보내고 초기화 문제를 해결합니다.
124
153
 
125
154
  ```bash
126
- # 의존성 그래프를 Mermaid 형식으로 내보내기
155
+ # 선택적 Studio 렌더러를 통해 Mermaid 내보내기
127
156
  fluo inspect ./src/app.module.ts --mermaid
128
157
 
129
158
  # @fluojs/studio용 snapshot 내보내기
130
159
  fluo inspect ./src/app.module.ts --json > snapshot.json
160
+
161
+ # shell redirection 없이 같은 JSON snapshot을 CI artifact 경로에 쓰기
162
+ fluo inspect ./src/app.module.ts --json --output artifacts/inspect-snapshot.json
163
+
164
+ # 런타임이 생산한 snapshot 옆에 bootstrap timing 포함하기
165
+ fluo inspect ./src/app.module.ts --json --timing
166
+
167
+ # 요약, snapshot, diagnostics, timing을 포함한 support triage report 내보내기
168
+ fluo inspect ./src/app.module.ts --report --output artifacts/inspect-report.json
131
169
  ```
132
170
 
171
+ 런타임이 inspection snapshot을 생산합니다. `fluo inspect`는 그 snapshot을 JSON으로 직렬화하고, `fluo inspect --mermaid`는 snapshot-to-Mermaid 렌더링을 선택적 `@fluojs/studio` 계약에 위임합니다. `--timing`은 JSON 출력에 bootstrap timing diagnostics를 기록하고, `--report`는 CI/support triage를 위해 런타임이 생산한 snapshot을 안정적인 요약과 함께 감쌉니다. `--output <path>`는 선택한 inspect payload를 stdout 대신 명시적 artifact 경로에 씁니다. 이 동작은 검사 대상 애플리케이션을 writable하게 만들지 않으며, 일반 bootstrap/close cycle 외에 module graph state를 바꾸지 않습니다. Mermaid 출력이 필요하면 명령을 실행하는 프로젝트에 Studio를 설치하세요:
172
+
173
+ ```bash
174
+ pnpm add -D @fluojs/studio
175
+ ```
176
+
177
+ Studio가 없으면 CI와 non-interactive 실행은 prompt나 package manager 실행 없이 설치 안내와 함께 빠르게 실패합니다. Interactive 실행에서는 Studio 설치 여부를 물을 수 있지만, 명시적으로 승인되고 구현된 설치 흐름이 없는 한 `fluo inspect`가 package manager install을 실행하지 않습니다.
178
+
133
179
  ## 공개 API
134
180
 
135
181
  다른 도구 내에서 CLI 동작을 트리거하기 위해 패키지를 프로그래밍 방식으로 사용할 수 있습니다.
@@ -142,8 +188,8 @@ fluo inspect ./src/app.module.ts --json > snapshot.json
142
188
 
143
189
  ## 관련 패키지
144
190
 
145
- - **[@fluojs/runtime](../runtime/README.ko.md)**: 검사 부트스트랩에 사용되는 기본 엔진입니다.
146
- - **[@fluojs/studio](../studio/README.ko.md)**: `inspect --json` 출력을 시각화하기 위한 웹 기반 UI입니다.
191
+ - **[@fluojs/runtime](../runtime/README.ko.md)**: 부트스트랩 안전 런타임 검사 inspection snapshot을 생산하는 기본 엔진입니다.
192
+ - **[@fluojs/studio](../studio/README.ko.md)**: `inspect --json` 출력을 확인하고 `inspect --mermaid`가 사용하는 canonical renderer를 제공하는 웹 기반 UI입니다.
147
193
  - **[@fluojs/testing](../testing/README.ko.md)**: 통합 및 E2E 테스트를 위해 생성된 테스트 템플릿에서 사용됩니다.
148
194
  - **[Canonical Runtime Package Matrix](../../docs/reference/package-surface.ko.md)**: 공식 런타임/패키지 조합을 보여주는 기준 문서입니다.
149
195
 
package/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  <p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
4
4
 
5
- The canonical CLI for fluo — bootstrap new applications, generate components, inspect runtime graphs, and run code transforms.
5
+ The canonical CLI for fluo — bootstrap new applications, generate components, export runtime inspection data, and run code transforms.
6
6
 
7
7
  ## Table of Contents
8
8
 
9
9
  - [Installation](#installation)
10
+ - [Update Checks](#update-checks)
10
11
  - [When to Use](#when-to-use)
11
12
  - [Quick Start](#quick-start)
12
13
  - [Common Patterns](#common-patterns)
@@ -32,12 +33,18 @@ pnpm dlx @fluojs/cli new my-app
32
33
  - The supported install paths are the global package (`pnpm add -g @fluojs/cli`) and the no-install runner (`pnpm dlx @fluojs/cli ...`).
33
34
  - The published `fluo` bin is backed by the dist-built CLI entrypoint declared in `package.json`.
34
35
 
36
+ ## Update Checks
37
+
38
+ When `fluo` runs in an interactive TTY, it checks the public npm `latest` dist-tag for `@fluojs/cli` using a local cache so every invocation does not hit the registry. If a newer version is available, the CLI asks whether to install it. Declining continues the current command with the installed version; accepting runs `pnpm add -g @fluojs/cli@<latest>` and then restarts `fluo` with the same arguments under the updated binary.
39
+
40
+ The update check is skipped in CI, non-TTY output, npm-script contexts, rerun-after-update contexts, registry/network failures, and explicit opt-out paths. Use `--no-update-check` (or the compatibility alias `--no-update-notifier`) for one invocation, or set `FLUO_NO_UPDATE_CHECK=1` when automation must never prompt.
41
+
35
42
  ## When to Use
36
43
 
37
44
  - **Bootstrapping**: When starting a new project with a standard, verifiable structure.
38
45
  - **Generation**: To create modules, controllers, services, and repositories with consistent naming and automatic wiring.
39
46
  - **Code transforms**: When aligning an existing codebase with fluo's standard decorator model.
40
- - **Inspection**: To visualize the runtime dependency graph and diagnose platform-level issues.
47
+ - **Inspection**: To export runtime snapshot data and delegate graph viewing or rendering to Studio-owned helpers.
41
48
 
42
49
  ## Quick Start
43
50
 
@@ -90,6 +97,16 @@ fluo new my-mixed-app --shape mixed --transport tcp --runtime node --platform fa
90
97
 
91
98
  When `fluo new` runs in an interactive TTY, the wizard uses the same flags/config model. It asks for the project name, shape-first branch (`application` -> runtime + HTTP platform, `microservice` -> transport), the maintained tooling preset, package-manager choice, whether to install dependencies immediately, and whether to initialize a git repository. Non-interactive flags and programmatic `runNewCommand(...)` calls use the same resolved defaults.
92
99
 
100
+ Use `--print-plan` when you want to preview the fully resolved starter without side effects:
101
+
102
+ ```bash
103
+ fluo new my-app --shape application --runtime node --platform fastify --print-plan
104
+ fluo new my-service --shape microservice --transport tcp --print-plan
105
+ fluo new my-mixed-app --shape mixed --print-plan
106
+ ```
107
+
108
+ Plan preview mode resolves the same project name, shape, runtime, platform, transport, tooling preset, package manager, install choice, and git choice as a real scaffold. It prints the selected starter recipe and dependency sets, then exits without creating files, installing dependencies, or initializing a git repository.
109
+
93
110
  For a docs-level table that separates the shipped starter matrix (Node.js Fastify/Express/raw Node.js HTTP, Bun, Deno, Cloudflare Workers, TCP/Redis Streams/NATS/Kafka/RabbitMQ/MQTT/gRPC microservices, plus mixed) from the remaining broader adapter ecosystem, see the [fluo new support matrix](../../docs/reference/fluo-new-support-matrix.md). Package-level integrations such as `@fluojs/redis` remain part of the broader ecosystem, but they are not extra `fluo new --transport` starter flags.
94
111
 
95
112
  ### 2. Generate a feature
@@ -99,8 +116,16 @@ Add a new resource with a controller and service, automatically wired into the m
99
116
  fluo generate module users
100
117
  fluo generate controller users
101
118
  fluo generate service users
119
+ fluo generate request-dto users CreateUser
120
+ fluo generate service users --dry-run
102
121
  ```
103
122
 
123
+ Request DTO generation accepts the feature directory separately from the DTO class name, so multiple input contracts such as `CreateUser` and `UpdateUser` can live inside the same `src/users/` slice.
124
+
125
+ Add `--dry-run` to preview the same target resolution, skipped or overwritten file decisions, module auto-registration plan, files-only wiring status, and next-step hint without creating directories, writing files, or updating modules. `--force` still changes existing-file plan entries from `SKIP` to `OVERWRITE` when content would change, and `--target-directory` scopes the preview to that source directory exactly as it does for a real run.
126
+
127
+ Generator discovery is intentionally limited to the built-in `@fluojs/cli/builtin` collection. External package-owned or app-local generator collections are deferred: `fluo generate` does not scan config files, load arbitrary packages, or execute workspace-owned collection code. This keeps generator metadata, option schemas, help output, and file-write boundaries deterministic and testable while preserving the shipped generator contract.
128
+
104
129
  ## Common Patterns
105
130
 
106
131
  ### Decorator Codemods
@@ -109,27 +134,48 @@ Run codemods to align your codebase with TC39 standard decorators.
109
134
  ```bash
110
135
  # Preview changes (dry-run)
111
136
  fluo migrate ./src
137
+ fluo migrate ./src --json
112
138
 
113
139
  # Apply transformations
114
140
  fluo migrate ./src --apply
141
+ fluo migrate ./src --apply --json
115
142
  ```
116
143
 
144
+ Use `--json` when CI jobs, dashboards, or migration reports need a stable machine-readable result. Human output remains the default. JSON mode writes only the structured report to stdout on success, while parser errors and invalid flag combinations still write their message to stderr and return exit code `1` without partial JSON output. The report includes `mode` (`dry-run` or `apply`), `dryRun`, `apply`, enabled `transforms`, `scannedFiles`, `changedFiles`, aggregate `warningCount`, and per-file metadata with `filePath`, `changed`, `appliedTransforms`, `warningCount`, and warnings including category labels and source line numbers.
145
+
117
146
  **Key Transformations:**
118
147
  - Rewrites imports from `@nestjs/common` to `@fluojs/core` or `@fluojs/http`.
119
148
  - Removes `@Injectable()` and maps scopes to `@Scope()`.
120
149
  - Updates `tsconfig.json` to disable `experimentalDecorators` and rewrites `baseUrl`-backed path aliases to TS6-safe `paths` entries.
121
150
 
122
151
  ### Runtime Inspection
123
- Visualize your application structure and troubleshoot initialization issues.
152
+ Export your application structure and troubleshoot initialization issues without making the CLI own graph rendering.
124
153
 
125
154
  ```bash
126
- # Export dependency graph as Mermaid
155
+ # Export Mermaid through the optional Studio renderer
127
156
  fluo inspect ./src/app.module.ts --mermaid
128
157
 
129
158
  # Export snapshot for @fluojs/studio
130
159
  fluo inspect ./src/app.module.ts --json > snapshot.json
160
+
161
+ # Write the same JSON snapshot to a CI artifact path without shell redirection
162
+ fluo inspect ./src/app.module.ts --json --output artifacts/inspect-snapshot.json
163
+
164
+ # Include bootstrap timing next to the runtime-produced snapshot
165
+ fluo inspect ./src/app.module.ts --json --timing
166
+
167
+ # Emit a support triage report with summary, snapshot, diagnostics, and timing
168
+ fluo inspect ./src/app.module.ts --report --output artifacts/inspect-report.json
131
169
  ```
132
170
 
171
+ The runtime produces the inspection snapshot. `fluo inspect` serializes that snapshot as JSON, and `fluo inspect --mermaid` delegates snapshot-to-Mermaid rendering to the optional `@fluojs/studio` contract. `--timing` records bootstrap timing diagnostics for JSON output, and `--report` wraps the runtime-produced snapshot with a stable summary for CI/support triage. `--output <path>` writes the selected inspect payload to an explicit artifact path instead of stdout; it does not make the inspected application writable or change module graph state beyond the normal bootstrap/close cycle. Install Studio in the project that runs the command when you need Mermaid output:
172
+
173
+ ```bash
174
+ pnpm add -D @fluojs/studio
175
+ ```
176
+
177
+ If Studio is missing, CI and other non-interactive runs fail fast with install guidance instead of prompting or running a package manager. Interactive runs may ask whether you want to install Studio, but `fluo inspect` does not run installs unless an explicit install flow is implemented and approved.
178
+
133
179
  ## Public API
134
180
 
135
181
  The package can be used programmatically to trigger CLI actions from within other tools.
@@ -142,8 +188,8 @@ The package can be used programmatically to trigger CLI actions from within othe
142
188
 
143
189
  ## Related Packages
144
190
 
145
- - **[@fluojs/runtime](../runtime/README.md)**: The underlying engine used for inspection and bootstrap.
146
- - **[@fluojs/studio](../studio/README.md)**: The web-based UI for visualizing `inspect --json` exports.
191
+ - **[@fluojs/runtime](../runtime/README.md)**: The underlying engine that produces inspection snapshots during bootstrap-safe runtime inspection.
192
+ - **[@fluojs/studio](../studio/README.md)**: The web-based UI for viewing `inspect --json` exports and the canonical renderer used by `inspect --mermaid`.
147
193
  - **[@fluojs/testing](../testing/README.md)**: Used by generated test templates for integration and E2E testing.
148
194
  - **[Canonical Runtime Package Matrix](../../docs/reference/package-surface.md)**: The source of truth for official runtime/package combinations.
149
195
 
package/dist/cli.d.ts CHANGED
@@ -1,14 +1,24 @@
1
+ import { type InspectCommandRuntimeOptions } from './commands/inspect.js';
1
2
  import { type NewCommandRuntimeOptions } from './commands/new.js';
3
+ import { type CliUpdateCheckRuntimeOptions } from './update-check.js';
2
4
  type CliStream = {
5
+ isTTY?: boolean;
3
6
  write(message: string): unknown;
4
7
  };
8
+ type CliReadableStream = {
9
+ isTTY?: boolean;
10
+ };
5
11
  /**
6
12
  * Runtime dependency overrides for embedding the CLI in tests or higher-level tooling.
7
13
  */
8
14
  export interface CliRuntimeOptions {
15
+ ci?: boolean;
9
16
  cwd?: string;
17
+ env?: NodeJS.ProcessEnv;
10
18
  stderr?: CliStream;
19
+ stdin?: CliReadableStream;
11
20
  stdout?: CliStream;
21
+ updateCheck?: false | CliUpdateCheckRuntimeOptions;
12
22
  }
13
23
  /**
14
24
  * Runs the top-level CLI command dispatcher and returns a process-style exit code.
@@ -29,9 +39,9 @@ export interface CliRuntimeOptions {
29
39
  * ```
30
40
  *
31
41
  * @param argv Argument vector to execute. Defaults to the current process arguments without the node/bin prefix.
32
- * @param runtime Optional runtime overrides shared by the top-level dispatcher and the `new` command.
42
+ * @param runtime Optional runtime overrides shared by the top-level dispatcher and delegated commands.
33
43
  * @returns `0` when the command completes successfully, otherwise `1` after writing the error message to `stderr`.
34
44
  */
35
- export declare function runCli(argv?: string[], runtime?: CliRuntimeOptions & NewCommandRuntimeOptions): Promise<number>;
45
+ export declare function runCli(argv?: string[], runtime?: CliRuntimeOptions & NewCommandRuntimeOptions & InspectCommandRuntimeOptions): Promise<number>;
36
46
  export {};
37
47
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,wBAAwB,EAA2B,MAAM,mBAAmB,CAAC;AAK3F,KAAK,SAAS,GAAG;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAmPD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE,iBAAiB,GAAG,wBAA6B,GACzD,OAAO,CAAC,MAAM,CAAC,CAgGjB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,4BAA4B,EAAmC,MAAM,uBAAuB,CAAC;AAE3G,OAAO,EAAE,KAAK,wBAAwB,EAA2B,MAAM,mBAAmB,CAAC;AAI3F,OAAO,EAAE,KAAK,4BAA4B,EAA6C,MAAM,mBAAmB,CAAC;AAEjH,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;AAEF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,WAAW,CAAC,EAAE,KAAK,GAAG,4BAA4B,CAAC;CACpD;AAkRD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAC1B,IAAI,WAAwB,EAC5B,OAAO,GAAE,iBAAiB,GAAG,wBAAwB,GAAG,4BAAiC,GACxF,OAAO,CAAC,MAAM,CAAC,CA2HjB"}
package/dist/cli.js CHANGED
@@ -5,8 +5,9 @@ import { runGenerateCommand } from './commands/generate.js';
5
5
  import { inspectUsage, runInspectCommand } from './commands/inspect.js';
6
6
  import { migrateUsage, runMigrateCommand } from './commands/migrate.js';
7
7
  import { newUsage, runNewCommand } from './commands/new.js';
8
- import { generatorManifest, resolveGeneratorKind } from './generators/manifest.js';
8
+ import { builtInGeneratorCollection, generatorManifest, generatorOptionSchemas, resolveGeneratorKind } from './generators/manifest.js';
9
9
  import { renderAliasList, renderHelpTable } from './help.js';
10
+ import { removeUpdateCheckFlags, runCliUpdateCheck } from './update-check.js';
10
11
 
11
12
  /**
12
13
  * Runtime dependency overrides for embedding the CLI in tests or higher-level tooling.
@@ -19,19 +20,11 @@ const GENERATE_KIND_HELP = [...generatorManifest.map(entry => ({
19
20
  schematic: entry.schematic,
20
21
  wiring: entry.wiringBehavior === 'auto-registered' ? 'auto' : 'manual'
21
22
  }))];
22
- const GENERATE_OPTION_HELP = [{
23
- aliases: ['-o'],
24
- description: 'Write generated files under a specific source directory.',
25
- option: '--target-directory <path>'
26
- }, {
27
- aliases: ['-f'],
28
- description: 'Overwrite files that already exist.',
29
- option: '--force'
30
- }, {
31
- aliases: ['-h'],
32
- description: 'Show help for the generate command.',
33
- option: '--help'
34
- }];
23
+ const GENERATE_OPTION_HELP = [...generatorOptionSchemas.map(option => ({
24
+ aliases: [...option.aliases],
25
+ description: option.description,
26
+ option: option.name
27
+ }))];
35
28
  const TOP_LEVEL_COMMAND_HELP = [{
36
29
  aliases: ['create'],
37
30
  command: 'new',
@@ -60,7 +53,7 @@ function isHelpFlag(value) {
60
53
  return value === '--help' || value === '-h';
61
54
  }
62
55
  function generateUsage() {
63
- return ['Usage: fluo generate|g <kind> <name> [options]', '', 'Schematics', renderHelpTable(GENERATE_KIND_HELP, [{
56
+ return ['Usage: fluo generate|g <kind> <name> [options]', ' fluo generate|g request-dto|req <feature> <name> [options]', '', 'Schematics', renderHelpTable(GENERATE_KIND_HELP, [{
64
57
  header: 'Schematic',
65
58
  render: entry => entry.schematic
66
59
  }, {
@@ -72,7 +65,7 @@ function generateUsage() {
72
65
  }, {
73
66
  header: 'Description',
74
67
  render: entry => entry.description
75
- }]), '', ' auto = class is auto-registered in the domain module (created if absent)', ' manual = files only; you must wire the generated class into a module yourself', '', 'Options', renderHelpTable(GENERATE_OPTION_HELP, [{
68
+ }]), '', ' auto = class is auto-registered in the domain module (created if absent)', ' manual = files only; you must wire the generated class into a module yourself', '', 'Collections', ` ${builtInGeneratorCollection.id} (${builtInGeneratorCollection.source})`, ' External or app-local generator collections are intentionally deferred; no packages or config files are loaded by generate.', '', 'Options', renderHelpTable(GENERATE_OPTION_HELP, [{
76
69
  header: 'Option',
77
70
  render: entry => entry.option
78
71
  }, {
@@ -93,7 +86,7 @@ function usage() {
93
86
  }, {
94
87
  header: 'Description',
95
88
  render: entry => entry.description
96
- }]), '', "Run 'fluo help <command>' for more information on a command.", 'Docs: https://github.com/fluojs/fluo/tree/main/docs/getting-started/quick-start.md'].join('\n');
89
+ }]), '', 'Options', ' --no-update-check Skip the interactive CLI update check for this invocation.', ' Alias: --no-update-notifier.', '', "Run 'fluo help <command>' for more information on a command.", 'Docs: https://github.com/fluojs/fluo/tree/main/docs/getting-started/quick-start.md'].join('\n');
97
90
  }
98
91
  function resolveDefaultTargetDirectory(startDirectory) {
99
92
  const resolvedStartDirectory = resolve(startDirectory);
@@ -114,24 +107,33 @@ function resolveDefaultTargetDirectory(startDirectory) {
114
107
  return resolvedStartDirectory;
115
108
  }
116
109
  function parseGenerateArgs(argv) {
117
- const [command, rawKind, name, ...optionArgs] = argv;
110
+ const [command, rawKind, firstName, ...optionArgs] = argv;
118
111
  const kind = normalizeGeneratorKind(rawKind);
119
112
  if (!(command === 'g' || command === 'generate')) {
120
113
  throw new Error(usage());
121
114
  }
122
- if (!kind || !name) {
115
+ if (!kind || !firstName) {
123
116
  throw new Error(generateUsage());
124
117
  }
125
- if (name.startsWith('-')) {
126
- throw new Error(`Invalid resource name "${name}": names cannot start with "-".`);
118
+ if (firstName.startsWith('-')) {
119
+ throw new Error(`Invalid resource name "${firstName}": names cannot start with "-".`);
127
120
  }
128
121
  const parsedOptions = {};
122
+ let name = firstName;
123
+ let seenRequestDtoName = false;
129
124
  let targetDirectory;
130
125
  let seenForce = false;
126
+ let seenDryRun = false;
131
127
  let seenTargetDirectory = false;
132
128
  for (let index = 0; index < optionArgs.length; index += 1) {
133
129
  const option = optionArgs[index];
134
130
  const next = optionArgs[index + 1];
131
+ if (kind === 'request-dto' && !seenRequestDtoName && !option.startsWith('-')) {
132
+ parsedOptions.targetFeature = firstName;
133
+ name = option;
134
+ seenRequestDtoName = true;
135
+ continue;
136
+ }
135
137
  if (option === '--target-directory' || option === '-o') {
136
138
  if (seenTargetDirectory) {
137
139
  throw new Error('Duplicate --target-directory option.');
@@ -152,6 +154,14 @@ function parseGenerateArgs(argv) {
152
154
  seenForce = true;
153
155
  continue;
154
156
  }
157
+ if (option === '--dry-run') {
158
+ if (seenDryRun) {
159
+ throw new Error('Duplicate --dry-run option.');
160
+ }
161
+ parsedOptions.dryRun = true;
162
+ seenDryRun = true;
163
+ continue;
164
+ }
155
165
  throw new Error(`Unknown option: ${option}`);
156
166
  }
157
167
  return {
@@ -207,19 +217,36 @@ function parseCommand(argv) {
207
217
  * ```
208
218
  *
209
219
  * @param argv Argument vector to execute. Defaults to the current process arguments without the node/bin prefix.
210
- * @param runtime Optional runtime overrides shared by the top-level dispatcher and the `new` command.
220
+ * @param runtime Optional runtime overrides shared by the top-level dispatcher and delegated commands.
211
221
  * @returns `0` when the command completes successfully, otherwise `1` after writing the error message to `stderr`.
212
222
  */
213
223
  export async function runCli(argv = process.argv.slice(2), runtime = {}) {
214
224
  const cwd = runtime.cwd ? resolve(runtime.cwd) : process.cwd();
215
225
  const stdout = runtime.stdout ?? process.stdout;
216
226
  const stderr = runtime.stderr ?? process.stderr;
227
+ const env = runtime.env ?? process.env;
228
+ const updateFlagResult = removeUpdateCheckFlags(argv);
229
+ const commandArgv = updateFlagResult.argv;
217
230
  try {
218
- if (argv.length === 0) {
231
+ const updateCheckOptions = runtime.updateCheck === false ? undefined : runtime.updateCheck;
232
+ const updateCheckResult = await runCliUpdateCheck(commandArgv, {
233
+ ...updateCheckOptions,
234
+ ci: runtime.ci,
235
+ env,
236
+ interactive: runtime.interactive,
237
+ skip: updateFlagResult.skipUpdateCheck || runtime.updateCheck === false,
238
+ stderr,
239
+ stdin: runtime.stdin,
240
+ stdout
241
+ });
242
+ if (updateCheckResult.action === 'reran') {
243
+ return updateCheckResult.exitCode;
244
+ }
245
+ if (commandArgv.length === 0) {
219
246
  throw new Error(usage());
220
247
  }
221
- if (argv[0] === 'help') {
222
- const topic = argv[1];
248
+ if (commandArgv[0] === 'help') {
249
+ const topic = commandArgv[1];
223
250
  if (topic === 'new' || topic === 'create') {
224
251
  stdout.write(`${newUsage()}\n`);
225
252
  return 0;
@@ -239,23 +266,23 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
239
266
  stdout.write(`${usage()}\n`);
240
267
  return 0;
241
268
  }
242
- if (isHelpFlag(argv[0])) {
269
+ if (isHelpFlag(commandArgv[0])) {
243
270
  stdout.write(`${usage()}\n`);
244
271
  return 0;
245
272
  }
246
- if ((argv[0] === 'g' || argv[0] === 'generate') && argv.slice(1).some(isHelpFlag)) {
273
+ if ((commandArgv[0] === 'g' || commandArgv[0] === 'generate') && commandArgv.slice(1).some(isHelpFlag)) {
247
274
  stdout.write(`${generateUsage()}\n`);
248
275
  return 0;
249
276
  }
250
- if (argv[0] === 'migrate' && argv.slice(1).some(isHelpFlag)) {
277
+ if (commandArgv[0] === 'migrate' && commandArgv.slice(1).some(isHelpFlag)) {
251
278
  stdout.write(`${migrateUsage()}\n`);
252
279
  return 0;
253
280
  }
254
- if (argv[0] === 'inspect' && argv.slice(1).some(isHelpFlag)) {
281
+ if (commandArgv[0] === 'inspect' && commandArgv.slice(1).some(isHelpFlag)) {
255
282
  stdout.write(`${inspectUsage()}\n`);
256
283
  return 0;
257
284
  }
258
- const parsedCommand = parseCommand(argv);
285
+ const parsedCommand = parseCommand(commandArgv);
259
286
  if (parsedCommand.command === 'new') {
260
287
  return runNewCommand(parsedCommand.argv, runtime);
261
288
  }
@@ -267,9 +294,17 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
267
294
  }
268
295
  const targetDirectory = resolve(cwd, parsedCommand.parsed.targetDirectory ?? resolveDefaultTargetDirectory(cwd));
269
296
  const result = runGenerateCommand(parsedCommand.parsed.kind, parsedCommand.parsed.name, targetDirectory, parsedCommand.parsed.options);
270
- stdout.write(`Generated ${result.generatedFiles.length} file(s):\n`);
271
- for (const file of result.generatedFiles) {
272
- stdout.write(` CREATE ${file}\n`);
297
+ if (parsedCommand.parsed.options.dryRun) {
298
+ stdout.write('Dry run: no files were written.\n');
299
+ stdout.write(`Planned ${result.plannedFiles.length} file action(s):\n`);
300
+ for (const file of result.plannedFiles) {
301
+ stdout.write(` ${file.action.toUpperCase()} ${file.path}\n`);
302
+ }
303
+ } else {
304
+ stdout.write(`Generated ${result.generatedFiles.length} file(s):\n`);
305
+ for (const file of result.generatedFiles) {
306
+ stdout.write(` CREATE ${file}\n`);
307
+ }
273
308
  }
274
309
  stdout.write('\n');
275
310
  if (result.wiringBehavior === 'auto-registered' && result.moduleRegistered) {
@@ -287,6 +322,7 @@ export async function runCli(argv = process.argv.slice(2), runtime = {}) {
287
322
  }
288
323
  if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
289
324
  process.exitCode = await runCli(undefined, {
325
+ ci: process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true',
290
326
  userAgent: process.env.npm_config_user_agent
291
327
  });
292
328
  }
@@ -1,5 +1,14 @@
1
1
  import type { GenerateOptions, GeneratorKind } from '../types.js';
2
2
  import type { GeneratorManifestEntry } from '../generators/manifest.js';
3
+ /** Describes how one generated artifact would interact with the workspace. */
4
+ export type GeneratePlanAction = 'create' | 'module-create' | 'module-unchanged' | 'module-update' | 'overwrite' | 'skip' | 'unchanged';
5
+ /** One path-level action reported by generate dry-run previews and structured results. */
6
+ export type GeneratePlanEntry = {
7
+ /** Planned action for this path. */
8
+ action: GeneratePlanAction;
9
+ /** Absolute path affected by the plan entry. */
10
+ path: string;
11
+ };
3
12
  /**
4
13
  * Structured result returned by {@link runGenerateCommand} for tooling-friendly automation.
5
14
  *
@@ -12,6 +21,7 @@ export type GenerateResult = {
12
21
  moduleRegistered: boolean;
13
22
  modulePath: string | undefined;
14
23
  nextStepHint: string;
24
+ plannedFiles: GeneratePlanEntry[];
15
25
  wiringBehavior: GeneratorManifestEntry['wiringBehavior'];
16
26
  };
17
27
  /**
@@ -32,7 +42,7 @@ export type GenerateResult = {
32
42
  * @param kind Generator kind to execute.
33
43
  * @param name Resource name supplied by the caller before normalization.
34
44
  * @param baseDirectory Source directory that should receive the generated domain folder.
35
- * @param options Optional generation flags that control overwrites and sibling-aware templates.
45
+ * @param options Optional generation flags that control overwrites, request DTO feature placement, and sibling-aware templates.
36
46
  * @returns Structured file and wiring metadata for the completed generation run.
37
47
  * @throws {Error} When the resource name is invalid, the generator kind is unknown, or the target module source cannot be updated safely.
38
48
  */
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,MAAM,2BAA2B,CAAC;AA+FxF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CAiD1I"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAkB,MAAM,2BAA2B,CAAC;AAexF,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,eAAe,GAAG,kBAAkB,GAAG,eAAe,GAAG,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAExI,0FAA0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AA0HF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;CAC1D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CAgE1I"}