@luckystack/core 0.13.0 → 0.15.0

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/bootUuid.d.ts +16 -0
  3. package/dist/{chunk-PN2FPJXI.js → chunk-55YXIACQ.js} +1 -1
  4. package/dist/chunk-55YXIACQ.js.map +1 -0
  5. package/dist/{chunk-RC7ZQBO6.js → chunk-EZIBIQXO.js} +3 -3
  6. package/dist/{chunk-C4AK6P5D.js → chunk-NJULJZ3N.js} +29 -17
  7. package/dist/chunk-NJULJZ3N.js.map +1 -0
  8. package/dist/{chunk-6UPWMV6L.js → chunk-YKVBUWB6.js} +2 -2
  9. package/dist/chunk-YKVBUWB6.js.map +1 -0
  10. package/dist/client.d.ts +1 -1
  11. package/dist/client.js +74 -12
  12. package/dist/client.js.map +1 -1
  13. package/dist/config.js +2 -2
  14. package/dist/deployConfigRegistry.d.ts +3 -1
  15. package/dist/emailRegistry.d.ts +12 -1
  16. package/dist/errorTrackerIdentity.d.ts +2 -0
  17. package/dist/hooks/registry.d.ts +8 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +142 -32
  20. package/dist/index.js.map +1 -1
  21. package/dist/localSocketEnumerationGuard.d.ts +2 -1
  22. package/dist/middlewareRegistry.d.ts +13 -1
  23. package/dist/{projectConfig-EOJRMH36.js → projectConfig-6QNMG5GZ.js} +2 -2
  24. package/dist/projectConfig.d.ts +13 -9
  25. package/dist/react/languageOverride.d.ts +8 -0
  26. package/dist/redactedLogKeys.d.ts +2 -0
  27. package/dist/roomNameFormatterRegistry.d.ts +7 -5
  28. package/dist/{sentrySetup-T7535RYZ.js → sentrySetup-MZTUOMHL.js} +3 -3
  29. package/dist/sessionProviderRegistry.d.ts +14 -6
  30. package/package.json +1 -1
  31. package/dist/chunk-6UPWMV6L.js.map +0 -1
  32. package/dist/chunk-C4AK6P5D.js.map +0 -1
  33. package/dist/chunk-PN2FPJXI.js.map +0 -1
  34. /package/dist/{chunk-RC7ZQBO6.js.map → chunk-EZIBIQXO.js.map} +0 -0
  35. /package/dist/{projectConfig-EOJRMH36.js.map → projectConfig-6QNMG5GZ.js.map} +0 -0
  36. /package/dist/{sentrySetup-T7535RYZ.js.map → sentrySetup-MZTUOMHL.js.map} +0 -0
@@ -6,7 +6,14 @@ export type DispatchResult = {
6
6
  signal: HookStopSignal;
7
7
  };
8
8
  export declare const registerHook: <TName extends HookName>(name: TName, handler: HookHandler<HookPayloads[TName]>) => (() => void);
9
- export declare const dispatchHook: <TName extends HookName>(name: TName, payload: HookPayloads[TName]) => Promise<DispatchResult>;
9
+ export interface DispatchHookOptions {
10
+ /**
11
+ * Receives a throwing handler's error instead of the default log line and
12
+ * tracker capture, e.g. to redact it or to report many dispatches at once.
13
+ */
14
+ onError?: (error: unknown) => void;
15
+ }
16
+ export declare const dispatchHook: <TName extends HookName>(name: TName, payload: HookPayloads[TName], options?: DispatchHookOptions) => Promise<DispatchResult>;
10
17
  export declare const clearAllHooks: () => void;
11
18
  export declare const registerSyncHook: <TName extends SyncHookName>(name: TName, handler: SyncHookHandler<SyncHookPayloads[TName]>) => (() => void);
12
19
  export declare const dispatchSyncHook: <TName extends SyncHookName>(name: TName, payload: SyncHookPayloads[TName]) => void;
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ export type { BaseSessionLayout, SessionLocation, AuthProps, Jsonify } from './s
49
49
  export * from './sentrySetup.js';
