@gaonjs/cli 0.1.5 → 0.1.6
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/hub.d.ts +22 -0
- package/dist/hub.js +82 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +13 -0
- package/package.json +4 -3
package/dist/hub.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface HubCommandOptions {
|
|
2
|
+
readonly json?: boolean;
|
|
3
|
+
/** NATS 접속지. 생략 시 GAON_NATS_URL, 그다음 기본(4222). */
|
|
4
|
+
readonly natsUrl?: string;
|
|
5
|
+
/** 리스 TTL(ms). 생략 시 env GAON_HUB_TTL_MS, 그다음 허브 기본(5000). */
|
|
6
|
+
readonly ttlMs?: number;
|
|
7
|
+
/** 멤버 만료 임계(ms). 생략 시 env GAON_HUB_MEMBER_TIMEOUT_MS. */
|
|
8
|
+
readonly memberTimeoutMs?: number;
|
|
9
|
+
/** 만료 스윕 주기(ms). 생략 시 env GAON_HUB_SWEEP_MS. */
|
|
10
|
+
readonly sweepMs?: number;
|
|
11
|
+
/** 시그널 등록·해제(테스트 주입). 기본 process. */
|
|
12
|
+
readonly signals?: {
|
|
13
|
+
on(sig: 'SIGINT' | 'SIGTERM', fn: () => void): void;
|
|
14
|
+
off(sig: 'SIGINT' | 'SIGTERM', fn: () => void): void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 허브를 띄우고 시그널까지 살아 있게 한다. 반환 프라미스는 종료 시 resolve.
|
|
19
|
+
* 그레이스풀 종료 시 리더 키를 비워(즉시 승계) 대기 인스턴스가 곧바로
|
|
20
|
+
* 승격하게 한다.
|
|
21
|
+
*/
|
|
22
|
+
export declare function runHubCommand(opts?: HubCommandOptions): Promise<void>;
|
package/dist/hub.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @gaonjs/cli · `gaon hub` — 실시간 허브 프로세스 (§7 · M6)
|
|
3
|
+
*
|
|
4
|
+
* 운영 프로세스 3종(serve · work · hub) 중 하나. 접속자 목록의 단일
|
|
5
|
+
* 권위이자 중계다(§7 실시간). NATS 에 붙어 프레즌스 명령을 처리하고
|
|
6
|
+
* KV 에 영속하며, HA 는 리스 기반 리더 선출로 잡는다(active-standby) —
|
|
7
|
+
* 여러 인스턴스를 띄워도 리더 하나만 명령을 처리한다.
|
|
8
|
+
*
|
|
9
|
+
* 개발 모드는 `gaon dev` 가 허브를 같은 프로세스에 내장 실행하므로,
|
|
10
|
+
* 이 명령은 운영 배치용이다. 모든 명령과 같이 `--json` 을 제공한다.
|
|
11
|
+
*/
|
|
12
|
+
import { hostname } from 'node:os';
|
|
13
|
+
import { connectNats, runHub } from '@gaonjs/async';
|
|
14
|
+
/** 환경변수를 정수로 파싱(미설정·비정상 시 undefined). */
|
|
15
|
+
function envInt(name) {
|
|
16
|
+
const raw = process.env[name];
|
|
17
|
+
if (raw == null || raw === '')
|
|
18
|
+
return undefined;
|
|
19
|
+
const n = Number(raw);
|
|
20
|
+
return Number.isFinite(n) ? n : undefined;
|
|
21
|
+
}
|
|
22
|
+
function humanEvent(e) {
|
|
23
|
+
switch (e.kind) {
|
|
24
|
+
case 'starting':
|
|
25
|
+
return ` gaon hub · 시작 — ${e.id} (NATS 연결·리더 선출 대기)`;
|
|
26
|
+
case 'leader':
|
|
27
|
+
return e.leader
|
|
28
|
+
? ` ▶ 리더로 승격 — 프레즌스 명령 처리·KV 권위·델타 중계 시작 (${e.id})`
|
|
29
|
+
: ` · 대기(standby) — 다른 인스턴스가 리더 (${e.id})`;
|
|
30
|
+
case 'stopped':
|
|
31
|
+
return ' gaon hub · 종료';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 허브를 띄우고 시그널까지 살아 있게 한다. 반환 프라미스는 종료 시 resolve.
|
|
36
|
+
* 그레이스풀 종료 시 리더 키를 비워(즉시 승계) 대기 인스턴스가 곧바로
|
|
37
|
+
* 승격하게 한다.
|
|
38
|
+
*/
|
|
39
|
+
export async function runHubCommand(opts = {}) {
|
|
40
|
+
const json = opts.json ?? false;
|
|
41
|
+
const signals = opts.signals ?? process;
|
|
42
|
+
const id = `${hostname()}#${process.pid}`;
|
|
43
|
+
const emit = (e) => {
|
|
44
|
+
if (json)
|
|
45
|
+
process.stdout.write(JSON.stringify(e) + '\n');
|
|
46
|
+
else
|
|
47
|
+
process.stdout.write(humanEvent(e) + '\n');
|
|
48
|
+
};
|
|
49
|
+
emit({ kind: 'starting', id });
|
|
50
|
+
const nats = await connectNats({ servers: opts.natsUrl, name: `hub@${id}` });
|
|
51
|
+
let hub;
|
|
52
|
+
try {
|
|
53
|
+
hub = await runHub({
|
|
54
|
+
nats,
|
|
55
|
+
id,
|
|
56
|
+
// 운영 튜닝: 옵션 > 환경변수 > 허브 기본. 컨테이너 배치에서 헬스체크
|
|
57
|
+
// 주기에 맞춰 리스 TTL·만료 임계를 조정한다.
|
|
58
|
+
ttlMs: opts.ttlMs ?? envInt('GAON_HUB_TTL_MS'),
|
|
59
|
+
memberTimeoutMs: opts.memberTimeoutMs ?? envInt('GAON_HUB_MEMBER_TIMEOUT_MS'),
|
|
60
|
+
sweepMs: opts.sweepMs ?? envInt('GAON_HUB_SWEEP_MS'),
|
|
61
|
+
onState: (s) => emit({ kind: 'leader', leader: s.leader, id }),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
await nats.close();
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
await new Promise((resolve) => {
|
|
69
|
+
const stop = () => {
|
|
70
|
+
signals.off('SIGINT', stop);
|
|
71
|
+
signals.off('SIGTERM', stop);
|
|
72
|
+
void (async () => {
|
|
73
|
+
await hub.stop();
|
|
74
|
+
await nats.close();
|
|
75
|
+
emit({ kind: 'stopped' });
|
|
76
|
+
resolve();
|
|
77
|
+
})();
|
|
78
|
+
};
|
|
79
|
+
signals.on('SIGINT', stop);
|
|
80
|
+
signals.on('SIGTERM', stop);
|
|
81
|
+
});
|
|
82
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { runDevCommand, startDev, resolveDevLayout, type DevDeps, type DevLayout, type DevApp, type DevEvent, type DevHandle, type DevCommandOptions, } from "./dev.js";
|
|
2
2
|
export { runCheck, runCheckCommand, type CheckDeps, type CheckResult, type TypecheckResult, type CheckCommandOptions, } from "./check.js";
|
|
3
3
|
export { writeAuthScaffold, authScaffoldFiles, patchRoutes, runGenerateAuthCommand, type AuthScaffoldOptions, type ScaffoldFile, type ScaffoldResult, type GenerateAuthOptions, } from "./generate.js";
|
|
4
|
+
export { runHubCommand, type HubCommandOptions } from "./hub.js";
|
|
4
5
|
export interface RoadmapReport {
|
|
5
6
|
readonly name: "gaon";
|
|
6
7
|
readonly version: string;
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,11 @@ import { MILESTONES, VERSION, HOMEPAGE } from "@gaonjs/core";
|
|
|
13
13
|
import { runDevCommand } from "./dev.js";
|
|
14
14
|
import { runCheckCommand } from "./check.js";
|
|
15
15
|
import { runGenerateAuthCommand } from "./generate.js";
|
|
16
|
+
import { runHubCommand } from "./hub.js";
|
|
16
17
|
export { runDevCommand, startDev, resolveDevLayout, } from "./dev.js";
|
|
17
18
|
export { runCheck, runCheckCommand, } from "./check.js";
|
|
18
19
|
export { writeAuthScaffold, authScaffoldFiles, patchRoutes, runGenerateAuthCommand, } from "./generate.js";
|
|
20
|
+
export { runHubCommand } from "./hub.js";
|
|
19
21
|
/** `--json` 출력용 구조화 리포트. */
|
|
20
22
|
export function roadmapReport(version = VERSION) {
|
|
21
23
|
return {
|
|
@@ -63,6 +65,7 @@ function renderHelp(version = VERSION) {
|
|
|
63
65
|
" gaon dev --json 재생성 이벤트를 JSON 으로 출력",
|
|
64
66
|
" gaon check .gaon 재생성 후 타입 검사 (CI·AI 정합)",
|
|
65
67
|
" gaon g auth 인증 스캐폴드 생성 (회원가입·로그인·세션·보호 라우트)",
|
|
68
|
+
" gaon hub 실시간 허브 프로세스 (프레즌스 권위·중계 · 리더 선출 HA)",
|
|
66
69
|
" gaon --json 같은 정보를 JSON 으로 출력",
|
|
67
70
|
" gaon --version 버전 출력",
|
|
68
71
|
" gaon --help 이 도움말",
|
|
@@ -97,6 +100,16 @@ export function runCli(argv, opts = {}) {
|
|
|
97
100
|
});
|
|
98
101
|
return;
|
|
99
102
|
}
|
|
103
|
+
// `gaon hub` — 실시간 허브 프로세스(§7 M6). 운영 프로세스 3종 중 하나.
|
|
104
|
+
// NATS·리더 선출 대기로 프로세스를 살려 두고, SIGINT/SIGTERM 에 그레이스풀 종료.
|
|
105
|
+
if (argv[0] === "hub") {
|
|
106
|
+
void runHubCommand({ json: argv.includes("--json") }).catch((err) => {
|
|
107
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
108
|
+
process.stderr.write(` ✗ gaon hub 실패: ${msg}\n`);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
100
113
|
// `gaon g auth` — 인증 스캐폴드 제너레이터(§7 M5). 다른 제너레이터는 후속.
|
|
101
114
|
if (argv[0] === "g" || argv[0] === "generate") {
|
|
102
115
|
if (argv[1] === "auth") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaonjs/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Gaon CLI 구현: 제너레이터·스캐폴딩·로드맵 출력 (M1 스텁)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@gaonjs/async": "0.2.0",
|
|
27
28
|
"@gaonjs/core": "0.1.3",
|
|
28
|
-
"@gaonjs/
|
|
29
|
-
"@gaonjs/
|
|
29
|
+
"@gaonjs/web": "0.2.1",
|
|
30
|
+
"@gaonjs/data": "0.2.2"
|
|
30
31
|
},
|
|
31
32
|
"scripts": {
|
|
32
33
|
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"require('fs').cpSync('src/templates','dist/templates',{recursive:true})\""
|