@gaonjs/cli 0.37.1 → 0.38.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.
- package/dist/commands/dev.d.ts +14 -3
- package/dist/commands/dev.js +118 -9
- package/dist/dev/console.d.ts +1 -1
- package/dist/dev/console.js +2 -0
- package/dist/index.js +8 -5
- package/dist/templates/project/AGENTS.md.tpl +2 -2
- package/dist/templates/project/agents/async.md.tpl +28 -2
- package/dist/work.js +4 -1
- package/package.json +4 -4
package/dist/commands/dev.d.ts
CHANGED
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
* 1) Docker Compose — pg · redis · nats · mailpit · minio 자동 up -d
|
|
6
6
|
* 2) .gaon 워처 — tables.d.ts · routes.d.ts 재생성 (기존 M3)
|
|
7
7
|
* 3) serve 자식 — 실 웹 서버(Fastify) · 소스 변경 시 재시작
|
|
8
|
-
* 4)
|
|
9
|
-
* 5)
|
|
10
|
-
* 6)
|
|
8
|
+
* 4) work 자식 — 워커(잡·리스너·아웃박스·스케줄) · 소스 변경 시 재시작 (결정 211)
|
|
9
|
+
* 5) hub 자식 — 실시간 허브(프레즌스 권위·중계) (결정 211)
|
|
10
|
+
* 6) tsc / vue-tsc — --watch 모드 · 타입 에러 즉시 리포트
|
|
11
|
+
* 7) restart 워처 — apps/ · domain/ · packages/ 변경 → serve·work 재시작
|
|
12
|
+
* 8) 통합 콘솔 — [source] 태그·색상별 · JSON 모드 지원
|
|
13
|
+
*
|
|
14
|
+
* 운영 프로세스 3종(serve·work·hub · §7)을 dev 에서 모두 내장 기동한다
|
|
15
|
+
* (Rails-like all-in-one · 결정 211). 이전에는 serve 만 떠 잡·이벤트·아웃박스·
|
|
16
|
+
* 크론이 dev 에서 조용히 처리되지 않았다(work 부재) — work.ts·hub.ts 주석이
|
|
17
|
+
* 이미 "dev 가 내장 실행" 이라 명시했으나 실제로는 미배선이던 드리프트를 닫는다.
|
|
11
18
|
*
|
|
12
19
|
* The One Way: `gaon dev` 하나로 전부 · 옵션은 개별 debug 용만 노출.
|
|
13
20
|
* fail-closed: Docker 없으면 명확 안내(§7.5.3), 목업 대체 절대 X (§9).
|
|
@@ -38,6 +45,10 @@ export interface DevCommandOptions {
|
|
|
38
45
|
readonly noDocker?: boolean;
|
|
39
46
|
/** 프론트 watch 빌드(vite build --watch) 끄기(개별 debug). */
|
|
40
47
|
readonly noVite?: boolean;
|
|
48
|
+
/** 워커(work) 자동 기동 끄기(개별 debug · 결정 211). 기본 기동. */
|
|
49
|
+
readonly noWork?: boolean;
|
|
50
|
+
/** 실시간 허브(hub) 자동 기동 끄기(개별 debug · 결정 211). 기본 기동. */
|
|
51
|
+
readonly noHub?: boolean;
|
|
41
52
|
/** 통합 콘솔 타임스탬프 표시. */
|
|
42
53
|
readonly timestamp?: boolean;
|
|
43
54
|
/** 시그널(테스트 주입 · 기본 process). */
|
package/dist/commands/dev.js
CHANGED
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
* 1) Docker Compose — pg · redis · nats · mailpit · minio 자동 up -d
|
|
6
6
|
* 2) .gaon 워처 — tables.d.ts · routes.d.ts 재생성 (기존 M3)
|
|
7
7
|
* 3) serve 자식 — 실 웹 서버(Fastify) · 소스 변경 시 재시작
|
|
8
|
-
* 4)
|
|
9
|
-
* 5)
|
|
10
|
-
* 6)
|
|
8
|
+
* 4) work 자식 — 워커(잡·리스너·아웃박스·스케줄) · 소스 변경 시 재시작 (결정 211)
|
|
9
|
+
* 5) hub 자식 — 실시간 허브(프레즌스 권위·중계) (결정 211)
|
|
10
|
+
* 6) tsc / vue-tsc — --watch 모드 · 타입 에러 즉시 리포트
|
|
11
|
+
* 7) restart 워처 — apps/ · domain/ · packages/ 변경 → serve·work 재시작
|
|
12
|
+
* 8) 통합 콘솔 — [source] 태그·색상별 · JSON 모드 지원
|
|
13
|
+
*
|
|
14
|
+
* 운영 프로세스 3종(serve·work·hub · §7)을 dev 에서 모두 내장 기동한다
|
|
15
|
+
* (Rails-like all-in-one · 결정 211). 이전에는 serve 만 떠 잡·이벤트·아웃박스·
|
|
16
|
+
* 크론이 dev 에서 조용히 처리되지 않았다(work 부재) — work.ts·hub.ts 주석이
|
|
17
|
+
* 이미 "dev 가 내장 실행" 이라 명시했으나 실제로는 미배선이던 드리프트를 닫는다.
|
|
11
18
|
*
|
|
12
19
|
* The One Way: `gaon dev` 하나로 전부 · 옵션은 개별 debug 용만 노출.
|
|
13
20
|
* fail-closed: Docker 없으면 명확 안내(§7.5.3), 목업 대체 절대 X (§9).
|
|
@@ -33,6 +40,12 @@ import { ensureInfra, composeDown } from '../dev/docker.js';
|
|
|
33
40
|
import { startTscWatchers, killChild } from '../dev/tsc.js';
|
|
34
41
|
import { startRestartWatcher } from '../dev/watcher.js';
|
|
35
42
|
import { startFrontendBuild } from '../dev/frontend-build.js';
|
|
43
|
+
/**
|
|
44
|
+
* dev 워커의 기본 큐 동시성(결정 211). 운영 기본은 1(보수적)이나 dev 는
|
|
45
|
+
* 잡을 병렬 처리해 반복을 빠르게 하고 동시성 속성(분산 락 등)을 단일
|
|
46
|
+
* 워커에서도 관측하게 한다. 사용자가 GAON_WORKER_CONCURRENCY 를 주면 존중.
|
|
47
|
+
*/
|
|
48
|
+
const DEV_WORKER_CONCURRENCY = 4;
|
|
36
49
|
/**
|
|
37
50
|
* gaon 셀프 경로를 찾는다. 부모가 gaon 으로 실행됐다면 argv[1] 이
|
|
38
51
|
* gaonjs/dist/cli.js (또는 개발 시 packages/gaonjs/src/cli.ts). 자식
|
|
@@ -78,6 +91,26 @@ function spawnServe(args) {
|
|
|
78
91
|
child.on('exit', (code, signal) => args.onExit(code, signal));
|
|
79
92
|
return child;
|
|
80
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* 운영 프로세스(work·hub)를 하나 띄운다(결정 211). serve 와 동일하게 셀프 CLI
|
|
96
|
+
* 진입점을 재사용해 사용자 환경에서 강제 폴백 없이 같은 gaon 을 다시 부른다.
|
|
97
|
+
* stdout/stderr 을 통합 콘솔의 해당 소스 태그로 파이프한다.
|
|
98
|
+
*/
|
|
99
|
+
function spawnProcess(args) {
|
|
100
|
+
const { node, args: nodeArgs } = resolveSelfCliEntry();
|
|
101
|
+
const cmdArgs = [args.kind];
|
|
102
|
+
if (args.json)
|
|
103
|
+
cmdArgs.push('--json');
|
|
104
|
+
const child = spawn(node, [...nodeArgs, ...cmdArgs], {
|
|
105
|
+
cwd: args.cwd,
|
|
106
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
107
|
+
env: { ...process.env, ...args.extraEnv },
|
|
108
|
+
});
|
|
109
|
+
child.stdout?.on('data', (b) => args.console.pipe(args.kind, b, 'info'));
|
|
110
|
+
child.stderr?.on('data', (b) => args.console.pipe(args.kind, b, 'error'));
|
|
111
|
+
child.on('exit', (code, signal) => args.onExit(code, signal));
|
|
112
|
+
return child;
|
|
113
|
+
}
|
|
81
114
|
/**
|
|
82
115
|
* .gaon 재생성 파이프라인(M3)을 띄운다. 로그는 통합 콘솔로 흘리고,
|
|
83
116
|
* 이 모듈은 층분리를 위해 startDev 를 그대로 재사용한다.
|
|
@@ -211,6 +244,69 @@ export async function runDevCommand(opts = {}) {
|
|
|
211
244
|
});
|
|
212
245
|
};
|
|
213
246
|
serveChild = startServe();
|
|
247
|
+
// ── 4') work 자식(잡·리스너·아웃박스·스케줄 · 결정 211) ─────────────
|
|
248
|
+
// work 는 도메인(잡·리스너·스케줄)을 로드하므로 serve 와 같은 파일 변경에
|
|
249
|
+
// 재시작한다. NATS 부재(예: --no-docker + 외부 NATS 없음)면 연결 실패로
|
|
250
|
+
// 종료하는데, 이는 dev 를 죽이지 않고 안내만 남긴다(fail-soft).
|
|
251
|
+
let workChild;
|
|
252
|
+
const abnormalHint = (kind) => `${kind} 자식이 비정상 종료 — NATS 가 떴는지 확인하세요(compose 재사용 또는 --no-${kind} 로 끌 수 있음).`;
|
|
253
|
+
const startWork = () => {
|
|
254
|
+
consoleOut.log('work', '▶ work 자식 프로세스 spawn');
|
|
255
|
+
return spawnProcess({
|
|
256
|
+
kind: 'work',
|
|
257
|
+
cwd,
|
|
258
|
+
json,
|
|
259
|
+
console: consoleOut,
|
|
260
|
+
// dev 워커는 잡을 병렬 처리한다(기본 1 → DEV_WORKER_CONCURRENCY). 헤드오브라인
|
|
261
|
+
// 블로킹을 없애 개발 반복을 빠르게 하고, 분산 락(중첩 방지) 같은 동시성 속성이
|
|
262
|
+
// 단일 dev 워커에서도 관측되게 한다. 사용자가 GAON_WORKER_CONCURRENCY 를 이미
|
|
263
|
+
// 주면 그 값을 존중한다(override 하지 않음).
|
|
264
|
+
extraEnv: process.env.GAON_WORKER_CONCURRENCY == null || process.env.GAON_WORKER_CONCURRENCY === ''
|
|
265
|
+
? { GAON_WORKER_CONCURRENCY: String(DEV_WORKER_CONCURRENCY) }
|
|
266
|
+
: undefined,
|
|
267
|
+
onExit: (code, signal) => {
|
|
268
|
+
if (shuttingDown)
|
|
269
|
+
return;
|
|
270
|
+
if (restartInFlight)
|
|
271
|
+
return;
|
|
272
|
+
if (code === 0 || signal === 'SIGTERM' || signal === 'SIGINT')
|
|
273
|
+
return;
|
|
274
|
+
consoleOut.log('work', abnormalHint('work'), 'warn');
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
if (opts.noWork !== true) {
|
|
279
|
+
workChild = startWork();
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
consoleOut.log('work', '--no-work · 워커 자동 기동 비활성화');
|
|
283
|
+
}
|
|
284
|
+
// ── 4'') hub 자식(실시간 프레즌스 권위·중계 · 결정 211) ─────────────
|
|
285
|
+
// hub 는 사용자 도메인 코드를 로드하지 않는(인프라 라우팅) 프로세스라
|
|
286
|
+
// 파일 변경에 재시작하지 않는다 — 시작 시 한 번 띄우고 종료 때만 내린다.
|
|
287
|
+
let hubChild;
|
|
288
|
+
const startHub = () => {
|
|
289
|
+
consoleOut.log('hub', '▶ hub 자식 프로세스 spawn');
|
|
290
|
+
return spawnProcess({
|
|
291
|
+
kind: 'hub',
|
|
292
|
+
cwd,
|
|
293
|
+
json,
|
|
294
|
+
console: consoleOut,
|
|
295
|
+
onExit: (code, signal) => {
|
|
296
|
+
if (shuttingDown)
|
|
297
|
+
return;
|
|
298
|
+
if (code === 0 || signal === 'SIGTERM' || signal === 'SIGINT')
|
|
299
|
+
return;
|
|
300
|
+
consoleOut.log('hub', abnormalHint('hub'), 'warn');
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
if (opts.noHub !== true) {
|
|
305
|
+
hubChild = startHub();
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
consoleOut.log('hub', '--no-hub · 허브 자동 기동 비활성화');
|
|
309
|
+
}
|
|
214
310
|
const restartServe = async (changed) => {
|
|
215
311
|
if (shuttingDown)
|
|
216
312
|
return;
|
|
@@ -221,12 +317,20 @@ export async function runDevCommand(opts = {}) {
|
|
|
221
317
|
consoleOut.log('watcher', `↻ 재시작 — ${summary}`);
|
|
222
318
|
restartInFlight = (async () => {
|
|
223
319
|
try {
|
|
320
|
+
// serve·work 를 함께 재시작한다(둘 다 도메인/앱 코드를 로드).
|
|
321
|
+
const killers = [];
|
|
224
322
|
if (serveChild && serveChild.exitCode === null && serveChild.signalCode === null) {
|
|
225
|
-
|
|
323
|
+
killers.push(killChild(serveChild, 3000));
|
|
226
324
|
}
|
|
325
|
+
if (workChild && workChild.exitCode === null && workChild.signalCode === null) {
|
|
326
|
+
killers.push(killChild(workChild, 3000));
|
|
327
|
+
}
|
|
328
|
+
await Promise.all(killers);
|
|
227
329
|
if (shuttingDown)
|
|
228
330
|
return;
|
|
229
331
|
serveChild = startServe();
|
|
332
|
+
if (opts.noWork !== true)
|
|
333
|
+
workChild = startWork();
|
|
230
334
|
}
|
|
231
335
|
finally {
|
|
232
336
|
restartInFlight = undefined;
|
|
@@ -246,7 +350,8 @@ export async function runDevCommand(opts = {}) {
|
|
|
246
350
|
? `▶ 재시작 워치 — ${restartWatcher.roots.map((r) => shortenPath(r, cwd)).join(', ')}`
|
|
247
351
|
: `▶ 재시작 워치 — 감시 대상 없음(apps/ · domain/ 등 확인)`);
|
|
248
352
|
}
|
|
249
|
-
|
|
353
|
+
const procs = ['serve', opts.noWork !== true && 'work', opts.noHub !== true && 'hub'].filter(Boolean).join(' · ');
|
|
354
|
+
consoleOut.log('dev', `준비 완료 — 부팅 ${Date.now() - startedAt}ms · 프로세스 ${procs} (Ctrl+C 로 종료)`);
|
|
250
355
|
// ── 6) SIGINT/SIGTERM → graceful ────────────────────────────────
|
|
251
356
|
await new Promise((resolvePromise) => {
|
|
252
357
|
const stop = () => {
|
|
@@ -258,11 +363,15 @@ export async function runDevCommand(opts = {}) {
|
|
|
258
363
|
consoleOut.log('dev', '종료 신호 수신 — 정리 중...');
|
|
259
364
|
void (async () => {
|
|
260
365
|
try {
|
|
261
|
-
// 순서: 재시작 워처 → serve → tsc → .gaon 워처 → Docker(옵션)
|
|
366
|
+
// 순서: 재시작 워처 → serve·work·hub → tsc → .gaon 워처 → Docker(옵션)
|
|
262
367
|
restartWatcher?.close();
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
368
|
+
// serve·work·hub 는 서로 독립이라 병렬로 내린다. work 은 graceful
|
|
369
|
+
// drain(진행 중 잡 대기)에 시간이 더 걸릴 수 있어 상한을 넉넉히 준다.
|
|
370
|
+
await Promise.all([
|
|
371
|
+
serveChild ? killChild(serveChild, 5000) : Promise.resolve(),
|
|
372
|
+
workChild ? killChild(workChild, 8000) : Promise.resolve(),
|
|
373
|
+
hubChild ? killChild(hubChild, 5000) : Promise.resolve(),
|
|
374
|
+
]);
|
|
266
375
|
if (tsc) {
|
|
267
376
|
await tsc.stop();
|
|
268
377
|
}
|
package/dist/dev/console.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 정책상 chalk 는 쓰지 않는다 — 필요한 코드만 직접 쓴다.
|
|
14
14
|
*/
|
|
15
15
|
/** 콘솔이 구분하는 로그 소스. */
|
|
16
|
-
export type DevSource = 'dev' | 'docker' | 'serve' | 'watcher' | 'tsc' | 'vue-tsc' | 'vite';
|
|
16
|
+
export type DevSource = 'dev' | 'docker' | 'serve' | 'work' | 'hub' | 'watcher' | 'tsc' | 'vue-tsc' | 'vite';
|
|
17
17
|
/** 로그 레벨 — human 은 색상 강조, json 은 필드로 실린다. */
|
|
18
18
|
export type DevLevel = 'info' | 'warn' | 'error';
|
|
19
19
|
export interface DevConsoleOptions {
|
package/dist/dev/console.js
CHANGED
|
@@ -23,6 +23,8 @@ const SOURCE_COLOR = {
|
|
|
23
23
|
dev: '\x1b[35m', // magenta — 오케스트레이터
|
|
24
24
|
docker: '\x1b[34m', // blue — 인프라
|
|
25
25
|
serve: '\x1b[32m', // green — 웹 서버
|
|
26
|
+
work: '\x1b[93m', // bright yellow — 워커(잡·리스너·아웃박스·스케줄)
|
|
27
|
+
hub: '\x1b[94m', // bright blue — 실시간 허브
|
|
26
28
|
watcher: '\x1b[36m', // cyan — 파일 감시
|
|
27
29
|
tsc: '\x1b[33m', // yellow — 타입 검사
|
|
28
30
|
'vue-tsc': '\x1b[95m', // bright magenta — vue 전용
|
package/dist/index.js
CHANGED
|
@@ -96,9 +96,9 @@ function renderHelp(version = VERSION) {
|
|
|
96
96
|
" 사용법:",
|
|
97
97
|
" gaon 로드맵과 개발 상태를 출력",
|
|
98
98
|
" gaon new <name> 새 프로젝트 스캐폴드 (파일 → 설치 → git · --skip-install · --skip-git · --package-manager <pnpm|npm|yarn>)",
|
|
99
|
-
" gaon dev 개발 스택 통합 (Docker · .gaon · serve · tsc/vue-tsc · 재시작 워처)",
|
|
99
|
+
" gaon dev 개발 스택 통합 (Docker · .gaon · serve · work · hub · tsc/vue-tsc · 재시작 워처)",
|
|
100
100
|
" gaon dev --stop-docker Ctrl+C 시 Docker Compose 도 down",
|
|
101
|
-
" gaon dev --no-watch|--no-tsc|--no-vue-tsc|--no-docker 개별 debug 옵션",
|
|
101
|
+
" gaon dev --no-watch|--no-tsc|--no-vue-tsc|--no-docker|--no-work|--no-hub 개별 debug 옵션",
|
|
102
102
|
" gaon dev --port <n> --host <h> serve 리슨 지정",
|
|
103
103
|
" gaon dev --json 통합 콘솔을 JSON 라인으로 출력(자동화)",
|
|
104
104
|
" gaon serve 웹 서버 부팅 (gaon.config.ts 자동 배선 · Fastify listen)",
|
|
@@ -207,9 +207,10 @@ export function runCli(argv, opts = {}) {
|
|
|
207
207
|
// 설정된 env 를 덮지 않고 파일이 없으면 조용히 지나가 멱등하다(재호출 안전).
|
|
208
208
|
loadDotEnv();
|
|
209
209
|
// `gaon dev` — 통합 개발 오케스트레이션(M9-C · v0.15 §13.5). Docker Compose
|
|
210
|
-
// 자동 기동 + .gaon 재생성 + serve 자식
|
|
211
|
-
//
|
|
212
|
-
//
|
|
210
|
+
// 자동 기동 + .gaon 재생성 + serve·work·hub 자식(운영 3종 all-in-one · 결정 211)
|
|
211
|
+
// + tsc/vue-tsc watch + 서버 재시작 워처. SIGINT/SIGTERM 시 순서대로 정리
|
|
212
|
+
// (serve·work·hub → tsc → 워처 → Docker[--stop-docker 시]).
|
|
213
|
+
// 개별 debug 옵션은 --no-watch / --no-tsc / --no-vue-tsc / --no-docker / --no-work / --no-hub.
|
|
213
214
|
if (argv[0] === "dev") {
|
|
214
215
|
const portIdx = argv.indexOf("--port");
|
|
215
216
|
const hostIdx = argv.indexOf("--host");
|
|
@@ -222,6 +223,8 @@ export function runCli(argv, opts = {}) {
|
|
|
222
223
|
noTsc: argv.includes("--no-tsc"),
|
|
223
224
|
noVueTsc: argv.includes("--no-vue-tsc"),
|
|
224
225
|
noDocker: argv.includes("--no-docker"),
|
|
226
|
+
noWork: argv.includes("--no-work"),
|
|
227
|
+
noHub: argv.includes("--no-hub"),
|
|
225
228
|
timestamp: argv.includes("--timestamp"),
|
|
226
229
|
port,
|
|
227
230
|
host,
|
|
@@ -204,8 +204,8 @@ gaon doctor # 정적 검사 26종 (§2.2)
|
|
|
204
204
|
| 명령 | 역할 |
|
|
205
205
|
|---|---|
|
|
206
206
|
| `gaon new <name>` | 프로젝트 스캐폴드 |
|
|
207
|
-
| `gaon dev` | 통합 개발 오케스트레이션 (Docker·`.gaon`
|
|
208
|
-
| `gaon serve` / `work` / `hub` | 운영 프로세스 3종 (웹 · 워커 · 실시간 허브) — **감시 없음** · 웹은 `PORT`, 허브는 `GAON_HUB_PORT` |
|
|
207
|
+
| `gaon dev` | 통합 개발 오케스트레이션 (Docker·`.gaon` 재생성·**serve·work·hub 자동 기동**·**코드 변경 감시·재시작** · 결정 211) |
|
|
208
|
+
| `gaon serve` / `work` / `hub` | 운영 프로세스 3종 (웹 · 워커 · 실시간 허브) — **감시 없음** · 배포 배치용(`gaon dev` 가 개발 중엔 셋을 내장 기동) · 웹은 `PORT`, 허브는 `GAON_HUB_PORT` |
|
|
209
209
|
| `gaon g <type> <name>` | 스캐폴드: `auth`·`controller`·`model`·`page`·`job` |
|
|
210
210
|
| `gaon db <sub>` | `diff`·`migrate`(`down`)·`status`·`reset`·`seed` (`agents/data.md` §10) |
|
|
211
211
|
| `gaon check` / `test` / `doctor` | 검증 루프 |
|
|
@@ -244,8 +244,9 @@ export default schedule((s) => {
|
|
|
244
244
|
처리돼도 안전하게).
|
|
245
245
|
- **`gaon serve` 는 스케줄러를 돌리지 않는다** — 스케줄·리더 선출·아웃박스
|
|
246
246
|
릴레이는 **`gaon work` 전용**이다. 웹 프로세스는 잡을 **발행**만 할 수 있고
|
|
247
|
-
(`.later()`), 처리·스케줄은 워커가 한다. 스케줄이 안 도는 흔한
|
|
248
|
-
`gaon work` 를 안 띄운
|
|
247
|
+
(`.later()`), 처리·스케줄은 워커가 한다. **운영에서** 스케줄이 안 도는 흔한
|
|
248
|
+
원인은 `gaon work` 를 안 띄운 것이다(개발은 `gaon dev` 가 work 를 자동 기동
|
|
249
|
+
하므로 해당 없음 · 결정 211 · §6).
|
|
249
250
|
|
|
250
251
|
#### 시간대 (결정 202)
|
|
251
252
|
|
|
@@ -282,6 +283,30 @@ drain — 스케줄러 리더를 반납하고 진행 중인 잡을 완료한 뒤
|
|
|
282
283
|
- **컨테이너 기본 워커 1** — `gaon serve` 클러스터(`--workers`)도 SIGTERM 에
|
|
283
284
|
워커들을 graceful drain 후 종료한다(결정 84).
|
|
284
285
|
|
|
286
|
+
#### 개발 모드는 work·hub 를 자동 기동한다 (결정 211)
|
|
287
|
+
|
|
288
|
+
**`gaon dev` 는 운영 3종(serve·work·hub)을 모두 내장 기동한다.** 개발 중에는
|
|
289
|
+
`gaon work`·`gaon hub` 를 따로 띄울 필요가 없다 — 잡·이벤트·리스너·아웃박스
|
|
290
|
+
릴레이·스케줄·실시간이 `gaon dev` 하나로 전부 처리된다(Rails-like all-in-one).
|
|
291
|
+
`gaon work`·`gaon hub` **명령 자체는 운영 배치용**으로 남는다(컨테이너·프로세스
|
|
292
|
+
매니저에서 각각 스케일).
|
|
293
|
+
|
|
294
|
+
- **재시작** — 도메인(잡·리스너·스케줄) 파일을 저장하면 serve 와 함께 work 도
|
|
295
|
+
재시작된다. hub 는 사용자 도메인을 로드하지 않아(인프라 라우팅) 재시작하지 않는다.
|
|
296
|
+
- **끄기** — 개별 debug 시 `gaon dev --no-work` · `--no-hub`. 기본은 켬.
|
|
297
|
+
- **동시성** — dev 워커는 잡을 병렬 처리하도록 큐 기본 동시성을 4로 올린다
|
|
298
|
+
(`GAON_WORKER_CONCURRENCY` 로 덮음 · 운영 `gaon work` 기본은 1). 그래서 지연
|
|
299
|
+
잡이 서로 막지 않고, 분산 락 중첩 방지(§7·§5) 같은 동시성 거동을 dev 에서도
|
|
300
|
+
관측할 수 있다.
|
|
301
|
+
- **NATS 필요** — work·hub 는 NATS 에 붙는다. `gaon dev` 의 compose 가 NATS 를
|
|
302
|
+
자동 기동하므로 보통 문제없다. `--no-docker` 로 외부 NATS 없이 띄우면 work·hub
|
|
303
|
+
는 연결 실패로 조용히 종료하고 dev 는 계속된다(안내 로그 · `--no-*` 로 끌 수 있음).
|
|
304
|
+
|
|
305
|
+
> 이전에는 `gaon dev` 가 serve 만 띄워, 잡·이벤트·아웃박스·스케줄이 개발 중
|
|
306
|
+
> 조용히 처리되지 않는 함정이 있었다(work 미기동). 결정 211 이 이를 닫았다 —
|
|
307
|
+
> "스케줄이 안 도는 흔한 원인은 `gaon work` 를 안 띄운 것" 은 이제 **운영에만**
|
|
308
|
+
> 해당하고, 개발에선 `gaon dev` 가 알아서 띄운다.
|
|
309
|
+
|
|
285
310
|
### 7. 분산 락 (`lock()`) (결정 147)
|
|
286
311
|
|
|
287
312
|
**동시 실행을 막아야 하면 `lock(key, fn)`** — 같은 `key` 에 대해 전
|
|
@@ -377,4 +402,5 @@ async create() {
|
|
|
377
402
|
| 결정 200 | DLQ 재처리 `retryDlq(nats, id)` (이미 설정된 잡 전송 보존 · DLQ 재적재 후 원본 삭제) |
|
|
378
403
|
| 결정 201 | graceful drain 계약 (serve 인플라이트 요청 완결 · work 진행 잡 완결 · §6) |
|
|
379
404
|
| 결정 202 | 락·캐시 백엔드 serve·work 공통 배선(wireDomain) · 잡/서비스 lock() 가능 · 스케줄러 시간대(서버 로컬 TZ)·중첩 방지(§5·§7) |
|
|
405
|
+
| 결정 211 | `gaon dev` all-in-one — serve·work·hub 자동 기동 · dev 워커 동시성 4(`GAON_WORKER_CONCURRENCY`) · `--no-work`/`--no-hub` (§6) |
|
|
380
406
|
| §7 | 비동기 배터리 원문 (백오프 기본값 = M7 벤치마크 확정) |
|
package/dist/work.js
CHANGED
|
@@ -104,7 +104,10 @@ export async function runWorkCommand(opts = {}) {
|
|
|
104
104
|
id,
|
|
105
105
|
db,
|
|
106
106
|
schedule: domain.schedule,
|
|
107
|
-
|
|
107
|
+
// 큐 기본 동시성: 옵션 > GAON_WORKER_CONCURRENCY > 잡별 선언 > 1. 잡에
|
|
108
|
+
// 명시 concurrency 가 있으면 그게 우선(worker.ts perQueueConc). 운영은
|
|
109
|
+
// 이 env 로 워커 처리량을 조절하고, gaon dev 는 개발 편의로 기본 상향한다.
|
|
110
|
+
concurrency: opts.concurrency ?? envInt('GAON_WORKER_CONCURRENCY'),
|
|
108
111
|
ackWaitMs: opts.ackWaitMs ?? envInt('GAON_WORKER_ACK_WAIT_MS'),
|
|
109
112
|
drainTimeoutMs: opts.drainTimeoutMs ?? envInt('GAON_WORKER_DRAIN_MS'),
|
|
110
113
|
onEvent: emit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaonjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Gaon CLI — 스캐폴딩·제너레이터·마이그레이션·dev/serve/work/hub·doctor·check (bin: gaon)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"typescript": "^5.9.0",
|
|
29
29
|
"vite": "^7.0.0",
|
|
30
30
|
"@gaonjs/async": "0.12.0",
|
|
31
|
+
"@gaonjs/data": "0.16.1",
|
|
31
32
|
"@gaonjs/config": "0.15.2",
|
|
32
33
|
"@gaonjs/core": "0.2.2",
|
|
33
|
-
"@gaonjs/mail": "0.2.1",
|
|
34
34
|
"@gaonjs/i18n": "0.2.1",
|
|
35
|
-
"@gaonjs/
|
|
36
|
-
"@gaonjs/
|
|
35
|
+
"@gaonjs/web": "0.18.1",
|
|
36
|
+
"@gaonjs/mail": "0.2.1"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"require('fs').cpSync('src/templates','dist/templates',{recursive:true})\""
|