50
50
  export { registerErrorTracker, registerErrorTrackers, appendErrorTracker, getActiveErrorTrackers, captureExceptionAcrossTrackers, captureMessageAcrossTrackers, setErrorTrackerUser, recordMetricAcrossTrackers, startSpanAcrossTrackers, startSpanHandle, registerPreCaptureFilter, flushErrorTrackers, sanitizeErrorString, sanitizeErrorStrings, } from './errorTrackerRegistry.js';
51
51
  export type { ErrorTracker, ErrorTrackerContext, ErrorTrackerUser, ErrorTrackerEvent, SpanResult, SpanHandle, PreCaptureFilter, } from './errorTrackerRegistry.js';
52
- export { runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, setCurrentErrorTrackerIdentity, getCurrentErrorTrackerIdentity, } from './errorTrackerIdentity.js';
52
+ export { runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, setCurrentErrorTrackerIdentity, getCurrentErrorTrackerIdentity, hasErrorTrackerIdentityScope, } from './errorTrackerIdentity.js';
53
53
  export * from './env.js';
54
54
  export * from './db.js';
55
55
  export { registerDbHealthCheck, getDbHealthCheck, isDbHealthCheckRegistered, resetDbHealthCheckForTests, } from './dbHealthCheck.js';
@@ -63,7 +63,7 @@ export type { RedisKeyFormatter } from './redisKeyFormatter.js';
63
63
  export { acquireLease, renewLease, releaseLease } from './lease.js';
64
64
  export { DEFAULT_CLIENT_KEY, registerPrismaClient, registerRedisClient, getPrismaClient, getRedisClient, getPrismaClientFor, getRedisClientFor, getPrismaClientKeys, getRedisClientKeys, isPrismaClientRegistered, isRedisClientRegistered, resetClientsForTests, } from './clients.js';
65
65
  export { attachSocketRedisAdapter, resolveSocketAdapterKey } from './socketRedisAdapter.js';
66
- export { writeBootUuid, readBootUuid, refreshBootUuid, startBootUuidHeartbeat, resolveEnvKey, BOOT_KEY_PREFIX, type BootUuidHeartbeat, } from './bootUuid.js';
66
+ export { writeBootUuid, readBootUuid, refreshBootUuid, startBootUuidHeartbeat, resolveEnvKey, BOOT_KEY_PREFIX, bootUuidKey, type BootUuidHeartbeat, } from './bootUuid.js';
67
67
  export { collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, hashSynchronizedValue, hashSynchronizedValueWith, resolveHealthHashConfig, describeHealthHashConfig, resolveHealthHashConfigFromDescriptor, } from './synchronizedEnvHashes.js';
68
68
  export type { HealthHashDescriptor } from './synchronizedEnvHashes.js';
69
69
  export { initConsolelog } from './consoleLog.js';
@@ -89,7 +89,7 @@ export { extractTokenFromRequest } from './extractTokenFromRequest.js';
89
89
  export { resolveClientIp, isLoopbackIp, UNKNOWN_CLIENT_IP, deriveTokenBucketId } from './resolveClientIp.js';
90
90
  export type { ResolveClientIpParams } from './resolveClientIp.js';
91
91
  export { registerSessionProvider, getRegisteredSessionProvider, isSessionProviderRegistered, resetSessionProviderForTests, readSession, writeSession, removeSession, performLogout, } from './sessionProviderRegistry.js';
92
- export type { SessionProvider, SessionSaveResult, SessionLogoutInput } from './sessionProviderRegistry.js';
92
+ export type { SessionProvider, SessionSaveResult, SessionSaveOptions, SessionLogoutInput } from './sessionProviderRegistry.js';
93
93
  export { validateRequest, isFalsy } from './validateRequest.js';
94
94
  export type { ValidationResult } from './validateRequest.js';
