@gaonjs/config 0.17.4 → 0.17.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/wire.d.ts CHANGED
@@ -3,13 +3,6 @@ import { Redis } from 'ioredis';
3
3
  import { type GaonNats } from '@gaonjs/async';
4
4
  import type { GaonConfig } from './types.js';
5
5
  import { type DiscoveredApp } from './apps.js';
6
- /**
7
- * 결정 230: gaon.config.ts 의 `timezone` 을 **실제로 배선**한다(이전엔 저장만·조용한 no-op).
8
- * process.env.TZ 를 세워 이후 Date·로깅 타임스탬프·스케줄러(크론)가 선언 타임존을 따르게 한다.
9
- * Node 는 런타임 TZ 변경을 반영한다(실측 · 첫 Date 사용 전에 부팅 경로에서 세운다). 유효하지
10
- * 않은 IANA 이름은 process.env.TZ 에 넣으면 조용히 UTC 로 떨어지므로, 넣기 전에 검증해
11
- * 부팅 에러(§7.5.3)로 알린다 — 또 다른 조용한 실패를 만들지 않는다.
12
- */
13
6
  export declare function applyTimezone(config: GaonConfig): void;
14
7
  /** 부팅 결과 — 이후 serve 가 listen 한다. close() 로 배선을 뒤집는다. */
15
8
  export interface WiredGaon {
package/dist/wire.js CHANGED
@@ -7,6 +7,7 @@
7
7
  // 세션은 세션 옵션이 세팅된 앱에만 등록된다. AppSessionOptions.secret 은
8
8
  // 필수이므로 auth 앱은 명시적으로 secret 을 제공해야 한다(env 로 주입 권장).
9
9
  import { Redis } from 'ioredis';
10
+ import { log } from '@gaonjs/core';
10
11
  import { createDb, registerConnection, destroyAllConnections, getConnection, hasConnection, setServiceTxWrapper, setCacheBackend, redisCacheBackend, } from '@gaonjs/data';
11
12
  import { connectNats, configureJobs, configureEvents, outboxServiceTxWrapper, ensureOutboxTable, setLockBackend, redisLockBackend, memoryLockBackend, } from '@gaonjs/async';
12
13
  import { configureMailer, smtpTransport } from '@gaonjs/mail';
@@ -23,6 +24,38 @@ import { resolve as resolvePath, join } from 'node:path';
23
24
  * 않은 IANA 이름은 process.env.TZ 에 넣으면 조용히 UTC 로 떨어지므로, 넣기 전에 검증해
24
25
  * 부팅 에러(§7.5.3)로 알린다 — 또 다른 조용한 실패를 만들지 않는다.
25
26
  */
27
+ /**
28
+ * A6(결정 262): Redis 부팅 도달성을 상한 시간 안에 확인한다. ping 이 상한 안에 응답하지
29
+ * 않으면(다운·URL 오타·방화벽) 수리 안내를 담아 throw 한다 — 상위(mid-wire catch)가 부분
30
+ * 핸들을 정리하고 rethrow 해 serve 가 확정 종료(exit 1)한다. 정상 Redis 면 ping 이 즉시
31
+ * 응답해 부팅 지연이 없다. 상한은 GAON_REDIS_PROBE_MS(기본 10000)로 조정 가능하다.
32
+ */
33
+ async function assertRedisReachable(redis, url) {
34
+ const raw = Number(process.env.GAON_REDIS_PROBE_MS);
35
+ const probeMs = Number.isFinite(raw) && raw > 0 ? raw : 10000;
36
+ const ping = redis.ping();
37
+ // 레이스 패자가 나중에 reject 해도 미처리(unhandledRejection) 되지 않게 미리 삼킨다.
38
+ ping.catch(() => { });
39
+ let timer;
40
+ try {
41
+ await Promise.race([
42
+ ping,
43
+ new Promise((_, reject) => {
44
+ timer = setTimeout(() => reject(new Error(`ping 응답 없음(${probeMs}ms 초과)`)), probeMs);
45
+ }),
46
+ ]);
47
+ }
48
+ catch (err) {
49
+ const msg = err instanceof Error ? err.message : String(err);
50
+ throw new Error(`[@gaonjs/config] Redis 연결 실패 (${url}): ${msg}\n` +
51
+ `→ 개발 인프라를 기동하세요: gaon dev (또는 docker compose up -d redis)\n` +
52
+ `→ 접속지를 확인하세요: gaon.config 의 redis.url 또는 환경변수 REDIS_URL (.env.example 참조).`);
53
+ }
54
+ finally {
55
+ if (timer)
56
+ clearTimeout(timer);
57
+ }
58
+ }
26
59
  export function applyTimezone(config) {
27
60
  const tz = config.timezone;
28
61
  if (tz === undefined)
@@ -84,6 +117,15 @@ export async function wireDomain(config, cwd = process.cwd()) {
84
117
  // 세션 스토어로도 재사용한다(프로세스당 커넥션 1개).
85
118
  if (config.redis) {
86
119
  redis = new Redis(config.redis.url, { lazyConnect: false, maxRetriesPerRequest: 3 });
120
+ // A6(FINAL-AUDIT-2026-08-04 · 결정 262): 부팅 시 Redis 도달성을 fail-loud 로 확인한다
121
+ // (DB 결정 250/252 와 대칭). 이전엔 error 리스너·접속 확인이 없어 Redis 다운·URL 오타에도
122
+ // wireGaon 이 정상 반환 → serve 가 green 부팅하고 세션·락·캐시가 요청 시점에야 타임아웃
123
+ // 파손했다(무신호). 배경 재접속 에러(부팅 후 순단)는 ioredis 가 명령 단위로 처리하므로
124
+ // 리스너로 받아 프로세스 크래시(unhandled 'error' 이벤트)만 막는다.
125
+ redis.on('error', (err) => {
126
+ log.debug('config: redis 배경 에러(재접속은 ioredis 가 처리)', { err: err.message });
127
+ });
128
+ await assertRedisReachable(redis, config.redis.url);
87
129
  setLockBackend(redisLockBackend(redis));
88
130
  setCacheBackend(redisCacheBackend(redis));
89
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaonjs/config",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "description": "Gaon 루트 설정 로더·자동 배선 (gaon.config.ts)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,13 +25,13 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "ioredis": "^5.4.1",
28
- "@gaonjs/async": "0.15.0",
29
- "@gaonjs/data": "0.17.0",
30
- "@gaonjs/i18n": "0.2.2",
31
- "@gaonjs/mail": "0.3.0",
32
- "@gaonjs/core": "0.2.3",
33
- "@gaonjs/storage": "0.1.4",
34
- "@gaonjs/web": "0.19.4"
28
+ "@gaonjs/async": "0.15.2",
29
+ "@gaonjs/core": "0.2.4",
30
+ "@gaonjs/mail": "0.3.1",
31
+ "@gaonjs/data": "0.17.1",
32
+ "@gaonjs/storage": "0.1.5",
33
+ "@gaonjs/web": "0.20.1",
34
+ "@gaonjs/i18n": "0.2.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "fastify": "^5.0.0"