@luckystack/core 0.6.3 → 0.6.5

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/CHANGELOG.md CHANGED
@@ -18,19 +18,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
18
  - **Decoupled secrets-resolved hook** (ADR 0026): `notifySecretsResolved(changedKeys?)`
19
19
  + `registerSecretsResolvedListener(fn)`. A secret resolver (e.g.
20
20
  `@luckystack/secret-manager` via `onApplied: notifySecretsResolved`) fires it after
21
- overwriting `process.env`; `redis.ts` self-registers a listener that drops the cached
22
- default client when a `REDIS_` credential changed, so Redis auth via a secret-manager
23
- POINTER survives boot AND rotation with no hand-wiring. Generic — Prisma pools / SDK
24
- clients can subscribe too. Also adds an optional structural `secretManager?` field on
25
- `ProjectConfig` (`SecretManagerConfigRef`) so the server boot can detect a resolver.
21
+ overwriting `process.env`; `redis.ts` self-registers a listener that EAGERLY REBUILDS +
22
+ registers the default client when a `REDIS_` credential changed, so Redis auth via a
23
+ secret-manager POINTER survives boot AND rotation with no hand-wiring. Generic — Prisma
24
+ pools / SDK clients can subscribe too. Also adds `rebuildDefaultRedisClient()` and an
25
+ optional structural `secretManager?` field on `ProjectConfig` (`SecretManagerConfigRef`)
26
+ so the server boot can detect a resolver.
26
27
 
27
28
  ### Fixed
28
29
 
29
30
  - **Redis secret-manager pointer boot** (ADR 0026): the default Redis client no longer
30
31
  fails auth with a baked-in `REDIS_PASSWORD_V<n>` pointer when it was built (during an
31
- early import) before secrets resolved — the decoupled hook above + the server-boot
32
- reset rebuild it from the resolved env. The `WRONGPASS` diagnostic now points at the
33
- automatic handling + the `envNames` allowlist requirement.
32
+ early import) before secrets resolved. The framework EAGERLY REBUILDS + registers a
33
+ fresh client from the resolved env (`rebuildDefaultRedisClient()`), so the boot-UUID
34
+ write authenticates with the real password — no consumer code. **Correction over
35
+ 0.6.3/0.6.4:** the rebuild logic was right but never TRIGGERED for a normal project (the
36
+ server-boot gate `getProjectConfig().secretManager?.url` is falsy — the scaffold doesn't
37
+ register `secretManager` into `projectConfig` — and bare `initSecretManager` wires no
38
+ `onApplied`). Core now publishes `notifySecretsResolved` onto a decoupled global-symbol
39
+ ARRAY (`Symbol.for('luckystack.secretsResolved.listeners')`) at module load, which
40
+ `@luckystack/secret-manager` fires automatically after every resolve — so the rebuild
41
+ happens at resolve time with zero consumer code, in prod and dev, and even survives a
42
+ dual `@luckystack/core` instance.
34
43
  - **CORE-2** — `tryCatchSync<T, P = void>` now mirrors `tryCatch`'s `P` default,
35
44
  so a params-less call can pass only the result type (`tryCatchSync<URL>(() =>
36
45
  new URL(raw))`) instead of failing with TS2558.
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ import { z } from 'zod';
6
6
  import * as _prisma_client_runtime_library from '@prisma/client/runtime/library';
7
7
  import * as _prisma_client from '@prisma/client';
8
8
  import { PrismaClient } from '@prisma/client';
9
- import Redis, { Redis as Redis$1 } from 'ioredis';
9
+ import Redis$1, { Redis } from 'ioredis';
10
10
  import { RedisAdapterOptions } from '@socket.io/redis-adapter';
11
11
  import { ServerResponse, IncomingMessage, IncomingHttpHeaders } from 'node:http';
12
12
  import 'socket.io-client';
@@ -679,6 +679,7 @@ declare const isDbHealthCheckRegistered: () => boolean;
679
679
  declare const resetDbHealthCheckForTests: () => void;
680
680
 
681
681
  declare const resetDefaultRedisClient: () => void;