95
95
  export { checkRateLimit, getRateLimitStatus, clearRateLimit, clearAllRateLimits, registerRateLimitStrategy, getRateLimitStrategy, defaultRateLimitStrategy, } from './rateLimiter.js';
@@ -106,5 +106,5 @@ export type { TestResetTarget } from './testResetTargets.js';
106
106
  export { issueOneTimeToken, consumeOneTimeToken, consumeOneTimeTokenJson, oneTimeTokenKey, } from './oneTimeToken.js';
107
107
  export type { OneTimeTokenHandle } from './oneTimeToken.js';
108
108
  export { registerHook, dispatchHook, clearAllHooks, registerSyncHook, dispatchSyncHook } from './hooks/registry.js';
109
- export type { DispatchResult } from './hooks/registry.js';
109
+ export type { DispatchHookOptions, DispatchResult } from './hooks/registry.js';
110
110
  export type { HookSessionShape, HookStopSignal, HookResult, HookHandler, HookName, HookPayloads, PreApiValidatePayload, PostApiValidatePayload, PreApiExecutePayload, PostApiExecutePayload, PreApiRespondPayload, PostApiRespondPayload, ApiResponseEnvelope, PreHttpRequestPayload, PostHttpRequestPayload, PreSocketMessagePayload, PreSyncAuthorizePayload, PostSyncAuthorizePayload, PreSyncValidatePayload, PostSyncValidatePayload, PreSyncExecutePayload, PostSyncExecutePayload, PreSyncFanoutPayload, PostSyncFanoutPayload, PreSyncRecipientPayload, ApiErrorPayload, SyncErrorPayload, ApiAuthRejectedPayload, RateLimitExceededPayload, CorsRejectedPayload, CsrfMismatchPayload, PreSessionRefreshPayload, PostSessionRefreshPayload, SessionCreatedPayload, SessionRevokedPayload, OnUploadStartPayload, OnUploadCompletePayload, PreAvatarServePayload, PostAvatarServePayload, PreServerStopPayload, SyncHookName, SyncHookHandler, SyncHookPayloads, PreErrorNormalizePayload, PostErrorNormalizePayload, } from './hooks/types.js';
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ import {
51
51
  tryCatchSync,
52
52
  validatePagePath,
53
53
  waitForSocket
54
- } from "./chunk-RC7ZQBO6.js";
54
+ } from "./chunk-EZIBIQXO.js";
55
55
  import {
56
56
  getDeployConfig,
57
57
  getPortOverride,
@@ -64,7 +64,7 @@ import {
64
64
  registerSecretsResolvedListener,
65
65
  registerServicesConfig,
66
66
  resetPortOverrideForTests
67
- } from "./chunk-6UPWMV6L.js";
67
+ } from "./chunk-YKVBUWB6.js";
68
68
  import {
69
69
  DEFAULT_REDACTED_LOG_KEYS,
70
70
  DEPTH_TRUNCATED_PLACEHOLDER,
@@ -98,7 +98,7 @@ import {
98
98
  startSpan,
99
99
  startSpanAcrossTrackers,
100
100
  startSpanHandle
101
- } from "./chunk-C4AK6P5D.js";
101
+ } from "./chunk-NJULJZ3N.js";
102
102
  import {
103
103
  DEFAULT_PROJECT_CONFIG,
104
104
  createRegistry,
@@ -108,7 +108,7 @@ import {
108
108
  isPlainObject,
109
109
  isProjectConfigRegistered,
110
110
  registerProjectConfig
111
- } from "./chunk-PN2FPJXI.js";
111
+ } from "./chunk-55YXIACQ.js";
112
112
 
113
113
  // src/escapeHtml.ts
114
114
  var HTML_ESCAPE_MAP = {
@@ -170,13 +170,20 @@ var registerHook = (name, handler) => {
170
170
  if (index !== -1) handlers.splice(index, 1);
171
171
  };
172
172
  };
