@luckystack/core 0.6.2 → 0.6.4

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/index.js CHANGED
@@ -49,16 +49,7 @@ import {
49
49
  tryCatchSync,
50
50
  validatePagePath,
51
51
  waitForSocket
52
- } from "./chunk-O457EZZQ.js";
53
- import {
54
- DEFAULT_PROJECT_CONFIG,
55
- deepMerge,
56
- getProjectConfig,
57
- getProjectName,
58
- isPlainObject,
59
- isProjectConfigRegistered,
60
- registerProjectConfig
61
- } from "./chunk-3HQDGXMX.js";
52
+ } from "./chunk-WBKHSJID.js";
62
53
  import {
63
54
  DEFAULT_REDACTED_LOG_KEYS,
64
55
  DEPTH_TRUNCATED_PLACEHOLDER,
@@ -96,10 +87,17 @@ import {
96
87
  startSpan,
97
88
  startSpanAcrossTrackers,
98
89
  startSpanHandle
99
- } from "./chunk-KPQX46TT.js";
90
+ } from "./chunk-PIUX2DOF.js";
100
91
  import {
101
- createRegistry
102
- } from "./chunk-USQA6QTX.js";
92
+ DEFAULT_PROJECT_CONFIG,
93
+ createRegistry,
94
+ deepMerge,
95
+ getProjectConfig,
96
+ getProjectName,
97
+ isPlainObject,
98
+ isProjectConfigRegistered,
99
+ registerProjectConfig
100
+ } from "./chunk-ZHNQOQBR.js";
103
101
 
104
102
  // src/escapeHtml.ts