682
+ declare const rebuildDefaultRedisClient: () => Redis;
682
683
  interface RedisConnectionOptions {
683
684
  host: string;
684
685
  port: number;
@@ -687,7 +688,7 @@ interface RedisConnectionOptions {
687
688
  }
688
689
  declare const getRedisConnectionOptions: () => RedisConnectionOptions;
689
690
  declare const registerStrayPrefixCommand: (...commands: string[]) => void;
690
- declare const redis: Redis;
691
+ declare const redis: Redis$1;
691
692
 
692
693
  type SecretsResolvedListener = (changedKeys: readonly string[] | undefined) => void;
693
694
  declare const registerSecretsResolvedListener: (listener: SecretsResolvedListener) => (() => void);
@@ -722,15 +723,15 @@ declare const releaseLease: (name: string, token: string) => Promise<boolean>;
722
723
 
723
724
  declare const DEFAULT_CLIENT_KEY = "default";
724
725
  declare const registerPrismaClient: (client: PrismaClient, key?: string) => PrismaClient;
725
- declare const registerRedisClient: (client: Redis$1, key?: string) => Redis$1;
726
+ declare const registerRedisClient: (client: Redis, key?: string) => Redis;
726
727
  declare const isPrismaClientRegistered: () => boolean;
727
728
  declare const isRedisClientRegistered: () => boolean;
728
729
  declare const getPrismaClientKeys: () => string[];
729
730
  declare const getRedisClientKeys: () => string[];
730
731
  declare const getPrismaClientFor: (key?: string) => PrismaClient;
731
- declare const getRedisClientFor: (key?: string) => Redis$1;
732
+ declare const getRedisClientFor: (key?: string) => Redis;
732
733
  declare const getPrismaClient: () => PrismaClient;
733
- declare const getRedisClient: () => Redis$1;
734
+ declare const getRedisClient: () => Redis;
734
735
  declare const resetClientsForTests: () => void;
735
736
 
736
737
  interface AttachSocketRedisAdapterOptions {
@@ -744,9 +745,9 @@ interface AttachSocketRedisAdapterOptions {
744
745
  * a dedicated adapter connection, custom auth, or a sentinel/cluster handle.
745
746
  * When omitted the framework duplicates the default `redis` handle.
746
747
  */
747
- pubClient?: Redis$1;
748
+ pubClient?: Redis;
748
749
  /** Pre-built sub client (see `pubClient`). */
749
- subClient?: Redis$1;
750
+ subClient?: Redis;
750
751
  }
751
752
  declare const attachSocketRedisAdapter: (io: Server, options?: AttachSocketRedisAdapterOptions) => void;
752
753
 
@@ -1894,4 +1895,4 @@ declare const clearAllHooks: () => void;
1894
1895
  declare const registerSyncHook: <TName extends SyncHookName>(name: TName, handler: SyncHookHandler<SyncHookPayloads[TName]>) => (() => void);
1895
1896
  declare const dispatchSyncHook: <TName extends SyncHookName>(name: TName, payload: SyncHookPayloads[TName]) => void;
1896
1897
 
1897
- export { type ApiAuthRejectedPayload, type ApiErrorPayload, type ApiResponseEnvelope, AuthProps, type AvatarConfig, type AvatarConfigInput, BOOT_KEY_PREFIX, BaseSessionLayout, type CheckRateLimitParams, type CookiePrefixConstraints, type CorsRejectedPayload, type CreateLocalizedNormalizerInput, type CsrfConfig, type CsrfCookieOptions, type CsrfMismatchPayload, DEFAULT_AVATAR_CONFIG, DEFAULT_CLIENT_KEY, DEFAULT_CSRF_CONFIG, DEFAULT_ENV_FILES, DEFAULT_REDACTED_LOG_KEYS, DEPTH_TRUNCATED_PLACEHOLDER, type DbHealthCheck, type DbHealthResult, DeepPartial, type DeployConfigShape, type DeployDevelopmentShape, type DeployEnvironmentShape, type DeployResourceShape, type DeployRoutingShape, type DispatchResult, type EmailAttachment, type EmailMessage, type EmailResult, type EmailSender, type EmailSenderRegistry, type ErrorFormatter, type ErrorFormatterContext, ErrorParam, ErrorResponseInput, type ErrorTracker, type ErrorTrackerContext, type ErrorTrackerEvent, type ErrorTrackerUser, type FormatterEnvelope, GENERATED_API_DOCS_PATH, GENERATED_API_SCHEMAS_PATH, GENERATED_SOCKET_TYPES_PATH, HealthHashConfig, type HealthHashDescriptor, type HookHandler, type HookName, type HookPayloads, type HookResult, type HookSessionShape, type HookStopSignal, type HttpMethod, type LanguageCode, type LocaleReloader, type LocalizedNormalizer, type NormalizeErrorResponseInput, NormalizedErrorResponse, type OnUploadCompletePayload, type OnUploadStartPayload, type OneTimeTokenHandle, PUBLIC_DIR, type PeerRequire, type PostApiExecutePayload, type PostApiRespondPayload, type PostApiValidatePayload, type PostAvatarServePayload, type PostErrorNormalizePayload, type PostHttpRequestPayload, type PostSessionRefreshPayload, type PostSyncAuthorizePayload, type PostSyncExecutePayload, type PostSyncFanoutPayload, type PostSyncValidatePayload, type PreApiExecutePayload, type PreApiRespondPayload, type PreApiValidatePayload, type PreAvatarServePayload, type PreCaptureFilter, type PreErrorNormalizePayload, type PreHttpRequestPayload, type PreServerStopPayload, type PreSessionRefreshPayload, type PreSocketMessagePayload, type PreSyncAuthorizePayload, type PreSyncExecutePayload, type PreSyncFanoutPayload, type PreSyncRecipientPayload, type PreSyncValidatePayload, type PresetDefinition, type ProcessUploadInput, type ProcessUploadResult, REDACTED_PLACEHOLDER, ROOT_DIR, type RateLimitExceededPayload, type RateLimitResult, type RateLimitStrategy, type RedisConnectionOptions, type RedisKeyFormatter, type Registry, type RegistryOptions, type ResolveClientIpParams, type RoomNameFormatter, type RoomNameFormatterContext, type RuntimeApiMapsResult, type RuntimeEnv, type RuntimeMapsProvider, type RuntimeSyncMapsResult, SERVER_DIR, SERVER_FUNCTIONS_DIR, SHARED_DIR, SRC_DIR, type SecretsResolvedListener, type ServiceDefinition, type ServicesConfigShape, type SessionCreatedPayload, type SessionLogoutInput, type SessionProvider, type SessionRevokedPayload, type SessionSaveResult, type SocketMiddleware, type SpanHandle, type SpanResult, type SyncErrorPayload, type SyncHookHandler, type SyncHookName, type SyncHookPayloads, TSCONFIG_ALIAS_FILES, type TranslateFunction, type TranslateInput, UNKNOWN_CLIENT_IP, UPLOADS_DIR, type ValidationResult$1 as ValidationResult, abortAllForSocket, abortApiByResponseIndex, abortSyncByCb, acquireLease, allowedOrigin, type apiMessage, appendErrorTracker, applyCookiePrefixConstraints, applyErrorFormatter, applySocketMiddlewares, applyStrayKeyPrefix, attachSocketRedisAdapter, bootstrapEnv, captureException, captureExceptionAcrossTrackers, captureMessage, captureMessageAcrossTrackers, checkRateLimit, clearAllHooks, clearAllRateLimits, clearRateLimit, clearSocketMiddlewares, collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, consumeOneTimeToken, consumeOneTimeTokenJson, createLocalizedNormalizer, createRegistry, defaultRateLimitStrategy, defaultRedisKeyFormatter, defaultRoomNameFormatter, deriveTokenBucketId, describeHealthHashConfig, dispatchHook, dispatchSyncHook, ensurePeerDepInstalled, env, escapeHtml, extractLanguageFromHeader, extractTokenFromRequest, extractTokenFromSocket, flushErrorTrackers, formatKey, formatRoomName, getActiveErrorTrackers, getAvatarConfig, getBindAddress, getCookieValue, getCsrfConfig, getCurrentErrorTrackerIdentity, getDbHealthCheck, getDeployConfig, getEffectiveHttpMethod, getEmailSender, getEmailSenderByName, getEnv, getEnvFiles, getErrorFormatter, getGeneratedApiDocsPath, getGeneratedApiSchemasPath, getGeneratedSocketTypesPath, getIoInstance, getLocaleReloader, getLocalizedNormalizer, getParams, getPrismaClient, getPrismaClientFor, getPrismaClientKeys, getPublicDir, getRateLimitStatus, getRateLimitStrategy, getRedactedLogKeys, getRedisClient, getRedisClientFor, getRedisClientKeys, getRedisConnectionOptions, getRedisKeyFormatter, getRegisteredSessionProvider, getRoomNameFormatter, getRuntimeApiMaps, getRuntimeSyncMaps, getServerDir, getServerFunctionDirs, getServerFunctionsDir, getServicesConfig, getSharedDir, getSocketMiddlewares, getSrcDir, getUploadsDir, hasCookie, hashSynchronizedValue, hashSynchronizedValueWith, inferHttpMethod, initConsolelog, initSharedSentry, isDbHealthCheckRegistered, isDeployConfigRegistered, isEmailSenderRegistered, isFalsy, isGetMethodName, isLocalizedNormalizerRegistered, isLoopbackIp, isMethodAllowed, isPrismaClientRegistered, isPrismaClientResolvable, isProduction, isRedactedLogKey, isRedisClientRegistered, isRuntimeMapsProviderRegistered, isServicesConfigRegistered, isSessionProviderRegistered, issueOneTimeToken, listEmailSenderNames, loadEnvFiles, loadPeer, normalizeErrorResponse, normalizeOrigin, notifySecretsResolved, oneTimeTokenKey, performLogout, prisma, processUpload, readBootUuid, readSession, recordMetricAcrossTrackers, redis, registerApiAbortController, registerAvatarConfig, registerBindAddress, registerCsrfConfig, registerDbHealthCheck, registerDeployConfig, registerEmailSender, registerEmailSenders, registerErrorFormatter, registerErrorTracker, registerErrorTrackers, registerHook, registerLocaleReloader, registerLocalizedNormalizer, registerPreCaptureFilter, registerPrismaClient, registerRateLimitStrategy, registerRedactedLogKeys, registerRedisClient, registerRedisKeyFormatter, registerRoomNameFormatter, registerRuntimeMapsProvider, registerSecretsResolvedListener, registerServicesConfig, registerSessionProvider, registerSocketMiddleware, registerStrayPrefixCommand, registerSyncAbortController, registerSyncHook, releaseLease, removeSession, renewLease, resetClientsForTests, resetCsrfConfigForTests, resetDbHealthCheckForTests, resetDefaultRedisClient, resetRedactedLogKeysForTests, resetRedisKeyFormatterForTests, resetSessionProviderForTests, resolveClientIp, resolveEnvKey, resolveErrorMessage, resolveFromRoot, resolveHealthHashConfig, resolveHealthHashConfigFromDescriptor, runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, sanitizeErrorString, sanitizeErrorStrings, sanitizeForLog, serveAvatar, setCurrentErrorTrackerIdentity, setErrorTrackerUser, setIoInstance, setSentryUser, startSpan, startSpanAcrossTrackers, startSpanHandle, type syncMessage, tryCatch, tryCatchSync, unregisterApiAbortController, unregisterSyncAbortController, validateInputByType, validateRequest, writeBootUuid, writeSession };
1898
+ export { type ApiAuthRejectedPayload, type ApiErrorPayload, type ApiResponseEnvelope, AuthProps, type AvatarConfig, type AvatarConfigInput, BOOT_KEY_PREFIX, BaseSessionLayout, type CheckRateLimitParams, type CookiePrefixConstraints, type CorsRejectedPayload, type CreateLocalizedNormalizerInput, type CsrfConfig, type CsrfCookieOptions, type CsrfMismatchPayload, DEFAULT_AVATAR_CONFIG, DEFAULT_CLIENT_KEY, DEFAULT_CSRF_CONFIG, DEFAULT_ENV_FILES, DEFAULT_REDACTED_LOG_KEYS, DEPTH_TRUNCATED_PLACEHOLDER, type DbHealthCheck, type DbHealthResult, DeepPartial, type DeployConfigShape, type DeployDevelopmentShape, type DeployEnvironmentShape, type DeployResourceShape, type DeployRoutingShape, type DispatchResult, type EmailAttachment, type EmailMessage, type EmailResult, type EmailSender, type EmailSenderRegistry, type ErrorFormatter, type ErrorFormatterContext, ErrorParam, ErrorResponseInput, type ErrorTracker, type ErrorTrackerContext, type ErrorTrackerEvent, type ErrorTrackerUser, type FormatterEnvelope, GENERATED_API_DOCS_PATH, GENERATED_API_SCHEMAS_PATH, GENERATED_SOCKET_TYPES_PATH, HealthHashConfig, type HealthHashDescriptor, type HookHandler, type HookName, type HookPayloads, type HookResult, type HookSessionShape, type HookStopSignal, type HttpMethod, type LanguageCode, type LocaleReloader, type LocalizedNormalizer, type NormalizeErrorResponseInput, NormalizedErrorResponse, type OnUploadCompletePayload, type OnUploadStartPayload, type OneTimeTokenHandle, PUBLIC_DIR, type PeerRequire, type PostApiExecutePayload, type PostApiRespondPayload, type PostApiValidatePayload, type PostAvatarServePayload, type PostErrorNormalizePayload, type PostHttpRequestPayload, type PostSessionRefreshPayload, type PostSyncAuthorizePayload, type PostSyncExecutePayload, type PostSyncFanoutPayload, type PostSyncValidatePayload, type PreApiExecutePayload, type PreApiRespondPayload, type PreApiValidatePayload, type PreAvatarServePayload, type PreCaptureFilter, type PreErrorNormalizePayload, type PreHttpRequestPayload, type PreServerStopPayload, type PreSessionRefreshPayload, type PreSocketMessagePayload, type PreSyncAuthorizePayload, type PreSyncExecutePayload, type PreSyncFanoutPayload, type PreSyncRecipientPayload, type PreSyncValidatePayload, type PresetDefinition, type ProcessUploadInput, type ProcessUploadResult, REDACTED_PLACEHOLDER, ROOT_DIR, type RateLimitExceededPayload, type RateLimitResult, type RateLimitStrategy, type RedisConnectionOptions, type RedisKeyFormatter, type Registry, type RegistryOptions, type ResolveClientIpParams, type RoomNameFormatter, type RoomNameFormatterContext, type RuntimeApiMapsResult, type RuntimeEnv, type RuntimeMapsProvider, type RuntimeSyncMapsResult, SERVER_DIR, SERVER_FUNCTIONS_DIR, SHARED_DIR, SRC_DIR, type SecretsResolvedListener, type ServiceDefinition, type ServicesConfigShape, type SessionCreatedPayload, type SessionLogoutInput, type SessionProvider, type SessionRevokedPayload, type SessionSaveResult, type SocketMiddleware, type SpanHandle, type SpanResult, type SyncErrorPayload, type SyncHookHandler, type SyncHookName, type SyncHookPayloads, TSCONFIG_ALIAS_FILES, type TranslateFunction, type TranslateInput, UNKNOWN_CLIENT_IP, UPLOADS_DIR, type ValidationResult$1 as ValidationResult, abortAllForSocket, abortApiByResponseIndex, abortSyncByCb, acquireLease, allowedOrigin, type apiMessage, appendErrorTracker, applyCookiePrefixConstraints, applyErrorFormatter, applySocketMiddlewares, applyStrayKeyPrefix, attachSocketRedisAdapter, bootstrapEnv, captureException, captureExceptionAcrossTrackers, captureMessage, captureMessageAcrossTrackers, checkRateLimit, clearAllHooks, clearAllRateLimits, clearRateLimit, clearSocketMiddlewares, collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, consumeOneTimeToken, consumeOneTimeTokenJson, createLocalizedNormalizer, createRegistry, defaultRateLimitStrategy, defaultRedisKeyFormatter, defaultRoomNameFormatter, deriveTokenBucketId, describeHealthHashConfig, dispatchHook, dispatchSyncHook, ensurePeerDepInstalled, env, escapeHtml, extractLanguageFromHeader, extractTokenFromRequest, extractTokenFromSocket, flushErrorTrackers, formatKey, formatRoomName, getActiveErrorTrackers, getAvatarConfig, getBindAddress, getCookieValue, getCsrfConfig, getCurrentErrorTrackerIdentity, getDbHealthCheck, getDeployConfig, getEffectiveHttpMethod, getEmailSender, getEmailSenderByName, getEnv, getEnvFiles, getErrorFormatter, getGeneratedApiDocsPath, getGeneratedApiSchemasPath, getGeneratedSocketTypesPath, getIoInstance, getLocaleReloader, getLocalizedNormalizer, getParams, getPrismaClient, getPrismaClientFor, getPrismaClientKeys, getPublicDir, getRateLimitStatus, getRateLimitStrategy, getRedactedLogKeys, getRedisClient, getRedisClientFor, getRedisClientKeys, getRedisConnectionOptions, getRedisKeyFormatter, getRegisteredSessionProvider, getRoomNameFormatter, getRuntimeApiMaps, getRuntimeSyncMaps, getServerDir, getServerFunctionDirs, getServerFunctionsDir, getServicesConfig, getSharedDir, getSocketMiddlewares, getSrcDir, getUploadsDir, hasCookie, hashSynchronizedValue, hashSynchronizedValueWith, inferHttpMethod, initConsolelog, initSharedSentry, isDbHealthCheckRegistered, isDeployConfigRegistered, isEmailSenderRegistered, isFalsy, isGetMethodName, isLocalizedNormalizerRegistered, isLoopbackIp, isMethodAllowed, isPrismaClientRegistered, isPrismaClientResolvable, isProduction, isRedactedLogKey, isRedisClientRegistered, isRuntimeMapsProviderRegistered, isServicesConfigRegistered, isSessionProviderRegistered, issueOneTimeToken, listEmailSenderNames, loadEnvFiles, loadPeer, normalizeErrorResponse, normalizeOrigin, notifySecretsResolved, oneTimeTokenKey, performLogout, prisma, processUpload, readBootUuid, readSession, rebuildDefaultRedisClient, recordMetricAcrossTrackers, redis, registerApiAbortController, registerAvatarConfig, registerBindAddress, registerCsrfConfig, registerDbHealthCheck, registerDeployConfig, registerEmailSender, registerEmailSenders, registerErrorFormatter, registerErrorTracker, registerErrorTrackers, registerHook, registerLocaleReloader, registerLocalizedNormalizer, registerPreCaptureFilter, registerPrismaClient, registerRateLimitStrategy, registerRedactedLogKeys, registerRedisClient, registerRedisKeyFormatter, registerRoomNameFormatter, registerRuntimeMapsProvider, registerSecretsResolvedListener, registerServicesConfig, registerSessionProvider, registerSocketMiddleware, registerStrayPrefixCommand, registerSyncAbortController, registerSyncHook, releaseLease, removeSession, renewLease, resetClientsForTests, resetCsrfConfigForTests, resetDbHealthCheckForTests, resetDefaultRedisClient, resetRedactedLogKeysForTests, resetRedisKeyFormatterForTests, resetSessionProviderForTests, resolveClientIp, resolveEnvKey, resolveErrorMessage, resolveFromRoot, resolveHealthHashConfig, resolveHealthHashConfigFromDescriptor, runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, sanitizeErrorString, sanitizeErrorStrings, sanitizeForLog, serveAvatar, setCurrentErrorTrackerIdentity, setErrorTrackerUser, setIoInstance, setSentryUser, startSpan, startSpanAcrossTrackers, startSpanHandle, type syncMessage, tryCatch, tryCatchSync, unregisterApiAbortController, unregisterSyncAbortController, validateInputByType, validateRequest, writeBootUuid, writeSession };
package/dist/index.js CHANGED
@@ -613,6 +613,15 @@ var notifySecretsResolved = (changedKeys) => {
613
613
  }
614
614
  }
615
615
  };