173
- var dispatchHook = async (name, payload) => {
174
- const handlers = hookHandlers.get(name) ?? [];
173
+ var dispatchHook = async (name, payload, options = {}) => {
174
+ const handlers = [...hookHandlers.get(name) ?? []];
175
175
  for (const handler of handlers) {
176
176
  let result;
177
177
  try {
178
178
  result = await handler(payload);
179
179
  } catch (error) {
180
+ if (options.onError) {
181
+ try {
182
+ options.onError(error);
183
+ } catch {
184
+ }
185
+ continue;
186
+ }
180
187
  try {
181
188
  getLogger().error(`hook: handler for "${name}" threw`, error, { hook: name });
182
189
  } catch {
@@ -211,7 +218,7 @@ var registerSyncHook = (name, handler) => {
211
218
  };
212
219
  };
213
220
  var dispatchSyncHook = (name, payload) => {
214
- const handlers = syncHookHandlers.get(name) ?? [];
221
+ const handlers = [...syncHookHandlers.get(name) ?? []];
215
222
  for (const handler of handlers) {
216
223
  try {
217
224
  handler(payload);
@@ -409,8 +416,13 @@ var warnOnBunEnvAutoload = (files) => {
409
416
  var loadEnvFiles = () => {
410
417
  const files = getEnvFiles();
411
418
  warnOnBunEnvAutoload(files);
412
- for (const [index, file] of files.entries()) {
413
- loadDotenv({ path: file, override: index > 0 });
419
+ const ambientKeys = new Set(Object.keys(process.env));
420
+ const fileValues = {};
421
+ for (const file of files) {
422
+ loadDotenv({ path: file, processEnv: fileValues, override: true });
423
+ }
424
+ for (const [key, value] of Object.entries(fileValues)) {
425
+ if (!ambientKeys.has(key)) process.env[key] = value;
414
426
  }
415
427
  if (process.env.LUCKYSTACK_ENV_DEBUG) {
416
428
  reportDuplicateEnvKeys(files);
@@ -472,32 +484,42 @@ var readBound = (target, prop) => {
472
484
  const value = Reflect.get(target, prop);
473
485
  return typeof value === "function" ? value.bind(target) : value;
474
486
  };
475
- var guardSocketsMap = (map) => new Proxy(map, {
487
+ var guardSocketsMap = (map, label) => new Proxy(map, {
476
488
  get(target, prop) {
477
489
  if (ENUMERATING_MAP_MEMBERS.has(prop)) {
478
- throw new LocalSocketEnumerationError("Enumerating `io.sockets.sockets`");
490
+ throw new LocalSocketEnumerationError(`Enumerating \`${label}.sockets\``);
479
491
  }
480
492
  return readBound(target, prop);
481
493
  }
482
494
  });
483
- var guardAdapter = (adapter) => new Proxy(adapter, {
495
+ var guardAdapter = (adapter, label) => new Proxy(adapter, {
484
496
  get(target, prop) {
485
497
  if (PER_INSTANCE_ADAPTER_MEMBERS.has(prop)) {
486
- throw new LocalSocketEnumerationError(`\`io.sockets.adapter.${String(prop)}\``);
498
+ throw new LocalSocketEnumerationError(`\`${label}.adapter.${String(prop)}\``);
487
499
  }
488
500
  return readBound(target, prop);
489
501
  }
490
502
  });
491
- var guardNamespace = (namespace) => new Proxy(namespace, {
503
+ var guardNamespace = (namespace, label) => new Proxy(namespace, {
492
504
  get(target, prop) {
493
- if (prop === "adapter") return guardAdapter(target.adapter);
494
- if (prop === "sockets") return guardSocketsMap(target.sockets);
505
+ if (prop === "adapter") return guardAdapter(target.adapter, label);
506
+ if (prop === "sockets") return guardSocketsMap(target.sockets, label);
495
507
  return readBound(target, prop);
496
508
  }
497
509
  });
510
+ var isGuardableNamespace = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "adapter") === "object" && Reflect.get(value, "sockets") instanceof Map;
498
511
  var guardLocalSocketEnumeration = (io) => new Proxy(io, {
499
512
  get(target, prop) {
500
- if (prop === "sockets") return guardNamespace(target.sockets);
513
+ if (prop === "sockets") return guardNamespace(target.sockets, "io.sockets");
514
+ const value = Reflect.get(target, prop);
515
+ if (prop === "of" && typeof value === "function") {
516
+ return (...args) => {
517
+ const namespace = Reflect.apply(value, target, args);
518
+ if (!isGuardableNamespace(namespace)) return namespace;
519
+ const name = args[0];
520
+ return guardNamespace(namespace, typeof name === "string" ? `io.of('${name}')` : "io.of(\u2026)");
521
+ };
522
+ }
501
523
  return readBound(target, prop);
502
524
  }
503
525
  });
@@ -554,9 +576,7 @@ var registerEmailSender = (sender) => {
554
576
  };
555
577
  var registerEmailSenders = (senders) => {
556
578
  registry4 = { ...senders };
557
- if (senders.default) {
558
- activeSender = senders.default;
559
- }
579
+ activeSender = senders.default ?? null;
560
580
  };
561
581
  var getEmailSender = () => activeSender ?? registry4.default ?? null;
562
582
  var getEmailSenderByName = (name) => {
@@ -585,6 +605,7 @@ var setCurrentErrorTrackerIdentity = (user) => {
585
605
  if (box) box.user = user;
586
606
  };
587
607
  var getCurrentErrorTrackerIdentity = () => identityStore.getStore()?.user ?? null;
608
+ var hasErrorTrackerIdentityScope = () => identityStore.getStore() !== void 0;
588
609
 
589
610
  // src/db.ts
590
611
  import { createRequire as createRequire2 } from "module";
@@ -946,6 +967,10 @@ import { createAdapter } from "@socket.io/redis-adapter";
946
967
  import { randomUUID } from "crypto";
947
968
  var BOOT_KEY_PREFIX = "luckystack:boot:";
948
969
  var DEFAULT_BOOT_KEY_TTL_SECONDS = 3600;
970
+ var bootUuidKey = (envKey) => {
971
+ const projectName = process.env.PROJECT_NAME;
972
+ return `${BOOT_KEY_PREFIX}${projectName && projectName.length > 0 ? projectName : "luckystack"}:${envKey}`;
973
+ };
949
974
  var getBootKeyTtlSeconds = () => getDeployConfig().routing?.bootKeyTtlSeconds ?? DEFAULT_BOOT_KEY_TTL_SECONDS;
950
975
  var getBootUuidRefreshIntervalMs = () => Math.max(100, Math.floor(getBootKeyTtlSeconds() * 1e3 / 3));
951
976
  var resolveEnvKey = () => {
@@ -954,16 +979,16 @@ var resolveEnvKey = () => {
954
979
  var writeBootUuid = async (envKey) => {
955
980
  const key = envKey ?? resolveEnvKey();
956
981
  const uuid = randomUUID();
957
- await redis.set(`${BOOT_KEY_PREFIX}${key}`, uuid, "EX", getBootKeyTtlSeconds());
982
+ await redis.set(bootUuidKey(key), uuid, "EX", getBootKeyTtlSeconds());
958
983
  return uuid;
959
984
  };
960
985
  var readBootUuid = async (envKey) => {
961
986
  const key = envKey ?? resolveEnvKey();
962
- return redis.get(`${BOOT_KEY_PREFIX}${key}`);
987
+ return redis.get(bootUuidKey(key));
963
988
  };
964
989
  var refreshBootUuid = async (envKey) => {
965
990
  const key = envKey ?? resolveEnvKey();
966
- const refreshed = await redis.expire(`${BOOT_KEY_PREFIX}${key}`, getBootKeyTtlSeconds());
991
+ const refreshed = await redis.expire(bootUuidKey(key), getBootKeyTtlSeconds());
967
992
  if (refreshed === 0) await writeBootUuid(key);
968
993
  };
969
994
  var startBootUuidHeartbeat = (envKey) => {
@@ -980,7 +1005,7 @@ var startBootUuidHeartbeat = (envKey) => {
980
1005
  },
981
1006
  (error) => {
982
1007
  getLogger().error(
983
- `[boot-uuid] failed to refresh ${BOOT_KEY_PREFIX}${key}; readiness may degrade if the TTL expires`,
1008
+ `[boot-uuid] failed to refresh ${bootUuidKey(key)}; readiness may degrade if the TTL expires`,
984
1009
  error instanceof Error ? error : new Error(String(error))
985
1010
  );
986
1011
  schedule();
@@ -1110,7 +1135,10 @@ var initConsolelog = () => {
1110
1135
  originalLog(`${colorCode}${extractedInfo}${COLORS.reset}`);
1111
1136
  originalLog(...args);
1112
1137
  } else {
1113
- originalLog(`${colorCode}${extractedInfo} -- ${args.join(" ")}${COLORS.reset}`);
1138
+ const firstNonString = args.findIndex((arg) => typeof arg !== "string");
1139
+ const leading = firstNonString === -1 ? args : args.slice(0, firstNonString);
1140
+ const rest = firstNonString === -1 ? [] : args.slice(firstNonString);
1141
+ originalLog(`${colorCode}${extractedInfo} -- ${leading.join(" ")}${COLORS.reset}`, ...rest);
1114
1142
  }
1115
1143
  };
1116
1144
  };
@@ -1440,7 +1468,8 @@ var extractTokenFromSocket = (socket2) => {
1440
1468
  const config = getProjectConfig();
1441
1469
  const cookie = socket2.handshake.headers.cookie;
1442
1470
  const sessionToken = typeof socket2.handshake.auth.token === "string" ? socket2.handshake.auth.token : null;
1443
- const cookieToken = getCookieValue(cookie, config.http.sessionCookieName);
1471
+ const cookieName = applyCookiePrefixConstraints(config.http.sessionCookieName, config.http.sessionCookiePrefix).name;
1472
+ const cookieToken = getCookieValue(cookie, cookieName);
1444
1473
  if (config.session.basedToken) {
1445
1474
  return sessionToken ?? cookieToken;
1446
1475
  }
@@ -1456,7 +1485,8 @@ var extractTokenFromRequest = (req) => {
1456
1485
  const authHeader = typeof rawAuthHeader === "string" ? rawAuthHeader : Array.isArray(rawAuthHeader) && typeof rawAuthHeader[0] === "string" ? rawAuthHeader[0] : void 0;
1457
1486
  const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
1458
1487
  const config = getProjectConfig();
1459
- const cookieToken = getCookieValue(req.headers.cookie, config.http.sessionCookieName);
1488
+ const cookieName = applyCookiePrefixConstraints(config.http.sessionCookieName, config.http.sessionCookiePrefix).name;
1489
+ const cookieToken = getCookieValue(req.headers.cookie, cookieName);
1460
1490
  if (config.session.basedToken) {
1461
1491
  return bearerToken ?? cookieToken;
1462
1492
  }
@@ -1948,6 +1978,36 @@ var parseObjectFields = (typeText) => {
1948
1978
  }
1949
1979
  return { fields, indexSignatures };
1950
1980
  };
1981
+ var wrapsWhole = (text, open, close) => {
1982
+ if (!text.startsWith(open) || !text.endsWith(close)) return false;
1983
+ let depth = 0;
1984
+ let inString = null;
1985
+ for (let index = 0; index < text.length; index += 1) {
1986
+ const char = text[index];
1987
+ if (inString) {
1988
+ if (char === "\\") index += 1;
1989
+ else if (char === inString) inString = null;
1990
+ continue;
1991
+ }
1992
+ if (char === '"' || char === "'") {
1993
+ inString = char;
1994
+ continue;
1995
+ }
1996
+ if (char === open) depth += 1;
1997
+ if (char === close) {
1998
+ depth -= 1;
1999
+ if (depth === 0) return index === text.length - 1;
2000
+ }
2001
+ }
2002
+ return false;
2003
+ };
2004
+ var unwrapParens = (text) => {
2005
+ let current = text.trim();
2006
+ while (wrapsWhole(current, "(", ")")) current = current.slice(1, -1).trim();
2007
+ return current;
2008
+ };
2009
+ var isObjectTypeText = (text) => wrapsWhole(text, "{", "}");
2010
+ var unquoteLiteral = (literal) => literal.slice(1, -1).replaceAll(/\\(['"\\])/g, "$1");
1951
2011
  var matchesIndexKeyType = ({ keyType, key }) => {
1952
2012
  const normalized = keyType.trim();
1953
2013
  if (normalized === "string") return true;
@@ -1957,7 +2017,7 @@ var matchesIndexKeyType = ({ keyType, key }) => {
1957
2017
  return parts.some((part) => matchesIndexKeyType({ keyType: part, key }));
1958
2018
  }
1959
2019
  if (normalized.startsWith("'") && normalized.endsWith("'") || normalized.startsWith('"') && normalized.endsWith('"')) {
1960
- return key === normalized.slice(1, -1);
2020
+ return key === unquoteLiteral(normalized);
1961
2021
  }
1962
2022
  return false;
1963
2023
  };
@@ -2003,6 +2063,45 @@ var parseRecordParts = (type) => {
2003
2063
  return null;
2004
2064
  };
2005
2065
  var MAX_VALIDATION_DEPTH = 64;
2066
+ var JSON_DATA_TYPES = /* @__PURE__ */ new Set(["JsonValue", "JsonObject", "JsonArray", "JsonPrimitive"]);
2067
+ var isJsonPrimitive = (value) => value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
2068
+ var isPlainObject2 = (value) => {
2069
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
2070
+ const prototype = Object.getPrototypeOf(value);
2071
+ return prototype === Object.prototype || prototype === null;
2072
+ };
2073
+ var validateJsonData = (type, value, path4, depth) => {
2074
+ if (depth > MAX_VALIDATION_DEPTH) {
2075
+ return { status: "error", message: `${path4}: input nesting exceeds the maximum depth of ${String(MAX_VALIDATION_DEPTH)}` };
2076
+ }
2077
+ if (isJsonPrimitive(value)) {
2078
+ return type === "JsonValue" || type === "JsonPrimitive" ? { status: "success" } : { status: "error", message: `${path4} should be ${type === "JsonArray" ? "a JSON array" : "a JSON object"}` };
2079
+ }
2080
+ if (type === "JsonPrimitive") {
2081
+ return { status: "error", message: `${path4} should be a JSON primitive` };
2082
+ }
2083
+ if (Array.isArray(value)) {
2084
+ if (type === "JsonObject") return { status: "error", message: `${path4} should be a JSON object` };
2085
+ for (const [index, element] of value.entries()) {
2086
+ const result = validateJsonData("JsonValue", element, `${path4}[${String(index)}]`, depth + 1);
2087
+ if (result.status === "error") return result;
2088
+ }
2089
+ return { status: "success" };
2090
+ }
2091
+ if (isPlainObject2(value)) {
2092
+ if (type === "JsonArray") return { status: "error", message: `${path4} should be a JSON array` };
2093
+ for (const [key, entry] of Object.entries(value)) {
2094
+ if (PROTO_POLLUTION_KEYS.has(key)) {
2095
+ return { status: "error", message: `${path4}.${key} is not allowed` };
2096
+ }
2097
+ if (entry === void 0) continue;
2098
+ const result = validateJsonData("JsonValue", entry, `${path4}.${key}`, depth + 1);
2099
+ if (result.status === "error") return result;
2100
+ }
2101
+ return { status: "success" };
2102
+ }
2103
+ return { status: "error", message: `${path4} should be JSON data` };
2104
+ };
2006
2105
  var validateType = (typeText, value, path4, depth = 0) => {
2007
2106
  if (depth > MAX_VALIDATION_DEPTH) {
2008
2107
  return { status: "error", message: `${path4}: input nesting exceeds the maximum depth of ${String(MAX_VALIDATION_DEPTH)}` };
@@ -2032,14 +2131,20 @@ var validateType = (typeText, value, path4, depth = 0) => {
2032
2131
  if (type.includes("&")) {
2033
2132
  const intersectionParts = splitTopLevel(type, "&").filter(Boolean);
2034
2133
  if (intersectionParts.length > 1) {
2134
+ const objectParts = intersectionParts.map((part) => unwrapParens(part));
2135
+ if (objectParts.every((part) => isObjectTypeText(part))) {
2136
+ const mergedBody = objectParts.map((part) => part.slice(1, -1)).join("; ");
2137
+ return validateType(`{ ${mergedBody} }`, value, path4, depth + 1);
2138
+ }
2035
2139
  for (const intersectionType of intersectionParts) {
2036
2140
  const result = validateType(intersectionType, value, path4, depth + 1);
2037
2141
  if (result.status === "error") return result;
2038
2142
  }
2039
2143
  return { status: "success" };
2040
2144
  }
2041
- if (intersectionParts.length === 1) {
2042
- return validateType(intersectionParts[0] ?? type, value, path4, depth + 1);
2145
+ const singleMember = intersectionParts[0] ?? type;
2146
+ if (intersectionParts.length === 1 && singleMember !== type) {
2147
+ return validateType(singleMember, value, path4, depth + 1);
2043
2148
  }
2044
2149
  }
2045
2150
  if (type.endsWith("[]")) {
@@ -2057,7 +2162,7 @@ var validateType = (typeText, value, path4, depth = 0) => {
2057
2162
  return { status: "success" };
2058
2163
  }
2059
2164
  if (type.startsWith("'") && type.endsWith("'") || type.startsWith('"') && type.endsWith('"')) {
2060
- const literal = type.slice(1, -1);
2165
+ const literal = unquoteLiteral(type);
2061
2166
  return value === literal ? { status: "success" } : { status: "error", message: `${path4} should equal ${literal}` };
2062
2167
  }
2063
2168
  const numericLiteral = parseFiniteNumericLiteral(type);
@@ -2074,6 +2179,9 @@ var validateType = (typeText, value, path4, depth = 0) => {
2074
2179
  if (type === "any" || type === "unknown") {
2075
2180
  return { status: "success" };
2076
2181
  }
2182
+ if (JSON_DATA_TYPES.has(type)) {
2183
+ return validateJsonData(type, value, path4, depth);
2184
+ }
2077
2185
  if (/^Record<.+>$/.test(type)) {
2078
2186
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2079
2187
  return { status: "error", message: `${path4} should be an object` };
@@ -2164,7 +2272,7 @@ var validateInputByType = async ({
2164
2272
  return { status: "success" };
2165
2273
  }
2166
2274
  if (isProductionRuntime()) {
2167
- const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-EOJRMH36.js");
2275
+ const { getProjectConfig: getProjectConfig2 } = await import("./projectConfig-6QNMG5GZ.js");
2168
2276
  if (getProjectConfig2().validation.runtimeMode === "off") {
2169
2277
  return { status: "success" };
2170
2278
  }
@@ -2371,6 +2479,7 @@ export {
2371
2479
  applySocketMiddlewares,
2372
2480
  applyStrayKeyPrefix,
2373
2481
  attachSocketRedisAdapter,
2482
+ bootUuidKey,
2374
2483
  bootstrapEnv,
2375
2484
  buildApiResponseEventName,
2376
2485
  buildApiStreamEventName,
@@ -2475,6 +2584,7 @@ export {
2475
2584
  getSyncQueueSize,
2476
2585
  getUploadsDir,
2477
2586
  hasCookie,
2587
+ hasErrorTrackerIdentityScope,
2478
2588
  hashSynchronizedValue,
2479
2589
  hashSynchronizedValueWith,
2480
2590
  httpFetch,