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,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 슬라이딩 윈도우 throttle — 최근 `windowMs` 안에 `max` 건까지만 허용(폭주 시 텔레그램 rate limit 직격 방지).
|
|
3
|
+
*
|
|
4
|
+
* @param {number} max - 윈도우당 최대 전송 건수(디폴트 5).
|
|
5
|
+
* @param {number} windowMs - 윈도우 길이 ms(디폴트 60_000 = 1분).
|
|
6
|
+
* @returns {{ tryAcquire(now: number): boolean, size(): number }}
|
|
7
|
+
*/
|
|
8
|
+
export function createThrottle(max?: number, windowMs?: number): {
|
|
9
|
+
tryAcquire(now: number): boolean;
|
|
10
|
+
size(): number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* pino 레코드를 텔레그램 메시지 텍스트로 포맷한다. redact 는 메인 스레드 pino 가 이미 적용했으므로
|
|
14
|
+
* 레코드에는 시크릿이 없다(transport 는 마스킹된 NDJSON 만 받음). 핵심 필드만 추린다(전체 payload X, P5).
|
|
15
|
+
*
|
|
16
|
+
* @param {Record<string, any>} record - pino 표준 레코드(JSON 파싱됨).
|
|
17
|
+
* @param {string} [serviceName]
|
|
18
|
+
* @returns {string} 텔레그램 sendMessage 용 텍스트.
|
|
19
|
+
*/
|
|
20
|
+
export function formatMessage(record: Record<string, any>, serviceName?: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* 텔레그램 Bot API `sendMessage` 호출. https 호출은 주입 가능(`httpsRequest`)이라 단위 테스트에서 모킹한다.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} args
|
|
25
|
+
* @param {string} args.botToken
|
|
26
|
+
* @param {string} args.chatId
|
|
27
|
+
* @param {string} args.text
|
|
28
|
+
* @param {(url: string, body: string) => Promise<{ statusCode: number }>} args.httpsRequest - 주입 HTTP.
|
|
29
|
+
* @returns {Promise<boolean>} 전송 성공(2xx) 여부.
|
|
30
|
+
* @throws 호출 자체가 throw 하면 호출자(transport)가 retry queue 로 보낸다.
|
|
31
|
+
*/
|
|
32
|
+
export function sendTelegram({ botToken, chatId, text, httpsRequest }: {
|
|
33
|
+
botToken: string;
|
|
34
|
+
chatId: string;
|
|
35
|
+
text: string;
|
|
36
|
+
httpsRequest: (url: string, body: string) => Promise<{
|
|
37
|
+
statusCode: number;
|
|
38
|
+
}>;
|
|
39
|
+
}): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* disk-backed retry queue — 전송 실패한 텍스트를 파일(JSONL)에 쌓고, 다음 기회에 재시도한다(전송 실패해도
|
|
42
|
+
* 메시지를 잃지 않음, ADR-023).
|
|
43
|
+
*
|
|
44
|
+
* # in-process 직렬화 락 (ADR-023)
|
|
45
|
+
* 단일 스레드라도 `append`(open→write→close)와 `drain`(rename→read→rm)은 각 await 사이에서 **이벤트루프
|
|
46
|
+
* 인터리브**된다. 인터리브되면 append 의 `open` 이 drain 의 `rename` 보다 먼저 일어날 때, write 가 이미
|
|
47
|
+
* rename·rm 된 inode 로 가서 항목이 **유실**된다(rename-claim 의 "유실 0" 이 깨지는 좁은 창). 그래서 큐
|
|
48
|
+
* 연산을 promise 체인 mutex 로 **직렬화**해 인터리브를 원천 차단한다. 큐 연산은 드물어(전송 실패 시
|
|
49
|
+
* append + 주기 drain) 직렬화 비용은 무시 가능. rename-claim 은 cross-process 안전용으로 그대로 둔다.
|
|
50
|
+
*
|
|
51
|
+
* # 상한(cap) — 무한 증가 방지 (H4, 최적화 리포트)
|
|
52
|
+
* 텔레그램이 장기 장애(토큰 만료·차단·네트워크 단절)면 실패분이 무한 적재되고, drain 이 파일 전체를
|
|
53
|
+
* 메모리로 읽어 재시도→또 실패→전량 재append 한다. 그래서 `drain` 이 **maxAge 만료 + maxEntries 상한**을
|
|
54
|
+
* 적용해(오래된 것부터 드롭, 드롭 수는 `onDrop` 으로 관측) 디스크·드레인 메모리를 함께 bound 한다.
|
|
55
|
+
* write 경로 append rate 는 transport throttle 로 이미 제한되므로 drain 시점 cap 으로 충분하다.
|
|
56
|
+
*/
|
|
57
|
+
export class RetryQueue {
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} filePath - JSONL 큐 파일 경로(없으면 비어 있음).
|
|
60
|
+
* @param {{ maxEntries?: number, maxAgeMs?: number, onDrop?: (count: number) => void }} [opts]
|
|
61
|
+
* maxEntries: 보관 최대 항목 수(기본 5000, 0=무제한). maxAgeMs: 항목 최대 보존 ms(기본 24h, 0=무제한).
|
|
62
|
+
* onDrop: cap 으로 드롭된 수 통지(관측용).
|
|
63
|
+
*/
|
|
64
|
+
constructor(filePath: string, { maxEntries, maxAgeMs, onDrop }?: {
|
|
65
|
+
maxEntries?: number;
|
|
66
|
+
maxAgeMs?: number;
|
|
67
|
+
onDrop?: (count: number) => void;
|
|
68
|
+
});
|
|
69
|
+
/** @type {string} */
|
|
70
|
+
_file: string;
|
|
71
|
+
/** @type {Promise<unknown>} 직렬화 락의 꼬리 — append/drain/clear 를 이 체인에 줄세운다. */
|
|
72
|
+
_tail: Promise<unknown>;
|
|
73
|
+
/** @type {number} 보관 최대 항목 수(0=무제한). */
|
|
74
|
+
_maxEntries: number;
|
|
75
|
+
/** @type {number} 항목 최대 보존 ms(0=무제한). */
|
|
76
|
+
_maxAgeMs: number;
|
|
77
|
+
/** @type {((count: number) => void) | null} cap 드롭 수 통지. */
|
|
78
|
+
_onDrop: ((count: number) => void) | null;
|
|
79
|
+
/**
|
|
80
|
+
* 큐 연산을 직렬화 — 앞 연산이 끝난 뒤(성패 무관) `fn` 을 실행한다. 한 연산의 실패가 다음 연산을 막지
|
|
81
|
+
* 않도록 락 체인은 에러를 삼키되, 호출자에겐 결과/에러를 그대로 전파한다.
|
|
82
|
+
* @template T @param {() => Promise<T>} fn @returns {Promise<T>} @private
|
|
83
|
+
*/
|
|
84
|
+
private _serialize;
|
|
85
|
+
/**
|
|
86
|
+
* 실패 메시지를 큐에 append (직렬화됨).
|
|
87
|
+
* @param {string} text
|
|
88
|
+
* @returns {Promise<void>}
|
|
89
|
+
*/
|
|
90
|
+
append(text: string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* 큐 전체를 가로채(claim) 비우고, 각 항목 텍스트 배열을 반환한다. 호출자가 재전송 시도 후, 다시
|
|
93
|
+
* 실패한 것만 `append` 로 되돌린다.
|
|
94
|
+
*
|
|
95
|
+
* read-then-`writeFile('')` 는 두 await 사이에 끼어든 `append` 를 통째로 지운다(producer=write 경로의
|
|
96
|
+
* 재시도 append, consumer=drain). 그래서 `rename` 으로 **단일 syscall** 가로채기를 쓴다: 가로챈 뒤 들어오는
|
|
97
|
+
* append 는 새 파일(`this._file`)에 쌓이므로 유실되지 않는다(유실 0, ADR-023/141). 동시 drain 이 한 번 더
|
|
98
|
+
* 들어오면 파일이 이미 옮겨져 `ENOENT` → 빈 배열(중복 처리 없음).
|
|
99
|
+
* @returns {Promise<string[]>}
|
|
100
|
+
*/
|
|
101
|
+
drain(): Promise<string[]>;
|
|
102
|
+
/** 큐 파일 제거(테스트·정리용, 직렬화됨). @returns {Promise<void>} */
|
|
103
|
+
clear(): Promise<void>;
|
|
104
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 실제 https POST — telegram-core 의 주입 시그니처에 맞춘 thin 래퍼.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} url
|
|
5
|
+
* @param {string} body
|
|
6
|
+
* @param {{ request?: typeof httpsRequestRaw, timeoutMs?: number }} [deps] - request 는 테스트 주입용(기본=node:https).
|
|
7
|
+
* @returns {Promise<{ statusCode: number }>}
|
|
8
|
+
*/
|
|
9
|
+
export function httpsPost(url: string, body: string, { request, timeoutMs }?: {
|
|
10
|
+
request?: typeof httpsRequestRaw;
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
statusCode: number;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* pino transport 진입점 — `{ target: <this>, options }` 로 등록되면 worker 가 이 default export 를 호출한다.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} opts
|
|
19
|
+
* @param {string} opts.botToken
|
|
20
|
+
* @param {string} opts.chatId
|
|
21
|
+
* @param {number} [opts.throttleMax] - 윈도우당 최대 전송(디폴트 5).
|
|
22
|
+
* @param {number} [opts.throttleWindowMs] - 윈도우 ms(디폴트 60_000).
|
|
23
|
+
* @param {string} [opts.retryDir] - retry queue 디렉터리(디폴트 './logs/telegram-retry').
|
|
24
|
+
* @param {string} [opts.serviceName]
|
|
25
|
+
* @param {number} [opts.retryDrainMs] - retry 드레인 주기(디폴트 30_000).
|
|
26
|
+
* @param {number} [opts.retryMaxEntries] - retry 큐 보관 최대 항목 수(디폴트 5000, 0=무제한, H4 cap).
|
|
27
|
+
* @param {number} [opts.retryMaxAgeMs] - retry 항목 최대 보존 ms(디폴트 24h, 0=무제한, H4 cap).
|
|
28
|
+
* @param {(url: string, body: string) => Promise<{ statusCode: number }>} [opts.httpsRequest] - HTTP 주입(기본=httpsPost). 단위 테스트용 seam(ADR-165 동일 패턴) — pino worker 는 미전달.
|
|
29
|
+
* @returns {import('node:stream').Writable}
|
|
30
|
+
*/
|
|
31
|
+
export default function telegramTransport(opts: {
|
|
32
|
+
botToken: string;
|
|
33
|
+
chatId: string;
|
|
34
|
+
throttleMax?: number;
|
|
35
|
+
throttleWindowMs?: number;
|
|
36
|
+
retryDir?: string;
|
|
37
|
+
serviceName?: string;
|
|
38
|
+
retryDrainMs?: number;
|
|
39
|
+
retryMaxEntries?: number;
|
|
40
|
+
retryMaxAgeMs?: number;
|
|
41
|
+
httpsRequest?: (url: string, body: string) => Promise<{
|
|
42
|
+
statusCode: number;
|
|
43
|
+
}>;
|
|
44
|
+
}): import("node:stream").Writable;
|
|
45
|
+
import { request as httpsRequestRaw } from 'node:https';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} BruteForceResult - check/fail 반환 형태 (03-api-spec §786).
|
|
3
|
+
* @property {boolean} locked - 현재 잠김 여부.
|
|
4
|
+
* @property {boolean} isLocked - `locked` 별칭(Boolean 컨벤션 — 둘 다 노출).
|
|
5
|
+
* @property {number} attemptsLeft - 잠기기까지 남은 실패 허용 횟수(잠겼으면 0).
|
|
6
|
+
* @property {number} retryAfterMs - 잠금 해제까지 남은 ms(안 잠겼으면 0).
|
|
7
|
+
*/
|
|
8
|
+
export class MegaBruteForce {
|
|
9
|
+
/**
|
|
10
|
+
* @param {object} opts
|
|
11
|
+
* @param {import('../adapters/mega-cache-adapter.js').MegaCacheAdapter} opts.cache - 연결된 redis 캐시 어댑터(`.native`=ioredis).
|
|
12
|
+
* @param {string} opts.key - 네임스페이스(도메인 구분). 03-api-spec 의 생성자 `key`.
|
|
13
|
+
* @param {number} [opts.maxAttempts=5] - 잠금까지 허용 실패 횟수.
|
|
14
|
+
* @param {number} [opts.windowMs=900000] - 시도 카운트 윈도우(ms). 첫 실패부터 이 시간 뒤 카운터 소멸.
|
|
15
|
+
* @param {number} [opts.lockMs=900000] - 잠금 지속 시간(ms).
|
|
16
|
+
* @throws {MegaValidationError} `bruteforce.invalid_option` - cache/key 누락 또는 수치 옵션 비-양의정수.
|
|
17
|
+
*/
|
|
18
|
+
constructor(opts?: {
|
|
19
|
+
cache: import("../adapters/mega-cache-adapter.js").MegaCacheAdapter;
|
|
20
|
+
key: string;
|
|
21
|
+
maxAttempts?: number;
|
|
22
|
+
windowMs?: number;
|
|
23
|
+
lockMs?: number;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* 시도 **전** 현재 상태 조회(부수효과 없음). 잠겨 있으면 호출부가 즉시 거부할 수 있다.
|
|
27
|
+
* @param {string} subject - 대상 식별자(이메일·IP 등).
|
|
28
|
+
* @returns {Promise<BruteForceResult>}
|
|
29
|
+
*/
|
|
30
|
+
check(subject: string): Promise<BruteForceResult>;
|
|
31
|
+
/**
|
|
32
|
+
* 실패 1건 기록. 카운터를 원자적으로 +1 하고, `maxAttempts` 도달 시 잠금을 건다.
|
|
33
|
+
* 이미 잠긴 상태면 카운터를 더 늘리지 않고 잠금 정보를 그대로 반환한다.
|
|
34
|
+
* @param {string} subject - 대상 식별자.
|
|
35
|
+
* @returns {Promise<BruteForceResult>}
|
|
36
|
+
*/
|
|
37
|
+
fail(subject: string): Promise<BruteForceResult>;
|
|
38
|
+
/**
|
|
39
|
+
* 카운터·잠금 즉시 제거(로그인 성공 등 정상 시도 후).
|
|
40
|
+
* @param {string} subject - 대상 식별자.
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
reset(subject: string): Promise<void>;
|
|
44
|
+
#private;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* - check/fail 반환 형태 (03-api-spec §786).
|
|
48
|
+
*/
|
|
49
|
+
export type BruteForceResult = {
|
|
50
|
+
/**
|
|
51
|
+
* - 현재 잠김 여부.
|
|
52
|
+
*/
|
|
53
|
+
locked: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* - `locked` 별칭(Boolean 컨벤션 — 둘 다 노출).
|
|
56
|
+
*/
|
|
57
|
+
isLocked: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* - 잠기기까지 남은 실패 허용 횟수(잠겼으면 0).
|
|
60
|
+
*/
|
|
61
|
+
attemptsLeft: number;
|
|
62
|
+
/**
|
|
63
|
+
* - 잠금 해제까지 남은 ms(안 잠겼으면 0).
|
|
64
|
+
*/
|
|
65
|
+
retryAfterMs: number;
|
|
66
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 외부 호출을 서킷 브레이커로 감싸 **재사용 가능한** 브레이커를 만든다(보호 함수 1개당 1 브레이커).
|
|
3
|
+
* `new MegaCircuitBreaker(fn, opts)` 의 함수형 별칭 — 다른 Mega* 유틸(`withRetry`)과 호출 모양 일관.
|
|
4
|
+
*
|
|
5
|
+
* @param {(...args: any[]) => Promise<any>} fn - 보호할 비동기 함수.
|
|
6
|
+
* @param {MegaCircuitBreakerOptions} [options] - 브레이커 옵션.
|
|
7
|
+
* @returns {MegaCircuitBreaker} 장수(long-lived) 브레이커. 이후 `.fire(...args)` 로 호출.
|
|
8
|
+
*/
|
|
9
|
+
export function wrap(fn: (...args: any[]) => Promise<any>, options?: MegaCircuitBreakerOptions): MegaCircuitBreaker;
|
|
10
|
+
/**
|
|
11
|
+
* 회로가 **열려** 호출이 거부됐을 때 `fire()` 가 reject 하는 에러의 `code`. 호출부가
|
|
12
|
+
* `err.code === OPEN_CIRCUIT_ERROR_CODE` 로 "지금은 차단 중" 을 분기 판별할 때 쓴다. (opossum 정본.)
|
|
13
|
+
* @type {'EOPENBREAKER'}
|
|
14
|
+
*/
|
|
15
|
+
export const OPEN_CIRCUIT_ERROR_CODE: "EOPENBREAKER";
|
|
16
|
+
/**
|
|
17
|
+
* 호출이 `timeout` ms 를 넘겨 실패 처리된 에러의 `code`. (opossum 정본.)
|
|
18
|
+
* @type {'ETIMEDOUT'}
|
|
19
|
+
*/
|
|
20
|
+
export const TIMEOUT_ERROR_CODE: "ETIMEDOUT";
|
|
21
|
+
/**
|
|
22
|
+
* 동시 호출이 `capacity` 한도를 넘어 거부된 에러의 `code`. (opossum 정본.)
|
|
23
|
+
* @type {'ESEMLOCKED'}
|
|
24
|
+
*/
|
|
25
|
+
export const CAPACITY_ERROR_CODE: "ESEMLOCKED";
|
|
26
|
+
/**
|
|
27
|
+
* 외부 호출을 서킷 브레이커로 감싸는 래퍼. opossum `CircuitBreaker` 인스턴스 1개를 보유하고, 우리
|
|
28
|
+
* 컨벤션(Boolean `is*` 게터·`getStats()`·이벤트 화이트리스트)으로 표면을 정리한다.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const breaker = new MegaCircuitBreaker(callPaymentApi, { timeout: 3000, errorThresholdPercentage: 50 })
|
|
32
|
+
* breaker.on('open', () => log.warn('payment circuit open'))
|
|
33
|
+
* try {
|
|
34
|
+
* const res = await breaker.fire(orderId)
|
|
35
|
+
* } catch (e) {
|
|
36
|
+
* if (e.code === OPEN_CIRCUIT_ERROR_CODE) return useCachedQuote() // 명시적 폴백
|
|
37
|
+
* throw e
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
export class MegaCircuitBreaker {
|
|
41
|
+
/**
|
|
42
|
+
* @param {(...args: any[]) => Promise<any>} fn - 보호할 비동기 함수(외부 호출).
|
|
43
|
+
* @param {MegaCircuitBreakerOptions} [options] - 브레이커 옵션.
|
|
44
|
+
* @throws {TypeError} `fn` 이 함수가 아니면.
|
|
45
|
+
*/
|
|
46
|
+
constructor(fn: (...args: any[]) => Promise<any>, options?: MegaCircuitBreakerOptions);
|
|
47
|
+
/**
|
|
48
|
+
* 보호된 함수를 호출한다. 회로가 closed/halfOpen 이면 원함수를 실행하고, open 이면 원함수를 건너뛰고
|
|
49
|
+
* 즉시 `EOPENBREAKER` 로 reject 한다(빠른 실패). 결과/에러는 **그대로 전파**(삼키지 않음).
|
|
50
|
+
*
|
|
51
|
+
* @param {...any} args - 원함수에 그대로 전달할 인자.
|
|
52
|
+
* @returns {Promise<any>} 원함수의 반환값. 거부/타임아웃/실패 시 reject(에러 `code` 로 분기).
|
|
53
|
+
*/
|
|
54
|
+
fire(...args: any[]): Promise<any>;
|
|
55
|
+
/**
|
|
56
|
+
* 회로가 열려 있거나 호출이 실패할 때 대신 실행할 폴백을 등록한다. 폴백이 있으면 `fire()` 는 에러로
|
|
57
|
+
* reject 하지 않고 폴백 반환값으로 resolve 한다(`fallback` 이벤트 발화).
|
|
58
|
+
*
|
|
59
|
+
* @param {(...args: any[]) => any} fn - 폴백 함수(동기/비동기 모두 허용). 원호출 인자를 그대로 받는다.
|
|
60
|
+
* @returns {this} 체이닝용.
|
|
61
|
+
*/
|
|
62
|
+
fallback(fn: (...args: any[]) => any): this;
|
|
63
|
+
/**
|
|
64
|
+
* 브레이커 이벤트를 구독한다(소비자가 분기점에 로그를 박는 지점). 알 수 없는 이벤트명은 오타로
|
|
65
|
+
* 보고 throw 한다(조용히 무시 금지).
|
|
66
|
+
*
|
|
67
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
68
|
+
* @param {(...args: any[]) => void} listener
|
|
69
|
+
* @returns {this} 체이닝용.
|
|
70
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
71
|
+
*/
|
|
72
|
+
on(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
73
|
+
/**
|
|
74
|
+
* {@link MegaCircuitBreaker#on} 으로 등록한 리스너를 해제한다. 알 수 없는 이벤트명은 오타로 보고
|
|
75
|
+
* throw 한다 — `on()` 과 동일한 화이트리스트. 검증 없이 위임하면 오타 이벤트명이 조용히 매칭
|
|
76
|
+
* 실패해 "해제했다고 믿지만 리스너가 그대로 살아있는" 디버깅 지옥을 만든다. (`removeListener` 별칭도 보호.)
|
|
77
|
+
*
|
|
78
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
79
|
+
* @param {(...args: any[]) => void} listener
|
|
80
|
+
* @returns {this} 체이닝용.
|
|
81
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
82
|
+
*/
|
|
83
|
+
off(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
84
|
+
/**
|
|
85
|
+
* 1회성 구독(EventEmitter `once`). on() 과 동일 화이트리스트(L-1).
|
|
86
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
87
|
+
* @param {(...args: any[]) => void} listener @returns {this}
|
|
88
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
89
|
+
*/
|
|
90
|
+
once(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
91
|
+
/**
|
|
92
|
+
* `on` 의 EventEmitter 별칭. 우회로 화이트리스트를 비껴가지 않도록 동일 검증(L-1).
|
|
93
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
94
|
+
* @param {(...args: any[]) => void} listener @returns {this}
|
|
95
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
96
|
+
*/
|
|
97
|
+
addListener(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
98
|
+
/**
|
|
99
|
+
* `off` 의 EventEmitter 별칭. 동일 화이트리스트 검증(L-1).
|
|
100
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
101
|
+
* @param {(...args: any[]) => void} listener @returns {this}
|
|
102
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
103
|
+
*/
|
|
104
|
+
removeListener(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
105
|
+
/**
|
|
106
|
+
* 리스너를 큐 앞에 추가(EventEmitter `prependListener`). 동일 화이트리스트 검증(L-1).
|
|
107
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
108
|
+
* @param {(...args: any[]) => void} listener @returns {this}
|
|
109
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
110
|
+
*/
|
|
111
|
+
prependListener(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
112
|
+
/**
|
|
113
|
+
* 1회성 리스너를 큐 앞에 추가(EventEmitter `prependOnceListener`). 동일 화이트리스트 검증(L-1).
|
|
114
|
+
* @param {'fire'|'success'|'failure'|'timeout'|'reject'|'open'|'halfOpen'|'close'|'fallback'|'semaphoreLocked'|'healthCheckFailed'|'shutdown'} event
|
|
115
|
+
* @param {(...args: any[]) => void} listener @returns {this}
|
|
116
|
+
* @throws {RangeError} 알 수 없는 이벤트명일 때.
|
|
117
|
+
*/
|
|
118
|
+
prependOnceListener(event: "fire" | "success" | "failure" | "timeout" | "reject" | "open" | "halfOpen" | "close" | "fallback" | "semaphoreLocked" | "healthCheckFailed" | "shutdown", listener: (...args: any[]) => void): this;
|
|
119
|
+
/** 회로를 강제로 연다(차단). 테스트/운영 수동 개입용. */
|
|
120
|
+
open(): void;
|
|
121
|
+
/** 회로를 강제로 닫는다(정상 복귀). 카운터를 비우고 호출을 다시 흘려보낸다. */
|
|
122
|
+
close(): void;
|
|
123
|
+
/** 브레이커를 비활성화한다 — 회로 로직을 우회하고 원함수를 항상 그대로 호출. */
|
|
124
|
+
disable(): void;
|
|
125
|
+
/** {@link MegaCircuitBreaker#disable} 를 되돌려 회로 로직을 다시 활성화한다. */
|
|
126
|
+
enable(): void;
|
|
127
|
+
/**
|
|
128
|
+
* 브레이커를 영구 종료한다 — 내부 롤링 윈도우 타이머를 정리한다. **테스트/graceful shutdown 시 필수**
|
|
129
|
+
* (호출 안 하면 setInterval 타이머가 남아 프로세스/테스트 종료를 막는다).
|
|
130
|
+
*/
|
|
131
|
+
shutdown(): void;
|
|
132
|
+
/** @returns {boolean} 회로가 열려(차단) 있으면 true. */
|
|
133
|
+
get isOpen(): boolean;
|
|
134
|
+
/** @returns {boolean} 회로가 닫혀(정상) 있으면 true. */
|
|
135
|
+
get isClosed(): boolean;
|
|
136
|
+
/** @returns {boolean} 복구 프로빙(half-open) 중이면 true. */
|
|
137
|
+
get isHalfOpen(): boolean;
|
|
138
|
+
/** @returns {boolean} 브레이커가 활성(enable) 상태면 true. */
|
|
139
|
+
get isEnabled(): boolean;
|
|
140
|
+
/** @returns {boolean} 브레이커가 종료(shutdown)됐으면 true. */
|
|
141
|
+
get isShutdown(): boolean;
|
|
142
|
+
/** @returns {string} 브레이커 이름. */
|
|
143
|
+
get name(): string;
|
|
144
|
+
/**
|
|
145
|
+
* 통계 스냅샷을 평탄화해 반환한다(핵심 카운터만). 모니터링/디버그 로그용.
|
|
146
|
+
* @returns {MegaCircuitBreakerStats}
|
|
147
|
+
*/
|
|
148
|
+
getStats(): MegaCircuitBreakerStats;
|
|
149
|
+
/**
|
|
150
|
+
* 내부 opossum 브레이커 raw 핸들(escape hatch). 여기서 노출하지 않은 opossum 고급 기능(캐시·
|
|
151
|
+
* coalesce·healthCheck·snapshot 등)이 필요할 때만 직접 접근한다. 어댑터의 `.native` 와 같은 취지.
|
|
152
|
+
* @returns {import('opossum')<any[], any>}
|
|
153
|
+
*/
|
|
154
|
+
get raw(): import("opossum")<any[], any>;
|
|
155
|
+
#private;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 브레이커 생성 옵션. opossum 옵션 중 **서킷 브레이커 본질에 해당하는 것만** 추려 노출한다(캐시·
|
|
159
|
+
* coalesce 등 부가 기능은 escape hatch {@link MegaCircuitBreaker#raw} 로 직접 접근). 디폴트는
|
|
160
|
+
* opossum 정본값을 **명시적으로** 다시 박는다 — opossum 버전 드리프트와 무관하게 동작을 고정.
|
|
161
|
+
*/
|
|
162
|
+
export type MegaCircuitBreakerOptions = {
|
|
163
|
+
/**
|
|
164
|
+
* - 단일 호출 제한 시간(ms). 초과 시 `ETIMEDOUT` 으로 실패 처리.
|
|
165
|
+
* `false`(또는 0)면 타임아웃 끔.
|
|
166
|
+
*/
|
|
167
|
+
timeout?: number;
|
|
168
|
+
/**
|
|
169
|
+
* - 롤링 윈도우 실패율(%)이 이 값을 넘으면 회로 open.
|
|
170
|
+
*/
|
|
171
|
+
errorThresholdPercentage?: number;
|
|
172
|
+
/**
|
|
173
|
+
* - open 상태 유지 시간(ms). 경과 후 halfOpen 으로 1회 프로빙.
|
|
174
|
+
*/
|
|
175
|
+
resetTimeout?: number;
|
|
176
|
+
/**
|
|
177
|
+
* - 실패율 집계 롤링 윈도우 길이(ms).
|
|
178
|
+
*/
|
|
179
|
+
rollingCountTimeout?: number;
|
|
180
|
+
/**
|
|
181
|
+
* - 롤링 윈도우를 나누는 버킷 수.
|
|
182
|
+
*/
|
|
183
|
+
rollingCountBuckets?: number;
|
|
184
|
+
/**
|
|
185
|
+
* - 이 횟수만큼 호출이 쌓이기 전엔 실패율이 높아도 open 안 함
|
|
186
|
+
* (표본 부족으로 인한 조기 trip 방지). 0=비활성.
|
|
187
|
+
*/
|
|
188
|
+
volumeThreshold?: number;
|
|
189
|
+
/**
|
|
190
|
+
* - 동시 진행(in-flight) 호출 상한. 초과분은 `ESEMLOCKED` 로 즉시 거부.
|
|
191
|
+
* 미지정=무제한.
|
|
192
|
+
*/
|
|
193
|
+
capacity?: number;
|
|
194
|
+
/**
|
|
195
|
+
* - `true` 반환 시 그 에러는 **실패로 집계하지 않음**
|
|
196
|
+
* (예: 4xx 같은 "정상적 거절"). 회로 trip 대상에서 제외.
|
|
197
|
+
*/
|
|
198
|
+
errorFilter?: (err: any, ...args: any[]) => boolean;
|
|
199
|
+
/**
|
|
200
|
+
* - 브레이커 이름(이벤트·stats 식별용). 미지정 시 `fn.name || 'anonymous'`.
|
|
201
|
+
*/
|
|
202
|
+
name?: string;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* 브레이커 통계 스냅샷({@link MegaCircuitBreaker#getStats} 반환). opossum `status.stats` 의 핵심
|
|
206
|
+
* 카운터만 추려 평탄화한다(핵심 필드만, 페이로드·시크릿 없음).
|
|
207
|
+
*/
|
|
208
|
+
export type MegaCircuitBreakerStats = {
|
|
209
|
+
/**
|
|
210
|
+
* - 브레이커 이름.
|
|
211
|
+
*/
|
|
212
|
+
name: string;
|
|
213
|
+
/**
|
|
214
|
+
* - 현재 상태. shutdown 후엔 `'shutdown'`.
|
|
215
|
+
*/
|
|
216
|
+
state: "closed" | "open" | "halfOpen" | "shutdown";
|
|
217
|
+
/**
|
|
218
|
+
* - 누적 호출 시도 수.
|
|
219
|
+
*/
|
|
220
|
+
fires: number;
|
|
221
|
+
/**
|
|
222
|
+
* - 성공 수.
|
|
223
|
+
*/
|
|
224
|
+
successes: number;
|
|
225
|
+
/**
|
|
226
|
+
* - 실패 수.
|
|
227
|
+
*/
|
|
228
|
+
failures: number;
|
|
229
|
+
/**
|
|
230
|
+
* - 타임아웃 수.
|
|
231
|
+
*/
|
|
232
|
+
timeouts: number;
|
|
233
|
+
/**
|
|
234
|
+
* - 회로 open 으로 거부된 수.
|
|
235
|
+
*/
|
|
236
|
+
rejects: number;
|
|
237
|
+
/**
|
|
238
|
+
* - 폴백이 실행된 수.
|
|
239
|
+
*/
|
|
240
|
+
fallbacks: number;
|
|
241
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cron 표현식 정적 유틸. 모든 메서드는 상태가 없다(인스턴스화 불필요).
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* MegaCron.isValid('0 3 * * *') // true
|
|
6
|
+
* MegaCron.validate('nope') // throws Error
|
|
7
|
+
* MegaCron.next('0 3 * * *', new Date(), { timezone: 'Asia/Seoul' }) // 다음 새벽 3시(KST)
|
|
8
|
+
*/
|
|
9
|
+
export class MegaCron {
|
|
10
|
+
/**
|
|
11
|
+
* 표현식을 파싱 검증한다. 유효하면 아무것도 반환하지 않고, 무효면 throw 한다(삼키지 않음).
|
|
12
|
+
* @param {string} expr - cron 표현식.
|
|
13
|
+
* @param {MegaCronOptions} [opts] - timezone 등(타임존도 함께 검증).
|
|
14
|
+
* @returns {void}
|
|
15
|
+
* @throws {TypeError} expr 이 비문자열/빈 문자열일 때.
|
|
16
|
+
* @throws {Error} 표현식/타임존 파싱 실패 시(원본은 `.cause`).
|
|
17
|
+
*/
|
|
18
|
+
static validate(expr: string, opts?: MegaCronOptions): void;
|
|
19
|
+
/**
|
|
20
|
+
* 표현식 유효 여부를 throw 없이 boolean 으로 돌려준다. 사용자 입력 검증 UI 등에 쓴다.
|
|
21
|
+
* @param {string} expr - cron 표현식.
|
|
22
|
+
* @param {MegaCronOptions} [opts] - timezone 등.
|
|
23
|
+
* @returns {boolean} 유효하면 true.
|
|
24
|
+
*/
|
|
25
|
+
static isValid(expr: string, opts?: MegaCronOptions): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* `from`(기본=현재) **이후** 첫 발생 시각을 계산한다.
|
|
28
|
+
* @param {string} expr - cron 표현식.
|
|
29
|
+
* @param {Date} [from] - 기준 시각(이 시각 이후의 다음 발생). 미지정 시 현재.
|
|
30
|
+
* @param {MegaCronOptions} [opts] - timezone 등.
|
|
31
|
+
* @returns {Date} 다음 발생 시각.
|
|
32
|
+
* @throws {Error} 표현식 무효 시, 또는 이후 발생이 없을 때(예: 일회성 과거 시각).
|
|
33
|
+
*/
|
|
34
|
+
static next(expr: string, from?: Date, opts?: MegaCronOptions): Date;
|
|
35
|
+
/**
|
|
36
|
+
* `from`(기본=현재) 이후 **n개**의 발생 시각을 계산한다. 모니터링/미리보기용.
|
|
37
|
+
* @param {string} expr - cron 표현식.
|
|
38
|
+
* @param {number} n - 개수(양의 정수).
|
|
39
|
+
* @param {Date} [from] - 기준 시각. 미지정 시 현재.
|
|
40
|
+
* @param {MegaCronOptions} [opts] - timezone 등.
|
|
41
|
+
* @returns {Date[]} 발생 시각 배열(시간 오름차순). 더 없으면 길이가 n 보다 짧을 수 있다.
|
|
42
|
+
* @throws {TypeError} n 이 양의 정수가 아닐 때.
|
|
43
|
+
* @throws {Error} 표현식 무효 시.
|
|
44
|
+
*/
|
|
45
|
+
static nextRuns(expr: string, n: number, from?: Date, opts?: MegaCronOptions): Date[];
|
|
46
|
+
/**
|
|
47
|
+
* `from`(기본=현재) **이전** 가장 가까운 발생 시각을 계산한다(역산). croner `previousRuns(1, ref)` 위임.
|
|
48
|
+
* @param {string} expr - cron 표현식.
|
|
49
|
+
* @param {Date} [from] - 기준 시각(이 시각 이전의 가장 최근 발생). 미지정 시 현재.
|
|
50
|
+
* @param {MegaCronOptions} [opts] - timezone 등.
|
|
51
|
+
* @returns {Date} 이전 발생 시각.
|
|
52
|
+
* @throws {Error} 표현식 무효 시, 또는 이전 발생이 없을 때.
|
|
53
|
+
*/
|
|
54
|
+
static prev(expr: string, from?: Date, opts?: MegaCronOptions): Date;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* cron 계산 옵션. timezone 은 발생 시각 계산에 영향을 준다(예: `0 3 * * *` 를 `Asia/Seoul` 로 보면
|
|
58
|
+
* 한국 새벽 3시 = UTC 18:00).
|
|
59
|
+
*/
|
|
60
|
+
export type MegaCronOptions = {
|
|
61
|
+
/**
|
|
62
|
+
* - IANA 타임존(예: `'Asia/Seoul'`, `'Europe/Stockholm'`). 미지정 시
|
|
63
|
+
* 호스트 로컬 타임존. 잘못된 타임존은 throw.
|
|
64
|
+
*/
|
|
65
|
+
timezone?: string;
|
|
66
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MegaHash — 비밀번호 해싱 진입점. 03-api-spec §765 의 `static password = { hash, verify }` 표면.
|
|
3
|
+
*/
|
|
4
|
+
export class MegaHash {
|
|
5
|
+
static password: {
|
|
6
|
+
/**
|
|
7
|
+
* 비밀번호를 scrypt 로 해시한다. 매 호출 새 random salt → 같은 비밀번호도 매번 다른 해시.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} plain - 평문 비밀번호.
|
|
10
|
+
* @param {{ N?: number, r?: number, p?: number, keylen?: number, saltBytes?: number }} [opts]
|
|
11
|
+
* - scrypt 파라미터 오버라이드(미지정 시 ADR-130 디폴트). 강한 보호가 필요할 때만 사용.
|
|
12
|
+
* @returns {Promise<string>} `$scrypt$N=..,r=..,p=..$<salt>$<hash>` 포맷 문자열.
|
|
13
|
+
* @throws {MegaValidationError} `hash.invalid_password` - plain 이 비-문자열/빈 문자열.
|
|
14
|
+
*/
|
|
15
|
+
hash(plain: string, opts?: {
|
|
16
|
+
N?: number;
|
|
17
|
+
r?: number;
|
|
18
|
+
p?: number;
|
|
19
|
+
keylen?: number;
|
|
20
|
+
saltBytes?: number;
|
|
21
|
+
}): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* 평문이 저장된 해시와 일치하는지 검증한다(상수 시간 비교). 03-api-spec §765 — verify 는 검증 액션
|
|
24
|
+
* 자체라 `is*` 접두사 룰의 예외로 동사형 그대로 둔다.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} plain - 평문 비밀번호.
|
|
27
|
+
* @param {string} stored - {@link MegaHash.password.hash} 가 만든 해시 문자열.
|
|
28
|
+
* @returns {Promise<boolean>} 일치하면 true. 포맷 손상/비-scrypt/불일치는 모두 false(fail-closed).
|
|
29
|
+
*/
|
|
30
|
+
verify(plain: string, stored: string): Promise<boolean>;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 헬스 체크 등록.
|
|
3
|
+
* @param {string} name
|
|
4
|
+
* @param {() => Promise<{ ok: boolean, [key: string]: any }> | { ok: boolean }} fn
|
|
5
|
+
* @param {{ timeoutMs?: number }} [opts] - 체크별 타임아웃(양의 정수 ms, 기본 {@link DEFAULT_CHECK_TIMEOUT_MS}).
|
|
6
|
+
*/
|
|
7
|
+
export function register(name: string, fn: () => Promise<{
|
|
8
|
+
ok: boolean;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}> | {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
}, opts?: {
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}): void;
|
|
15
|
+
/**
|
|
16
|
+
* 모든 체크 실행 (병렬). 하나라도 false 면 전체 ok=false.
|
|
17
|
+
* @returns {Promise<{ ok: boolean, checks: Record<string, { ok: boolean, error?: string, [key: string]: any }> }>}
|
|
18
|
+
*/
|
|
19
|
+
export function checkAll(): Promise<{
|
|
20
|
+
ok: boolean;
|
|
21
|
+
checks: Record<string, {
|
|
22
|
+
ok: boolean;
|
|
23
|
+
error?: string;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}>;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* @returns {Promise<boolean>}
|
|
29
|
+
*/
|
|
30
|
+
export function isReady(): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* 디버그·테스트용.
|
|
33
|
+
* @returns {number}
|
|
34
|
+
*/
|
|
35
|
+
export function registeredCount(): number;
|
|
36
|
+
/**
|
|
37
|
+
* 테스트용 reset.
|
|
38
|
+
*/
|
|
39
|
+
export function _reset(): void;
|
|
40
|
+
/** 체크 1개의 기본 타임아웃(ms) — hung 체크가 readiness 응답을 무기한 막지 않게 한다. */
|
|
41
|
+
export const DEFAULT_CHECK_TIMEOUT_MS: 5000;
|