616
+ var GLOBAL_LISTENERS_SYMBOL = /* @__PURE__ */ Symbol.for("luckystack.secretsResolved.listeners");
617
+ var getGlobalSecretsResolvedListeners = () => {
618
+ const existing = Reflect.get(globalThis, GLOBAL_LISTENERS_SYMBOL);
619
+ if (Array.isArray(existing)) return existing;
620
+ const created = [];
621
+ Reflect.set(globalThis, GLOBAL_LISTENERS_SYMBOL, created);
622
+ return created;
623
+ };
624
+ getGlobalSecretsResolvedListeners().push(notifySecretsResolved);
616
625
 
617
626
  // src/redis.ts
618
627
  var cachedDefault = null;
@@ -624,11 +633,10 @@ var readPositiveIntEnv = (name, fallback) => {
624
633
  const parsed = Number.parseInt(raw, 10);
625
634
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
626
635
  };
627
- var buildDefaultRedisClient = () => {
628
- if (cachedDefault) return cachedDefault;
636
+ var constructRedisClient = () => {
629
637
  const maxReconnectAttempts = readPositiveIntEnv("LUCKYSTACK_REDIS_MAX_RECONNECTS", DEFAULT_MAX_REDIS_RECONNECT_ATTEMPTS);
630
638
  const maxBackoffMs = readPositiveIntEnv("LUCKYSTACK_REDIS_MAX_BACKOFF_MS", DEFAULT_MAX_REDIS_BACKOFF_MS);
631
- cachedDefault = new Redis({
639
+ const client = new Redis({
632
640
  host: env.REDIS_HOST,
633
641
  port: Number.parseInt(env.REDIS_PORT, 10),
634
642
  ...process.env.REDIS_USER && { username: process.env.REDIS_USER },
@@ -643,21 +651,26 @@ var buildDefaultRedisClient = () => {
643
651
  return Math.min(times * 50, maxBackoffMs);
644
652
  }
645
653
  });
646
- cachedDefault.on("ready", async () => {
654
+ client.on("ready", async () => {
647
655
  getLogger().info("Connected to Redis");
648
656
  });
649
- cachedDefault.on("error", (err) => {
657
+ client.on("error", (err) => {
650
658
  const message = String(err?.message ?? err);
651
659
  const password = process.env.REDIS_PASSWORD ?? "";
652
660
  if (/WRONGPASS|NOAUTH|invalid username-password/i.test(message) && /_V\d+$/i.test(password)) {
653
661
  getLogger().error(
654
- `Redis auth failed and REDIS_PASSWORD ("${password}") looks like an UNRESOLVED secret-manager pointer. The default Redis client was likely built before secret-manager init ran. The framework now drops a stale client automatically when \`config.secretManager.url\` is set (server boot) and when \`initSecretManager\` is wired with \`onApplied: notifySecretsResolved\`; if you resolve secrets by other means, call \`resetDefaultRedisClient()\` (or \`registerRedisClient(new Redis(getRedisConnectionOptions()))\`) after resolution. Also confirm \`REDIS_PASSWORD\` is listed in the secret-manager \`envNames\` allowlist (unset resolves nothing). See docs/luckystack/ARCHITECTURE_SECRET_MANAGER.md.`,
662
+ `Redis auth failed and REDIS_PASSWORD ("${password}") looks like an UNRESOLVED secret-manager pointer. The default Redis client was likely built before secret-manager init ran. The framework now EAGERLY REBUILDS + registers a fresh client automatically when \`config.secretManager.url\` is set (server boot) and when \`initSecretManager\` is wired with \`onApplied: notifySecretsResolved\`; if you resolve secrets by other means, call \`rebuildDefaultRedisClient()\` (or \`registerRedisClient(new Redis(getRedisConnectionOptions()))\`) after resolution. Also confirm \`REDIS_PASSWORD\` is listed in the secret-manager \`envNames\` allowlist (unset resolves nothing). See docs/luckystack/ARCHITECTURE_SECRET_MANAGER.md.`,
655
663
  err
656
664
  );
657
665
  return;
658
666
  }
659
667
  getLogger().error("Error connecting to Redis", err);
660
668
  });
669
+ return client;
670
+ };
671
+ var buildDefaultRedisClient = () => {
672
+ if (cachedDefault) return cachedDefault;
673
+ cachedDefault = constructRedisClient();
661
674
  return cachedDefault;
662
675
  };
663
676
  var resetDefaultRedisClient = () => {
@@ -669,10 +682,23 @@ var resetDefaultRedisClient = () => {
669
682
  } catch {
670
683
  }
671
684
  };
685
+ var rebuildDefaultRedisClient = () => {
686
+ const previousRegistered = isRedisClientRegistered() ? getRedisClient() : null;
687
+ resetDefaultRedisClient();
688
+ const client = constructRedisClient();
689
+ registerRedisClient(client);
690
+ if (previousRegistered && previousRegistered !== client) {
691
+ try {
692
+ previousRegistered.disconnect();
693
+ } catch {
694
+ }
695
+ }
696
+ return client;
697
+ };
672
698
  setDefaultRedisResolver(buildDefaultRedisClient);
673
699
  registerSecretsResolvedListener((changedKeys) => {
674
700
  if (changedKeys === void 0 || changedKeys.some((key) => /^REDIS_/i.test(key))) {
675
- resetDefaultRedisClient();
701
+ rebuildDefaultRedisClient();
676
702
  }
677
703
  });
678
704
  var getRedisConnectionOptions = () => ({
@@ -2295,6 +2321,7 @@ export {
2295
2321
  processUpload,
2296
2322
  readBootUuid,
2297
2323
  readSession,
2324
+ rebuildDefaultRedisClient,
2298
2325
  recordMetricAcrossTrackers,
2299
2326
  redis,
2300
2327
  registerApiAbortController,