@mastra/core
Version:
1 lines • 15.6 kB
Source Map (JSON)
{"version":3,"sources":["../../_internal-core/src/request-context/index.ts"],"names":[],"mappings":"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","file":"chunk-D2FOXUYM.cjs","sourcesContent":["type RecordToTuple<T> = {\n [K in keyof T]: [K, T[K]];\n}[keyof T][];\n\n/**\n * Reserved key for setting resourceId from middleware.\n * When set in RequestContext, this takes precedence over client-provided values\n * for security (prevents attackers from hijacking another user's memory).\n *\n * @example\n * ```typescript\n * // In your auth middleware:\n * const requestContext = c.get('requestContext');\n * requestContext.set(MASTRA_RESOURCE_ID_KEY, authenticatedUser.id);\n * ```\n */\nexport const MASTRA_RESOURCE_ID_KEY = 'mastra__resourceId';\n\n/**\n * Reserved key for setting threadId from middleware.\n * When set in RequestContext, this takes precedence over client-provided values\n * for security (prevents attackers from hijacking another user's memory).\n *\n * @example\n * ```typescript\n * // In your auth middleware:\n * const requestContext = c.get('requestContext');\n * requestContext.set(MASTRA_THREAD_ID_KEY, threadId);\n * ```\n */\nexport const MASTRA_THREAD_ID_KEY = 'mastra__threadId';\n\n/**\n * Reserved key for storing version overrides on RequestContext.\n * When set, sub-agent delegation resolves versioned agents from these overrides.\n *\n * @example\n * ```typescript\n * requestContext.set(MASTRA_VERSIONS_KEY, {\n * agents: { 'researcher-agent': { versionId: '123' } },\n * });\n * ```\n */\nexport const MASTRA_VERSIONS_KEY = 'mastra__versions';\n\n/**\n * Reserved key for storing the raw auth token from the incoming request.\n * Used by the editor to forward authentication when connecting to MCP servers\n * that require the same auth as the Mastra server itself.\n */\nexport const MASTRA_AUTH_TOKEN_KEY = 'mastra__authToken';\n\nexport type VersionSelector = { versionId: string } | { status: 'draft' | 'published' };\n\nexport type VersionOverrides = {\n agents?: Record<string, VersionSelector>;\n /** Fallback status for sub-agents (and future primitives) without an explicit entry. */\n defaultStatus?: 'draft' | 'published';\n};\n\nexport function mergeVersionOverrides(\n base?: VersionOverrides,\n overrides?: VersionOverrides,\n): VersionOverrides | undefined {\n if (!base && !overrides) return undefined;\n\n return {\n ...base,\n ...overrides,\n agents: {\n ...base?.agents,\n ...overrides?.agents,\n },\n // overrides.defaultStatus wins; fall back to base.defaultStatus\n ...(overrides?.defaultStatus\n ? { defaultStatus: overrides.defaultStatus }\n : base?.defaultStatus\n ? { defaultStatus: base.defaultStatus }\n : {}),\n };\n}\n\n/**\n * Marker thrown by `RequestContext.toJSON()` when it detects cyclic re-entry.\n *\n * Cyclic re-entry happens when a stored value transitively references another\n * `RequestContext` whose `toJSON()` is already on the call stack. `JSON.stringify`\n * inside `isSerializable` then walks into that context, V8 invokes its\n * `toJSON()`, which iterates its registry and calls `JSON.stringify` on values\n * that may walk back through the first context — and so on. Each step is a\n * fresh `JSON.stringify` call with a fresh internal cycle stack, so V8's\n * built-in cycle detection never trips and the recursion would pin one CPU\n * core at 100% indefinitely.\n *\n * The fix: throw this marker on reentry. The marker propagates upward through\n * `isSerializable`'s nested catches (which re-throw it) until it reaches the\n * outermost `toJSON()`'s `isSerializable` — there it is swallowed and the\n * offending key is filtered, the same way in-value circular references are\n * filtered today.\n */\nclass CyclicRequestContextToJSONError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CyclicRequestContextToJSONError';\n }\n}\n\n/**\n * Tracks `RequestContext` instances whose `toJSON()` is currently on the call\n * stack. Used to detect cyclic re-entry. Stored as a `WeakSet` so entries are\n * garbage-collected with their owning context.\n */\nconst _toJSONInProgress = new WeakSet<RequestContext<any>>();\n\n/**\n * Nesting depth of active `toJSON()` calls. The outermost call (depth === 1\n * after entry) catches the cyclic marker error and filters the offending\n * value; inner calls re-throw so the marker propagates to the outermost.\n */\nlet _toJSONDepth = 0;\n\nexport class RequestContext<Values extends Record<string, any> | unknown = unknown> {\n private registry = new Map<string, unknown>();\n\n constructor(\n iterable?: Values extends Record<string, any>\n ? RecordToTuple<Partial<Values>>\n : Iterable<readonly [string, unknown]>,\n ) {\n if (iterable && typeof iterable === 'object' && typeof (iterable as any)[Symbol.iterator] !== 'function') {\n this.registry = new Map(Object.entries(iterable));\n } else {\n this.registry = new Map(iterable);\n }\n }\n\n /**\n * set a value with strict typing if `Values` is a Record and the key exists in it.\n */\n public set<K extends Values extends Record<string, any> ? keyof Values : string>(\n key: K,\n value: Values extends Record<string, any> ? (K extends keyof Values ? Values[K] : never) : unknown,\n ): void {\n // The type assertion `key as string` is safe because K always extends string ultimately.\n this.registry.set(key as string, value);\n }\n\n /**\n * Get a value with its type\n */\n public get<\n K extends Values extends Record<string, any> ? keyof Values : string,\n R = Values extends Record<string, any> ? (K extends keyof Values ? Values[K] : never) : unknown,\n >(key: K): R {\n return this.registry.get(key as string) as R;\n }\n\n /**\n * Check if a key exists in the container\n */\n public has<K extends Values extends Record<string, any> ? keyof Values : string>(key: K): boolean {\n return this.registry.has(key);\n }\n\n /**\n * Delete a value by key\n */\n public delete<K extends Values extends Record<string, any> ? keyof Values : string>(key: K): boolean {\n return this.registry.delete(key);\n }\n\n /**\n * Clear all values from the container\n */\n public clear(): void {\n this.registry.clear();\n }\n\n /**\n * Get all keys in the container\n */\n public keys(): IterableIterator<Values extends Record<string, any> ? keyof Values : string> {\n return this.registry.keys() as IterableIterator<Values extends Record<string, any> ? keyof Values : string>;\n }\n\n /**\n * Get all values in the container\n */\n public values(): IterableIterator<Values extends Record<string, any> ? Values[keyof Values] : unknown> {\n return this.registry.values() as IterableIterator<\n Values extends Record<string, any> ? Values[keyof Values] : unknown\n >;\n }\n\n /**\n * Get all entries in the container.\n * Returns a discriminated union of tuples for proper type narrowing when iterating.\n */\n public entries(): IterableIterator<\n Values extends Record<string, any> ? { [K in keyof Values]: [K, Values[K]] }[keyof Values] : [string, unknown]\n > {\n return this.registry.entries() as IterableIterator<\n Values extends Record<string, any> ? { [K in keyof Values]: [K, Values[K]] }[keyof Values] : [string, unknown]\n >;\n }\n\n /**\n * Get the size of the container\n */\n public size(): number {\n return this.registry.size;\n }\n\n /**\n * Execute a function for each entry in the container.\n * The callback receives properly typed key-value pairs.\n */\n public forEach<K extends Values extends Record<string, any> ? keyof Values : string>(\n callbackfn: (\n value: Values extends Record<string, any> ? (K extends keyof Values ? Values[K] : unknown) : unknown,\n key: K,\n map: Map<string, unknown>,\n ) => void,\n ): void {\n this.registry.forEach(callbackfn as (value: unknown, key: string, map: Map<string, unknown>) => void);\n }\n\n /**\n * Custom JSON serialization method.\n * Converts the internal Map to a plain object for proper JSON serialization.\n * Non-serializable values (functions, symbols, RPC proxies, in-value\n * circular references, and values whose serialization re-enters this\n * `toJSON` via cross-context back-references) are skipped to prevent\n * serialization errors when storing to database.\n *\n * Reentry safety: if a stored value's `isSerializable` probe re-enters\n * `toJSON()` on this same instance (through a chain of RequestContexts\n * holding references to each other), we throw `CyclicRequestContextToJSONError`.\n * Inner `isSerializable` calls re-throw the marker; the outermost\n * `isSerializable` swallows it and filters the offending key, the same\n * way it filters in-value circular references today.\n */\n public toJSON(): Record<string, any> {\n if (_toJSONInProgress.has(this)) {\n throw new CyclicRequestContextToJSONError(\n 'RequestContext.toJSON: detected cyclic re-entry (a stored value transitively references this context)',\n );\n }\n _toJSONInProgress.add(this);\n _toJSONDepth++;\n try {\n const result: Record<string, any> = {};\n for (const [key, value] of this.registry.entries()) {\n if (this.isSerializable(value)) {\n result[key] = value;\n }\n }\n return result;\n } finally {\n _toJSONInProgress.delete(this);\n _toJSONDepth--;\n }\n }\n\n /**\n * Check if a value can be safely serialized to JSON.\n *\n * Re-throws `CyclicRequestContextToJSONError` when called from a nested\n * `toJSON()` (`_toJSONDepth > 1`), so the marker propagates up to the\n * outermost `toJSON()`'s `isSerializable`, which then swallows it and\n * filters the offending key. This is what lets the outermost call return\n * a clean JSON-safe dict for cross-context cycles.\n */\n private isSerializable(value: unknown): boolean {\n if (value === null || value === undefined) return true;\n if (typeof value === 'function') return false;\n if (typeof value === 'symbol') return false;\n if (typeof value !== 'object') return true;\n\n try {\n JSON.stringify(value);\n return true;\n } catch (e) {\n if (e instanceof CyclicRequestContextToJSONError && _toJSONDepth > 1) {\n throw e;\n }\n return false;\n }\n }\n\n /**\n * Custom span serialization to prevent leaking internal state (like auth\n * tokens stored in the private `registry` Map) into observability spans.\n *\n * `deepClean` in `@mastra/observability` calls this method before falling\n * back to `Object.keys()` — which would walk the runtime-enumerable\n * `registry` field and serialize its raw Map entries (including any\n * bearer tokens) into exported spans.\n */\n serializeForSpan(): Record<string, unknown> {\n const safe: Record<string, unknown> = {};\n for (const [key, value] of this.registry.entries()) {\n if (key === MASTRA_AUTH_TOKEN_KEY) {\n safe[key] = '[REDACTED]';\n } else if (\n value === null ||\n value === undefined ||\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n safe[key] = value;\n } else {\n safe[key] = `[${typeof value}]`;\n }\n }\n return safe;\n }\n\n /**\n * Get all values as a typed object for destructuring.\n * Returns Record<string, any> when untyped, or the Values type when typed.\n *\n * @example\n * ```typescript\n * const ctx = new RequestContext<{ userId: string; apiKey: string }>();\n * ctx.set('userId', 'user-123');\n * ctx.set('apiKey', 'key-456');\n * const { userId, apiKey } = ctx.all;\n * ```\n */\n public get all(): Values extends Record<string, any> ? Values : Record<string, any> {\n return Object.fromEntries(this.registry) as Values extends Record<string, any> ? Values : Record<string, any>;\n }\n}\n"]}