mega-framework 0.1.6 → 0.1.7
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/bin/mega-ws-hub.js +2 -2
- package/package.json +32 -8
- package/sample/crud/.env +1 -1
- package/sample/crud/.env.example +1 -1
- package/sample/crud/.mega/journal/history/20260612092543-create-users.json +261 -0
- package/sample/crud/.mega/journal/snapshot.json +261 -0
- package/sample/crud/apps/main/controllers/auth-controller.js +22 -14
- package/sample/crud/apps/main/controllers/web-controller.js +7 -5
- package/sample/crud/apps/main/migrations/20260606000001-create-users.js +91 -13
- package/sample/crud/apps/main/migrations/20260606000002-create-boards.js +165 -0
- package/sample/crud/apps/main/migrations/20260606000003-create-logs.js +107 -0
- package/sample/crud/apps/main/models/log-partition-model.js +105 -0
- package/sample/crud/apps/main/models/note-model.js +79 -0
- package/sample/crud/apps/main/models/user-level-model.js +24 -0
- package/sample/crud/apps/main/models/user-model.js +146 -0
- package/sample/crud/apps/main/models/user-type-model.js +21 -0
- package/sample/crud/apps/main/models/wallet-model.js +24 -0
- package/sample/crud/apps/main/routes/users.js +55 -10
- package/sample/crud/apps/main/schedules/log-partition-schedule.js +33 -0
- package/sample/crud/apps/main/services/auth-service.js +39 -24
- package/sample/crud/apps/main/services/log-partition-service.js +101 -0
- package/sample/crud/apps/main/services/note-service.js +6 -6
- package/sample/crud/apps/main/services/redis-demo-service.js +3 -3
- package/sample/crud/apps/main/services/user-service.js +62 -21
- package/sample/crud/apps/main/views/auth/login.ejs +6 -6
- package/sample/crud/apps/main/views/auth/register.ejs +46 -5
- package/sample/crud/apps/main/views/users/edit.ejs +42 -5
- package/sample/crud/apps/main/views/users/list.ejs +6 -2
- package/sample/crud/apps/main/views/users/new.ejs +56 -4
- package/sample/crud/docs/log_partition_design.mm.md +23 -0
- package/sample/crud/mega.config.js +3 -2
- package/sample/crud/package.json +1 -1
- package/sample/crud/scripts/start-ws-hub.sh +2 -2
- package/sample/simple/package.json +2 -2
- package/src/adapters/adapter-manager.js +2 -1
- package/src/adapters/adapter-options.js +30 -0
- package/src/adapters/maria-adapter.js +26 -3
- package/src/adapters/mega-db-adapter.js +7 -1
- package/src/adapters/mongo-adapter.js +19 -1
- package/src/adapters/postgres-adapter.js +25 -2
- package/src/adapters/sqlite-adapter.js +20 -1
- package/src/cli/commands/new.js +13 -3
- package/src/cli/commands/scaffold.js +137 -33
- package/src/cli/generators/index.js +82 -2
- package/src/cli/index.js +353 -100
- package/src/core/ajv-mapper.js +27 -2
- package/src/core/boot.js +464 -245
- package/src/core/cluster-metrics.js +13 -4
- package/src/core/ctx-builder.js +6 -2
- package/src/core/envelope.js +112 -12
- package/src/core/hub-link.js +65 -4
- package/src/core/i18n.js +11 -1
- package/src/core/index.js +6 -2
- package/src/core/mega-app.js +201 -463
- package/src/core/mega-cluster.js +4 -1
- package/src/core/mega-server.js +40 -9
- package/src/core/migration/dialect-registry.js +107 -0
- package/src/core/migration/dialects/README.md +62 -0
- package/src/core/migration/dialects/maria.js +496 -0
- package/src/core/migration/dialects/mongo.js +824 -0
- package/src/core/migration/dialects/postgres.js +563 -0
- package/src/core/migration/dialects/sqlite.js +476 -0
- package/src/core/migration/differ.js +456 -0
- package/src/core/migration/generate.js +508 -0
- package/src/core/migration/journal.js +167 -0
- package/src/core/migration/model-scan.js +84 -0
- package/src/core/migration/mongo-migration-db.js +97 -0
- package/src/core/migration/schema-builder.js +400 -0
- package/src/core/migration/schema-validator.js +315 -0
- package/src/core/migration-lock.js +205 -0
- package/src/core/migration-runner.js +166 -38
- package/src/core/multipart.js +28 -5
- package/src/core/pipeline.js +129 -0
- package/src/core/router.js +70 -65
- package/src/core/security.js +67 -9
- package/src/core/workers-manager.js +12 -1
- package/src/core/ws-cluster.js +10 -3
- package/src/core/ws-message.js +48 -4
- package/src/core/ws-presence.js +624 -0
- package/src/core/ws-roster.js +4 -1
- package/src/core/ws-upgrade.js +118 -12
- package/src/index.js +1 -1
- package/src/lib/hub-protocol.js +29 -0
- package/src/lib/mega-health.js +25 -4
- package/src/lib/mega-job-queue.js +98 -21
- package/src/lib/mega-job.js +29 -0
- package/src/lib/mega-metrics.js +3 -12
- package/src/lib/mega-plugin.js +34 -3
- package/src/lib/mega-schedule.js +40 -22
- package/src/lib/mega-shutdown.js +114 -39
- package/src/lib/mega-tracing.js +66 -19
- package/src/lib/mega-worker.js +5 -1
- package/src/lib/otel-resource.js +36 -0
- package/src/{cli → lib}/ws-hub.js +51 -8
- package/src/models/crud-sql-builder.js +133 -0
- package/src/models/mega-model.js +82 -2
- package/src/models/model-crud.js +483 -0
- package/src/models/mongo-crud.js +285 -0
- package/templates/model/code-mongo.tpl +35 -0
- package/templates/model/code.tpl +15 -1
- package/templates/model/test-mongo.tpl +38 -0
- package/templates/model/test.tpl +4 -0
- package/types/adapters/adapter-manager.d.ts +95 -0
- package/types/adapters/adapter-options.d.ts +91 -0
- package/types/adapters/file-adapter.d.ts +94 -0
- package/types/adapters/file-session-adapter.d.ts +101 -0
- package/types/adapters/index.d.ts +20 -0
- package/types/adapters/maria-adapter.d.ts +115 -0
- package/types/adapters/mega-adapter.d.ts +215 -0
- package/types/adapters/mega-bus-adapter.d.ts +45 -0
- package/types/adapters/mega-cache-adapter.d.ts +47 -0
- package/types/adapters/mega-db-adapter.d.ts +47 -0
- package/types/adapters/mega-lock-adapter.d.ts +62 -0
- package/types/adapters/mega-log-sink-adapter.d.ts +15 -0
- package/types/adapters/mega-session-adapter.d.ts +32 -0
- package/types/adapters/mongo-adapter.d.ts +139 -0
- package/types/adapters/nats-adapter.d.ts +108 -0
- package/types/adapters/postgres-adapter.d.ts +139 -0
- package/types/adapters/redis-adapter.d.ts +70 -0
- package/types/adapters/redis-session-adapter.d.ts +82 -0
- package/types/adapters/redlock-adapter.d.ts +149 -0
- package/types/adapters/registry.d.ts +46 -0
- package/types/adapters/sqlite-adapter.d.ts +106 -0
- package/types/auth/index.d.ts +24 -0
- package/types/cli/commands/console-cmd.d.ts +37 -0
- package/types/cli/commands/new.d.ts +16 -0
- package/types/cli/commands/routes.d.ts +36 -0
- package/types/cli/commands/scaffold.d.ts +78 -0
- package/types/cli/commands/test-cmd.d.ts +14 -0
- package/types/cli/generators/index.d.ts +112 -0
- package/types/cli/index.d.ts +249 -0
- package/types/cli/template-engine.d.ts +40 -0
- package/types/core/ajv-mapper.d.ts +27 -0
- package/types/core/boot.d.ts +233 -0
- package/types/core/cluster-metrics.d.ts +52 -0
- package/types/core/config-loader.d.ts +13 -0
- package/types/core/config-validator.d.ts +30 -0
- package/types/core/ctx-builder.d.ts +80 -0
- package/types/core/envelope.d.ts +79 -0
- package/types/core/error-mapper.d.ts +17 -0
- package/types/core/formbody.d.ts +41 -0
- package/types/core/hub-link.d.ts +264 -0
- package/types/core/i18n.d.ts +178 -0
- package/types/core/index.d.ts +28 -0
- package/types/core/mega-app.d.ts +529 -0
- package/types/core/mega-cluster.d.ts +104 -0
- package/types/core/mega-server.d.ts +91 -0
- package/types/core/mega-service.d.ts +31 -0
- package/types/core/migration/dialect-registry.d.ts +22 -0
- package/types/core/migration/dialects/maria.d.ts +99 -0
- package/types/core/migration/dialects/mongo.d.ts +89 -0
- package/types/core/migration/dialects/postgres.d.ts +117 -0
- package/types/core/migration/dialects/sqlite.d.ts +111 -0
- package/types/core/migration/differ.d.ts +47 -0
- package/types/core/migration/generate.d.ts +56 -0
- package/types/core/migration/journal.d.ts +52 -0
- package/types/core/migration/model-scan.d.ts +19 -0
- package/types/core/migration/mongo-migration-db.d.ts +7 -0
- package/types/core/migration/schema-builder.d.ts +197 -0
- package/types/core/migration/schema-validator.d.ts +20 -0
- package/types/core/migration-lock.d.ts +33 -0
- package/types/core/migration-runner.d.ts +101 -0
- package/types/core/multipart.d.ts +86 -0
- package/types/core/openapi.d.ts +62 -0
- package/types/core/pipeline.d.ts +92 -0
- package/types/core/router.d.ts +159 -0
- package/types/core/routes-loader.d.ts +21 -0
- package/types/core/scope-registry.d.ts +14 -0
- package/types/core/security.d.ts +77 -0
- package/types/core/services-loader.d.ts +27 -0
- package/types/core/session-cleanup-schedule.d.ts +19 -0
- package/types/core/session-store.d.ts +18 -0
- package/types/core/session.d.ts +77 -0
- package/types/core/static-assets.d.ts +73 -0
- package/types/core/template.d.ts +106 -0
- package/types/core/workers-manager.d.ts +79 -0
- package/types/core/ws-cluster.d.ts +208 -0
- package/types/core/ws-compression.d.ts +112 -0
- package/types/core/ws-controller.d.ts +65 -0
- package/types/core/ws-message.d.ts +106 -0
- package/types/core/ws-presence.d.ts +273 -0
- package/types/core/ws-roster.d.ts +96 -0
- package/types/core/ws-upgrade.d.ts +231 -0
- package/types/errors/config-error.d.ts +10 -0
- package/types/errors/http-errors.d.ts +120 -0
- package/types/errors/index.d.ts +3 -0
- package/types/errors/mega-error.d.ts +32 -0
- package/types/index.d.ts +39 -0
- package/types/lib/asp/config.d.ts +49 -0
- package/types/lib/asp/crypto.d.ts +43 -0
- package/types/lib/asp/errors.d.ts +30 -0
- package/types/lib/asp/nonce-cache.d.ts +52 -0
- package/types/lib/asp/plugin.d.ts +30 -0
- package/types/lib/asp/ws-terminator.d.ts +45 -0
- package/types/lib/env-mapper.d.ts +14 -0
- package/types/lib/hub-protocol.d.ts +106 -0
- package/types/lib/index.d.ts +22 -0
- package/types/lib/logger/telegram-core.d.ts +104 -0
- package/types/lib/logger/telegram-transport.d.ts +45 -0
- package/types/lib/mega-brute-force.d.ts +66 -0
- package/types/lib/mega-circuit-breaker.d.ts +241 -0
- package/types/lib/mega-cron.d.ts +66 -0
- package/types/lib/mega-hash.d.ts +32 -0
- package/types/lib/mega-health.d.ts +41 -0
- package/types/lib/mega-job-queue.d.ts +176 -0
- package/types/lib/mega-job-worker.d.ts +130 -0
- package/types/lib/mega-job.d.ts +138 -0
- package/types/lib/mega-logger.d.ts +45 -0
- package/types/lib/mega-metrics.d.ts +285 -0
- package/types/lib/mega-plugin.d.ts +245 -0
- package/types/lib/mega-retry.d.ts +85 -0
- package/types/lib/mega-schedule.d.ts +260 -0
- package/types/lib/mega-shutdown.d.ts +135 -0
- package/types/lib/mega-tracing.d.ts +224 -0
- package/types/lib/mega-worker.d.ts +127 -0
- package/types/lib/otel-resource.d.ts +16 -0
- package/types/lib/worker-runner/process-entry.d.ts +1 -0
- package/types/lib/worker-runner/task-dispatch.d.ts +28 -0
- package/types/lib/worker-runner/thread-entry.d.ts +1 -0
- package/types/lib/ws-hub.d.ts +234 -0
- package/types/models/crud-sql-builder.d.ts +48 -0
- package/types/models/index.d.ts +1 -0
- package/types/models/mega-model.d.ts +138 -0
- package/types/models/model-crud.d.ts +82 -0
- package/types/models/mongo-crud.d.ts +59 -0
- package/types/test/index.d.ts +84 -0
- package/.env +0 -127
- package/sample/crud/apps/main/migrations/20260606000002-add-auth-to-users.js +0 -30
- package/sample/crud/apps/main/models/note.js +0 -71
- package/sample/crud/apps/main/models/user.js +0 -86
- package/sample/crud/package-lock.json +0 -5665
- package/sample/crud/yarn.lock +0 -2142
- package/sample/simple/package-lock.json +0 -1851
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} RedisSessionConfig
|
|
3
|
+
* @property {string} [driver] - 'redis' (팩토리가 사용 — 어댑터는 무시).
|
|
4
|
+
* @property {string} [url] - `redis://[:pw@]host:port[/db]` (discrete 와 배타).
|
|
5
|
+
* @property {string} [connectionString] - `url` 의 deprecated 별칭.
|
|
6
|
+
* @property {string} [host] @property {number} [port] @property {string} [user] @property {string} [password]
|
|
7
|
+
* @property {number} [db] - 논리 DB 번호 0~15.
|
|
8
|
+
* @property {string} [keyPrefix] - 세션 키 prefix(기본 'mega:sess:').
|
|
9
|
+
* @property {number} [ttlMs] - save() 가 적용할 기본 TTL(ms). 미지정 시 24시간.
|
|
10
|
+
* @property {Record<string, any>} [options] - ioredis passthrough.
|
|
11
|
+
*/
|
|
12
|
+
export class MegaRedisSessionAdapter extends MegaSessionAdapter {
|
|
13
|
+
/**
|
|
14
|
+
* @param {RedisSessionConfig} [config]
|
|
15
|
+
* @throws {MegaValidationError} `adapter.connection_required` / `adapter.connection_conflict` / `session.invalid_option`
|
|
16
|
+
*/
|
|
17
|
+
constructor(config?: RedisSessionConfig);
|
|
18
|
+
/**
|
|
19
|
+
* raw ioredis handle (ADR-009).
|
|
20
|
+
* @protected
|
|
21
|
+
* @returns {import('ioredis').Redis}
|
|
22
|
+
*/
|
|
23
|
+
protected _native(): import("ioredis").Redis;
|
|
24
|
+
/**
|
|
25
|
+
* 헬스 체크 — 실제 ping. 비밀번호/url 은 노출하지 않는다.
|
|
26
|
+
* @returns {Promise<{ ok: boolean, driver: 'redis', state: string, db?: number, error?: string }>}
|
|
27
|
+
*/
|
|
28
|
+
healthCheck(): Promise<{
|
|
29
|
+
ok: boolean;
|
|
30
|
+
driver: "redis";
|
|
31
|
+
state: string;
|
|
32
|
+
db?: number;
|
|
33
|
+
error?: string;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* 누적 통계 + redis 세션 특화.
|
|
37
|
+
* @returns {ReturnType<import('./mega-adapter.js').MegaAdapter['getStats']> & { driver: string, db: number | undefined, keyPrefix: string, ttlMs: number, status: string | undefined }}
|
|
38
|
+
*/
|
|
39
|
+
getStats(): ReturnType<import("./mega-adapter.js").MegaAdapter["getStats"]> & {
|
|
40
|
+
driver: string;
|
|
41
|
+
db: number | undefined;
|
|
42
|
+
keyPrefix: string;
|
|
43
|
+
ttlMs: number;
|
|
44
|
+
status: string | undefined;
|
|
45
|
+
};
|
|
46
|
+
#private;
|
|
47
|
+
}
|
|
48
|
+
export type RedisSessionConfig = {
|
|
49
|
+
/**
|
|
50
|
+
* - 'redis' (팩토리가 사용 — 어댑터는 무시).
|
|
51
|
+
*/
|
|
52
|
+
driver?: string;
|
|
53
|
+
/**
|
|
54
|
+
* - `redis://[:pw@]host:port[/db]` (discrete 와 배타).
|
|
55
|
+
*/
|
|
56
|
+
url?: string;
|
|
57
|
+
/**
|
|
58
|
+
* - `url` 의 deprecated 별칭.
|
|
59
|
+
*/
|
|
60
|
+
connectionString?: string;
|
|
61
|
+
host?: string;
|
|
62
|
+
port?: number;
|
|
63
|
+
user?: string;
|
|
64
|
+
password?: string;
|
|
65
|
+
/**
|
|
66
|
+
* - 논리 DB 번호 0~15.
|
|
67
|
+
*/
|
|
68
|
+
db?: number;
|
|
69
|
+
/**
|
|
70
|
+
* - 세션 키 prefix(기본 'mega:sess:').
|
|
71
|
+
*/
|
|
72
|
+
keyPrefix?: string;
|
|
73
|
+
/**
|
|
74
|
+
* - save() 가 적용할 기본 TTL(ms). 미지정 시 24시간.
|
|
75
|
+
*/
|
|
76
|
+
ttlMs?: number;
|
|
77
|
+
/**
|
|
78
|
+
* - ioredis passthrough.
|
|
79
|
+
*/
|
|
80
|
+
options?: Record<string, any>;
|
|
81
|
+
};
|
|
82
|
+
import { MegaSessionAdapter } from './mega-session-adapter.js';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} RedlockConfig
|
|
3
|
+
* @property {string} [driver] - 'redlock' (매니저가 사용 — 어댑터는 무시).
|
|
4
|
+
* @property {string} redis - 참조할 Redis 캐시 어댑터 key (services.caches.<key>). 필수.
|
|
5
|
+
* @property {Partial<RedlockSettings>} [options] - redlock Settings passthrough(driftFactor/retryCount/retryDelay/retryJitter/automaticExtensionThreshold).
|
|
6
|
+
*/
|
|
7
|
+
export class MegaRedlockAdapter extends MegaLockAdapter {
|
|
8
|
+
/**
|
|
9
|
+
* redlock Settings 검증·정규화. options 없으면 빈 객체(=redlock 디폴트 사용). 알 수 없는 키는 오타로
|
|
10
|
+
* 보고 거부, 숫자 4종은 음 아닌 정수, driftFactor 는 [0,1) 유한수.
|
|
11
|
+
* @param {unknown} options @returns {Partial<RedlockSettings>}
|
|
12
|
+
*/
|
|
13
|
+
static "__#private@#normalizeSettings"(options: unknown): Partial<RedlockSettings>;
|
|
14
|
+
/**
|
|
15
|
+
* @param {RedlockConfig} [config] - services.locks.<key> 설정.
|
|
16
|
+
* @throws {MegaValidationError} `lock.redis_required` - `redis` 키 누락/비문자열.
|
|
17
|
+
* @throws {MegaValidationError} `adapter.invalid_option` - options 타입/알 수 없는 settings 키/음수.
|
|
18
|
+
*/
|
|
19
|
+
constructor(config?: RedlockConfig);
|
|
20
|
+
/**
|
|
21
|
+
* raw redlock handle (ADR-009). 베이스 `native` getter 가 connected 상태에서만 호출한다.
|
|
22
|
+
* @protected
|
|
23
|
+
* @returns {import('redlock').default}
|
|
24
|
+
*/
|
|
25
|
+
protected _native(): import("redlock").default;
|
|
26
|
+
/**
|
|
27
|
+
* 헬스 체크 — 락 가용성은 곧 참조 Redis 의 가용성이라 그 어댑터의 healthCheck 에 위임한다.
|
|
28
|
+
* 실패는 throw 없이 `ok:false` + 사유(베이스 계약).
|
|
29
|
+
* @returns {Promise<{ ok: boolean, driver: 'redlock', state: string, redis: string, redisOk?: boolean, error?: string }>}
|
|
30
|
+
*/
|
|
31
|
+
healthCheck(): Promise<{
|
|
32
|
+
ok: boolean;
|
|
33
|
+
driver: "redlock";
|
|
34
|
+
state: string;
|
|
35
|
+
redis: string;
|
|
36
|
+
redisOk?: boolean;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* 누적 통계 + redlock 특화(driver/redis 키/settings). settings 는 숫자 계수뿐이라 시크릿 없음.
|
|
41
|
+
* @returns {ReturnType<import('./mega-adapter.js').MegaAdapter['getStats']> & { driver: string, redis: string, settings: Partial<RedlockSettings> }}
|
|
42
|
+
*/
|
|
43
|
+
getStats(): ReturnType<import("./mega-adapter.js").MegaAdapter["getStats"]> & {
|
|
44
|
+
driver: string;
|
|
45
|
+
redis: string;
|
|
46
|
+
settings: Partial<RedlockSettings>;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* 락 획득 — `redlock.acquire([key], ttl, settings)`. 경합 시 retry(opts) 소진 후 실패하면 redlock 이
|
|
50
|
+
* `ExecutionError` 를 throw(정상 도메인 결과 — 호출부가 처리).
|
|
51
|
+
*
|
|
52
|
+
* @param {string} key - 락 자원 키.
|
|
53
|
+
* @param {{ ttl?: number, retryCount?: number, retryDelay?: number, retryJitter?: number }} [opts]
|
|
54
|
+
* - `ttl`(ms, 양의 정수 필수). retry 3종은 이번 호출 한정 override(미지정 시 생성자 settings 사용).
|
|
55
|
+
* @returns {Promise<import('redlock').Lock>} redlock Lock 핸들.
|
|
56
|
+
*/
|
|
57
|
+
acquire(key: string, opts?: {
|
|
58
|
+
ttl?: number;
|
|
59
|
+
retryCount?: number;
|
|
60
|
+
retryDelay?: number;
|
|
61
|
+
retryJitter?: number;
|
|
62
|
+
}): Promise<import("redlock").Lock>;
|
|
63
|
+
/**
|
|
64
|
+
* 락 해제 — `lock.release()`. 이미 만료/탈취된 락 release 는 redlock 이 'error' 로 표면화하거나
|
|
65
|
+
* throw 할 수 있다(비치명 — 락은 어차피 만료됨). 본 메서드는 정상 해제 경로를 instrument 한다.
|
|
66
|
+
*
|
|
67
|
+
* @param {import('redlock').Lock} lock - {@link acquire}/{@link extend} 가 돌려준 핸들.
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
*/
|
|
70
|
+
release(lock: import("redlock").Lock): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* 락 TTL 연장 — `redlock.extend(lock, ttl)`. **새 Lock 핸들을 반환**한다(redlock 이 연장 시 새 핸들을
|
|
73
|
+
* 만듦) — 이후 release/extend 는 반환된 핸들을 써야 한다(기존 핸들 무효, ADR-113).
|
|
74
|
+
*
|
|
75
|
+
* @param {import('redlock').Lock} lock @param {number} ttl - 추가 보유 시간(ms, 양의 정수).
|
|
76
|
+
* @returns {Promise<import('redlock').Lock>} 연장된 새 Lock 핸들.
|
|
77
|
+
*/
|
|
78
|
+
extend(lock: import("redlock").Lock, ttl: number): Promise<import("redlock").Lock>;
|
|
79
|
+
/**
|
|
80
|
+
* convenience — `redlock.using([key], ttl, settings, routine)`. 락 획득 + **자동 연장**
|
|
81
|
+
* (automaticExtensionThreshold 임박 시 자동 extend) + 종료 시 자동 release(routine 이 throw 해도 보장).
|
|
82
|
+
*
|
|
83
|
+
* routine 은 `(signal)` 을 받는다 — `signal.aborted` 가 true 면 자동 연장이 실패해 더는 배타성을
|
|
84
|
+
* 보장할 수 없는 상태다(`signal.error` 에 사유). routine 은 이때 즉시 중단하고 예외 상황으로 처리해야
|
|
85
|
+
* 한다(베이스 `withLock` 의 `(lock)` 시그니처와 다름 — auto-extension 모델 차이, ADR-113).
|
|
86
|
+
*
|
|
87
|
+
* @template T
|
|
88
|
+
* @param {string} key
|
|
89
|
+
* @param {{ ttl?: number, retryCount?: number, retryDelay?: number, retryJitter?: number }} opts - `ttl`(ms) 필수.
|
|
90
|
+
* @param {(signal: RedlockAbortSignal) => Promise<T>} fn - 임계구역 routine.
|
|
91
|
+
* @returns {Promise<T>} routine 반환값.
|
|
92
|
+
*/
|
|
93
|
+
withLock<T>(key: string, opts: {
|
|
94
|
+
ttl?: number;
|
|
95
|
+
retryCount?: number;
|
|
96
|
+
retryDelay?: number;
|
|
97
|
+
retryJitter?: number;
|
|
98
|
+
}, fn: (signal: RedlockAbortSignal) => Promise<T>): Promise<T>;
|
|
99
|
+
#private;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* redlock Settings 부분집합. redlock 패키지의 `Settings` interface 는 **type-only export** 라 패키지
|
|
103
|
+
* `exports` 맵(→ .d.ts 없는 esm .js)을 통과하면 TS 에 안 보인다(런타임 값 있는 class 만 보임). 그래서
|
|
104
|
+
* 같은 shape 를 로컬 typedef 로 둔다(redlock 정의와 1:1, dist/index.d.ts 확인).
|
|
105
|
+
*/
|
|
106
|
+
export type RedlockSettings = {
|
|
107
|
+
/**
|
|
108
|
+
* - 시계 드리프트 보정 계수 [0,1).
|
|
109
|
+
*/
|
|
110
|
+
driftFactor?: number;
|
|
111
|
+
/**
|
|
112
|
+
* - 경합 시 재시도 횟수.
|
|
113
|
+
*/
|
|
114
|
+
retryCount?: number;
|
|
115
|
+
/**
|
|
116
|
+
* - 재시도 간 지연(ms).
|
|
117
|
+
*/
|
|
118
|
+
retryDelay?: number;
|
|
119
|
+
/**
|
|
120
|
+
* - 재시도 지연 지터(ms).
|
|
121
|
+
*/
|
|
122
|
+
retryJitter?: number;
|
|
123
|
+
/**
|
|
124
|
+
* - `using` 자동 연장 임계(ms).
|
|
125
|
+
*/
|
|
126
|
+
automaticExtensionThreshold?: number;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* redlock `using` routine 이 받는 중단 신호 (redlock 의 `RedlockAbortSignal` 과 동일 shape — 위와 같은
|
|
130
|
+
* type-only export 사유로 로컬 정의). `aborted` 가 true 면 자동 연장 실패로 배타성 상실, `error` 에 사유.
|
|
131
|
+
*/
|
|
132
|
+
export type RedlockAbortSignal = AbortSignal & {
|
|
133
|
+
error?: Error;
|
|
134
|
+
};
|
|
135
|
+
export type RedlockConfig = {
|
|
136
|
+
/**
|
|
137
|
+
* - 'redlock' (매니저가 사용 — 어댑터는 무시).
|
|
138
|
+
*/
|
|
139
|
+
driver?: string;
|
|
140
|
+
/**
|
|
141
|
+
* - 참조할 Redis 캐시 어댑터 key (services.caches.<key>). 필수.
|
|
142
|
+
*/
|
|
143
|
+
redis: string;
|
|
144
|
+
/**
|
|
145
|
+
* - redlock Settings passthrough(driftFactor/retryCount/retryDelay/retryJitter/automaticExtensionThreshold).
|
|
146
|
+
*/
|
|
147
|
+
options?: Partial<RedlockSettings>;
|
|
148
|
+
};
|
|
149
|
+
import { MegaLockAdapter } from './mega-lock-adapter.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* driver → 어댑터 클래스 등록. 이미 같은 driver 가 **다른 클래스**로 등록돼 있으면 throw
|
|
3
|
+
* (silent override 방지). 같은 클래스 재등록은 idempotent no-op.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} driver - driver 이름 (예: 'postgres').
|
|
6
|
+
* @param {typeof MegaAdapter} AdapterClass - MegaAdapter 를 상속한 어댑터 클래스.
|
|
7
|
+
* @returns {void}
|
|
8
|
+
*/
|
|
9
|
+
export function register(driver: string, AdapterClass: typeof MegaAdapter): void;
|
|
10
|
+
/**
|
|
11
|
+
* `config.adapters.register` 맵을 일괄 등록 (ADR-044 3rd party). 빌트인 예약어 점유 시 throw.
|
|
12
|
+
*
|
|
13
|
+
* @param {Record<string, typeof MegaAdapter>} [registerMap]
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
export function registerFromConfig(registerMap?: Record<string, typeof MegaAdapter>): void;
|
|
17
|
+
/**
|
|
18
|
+
* driver 이름으로 등록된 어댑터 클래스 조회. 없으면 `adapter.unknown_driver` throw.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} driver
|
|
21
|
+
* @returns {typeof MegaAdapter}
|
|
22
|
+
*/
|
|
23
|
+
export function resolve(driver: string): typeof MegaAdapter;
|
|
24
|
+
/**
|
|
25
|
+
* driver 가 등록돼 있는지 (Boolean — `has*`, ADR-036).
|
|
26
|
+
* @param {string} driver
|
|
27
|
+
* @returns {boolean}
|
|
28
|
+
*/
|
|
29
|
+
export function has(driver: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 등록된 driver 이름 목록.
|
|
32
|
+
* @returns {string[]}
|
|
33
|
+
*/
|
|
34
|
+
export function list(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* 테스트용 reset — 등록 전부 비움.
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
export function _reset(): void;
|
|
40
|
+
/**
|
|
41
|
+
* 빌트인 driver 예약어 (ADR-044, ADR-082 file 포함, ADR-113 redlock 포함). 구체 클래스는 각 Step 에서 등록.
|
|
42
|
+
* 3rd party 가 이 이름을 점유하지 못하도록 예약 — `registerFromConfig` 가 거부.
|
|
43
|
+
* @type {ReadonlySet<string>}
|
|
44
|
+
*/
|
|
45
|
+
export const BUILTIN_DRIVERS: ReadonlySet<string>;
|
|
46
|
+
import { MegaAdapter } from './mega-adapter.js';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} SqliteConfig
|
|
3
|
+
* @property {string} [driver] - 'sqlite' (매니저가 사용 — 어댑터는 무시).
|
|
4
|
+
* @property {string} filename - DB 파일 경로 또는 ':memory:' (필수).
|
|
5
|
+
* @property {boolean} [readonly] - 읽기 전용 연결.
|
|
6
|
+
* @property {boolean} [fileMustExist] - 파일이 없으면 connect 시 throw.
|
|
7
|
+
* @property {number} [timeout] - 잠긴 DB 대기 ms (SQLITE_BUSY 전, 기본 5000).
|
|
8
|
+
* @property {boolean} [wal] - true 면 connect 후 `journal_mode = WAL` 적용.
|
|
9
|
+
* @property {Record<string, string | number>} [pragmas] - connect 후 적용할 추가 PRAGMA 맵.
|
|
10
|
+
*/
|
|
11
|
+
export class MegaSqliteAdapter extends MegaDbAdapter {
|
|
12
|
+
/**
|
|
13
|
+
* @param {SqliteConfig} [config] - services.databases.<key> 설정.
|
|
14
|
+
* @throws {MegaValidationError} `sqlite.filename_required` - filename 누락/빈 문자열.
|
|
15
|
+
* @throws {MegaValidationError} `sqlite.invalid_option` - 옵션 타입 오류.
|
|
16
|
+
*/
|
|
17
|
+
constructor(config?: SqliteConfig);
|
|
18
|
+
/** driver 식별자 — dialect 디스패치(getDialect)·CRUD 의 단일 출처(ADR-212). @returns {'sqlite'} */
|
|
19
|
+
get driver(): "sqlite";
|
|
20
|
+
/**
|
|
21
|
+
* raw Database handle (ADR-009). `connect()` 후에만 베이스 `native` getter 가 호출한다.
|
|
22
|
+
* @protected
|
|
23
|
+
* @returns {import('better-sqlite3').Database}
|
|
24
|
+
*/
|
|
25
|
+
protected _native(): import("better-sqlite3").Database;
|
|
26
|
+
/**
|
|
27
|
+
* 헬스 체크 — 실제 `SELECT 1` 으로 응답성 확인 (베이스 디폴트는 상태만 반영).
|
|
28
|
+
* connect 전이거나 쿼리 실패면 `ok:false` + 사유. `/health/ready` 가 모든 어댑터 ok 를 AND.
|
|
29
|
+
*
|
|
30
|
+
* @returns {Promise<{ ok: boolean, driver: 'sqlite', state: string, filename?: string, error?: string }>}
|
|
31
|
+
*/
|
|
32
|
+
healthCheck(): Promise<{
|
|
33
|
+
ok: boolean;
|
|
34
|
+
driver: "sqlite";
|
|
35
|
+
state: string;
|
|
36
|
+
filename?: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* 누적 통계 + sqlite 특화 필드.
|
|
41
|
+
* @returns {ReturnType<import('./mega-adapter.js').MegaAdapter['getStats']> & { driver: string, filename: string, inMemory: boolean, readonly: boolean, journalMode: string | undefined }}
|
|
42
|
+
*/
|
|
43
|
+
getStats(): ReturnType<import("./mega-adapter.js").MegaAdapter["getStats"]> & {
|
|
44
|
+
driver: string;
|
|
45
|
+
filename: string;
|
|
46
|
+
inMemory: boolean;
|
|
47
|
+
readonly: boolean;
|
|
48
|
+
journalMode: string | undefined;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* 명시적 트랜잭션 경계 (ADR-010, ADR-105). manual `BEGIN/COMMIT/ROLLBACK` 으로
|
|
52
|
+
* async `fn` 과 better-sqlite3 의 동기 트랜잭션 모델을 정합시킨다(모듈 docstring 참조).
|
|
53
|
+
*
|
|
54
|
+
* `fn` 은 **트랜잭션 컨텍스트 native handle**(= 같은 Database 인스턴스)을 인자로 받는다.
|
|
55
|
+
* 성공 시 COMMIT 후 `fn` 반환값을 그대로 돌려주고, `fn` 이 throw 하면 ROLLBACK 후 원본 에러를
|
|
56
|
+
* re-throw 한다. nested / 동시 호출은 `db.inTransaction` 으로 감지해 거부(ADR-105).
|
|
57
|
+
*
|
|
58
|
+
* `opts.isolation`(ADR-190) — SQLite 트랜잭션은 항상 SERIALIZABLE 동작(단일 writer, 공식 문서)이라
|
|
59
|
+
* `'serializable'` 만 수용(no-op)하고 다른 값은 `adapter.invalid_option` 으로 명시 거부한다
|
|
60
|
+
* (조용히 무시하면 사용자가 다른 격리수준이 적용된 줄 오인 — P4).
|
|
61
|
+
*
|
|
62
|
+
* hook(`onCallStart/onCallEnd`) + 상태 검증 + stats 누적은 `_instrument` 가 처리한다(ADR-077).
|
|
63
|
+
*
|
|
64
|
+
* @template T
|
|
65
|
+
* @param {(db: import('better-sqlite3').Database) => Promise<T> | T} fn
|
|
66
|
+
* @param {{ isolation?: 'serializable' }} [opts] - 트랜잭션 옵션(ADR-190).
|
|
67
|
+
* @returns {Promise<T>}
|
|
68
|
+
* @throws {MegaInternalError} `adapter.nested_transaction_unsupported` - 이미 트랜잭션 진행 중.
|
|
69
|
+
* @throws {MegaValidationError} `adapter.invalid_option` - 'serializable' 외 isolation 값.
|
|
70
|
+
*/
|
|
71
|
+
withTransaction<T>(fn: (db: import("better-sqlite3").Database) => Promise<T> | T, opts?: {
|
|
72
|
+
isolation?: "serializable";
|
|
73
|
+
}): Promise<T>;
|
|
74
|
+
#private;
|
|
75
|
+
}
|
|
76
|
+
export type SqliteConfig = {
|
|
77
|
+
/**
|
|
78
|
+
* - 'sqlite' (매니저가 사용 — 어댑터는 무시).
|
|
79
|
+
*/
|
|
80
|
+
driver?: string;
|
|
81
|
+
/**
|
|
82
|
+
* - DB 파일 경로 또는 ':memory:' (필수).
|
|
83
|
+
*/
|
|
84
|
+
filename: string;
|
|
85
|
+
/**
|
|
86
|
+
* - 읽기 전용 연결.
|
|
87
|
+
*/
|
|
88
|
+
readonly?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* - 파일이 없으면 connect 시 throw.
|
|
91
|
+
*/
|
|
92
|
+
fileMustExist?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* - 잠긴 DB 대기 ms (SQLITE_BUSY 전, 기본 5000).
|
|
95
|
+
*/
|
|
96
|
+
timeout?: number;
|
|
97
|
+
/**
|
|
98
|
+
* - true 면 connect 후 `journal_mode = WAL` 적용.
|
|
99
|
+
*/
|
|
100
|
+
wal?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* - connect 후 적용할 추가 PRAGMA 맵.
|
|
103
|
+
*/
|
|
104
|
+
pragmas?: Record<string, string | number>;
|
|
105
|
+
};
|
|
106
|
+
import { MegaDbAdapter } from './mega-db-adapter.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 로그인(세션 `userId` 존재)을 요구하는 `before` 가드. 통과 시 `req.user`(`{ id, roles }`)를 채운다.
|
|
3
|
+
* 실패 시 401 `auth.required`.
|
|
4
|
+
*
|
|
5
|
+
* @param {import('fastify').FastifyRequest} req
|
|
6
|
+
* @param {import('fastify').FastifyReply} _reply
|
|
7
|
+
* @param {Record<string, any>} [ctx] - 글로벌 미들웨어 경로에서만 주입(라우트 before 에선 undefined).
|
|
8
|
+
* @returns {Promise<void>}
|
|
9
|
+
* @throws {MegaAuthError} 세션·userId 부재 시 401.
|
|
10
|
+
* @example
|
|
11
|
+
* router.http.get('/api/me', MeController.show, { before: [requireAuth] })
|
|
12
|
+
*/
|
|
13
|
+
export function requireAuth(req: import("fastify").FastifyRequest, _reply: import("fastify").FastifyReply, ctx?: Record<string, any>): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* 지정한 역할 중 하나 이상을 가진 로그인 사용자를 요구하는 `before` 가드 팩토리. 먼저 인증을
|
|
16
|
+
* 확정(`requireAuth` 동일 로직)한 뒤 역할 교집합을 검사한다.
|
|
17
|
+
*
|
|
18
|
+
* @param {...string} roles - 허용 역할 목록(하나라도 보유 시 통과). 최소 1개 필수.
|
|
19
|
+
* @returns {(req: import('fastify').FastifyRequest, reply: import('fastify').FastifyReply, ctx?: Record<string, any>) => Promise<void>}
|
|
20
|
+
* @throws {TypeError} 역할 인자가 비었거나 문자열이 아니면(설정 실수 fail-fast).
|
|
21
|
+
* @example
|
|
22
|
+
* router.http.post('/api/users', UsersController.create, { before: [requireRole('admin')] })
|
|
23
|
+
*/
|
|
24
|
+
export function requireRole(...roles: string[]): (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply, ctx?: Record<string, any>) => Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 앱 컨텍스트를 로딩하고 REPL 을 연다.
|
|
3
|
+
* @param {string} projectRoot
|
|
4
|
+
* @param {object} [deps]
|
|
5
|
+
* @param {{ debug?: Function, info?: Function, warn?: Function }} [deps.logger]
|
|
6
|
+
* @param {() => { context: Record<string, any>, on?: (event: string, listener: (...args: any[]) => void) => void }} [deps.replFactory] -
|
|
7
|
+
* 주입용(테스트). context 객체를 가진 REPL-유사 객체를 반환해야 한다('exit' 이벤트용 `on` 은 선택).
|
|
8
|
+
* @param {(msg: string) => void} [deps.out]
|
|
9
|
+
* @param {() => (Promise<void> | void)} [deps.shutdown] - 주입용(테스트). REPL 종료 시 호출하는 graceful
|
|
10
|
+
* shutdown 트리거. 기본 {@link MegaShutdown.now}(등록 hook 실행 후 process.exit).
|
|
11
|
+
* @param {(opts: { signals?: string[], globalErrorHandlers?: boolean }) => void} [deps.setupSignals] - 주입용(테스트). 시그널 핸들러 등록.
|
|
12
|
+
* 기본 {@link MegaShutdown.setupSignals}.
|
|
13
|
+
* @returns {Promise<{ ctx: Record<string, any>, config: object, server: { context: Record<string, any> } }>}
|
|
14
|
+
*/
|
|
15
|
+
export function startConsole(projectRoot: string, { logger, replFactory, out, shutdown, setupSignals }?: {
|
|
16
|
+
logger?: {
|
|
17
|
+
debug?: Function;
|
|
18
|
+
info?: Function;
|
|
19
|
+
warn?: Function;
|
|
20
|
+
};
|
|
21
|
+
replFactory?: () => {
|
|
22
|
+
context: Record<string, any>;
|
|
23
|
+
on?: (event: string, listener: (...args: any[]) => void) => void;
|
|
24
|
+
};
|
|
25
|
+
out?: (msg: string) => void;
|
|
26
|
+
shutdown?: () => (Promise<void> | void);
|
|
27
|
+
setupSignals?: (opts: {
|
|
28
|
+
signals?: string[];
|
|
29
|
+
globalErrorHandlers?: boolean;
|
|
30
|
+
}) => void;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
ctx: Record<string, any>;
|
|
33
|
+
config: object;
|
|
34
|
+
server: {
|
|
35
|
+
context: Record<string, any>;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프로젝트를 스캐폴드한다(순수 — prompts 없이 opts 로만 동작, 테스트 가능).
|
|
3
|
+
* @param {string} targetDir - 프로젝트 루트 절대/상대 경로.
|
|
4
|
+
* @param {object} [opts]
|
|
5
|
+
* @param {string} [opts.name] - 프로젝트 이름(기본: targetDir 의 마지막 세그먼트).
|
|
6
|
+
* @param {boolean} [opts.force] - 기존 파일 덮어쓰기(기본 false — 건너뜀).
|
|
7
|
+
* @returns {{ root: string, written: string[], skipped: string[] }}
|
|
8
|
+
*/
|
|
9
|
+
export function scaffoldProject(targetDir: string, { name, force }?: {
|
|
10
|
+
name?: string;
|
|
11
|
+
force?: boolean;
|
|
12
|
+
}): {
|
|
13
|
+
root: string;
|
|
14
|
+
written: string[];
|
|
15
|
+
skipped: string[];
|
|
16
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프로젝트의 앱별 라우트를 수집한다.
|
|
3
|
+
* @param {string} projectRoot
|
|
4
|
+
* @returns {Promise<Array<{ app: string, hosts: string[], routes: Array<{ method: string, path: string, file: string }> }>>}
|
|
5
|
+
*/
|
|
6
|
+
export function collectRoutes(projectRoot: string): Promise<Array<{
|
|
7
|
+
app: string;
|
|
8
|
+
hosts: string[];
|
|
9
|
+
routes: Array<{
|
|
10
|
+
method: string;
|
|
11
|
+
path: string;
|
|
12
|
+
file: string;
|
|
13
|
+
}>;
|
|
14
|
+
}>>;
|
|
15
|
+
/**
|
|
16
|
+
* 수집한 라우트를 사람이 읽는 트리 문자열로 포맷.
|
|
17
|
+
* @param {Array<{ app: string, hosts: string[], routes: Array<{ method: string, path: string }> }>} data
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export function formatRoutes(data: Array<{
|
|
21
|
+
app: string;
|
|
22
|
+
hosts: string[];
|
|
23
|
+
routes: Array<{
|
|
24
|
+
method: string;
|
|
25
|
+
path: string;
|
|
26
|
+
}>;
|
|
27
|
+
}>): string;
|
|
28
|
+
/**
|
|
29
|
+
* `mega routes` 명령 본체.
|
|
30
|
+
* @param {string} projectRoot
|
|
31
|
+
* @param {{ out?: (msg: string) => void }} [deps]
|
|
32
|
+
* @returns {Promise<number>} exit code.
|
|
33
|
+
*/
|
|
34
|
+
export function runRoutesCommand(projectRoot: string, { out }?: {
|
|
35
|
+
out?: (msg: string) => void;
|
|
36
|
+
}): Promise<number>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scaffold/dev 명령 5종을 commander program 에 등록한다 — 명령 정의의 단일 정본(ADR-195).
|
|
3
|
+
* action 은 `process.exit` 를 부르지 않고 `setExit(code)` 로 종료 코드를 보고한다(runCli 계약).
|
|
4
|
+
*
|
|
5
|
+
* @param {import('commander').Command} program - 등록 대상 program.
|
|
6
|
+
* @param {object} deps
|
|
7
|
+
* @param {(msg: string) => void} deps.out
|
|
8
|
+
* @param {string} deps.projectRoot - 이미 `--root` 해석된 기준 디렉토리.
|
|
9
|
+
* @param {{ debug?: Function, info?: Function, warn?: Function }} [deps.logger]
|
|
10
|
+
* @param {(kind: string) => Promise<{ dir: string, files: Array<{ path: string, template: string }> } | undefined>} [deps.resolvePluginGenerator] -
|
|
11
|
+
* 빌트인이 아닌 kind 의 플러그인 scaffold manifest 조회(`mega.scaffold.register`, 03-api-spec §11).
|
|
12
|
+
* 호출측(runCli)이 config 로드 + 플러그인 install 을 감싼 함수를 주입한다 — 본 모듈이 cli/index.js 를
|
|
13
|
+
* import 하면 순환이라 주입식으로 푼다. 미주입/미발견이면 unknown kind 에러.
|
|
14
|
+
* @param {(code: number) => void} deps.setExit - 명령 종료 코드 보고 콜백.
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
export function registerScaffoldCommands(program: import("commander").Command, { out, projectRoot, logger, resolvePluginGenerator, setExit }: {
|
|
18
|
+
out: (msg: string) => void;
|
|
19
|
+
projectRoot: string;
|
|
20
|
+
logger?: {
|
|
21
|
+
debug?: Function;
|
|
22
|
+
info?: Function;
|
|
23
|
+
warn?: Function;
|
|
24
|
+
};
|
|
25
|
+
resolvePluginGenerator?: (kind: string) => Promise<{
|
|
26
|
+
dir: string;
|
|
27
|
+
files: Array<{
|
|
28
|
+
path: string;
|
|
29
|
+
template: string;
|
|
30
|
+
}>;
|
|
31
|
+
} | undefined>;
|
|
32
|
+
setExit: (code: number) => void;
|
|
33
|
+
}): void;
|
|
34
|
+
/**
|
|
35
|
+
* commander 의 exitOverride 예외를 exit code 로 환산한다 — help/version 출력은 정상 종료(0),
|
|
36
|
+
* CommanderError 는 자체 exitCode, 그 외 에러는 메시지 출력 후 1. commander 가 아닌 에러를 그대로
|
|
37
|
+
* 삼키지 않도록 `rethrowUnknown` 옵션을 둔다(runCli 가 부팅·config 에러를 bin 으로 전파할 때 사용).
|
|
38
|
+
*
|
|
39
|
+
* @param {unknown} e - parseAsync 가 던진 예외.
|
|
40
|
+
* @param {(msg: string) => void} err
|
|
41
|
+
* @param {{ rethrowUnknown?: boolean }} [opts] - true 면 CommanderError 가 아닌 예외를 재throw.
|
|
42
|
+
* @returns {number} exit code.
|
|
43
|
+
*/
|
|
44
|
+
export function commanderErrorToExitCode(e: unknown, err: (msg: string) => void, { rethrowUnknown }?: {
|
|
45
|
+
rethrowUnknown?: boolean;
|
|
46
|
+
}): number;
|
|
47
|
+
/**
|
|
48
|
+
* scaffold/dev 명령만 담은 독립 program 으로 실행한다(하위호환 진입점 — 단위 테스트 경계 유지).
|
|
49
|
+
* commander 로 파싱하되 `process.exit` 를 부르지 않고 exit code 를 반환한다(runCli 계약 정합).
|
|
50
|
+
*
|
|
51
|
+
* @param {string[]} argv - `process.argv.slice(2)` (첫 토큰 = 명령).
|
|
52
|
+
* @param {object} deps
|
|
53
|
+
* @param {(msg: string) => void} deps.out
|
|
54
|
+
* @param {(msg: string) => void} deps.err
|
|
55
|
+
* @param {string} deps.projectRoot - 이미 `--root` 해석된 기준 디렉토리.
|
|
56
|
+
* @param {{ debug?: Function, info?: Function, warn?: Function }} [deps.logger]
|
|
57
|
+
* @param {(kind: string) => Promise<{ dir: string, files: Array<{ path: string, template: string }> } | undefined>} [deps.resolvePluginGenerator]
|
|
58
|
+
* @returns {Promise<number>} exit code.
|
|
59
|
+
*/
|
|
60
|
+
export function runScaffoldCommand(argv: string[], { out, err, projectRoot, logger, resolvePluginGenerator }: {
|
|
61
|
+
out: (msg: string) => void;
|
|
62
|
+
err: (msg: string) => void;
|
|
63
|
+
projectRoot: string;
|
|
64
|
+
logger?: {
|
|
65
|
+
debug?: Function;
|
|
66
|
+
info?: Function;
|
|
67
|
+
warn?: Function;
|
|
68
|
+
};
|
|
69
|
+
resolvePluginGenerator?: (kind: string) => Promise<{
|
|
70
|
+
dir: string;
|
|
71
|
+
files: Array<{
|
|
72
|
+
path: string;
|
|
73
|
+
template: string;
|
|
74
|
+
}>;
|
|
75
|
+
} | undefined>;
|
|
76
|
+
}): Promise<number>;
|
|
77
|
+
/** scaffold/dev 명령 이름(별칭 포함). */
|
|
78
|
+
export const SCAFFOLD_COMMANDS: Set<string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vitest 를 자식 프로세스로 실행한다.
|
|
3
|
+
* @param {string} projectRoot - 실행 cwd.
|
|
4
|
+
* @param {string[]} [args] - vitest 에 넘길 추가 인자.
|
|
5
|
+
* @param {object} [deps]
|
|
6
|
+
* @param {typeof nodeSpawn} [deps.spawn] - 주입용(테스트).
|
|
7
|
+
* @param {(msg: string) => void} [deps.out]
|
|
8
|
+
* @returns {Promise<number>} vitest exit code.
|
|
9
|
+
*/
|
|
10
|
+
export function runTestCommand(projectRoot: string, args?: string[], { spawn, out }?: {
|
|
11
|
+
spawn?: typeof nodeSpawn;
|
|
12
|
+
out?: (msg: string) => void;
|
|
13
|
+
}): Promise<number>;
|
|
14
|
+
import { spawn as nodeSpawn } from 'node:child_process';
|