@dokebi/cli 0.2.4 → 0.2.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.
package/AI.md CHANGED
@@ -1,4 +1,4 @@
1
- # AI 작업 안내 — @dokebi/cli 0.2.4 (`dokebi`)
1
+ # AI 작업 안내 — @dokebi/cli 0.2.5 (`dokebi`)
2
2
 
3
3
  도깨비 프로젝트를 만들고 플러그인·앱·모듈을 설치·제거·업그레이드하는 명령이다. 프로젝트가 가진 「CLI 소유 파일」(생성 등록 코드, 상태, 견본 설정)을 트랜잭션으로 고친다. AI는 이 파일들을 손으로 편집하지 말고 반드시 명령으로 바꾼다. 명령 세부 정의는 [docs/cli/README.md](../../docs/cli/README.md), 설치 생명주기는 [docs/cli/installation.md](../../docs/cli/installation.md), 자동화 계약은 [docs/cli/automation.md](../../docs/cli/automation.md)다.
4
4
 
@@ -16,7 +16,7 @@
16
16
  | `dokebi migrate [--dry-run]` | 0.1.x JSON 설정 프로젝트를 YAML로 이전 |
17
17
  | `dokebi inspect` / `dokebi doctor` | 읽기 전용 진단. doctor는 error 진단이 있으면 종료 1 |
18
18
  | `dokebi app add <이름> [--domain <host>]... [--template <이름>] [--inertia [--ssr]]` | `src/apps/<이름>` 앱(D-0030). `--inertia`는 Vue 골격과 Vite 빌드 준비, `--ssr`은 서버 렌더링 준비(D-0039) |
19
- | `dokebi app remove <이름>` · `app link <앱> <도메인> [--template]` · `app unlink <도메인>` · `app list` | 앱·도메인 관리 |
19
+ | `dokebi app remove <이름>` · `app link <앱> <도메인> [--template]` · `app unlink <도메인>` · `app list` | 앱·도메인 관리. `app remove`는 그 앱을 가리키는 도메인이 남아 있으면 `APP_IN_USE`(먼저 unlink), 등록을 지우며 dokebi.yaml·견본의 `options.dokebi-app.apps.<이름>`(D-0061 앱별 설정)도 지운다(0.2.5; 남기면 다음 시작이 `UNKNOWN_APP`). 폴더는 남는다 |
20
20
  | `dokebi module add <이름> [--no-views]` · `module remove` · `module list` | `src/shared/modules/<이름>` 기능 모듈(D-0032) |
21
21
  | `dokebi dev` | 개발 실행(앞에서). `src/`·`dokebi.yaml`·`.env`가 바뀌면 drain 뒤 다시 띄움. Vite 설정이 있고 template-inertia ≥0.1.2면 `DOKEBI_VITE_DEV=1`로 Vite 개발 서버를 앱 안에 띄워 **HMR**(빌드 불필요, `.vue`·`views/` 변경은 재시작 없음, D-0045). 옛 template-inertia면 감시 빌드 + 재시작. 띄운 앱에 `DOKEBI_DEV=1`을 넣어 cluster 앱은 워커 1개로 돈다(`DOKEBI_DEV_WORKERS`, D-0044·D-0045) |
22
22
  | `dokebi start [--foreground] [--wait-ms <ms>] [--log-max-mb <MB>] [--log-files <n>]` · `stop [--timeout-ms <ms>] [--force]` · `status` · `logs [-n <줄>] [-f]` | 백그라운드 실행·종료·상태·로그와 로그 회전(아래 §2.1, 다음 판, D-0044·D-0053) |
@@ -12,6 +12,12 @@ export declare function parseSetOption(argument: string): SetOption;
12
12
  export declare function setWarnings(sets: readonly SetOption[]): Warning[];
13
13
  export declare function applySetOptions(source: string, id: string, sets: readonly SetOption[]): string;
14
14
  export declare function applyConfigChanges(source: string, id: string, changes: readonly ConfigChange[]): string;
15
+ /**
16
+ * Removes `options.dokebi-app.apps.<app>` (D-0061 per-app settings) and the `apps` map when it is
17
+ * left empty; a source without that entry is returned unchanged. dokebi-app refuses to start with
18
+ * an entry for an app that is not defined (UNKNOWN_APP), so `dokebi app remove` calls this.
19
+ */
20
+ export declare function removeAppSettings(source: string, app: string): string;
15
21
  /** Keeps a removed plugin's section and marks it for a later reinstall or manual cleanup. */