105
103
  var HTML_ESCAPE_MAP = {
@@ -599,6 +597,23 @@ var resetRedisKeyFormatterForTests = () => {
599
597
  var formatKey = (namespace, suffix = "") => getRedisKeyFormatter()(namespace, suffix);
600
598
  var applyStrayKeyPrefix = (key) => key.includes(":") ? key : `${getProjectName()}:${key}`;
601
599
 
600
+ // src/secretsResolved.ts
601
+ var listeners = /* @__PURE__ */ new Set();
602
+ var registerSecretsResolvedListener = (listener) => {
603
+ listeners.add(listener);
604
+ return () => {
605
+ listeners.delete(listener);
606
+ };
607
+ };
608
+ var notifySecretsResolved = (changedKeys) => {
609
+ for (const listener of listeners) {
610
+ try {
611
+ listener(changedKeys);
612
+ } catch {
613
+ }
614
+ }
615
+ };
616
+
602
617
  // src/redis.ts
603
618
  var cachedDefault = null;
604
619
  var DEFAULT_MAX_REDIS_RECONNECT_ATTEMPTS = 50;
@@ -609,11 +624,10 @@ var readPositiveIntEnv = (name, fallback) => {
609
624
  const parsed = Number.parseInt(raw, 10);
610
625
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
611
626
  };
612
- var buildDefaultRedisClient = () => {
613
- if (cachedDefault) return cachedDefault;
627
+ var constructRedisClient = () => {
614
628
  const maxReconnectAttempts = readPositiveIntEnv("LUCKYSTACK_REDIS_MAX_RECONNECTS", DEFAULT_MAX_REDIS_RECONNECT_ATTEMPTS);
615
629
  const maxBackoffMs = readPositiveIntEnv("LUCKYSTACK_REDIS_MAX_BACKOFF_MS", DEFAULT_MAX_REDIS_BACKOFF_MS);
616
- cachedDefault = new Redis({
630
+ const client = new Redis({
617
631
  host: env.REDIS_HOST,
618
632
  port: Number.parseInt(env.REDIS_PORT, 10),
619
633
  ...process.env.REDIS_USER && { username: process.env.REDIS_USER },
@@ -628,21 +642,26 @@ var buildDefaultRedisClient = () => {
628
642
  return Math.min(times * 50, maxBackoffMs);
629
643
  }
630
644
  });
631
- cachedDefault.on("ready", async () => {
645
+ client.on("ready", async () => {
632
646
  getLogger().info("Connected to Redis");
633
647
  });
634
- cachedDefault.on("error", (err) => {
648
+ client.on("error", (err) => {
635
649
  const message = String(err?.message ?? err);
636
650
  const password = process.env.REDIS_PASSWORD ?? "";
637
651
  if (/WRONGPASS|NOAUTH|invalid username-password/i.test(message) && /_V\d+$/i.test(password)) {
638
652
  getLogger().error(
639
- `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. Resolve secrets first, then call \`resetDefaultRedisClient()\` (or \`registerRedisClient(new Redis(getRedisConnectionOptions()))\`) after init so the client is rebuilt with the resolved value. See docs/luckystack/ARCHITECTURE_SECRET_MANAGER.md.`,
653
+ `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.`,
640
654
  err
641
655
  );
642
656
  return;
643
657
  }
644
658
  getLogger().error("Error connecting to Redis", err);
645
659
  });
660
+ return client;
661
+ };
662
+ var buildDefaultRedisClient = () => {
663
+ if (cachedDefault) return cachedDefault;
664
+ cachedDefault = constructRedisClient();
646
665
  return cachedDefault;
647
666
  };
648
667
  var resetDefaultRedisClient = () => {
@@ -654,7 +673,25 @@ var resetDefaultRedisClient = () => {
654
673
  } catch {
655
674
  }
656
675
  };
676
+ var rebuildDefaultRedisClient = () => {
677
+ const previousRegistered = isRedisClientRegistered() ? getRedisClient() : null;
678
+ resetDefaultRedisClient();
679
+ const client = constructRedisClient();
680
+ registerRedisClient(client);
681
+ if (previousRegistered && previousRegistered !== client) {
682
+ try {
683
+ previousRegistered.disconnect();
684
+ } catch {
685
+ }
686
+ }
687
+ return client;
688
+ };
657
689
  setDefaultRedisResolver(buildDefaultRedisClient);
690
+ registerSecretsResolvedListener((changedKeys) => {
691
+ if (changedKeys === void 0 || changedKeys.some((key) => /^REDIS_/i.test(key))) {
692
+ rebuildDefaultRedisClient();
693
+ }
694
+ });
658
695
  var getRedisConnectionOptions = () => ({
659
696
  host: env.REDIS_HOST,
660
697
  port: Number.parseInt(env.REDIS_PORT, 10),
@@ -1932,7 +1969,7 @@ var validateInputByType = async ({
1932
1969
  return { status: "success" };
1933
1970
  }
1934
1971
  if (resolveEnvKey() === "production") {
1935
- const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-USHD3PRE.js");
1972
+ const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-FC544PY2.js");
1936
1973
  if (getProjectConfig2().validation.runtimeMode === "off") {
1937
1974
  return { status: "success" };
1938
1975
  }
@@ -2266,6 +2303,7 @@ export {
2266
2303
  normalizeErrorResponseCore,
2267
2304
  normalizeOrigin,
2268
2305
  notify,
2306
+ notifySecretsResolved,
2269
2307
  oneTimeTokenKey,
2270
2308
  parseServiceRouteName,
2271
2309
  parseTransportRouteName,
@@ -2274,6 +2312,7 @@ export {
2274
2312
  processUpload,
2275
2313
  readBootUuid,
2276
2314
  readSession,
2315
+ rebuildDefaultRedisClient,
2277
2316
  recordMetricAcrossTrackers,
2278
2317
  redis,
2279
2318
  registerApiAbortController,
@@ -2302,6 +2341,7 @@ export {
2302
2341
  registerRedisKeyFormatter,
2303
2342
  registerRoomNameFormatter,
2304
2343
  registerRuntimeMapsProvider,
2344
+ registerSecretsResolvedListener,
2305
2345
  registerServicesConfig,
2306
2346
  registerSessionProvider,
2307
2347
  registerSocketMiddleware,