16
22
  export declare function markRemoved(source: string, id: string): {
17
23
  source: string;
@@ -109,6 +109,22 @@ export function applyConfigChanges(source, id, changes) {
109
109
  }
110
110
  });
111
111
  }
112
+ /**
113
+ * Removes `options.dokebi-app.apps.<app>` (D-0061 per-app settings) and the `apps` map when it is
114
+ * left empty; a source without that entry is returned unchanged. dokebi-app refuses to start with
115
+ * an entry for an app that is not defined (UNKNOWN_APP), so `dokebi app remove` calls this.
116
+ */
117
+ export function removeAppSettings(source, app) {
118
+ const path = ['options', 'dokebi-app', 'apps', app];
119
+ if (!parseDocument(source, { version: '1.2' }).hasIn(path))
120
+ return source;
121
+ return edit(source, 'dokebi-app', (document) => {
122
+ document.deleteIn(path);
123
+ const apps = document.getIn(['options', 'dokebi-app', 'apps'], true);
124
+ if (isMap(apps) && apps.items.length === 0)
125
+ document.deleteIn(['options', 'dokebi-app', 'apps']);
126
+ });
127
+ }
112
128
  /** Keeps a removed plugin's section and marks it for a later reinstall or manual cleanup. */
113
129
  export function markRemoved(source, id) {
114
130
  const document = parseDocument(source, { version: '1.2' });
package/dist/output.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export declare const CLI_VERSION = "0.2.4";
1
+ export declare const CLI_VERSION = "0.2.5";
2
2
  export declare function envelope(command: string, result: unknown, ok?: boolean): object;
3
3
  export declare function failure(command: string, error: unknown, mutationStarted: boolean): object;
package/dist/output.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CliError } from './errors.js';
2
- export const CLI_VERSION = '0.2.4';
2
+ export const CLI_VERSION = '0.2.5';
3
3
  export function envelope(command, result, ok = true) {
4
4
  return { schemaVersion: 1, cliVersion: CLI_VERSION, command, ok, result, error: null };
5
5
  }
package/dist/project.js CHANGED
@@ -3,7 +3,7 @@ import { basename, dirname, join, resolve } from 'node:path';
3
3
  import lt from 'semver/functions/lt.js';
4
4
  import satisfies from 'semver/functions/satisfies.js';
5
5
  import validRange from 'semver/ranges/valid.js';
6
- import { applyConfigChanges, applySetOptions, markRemoved, setWarnings, } from './config-edit.js';
6
+ import { applyConfigChanges, applySetOptions, markRemoved, removeAppSettings, setWarnings, } from './config-edit.js';
7
7
  import { CONFIG_BACKUP, CONFIG_EXAMPLE, CONFIG_FILE, CONFIG_TEMPLATE, ignoreConfig, LEGACY_CONFIG_FILE, migrateConfig, parseProjectConfig, } from './configuration.js';
8
8
  import { scaffoldConfig } from './configuration-scaffold.js';
9
9
  import { CliError } from './errors.js';
@@ -997,8 +997,29 @@ export class Project {
997
997
  state.apps = (state.apps ?? []).filter((app) => app !== name);
998
998
  if (!state.apps.length)
999
999
  delete state.apps;
1000
- await this.writeConfiguration(tx, state, session.configSource);
1001
- const result = { changed: tx.paths, preserved: [`src/apps/${name}`] };
1000
+ // Its `apps.<name>` settings (D-0061) go too: dokebi-app refuses an entry for an app that
1001
+ // is not defined (UNKNOWN_APP). The sample keeps the same shape when it has the entry.
1002
+ const warnings = [];
1003
+ if (await exists(await securePath(session.root, EXAMPLE))) {
1004
+ const example = await tx.read(EXAMPLE);
1005
+ let next = example;
1006
+ try {
1007
+ next = removeAppSettings(example, name);
1008
+ }
1009
+ catch (error) {
1010
+ if (!(error instanceof CliError))
1011
+ throw error;
1012
+ warnings.push({ code: 'EXAMPLE_INVALID', path: EXAMPLE });
1013
+ }
1014
+ if (next !== example)
1015
+ await tx.write(EXAMPLE, next);
1016
+ }
1017
+ await this.writeConfiguration(tx, state, removeAppSettings(session.configSource, name));
1018
+ const result = {
1019
+ changed: tx.paths,
1020
+ preserved: [`src/apps/${name}`],
1021
+ ...(warnings.length ? { warnings } : {}),
1022
+ };
1002
1023
  return await this.#finish(session, result, dryRun);
1003
1024
  }
1004
1025
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dokebi/cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",