@burdenoff/be-sdk 2026.703.2 → 2026.703.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/auth/index.d.ts +2 -2
- package/dist/auth/index.js +2 -2
- package/dist/{chunk-OWUVYM2S.js → chunk-5ITGIBF4.js} +14 -6
- package/dist/chunk-5ITGIBF4.js.map +1 -0
- package/dist/{chunk-4OQRBSHM.js → chunk-A6DGEZJ4.js} +3 -3
- package/dist/{chunk-4OQRBSHM.js.map → chunk-A6DGEZJ4.js.map} +1 -1
- package/dist/{chunk-F5BPKP75.js → chunk-BMK6ZIP6.js} +3 -3
- package/dist/{chunk-F5BPKP75.js.map → chunk-BMK6ZIP6.js.map} +1 -1
- package/dist/{chunk-YEA4BZOL.js → chunk-MNKU7I2G.js} +3 -3
- package/dist/{chunk-YEA4BZOL.js.map → chunk-MNKU7I2G.js.map} +1 -1
- package/dist/{chunk-EZ4PKRUK.js → chunk-QGBXVNRM.js} +2 -2
- package/dist/chunk-QGBXVNRM.js.map +1 -0
- package/dist/{chunk-NRSIT4VI.js → chunk-RNR45JQH.js} +2 -2
- package/dist/{chunk-NRSIT4VI.js.map → chunk-RNR45JQH.js.map} +1 -1
- package/dist/{chunk-SS4XWRYR.js → chunk-RXMOV3YE.js} +3 -3
- package/dist/{chunk-SS4XWRYR.js.map → chunk-RXMOV3YE.js.map} +1 -1
- package/dist/{chunk-DXX6C6X6.js → chunk-RZOFS2BQ.js} +26 -11
- package/dist/chunk-RZOFS2BQ.js.map +1 -0
- package/dist/{chunk-6NB7P6SR.js → chunk-S32JOMMW.js} +3 -3
- package/dist/{chunk-6NB7P6SR.js.map → chunk-S32JOMMW.js.map} +1 -1
- package/dist/{chunk-P7SUUFEX.js → chunk-STLDHYPB.js} +3 -3
- package/dist/{chunk-P7SUUFEX.js.map → chunk-STLDHYPB.js.map} +1 -1
- package/dist/{chunk-UJI2S556.js → chunk-WUXITWC2.js} +3 -3
- package/dist/{chunk-UJI2S556.js.map → chunk-WUXITWC2.js.map} +1 -1
- package/dist/{chunk-TYXGWVRL.js → chunk-XN3DNXXV.js} +5 -5
- package/dist/{chunk-TYXGWVRL.js.map → chunk-XN3DNXXV.js.map} +1 -1
- package/dist/{chunk-IUL5TEKE.js → chunk-ZMCD5XAK.js} +3 -3
- package/dist/{chunk-IUL5TEKE.js.map → chunk-ZMCD5XAK.js.map} +1 -1
- package/dist/database/index.js +2 -2
- package/dist/event-queue/index.d.ts +3 -3
- package/dist/export/index.js +3 -3
- package/dist/feature-flags/index.js +2 -2
- package/dist/gateway/index.js +2 -2
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.js +2 -2
- package/dist/{graphql-BRj5KPOL.d.ts → graphql-Dxxpbzr3.d.ts} +1 -1
- package/dist/{index-B3ECnOLp.d.ts → index-5QTY_V54.d.ts} +2 -2
- package/dist/{index-DDCOGyYh.d.ts → index-B8viwwDM.d.ts} +1 -0
- package/dist/{index-DvDoMbXM.d.ts → index-Bui1kclE.d.ts} +2 -2
- package/dist/{index-CNy4MwKc.d.ts → index-C0qWSC_Q.d.ts} +1 -1
- package/dist/{index-DsBZRR82.d.ts → index-CDPSJW0O.d.ts} +3 -3
- package/dist/{index-BIhZJ1w7.d.ts → index-CXPvdXhS.d.ts} +1 -1
- package/dist/{index-1qCbBvKE.d.ts → index-Cpr38QiE.d.ts} +2 -2
- package/dist/index.d.ts +15 -14
- package/dist/index.js +27 -23
- package/dist/index.js.map +1 -1
- package/dist/init/index.js +3 -3
- package/dist/instrumentation/index.d.ts +3 -3
- package/dist/logger/index.d.ts +2 -2
- package/dist/notifications/index.d.ts +3 -3
- package/dist/notifications/index.js +2 -2
- package/dist/pino-logger-XF275Y5C.js +5 -0
- package/dist/{pino-logger-TSRWXWIU.js.map → pino-logger-XF275Y5C.js.map} +1 -1
- package/dist/rbac/index.d.ts +2 -2
- package/dist/rbac/index.js +2 -2
- package/dist/resource-caps/index.d.ts +2 -2
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/telemetry/index.d.ts +4 -4
- package/dist/telemetry/index.js +2 -2
- package/dist/tracker/index.d.ts +4 -4
- package/dist/tracker/index.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-DXX6C6X6.js.map +0 -1
- package/dist/chunk-EZ4PKRUK.js.map +0 -1
- package/dist/chunk-OWUVYM2S.js.map +0 -1
- package/dist/pino-logger-TSRWXWIU.js +0 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/feature-flags/index.ts","../src/feature-flags/provider.ts"],"names":["EVALUATE_QUERY","FeatureFlagProvider","gatewayUrl","cacheTtlMs","serviceToken","serviceName","defaultActorType","cache","Map","config","cacheTtlSeconds","process","env","resolveBoolean","flagKey","defaultValue","context","evaluation","getEvaluation","value","reason","flagType","Boolean","variant","undefined","errorCode","resolveString","String","resolveNumber","Number","resolveObject","isEnabled","result","workspaceId","actorType","actorId","evaluateAll","keys","evaluations","fetchEvaluations","key","set","invalidateCache","cacheKey","buildCacheKey","delete","clear","get","cached","expiresAt","Date","now","bearerToken","signServiceJwt","response","fetch","method","headers","Authorization","ContextManager","getCfAccessHeaders","body","JSON","stringify","query","variables","input","ok","json","errors","data","evaluateFeatureFlags","ctx"],"mappings":"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","file":"chunk-YEA4BZOL.js","sourcesContent":["/**\n * Feature Flags Module\n *\n * In-house feature flag evaluation for Burdenoff backend services.\n * Compatible with OpenFeature specification concepts.\n *\n * @module feature-flags\n * @category FeatureFlags\n *\n * @example\n * ```typescript\n * import { FeatureFlagProvider } from '@burdenoff/be-sdk/feature-flags';\n *\n * // Create provider (typically done once at service startup)\n * const provider = new FeatureFlagProvider({\n * gatewayUrl: process.env.GLOBAL_INT_GATEWAY_URL || 'http://global-int-gateway:4000/global/graphql',\n * cacheTtlSeconds: 60,\n * });\n *\n * // Check a boolean flag\n * const ctx = { actorId: 'user-123', actorType: 'user' as const, workspaceId: 'ws-456' };\n * const isEnabled = await provider.isEnabled('calendar.recurring-events', ctx);\n *\n * // Resolve with full details\n * const details = await provider.resolveBoolean('calendar.recurring-events', true, ctx);\n * console.log(details.value); // true\n * console.log(details.reason); // 'TARGETING_MATCH'\n * console.log(details.variant); // 'user-opted-in'\n * ```\n */\n\nexport { FeatureFlagProvider } from './provider.js';\nexport type {\n EvaluationContext,\n FeatureFlagProviderConfig,\n FlagEvaluation,\n FlagType,\n ResolutionDetails,\n} from './types.js';\n","/**\n * Burdenoff Feature Flags Server Provider\n *\n * Server-side feature flag provider for backend services.\n * Evaluates flags via the global-tenant-svc GraphQL API through the internal gateway.\n * Supports in-memory caching with configurable TTL.\n *\n * @module feature-flags/provider\n * @category FeatureFlags\n *\n * @example\n * ```typescript\n * import { FeatureFlagProvider } from '@burdenoff/be-sdk/feature-flags';\n *\n * const provider = new FeatureFlagProvider({\n * gatewayUrl: 'http://global-int-gateway:4000/global/graphql',\n * cacheTtlSeconds: 60,\n * });\n *\n * const isEnabled = await provider.resolveBoolean('calendar.recurring-events', false, {\n * actorId: 'user-123',\n * actorType: 'user',\n * workspaceId: 'ws-456',\n * });\n *\n * console.log(isEnabled.value); // true or false\n * console.log(isEnabled.reason); // 'TARGETING_MATCH', 'DEFAULT', etc.\n * ```\n */\n\nimport { ContextManager } from '../context/index.js';\nimport { signServiceJwt } from '../gateway/service-jwt.js';\nimport type { EvaluationContext, FeatureFlagProviderConfig, FlagEvaluation, ResolutionDetails } from './types.js';\n\ninterface CacheEntry {\n evaluations: Map<string, FlagEvaluation>;\n expiresAt: number;\n}\n\nconst EVALUATE_QUERY = `\n query EvaluateFeatureFlags($input: EvaluateFeatureFlagsInput!) {\n evaluateFeatureFlags(input: $input) {\n evaluations {\n key\n value\n variant\n reason\n flagType\n errorCode\n }\n }\n }\n`;\n\n/**\n * Burdenoff Feature Flag Server Provider\n *\n * Evaluates feature flags by querying the global-tenant-svc through\n * the internal gateway. Results are cached in-memory per workspace+actor\n * combination with a configurable TTL.\n */\nexport class FeatureFlagProvider {\n private readonly gatewayUrl: string;\n private readonly cacheTtlMs: number;\n private readonly serviceToken?: string;\n private readonly serviceName: string;\n private readonly defaultActorType: string;\n\n /** In-memory cache keyed by `${workspaceId}:${actorId}` */\n private readonly cache = new Map<string, CacheEntry>();\n\n constructor(config: FeatureFlagProviderConfig) {\n this.gatewayUrl = config.gatewayUrl;\n this.cacheTtlMs = (config.cacheTtlSeconds ?? 60) * 1000;\n this.serviceToken = config.serviceToken;\n this.serviceName = config.serviceName ?? process.env['SERVICE_NAME'] ?? 'burdenoff-be-sdk';\n this.defaultActorType = config.defaultActorType ?? 'service';\n }\n\n // =========================================================================\n // Public Resolution Methods (OpenFeature-compatible interface)\n // =========================================================================\n\n /**\n * Resolve a boolean feature flag\n *\n * @param flagKey - The flag key (e.g. \"calendar.recurring-events\")\n * @param defaultValue - Default value if resolution fails\n * @param context - Evaluation context with actor and workspace info\n * @returns Resolution details with the resolved boolean value\n */\n async resolveBoolean(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext\n ): Promise<ResolutionDetails<boolean>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'BOOLEAN',\n };\n }\n\n return {\n value: Boolean(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve a string feature flag\n */\n async resolveString(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext\n ): Promise<ResolutionDetails<string>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'STRING',\n };\n }\n\n return {\n value: String(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve a number feature flag\n */\n async resolveNumber(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext\n ): Promise<ResolutionDetails<number>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'NUMBER',\n };\n }\n\n return {\n value: Number(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve an object (JSON) feature flag\n */\n async resolveObject<T extends Record<string, unknown>>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext\n ): Promise<ResolutionDetails<T>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'JSON',\n };\n }\n\n return {\n value: evaluation.value as T,\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n // =========================================================================\n // Convenience Methods\n // =========================================================================\n\n /**\n * Quick boolean check — returns just the value.\n * Defaults to `true` (default-allow philosophy) if the flag is not found.\n *\n * @example\n * ```typescript\n * if (await provider.isEnabled('calendar.recurring-events', ctx)) {\n * // Feature is enabled\n * }\n * ```\n */\n async isEnabled(flagKey: string, context?: EvaluationContext): Promise<boolean> {\n const result = await this.resolveBoolean(\n flagKey,\n true,\n context ?? { workspaceId: 'system', actorType: 'service', actorId: 'boot' }\n );\n return result.value;\n }\n\n /**\n * Bulk evaluate multiple flags at once.\n * Returns a map of key -> ResolutionDetails.\n */\n async evaluateAll(context: EvaluationContext, keys?: string[]): Promise<Map<string, ResolutionDetails<unknown>>> {\n const evaluations = await this.fetchEvaluations(context, keys);\n const result = new Map<string, ResolutionDetails<unknown>>();\n\n for (const [key, evaluation] of evaluations) {\n result.set(key, {\n value: evaluation.value,\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n });\n }\n\n return result;\n }\n\n /**\n * Invalidate the cache for a specific workspace+actor combination,\n * or all cache entries if no context is provided.\n */\n invalidateCache(context?: EvaluationContext): void {\n if (context) {\n const cacheKey = this.buildCacheKey(context);\n this.cache.delete(cacheKey);\n } else {\n this.cache.clear();\n }\n }\n\n // =========================================================================\n // Private Methods\n // =========================================================================\n\n /**\n * Get a single flag evaluation, fetching from cache or API\n */\n private async getEvaluation(flagKey: string, context: EvaluationContext | undefined): Promise<FlagEvaluation | null> {\n try {\n const evaluations = await this.fetchEvaluations(context);\n return evaluations.get(flagKey) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Fetch flag evaluations — uses cache if available, otherwise calls the API\n */\n private async fetchEvaluations(\n context: EvaluationContext | undefined,\n keys?: string[]\n ): Promise<Map<string, FlagEvaluation>> {\n const cacheKey = this.buildCacheKey(context);\n const cached = this.cache.get(cacheKey);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.evaluations;\n }\n\n // Without a workspace context there's nothing the backend can evaluate\n // against — fall through to caller defaults instead of hitting the API.\n if (!context?.workspaceId) {\n return new Map();\n }\n\n // Cross-layer flag evaluation transits the global PUBLIC gateway, which\n // strips raw actor headers unless the request carries a valid JWT. Use an\n // explicit serviceToken if provided, otherwise mint a short-lived `service`\n // actor JWT (no-op when SERVICE_JWT_SECRET is unset → falls back to header\n // context for same-env/internal-gateway evaluation).\n const bearerToken = this.serviceToken ?? (await signServiceJwt(this.serviceName)) ?? undefined;\n\n // Fetch from API\n try {\n const response = await fetch(this.gatewayUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(context.actorId && { 'x-actor-id': context.actorId }),\n 'x-actor-type': context.actorType ?? this.defaultActorType,\n 'x-workspace-id': context.workspaceId,\n ...(bearerToken && { Authorization: `Bearer ${bearerToken}` }),\n // Re-present the inbound CF Access service token when the flag check\n // runs against a CF-Access-gated public gateway (alpha). No-op when\n // request context carries no CF creds (local dev, production).\n ...ContextManager.getCfAccessHeaders(),\n },\n body: JSON.stringify({\n query: EVALUATE_QUERY,\n variables: {\n input: {\n workspaceId: context.workspaceId,\n keys: keys ?? null,\n },\n },\n }),\n });\n\n if (!response.ok) {\n // Return empty map on error — default-allow handled by callers\n return new Map();\n }\n\n const json = (await response.json()) as {\n data?: {\n evaluateFeatureFlags?: {\n evaluations?: FlagEvaluation[];\n };\n };\n errors?: Array<{ message: string }>;\n };\n\n if (json.errors || !json.data?.evaluateFeatureFlags?.evaluations) {\n return new Map();\n }\n\n const evaluations = new Map<string, FlagEvaluation>();\n for (const evaluation of json.data.evaluateFeatureFlags.evaluations) {\n evaluations.set(evaluation.key, evaluation);\n }\n\n // Cache the results\n this.cache.set(cacheKey, {\n evaluations,\n expiresAt: Date.now() + this.cacheTtlMs,\n });\n\n return evaluations;\n } catch {\n // On network error, return empty map (default-allow handled by callers)\n return new Map();\n }\n }\n\n /**\n * Build a cache key from the evaluation context.\n *\n * Safe against undefined/null context — callers like isEnabled() may\n * pass no context during service boot (e.g. checking a default-allow\n * feature flag at startup before the first request builds a real\n * actor context). Without the guard, isEnabled(flag) used to throw\n * \"undefined is not an object (evaluating 'context.workspaceId')\"\n * and crash the svc at startServer().\n */\n private buildCacheKey(context: EvaluationContext | undefined): string {\n const ctx = context ?? ({} as EvaluationContext);\n return `${ctx.workspaceId ?? 'system'}:${ctx.actorId ?? 'anonymous'}:${ctx.actorType ?? 'user'}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/feature-flags/index.ts","../src/feature-flags/provider.ts"],"names":["EVALUATE_QUERY","FeatureFlagProvider","gatewayUrl","cacheTtlMs","serviceToken","serviceName","defaultActorType","cache","Map","config","cacheTtlSeconds","process","env","resolveBoolean","flagKey","defaultValue","context","evaluation","getEvaluation","value","reason","flagType","Boolean","variant","undefined","errorCode","resolveString","String","resolveNumber","Number","resolveObject","isEnabled","result","workspaceId","actorType","actorId","evaluateAll","keys","evaluations","fetchEvaluations","key","set","invalidateCache","cacheKey","buildCacheKey","delete","clear","get","cached","expiresAt","Date","now","bearerToken","signServiceJwt","response","fetch","method","headers","Authorization","ContextManager","getCfAccessHeaders","body","JSON","stringify","query","variables","input","ok","json","errors","data","evaluateFeatureFlags","ctx"],"mappings":"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","file":"chunk-MNKU7I2G.js","sourcesContent":["/**\n * Feature Flags Module\n *\n * In-house feature flag evaluation for Burdenoff backend services.\n * Compatible with OpenFeature specification concepts.\n *\n * @module feature-flags\n * @category FeatureFlags\n *\n * @example\n * ```typescript\n * import { FeatureFlagProvider } from '@burdenoff/be-sdk/feature-flags';\n *\n * // Create provider (typically done once at service startup)\n * const provider = new FeatureFlagProvider({\n * gatewayUrl: process.env.GLOBAL_INT_GATEWAY_URL || 'http://global-int-gateway:4000/global/graphql',\n * cacheTtlSeconds: 60,\n * });\n *\n * // Check a boolean flag\n * const ctx = { actorId: 'user-123', actorType: 'user' as const, workspaceId: 'ws-456' };\n * const isEnabled = await provider.isEnabled('calendar.recurring-events', ctx);\n *\n * // Resolve with full details\n * const details = await provider.resolveBoolean('calendar.recurring-events', true, ctx);\n * console.log(details.value); // true\n * console.log(details.reason); // 'TARGETING_MATCH'\n * console.log(details.variant); // 'user-opted-in'\n * ```\n */\n\nexport { FeatureFlagProvider } from './provider.js';\nexport type {\n EvaluationContext,\n FeatureFlagProviderConfig,\n FlagEvaluation,\n FlagType,\n ResolutionDetails,\n} from './types.js';\n","/**\n * Burdenoff Feature Flags Server Provider\n *\n * Server-side feature flag provider for backend services.\n * Evaluates flags via the global-tenant-svc GraphQL API through the internal gateway.\n * Supports in-memory caching with configurable TTL.\n *\n * @module feature-flags/provider\n * @category FeatureFlags\n *\n * @example\n * ```typescript\n * import { FeatureFlagProvider } from '@burdenoff/be-sdk/feature-flags';\n *\n * const provider = new FeatureFlagProvider({\n * gatewayUrl: 'http://global-int-gateway:4000/global/graphql',\n * cacheTtlSeconds: 60,\n * });\n *\n * const isEnabled = await provider.resolveBoolean('calendar.recurring-events', false, {\n * actorId: 'user-123',\n * actorType: 'user',\n * workspaceId: 'ws-456',\n * });\n *\n * console.log(isEnabled.value); // true or false\n * console.log(isEnabled.reason); // 'TARGETING_MATCH', 'DEFAULT', etc.\n * ```\n */\n\nimport { ContextManager } from '../context/index.js';\nimport { signServiceJwt } from '../gateway/service-jwt.js';\nimport type { EvaluationContext, FeatureFlagProviderConfig, FlagEvaluation, ResolutionDetails } from './types.js';\n\ninterface CacheEntry {\n evaluations: Map<string, FlagEvaluation>;\n expiresAt: number;\n}\n\nconst EVALUATE_QUERY = `\n query EvaluateFeatureFlags($input: EvaluateFeatureFlagsInput!) {\n evaluateFeatureFlags(input: $input) {\n evaluations {\n key\n value\n variant\n reason\n flagType\n errorCode\n }\n }\n }\n`;\n\n/**\n * Burdenoff Feature Flag Server Provider\n *\n * Evaluates feature flags by querying the global-tenant-svc through\n * the internal gateway. Results are cached in-memory per workspace+actor\n * combination with a configurable TTL.\n */\nexport class FeatureFlagProvider {\n private readonly gatewayUrl: string;\n private readonly cacheTtlMs: number;\n private readonly serviceToken?: string;\n private readonly serviceName: string;\n private readonly defaultActorType: string;\n\n /** In-memory cache keyed by `${workspaceId}:${actorId}` */\n private readonly cache = new Map<string, CacheEntry>();\n\n constructor(config: FeatureFlagProviderConfig) {\n this.gatewayUrl = config.gatewayUrl;\n this.cacheTtlMs = (config.cacheTtlSeconds ?? 60) * 1000;\n this.serviceToken = config.serviceToken;\n this.serviceName = config.serviceName ?? process.env['SERVICE_NAME'] ?? 'burdenoff-be-sdk';\n this.defaultActorType = config.defaultActorType ?? 'service';\n }\n\n // =========================================================================\n // Public Resolution Methods (OpenFeature-compatible interface)\n // =========================================================================\n\n /**\n * Resolve a boolean feature flag\n *\n * @param flagKey - The flag key (e.g. \"calendar.recurring-events\")\n * @param defaultValue - Default value if resolution fails\n * @param context - Evaluation context with actor and workspace info\n * @returns Resolution details with the resolved boolean value\n */\n async resolveBoolean(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext\n ): Promise<ResolutionDetails<boolean>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'BOOLEAN',\n };\n }\n\n return {\n value: Boolean(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve a string feature flag\n */\n async resolveString(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext\n ): Promise<ResolutionDetails<string>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'STRING',\n };\n }\n\n return {\n value: String(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve a number feature flag\n */\n async resolveNumber(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext\n ): Promise<ResolutionDetails<number>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'NUMBER',\n };\n }\n\n return {\n value: Number(evaluation.value),\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n /**\n * Resolve an object (JSON) feature flag\n */\n async resolveObject<T extends Record<string, unknown>>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext\n ): Promise<ResolutionDetails<T>> {\n const evaluation = await this.getEvaluation(flagKey, context);\n if (!evaluation) {\n return {\n value: defaultValue,\n reason: 'DEFAULT',\n flagType: 'JSON',\n };\n }\n\n return {\n value: evaluation.value as T,\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n };\n }\n\n // =========================================================================\n // Convenience Methods\n // =========================================================================\n\n /**\n * Quick boolean check — returns just the value.\n * Defaults to `true` (default-allow philosophy) if the flag is not found.\n *\n * @example\n * ```typescript\n * if (await provider.isEnabled('calendar.recurring-events', ctx)) {\n * // Feature is enabled\n * }\n * ```\n */\n async isEnabled(flagKey: string, context?: EvaluationContext): Promise<boolean> {\n const result = await this.resolveBoolean(\n flagKey,\n true,\n context ?? { workspaceId: 'system', actorType: 'service', actorId: 'boot' }\n );\n return result.value;\n }\n\n /**\n * Bulk evaluate multiple flags at once.\n * Returns a map of key -> ResolutionDetails.\n */\n async evaluateAll(context: EvaluationContext, keys?: string[]): Promise<Map<string, ResolutionDetails<unknown>>> {\n const evaluations = await this.fetchEvaluations(context, keys);\n const result = new Map<string, ResolutionDetails<unknown>>();\n\n for (const [key, evaluation] of evaluations) {\n result.set(key, {\n value: evaluation.value,\n variant: evaluation.variant ?? undefined,\n reason: evaluation.reason,\n flagType: evaluation.flagType,\n errorCode: evaluation.errorCode ?? undefined,\n });\n }\n\n return result;\n }\n\n /**\n * Invalidate the cache for a specific workspace+actor combination,\n * or all cache entries if no context is provided.\n */\n invalidateCache(context?: EvaluationContext): void {\n if (context) {\n const cacheKey = this.buildCacheKey(context);\n this.cache.delete(cacheKey);\n } else {\n this.cache.clear();\n }\n }\n\n // =========================================================================\n // Private Methods\n // =========================================================================\n\n /**\n * Get a single flag evaluation, fetching from cache or API\n */\n private async getEvaluation(flagKey: string, context: EvaluationContext | undefined): Promise<FlagEvaluation | null> {\n try {\n const evaluations = await this.fetchEvaluations(context);\n return evaluations.get(flagKey) ?? null;\n } catch {\n return null;\n }\n }\n\n /**\n * Fetch flag evaluations — uses cache if available, otherwise calls the API\n */\n private async fetchEvaluations(\n context: EvaluationContext | undefined,\n keys?: string[]\n ): Promise<Map<string, FlagEvaluation>> {\n const cacheKey = this.buildCacheKey(context);\n const cached = this.cache.get(cacheKey);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.evaluations;\n }\n\n // Without a workspace context there's nothing the backend can evaluate\n // against — fall through to caller defaults instead of hitting the API.\n if (!context?.workspaceId) {\n return new Map();\n }\n\n // Cross-layer flag evaluation transits the global PUBLIC gateway, which\n // strips raw actor headers unless the request carries a valid JWT. Use an\n // explicit serviceToken if provided, otherwise mint a short-lived `service`\n // actor JWT (no-op when SERVICE_JWT_SECRET is unset → falls back to header\n // context for same-env/internal-gateway evaluation).\n const bearerToken = this.serviceToken ?? (await signServiceJwt(this.serviceName)) ?? undefined;\n\n // Fetch from API\n try {\n const response = await fetch(this.gatewayUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(context.actorId && { 'x-actor-id': context.actorId }),\n 'x-actor-type': context.actorType ?? this.defaultActorType,\n 'x-workspace-id': context.workspaceId,\n ...(bearerToken && { Authorization: `Bearer ${bearerToken}` }),\n // Re-present the inbound CF Access service token when the flag check\n // runs against a CF-Access-gated public gateway (alpha). No-op when\n // request context carries no CF creds (local dev, production).\n ...ContextManager.getCfAccessHeaders(),\n },\n body: JSON.stringify({\n query: EVALUATE_QUERY,\n variables: {\n input: {\n workspaceId: context.workspaceId,\n keys: keys ?? null,\n },\n },\n }),\n });\n\n if (!response.ok) {\n // Return empty map on error — default-allow handled by callers\n return new Map();\n }\n\n const json = (await response.json()) as {\n data?: {\n evaluateFeatureFlags?: {\n evaluations?: FlagEvaluation[];\n };\n };\n errors?: Array<{ message: string }>;\n };\n\n if (json.errors || !json.data?.evaluateFeatureFlags?.evaluations) {\n return new Map();\n }\n\n const evaluations = new Map<string, FlagEvaluation>();\n for (const evaluation of json.data.evaluateFeatureFlags.evaluations) {\n evaluations.set(evaluation.key, evaluation);\n }\n\n // Cache the results\n this.cache.set(cacheKey, {\n evaluations,\n expiresAt: Date.now() + this.cacheTtlMs,\n });\n\n return evaluations;\n } catch {\n // On network error, return empty map (default-allow handled by callers)\n return new Map();\n }\n }\n\n /**\n * Build a cache key from the evaluation context.\n *\n * Safe against undefined/null context — callers like isEnabled() may\n * pass no context during service boot (e.g. checking a default-allow\n * feature flag at startup before the first request builds a real\n * actor context). Without the guard, isEnabled(flag) used to throw\n * \"undefined is not an object (evaluating 'context.workspaceId')\"\n * and crash the svc at startServer().\n */\n private buildCacheKey(context: EvaluationContext | undefined): string {\n const ctx = context ?? ({} as EvaluationContext);\n return `${ctx.workspaceId ?? 'system'}:${ctx.actorId ?? 'anonymous'}:${ctx.actorType ?? 'user'}`;\n }\n}\n"]}
|
|
@@ -85,5 +85,5 @@ var TimeoutError = class _TimeoutError extends SDKError {
|
|
|
85
85
|
};
|
|
86
86
|
|
|
87
87
|
export { APIError, AuthenticationError, NetworkError, PermissionDeniedError, QuotaExceededError, TimeoutError, ValidationError };
|
|
88
|
-
//# sourceMappingURL=chunk-
|
|
89
|
-
//# sourceMappingURL=chunk-
|
|
88
|
+
//# sourceMappingURL=chunk-QGBXVNRM.js.map
|
|
89
|
+
//# sourceMappingURL=chunk-QGBXVNRM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/index.ts"],"names":["SDKError","Error","message","code","details","name","Object","setPrototypeOf","prototype","PermissionDeniedError","AuthenticationError","APIError","ValidationError","QuotaExceededError","NetworkError","TimeoutError"],"mappings":";;;AA6IO,IAAMA,QAAAA,GAAN,MAAMA,SAAAA,SAAiBC,KAAAA,CAAAA;EA7I9B;;;;;EA8IE,WAAA,CACEC,OAAAA,EACOC,MACAC,OAAAA,EACP;AACA,IAAA,KAAA,CAAMF,OAAAA,CAAAA,EAAAA,IAAAA,CAHCC,IAAAA,GAAAA,IAAAA,EAAAA,KACAC,OAAAA,GAAAA,OAAAA;AAGP,IAAA,IAAA,CAAKC,IAAAA,GAAO,UAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMP,SAAAA,CAASQ,SAAS,CAAA;AAChD,EAAA;AACF,CAAA;AAKO,IAAMC,qBAAAA,GAAN,MAAMA,sBAAAA,SAA8BT,QAAAA,CAAAA;EA5J3C;;;AA6JE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,qBAAqBE,OAAAA,CAAAA;AACpC,IAAA,IAAA,CAAKC,IAAAA,GAAO,uBAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAME,sBAAAA,CAAsBD,SAAS,CAAA;AAC7D,EAAA;AACF;AAKO,IAAME,mBAAAA,GAAN,MAAMA,oBAAAA,SAA4BV,QAAAA,CAAAA;EAvKzC;;;AAwKE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,wBAAwBE,OAAAA,CAAAA;AACvC,IAAA,IAAA,CAAKC,IAAAA,GAAO,qBAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMG,oBAAAA,CAAoBF,SAAS,CAAA;AAC3D,EAAA;AACF;AAKO,IAAMG,QAAAA,GAAN,MAAMA,SAAAA,SAAiBX,QAAAA,CAAAA;EAlL9B;;;AAmLE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,aAAaE,OAAAA,CAAAA;AAC5B,IAAA,IAAA,CAAKC,IAAAA,GAAO,UAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMI,SAAAA,CAASH,SAAS,CAAA;AAChD,EAAA;AACF;AAKO,IAAMI,eAAAA,GAAN,MAAMA,gBAAAA,SAAwBZ,QAAAA,CAAAA;EA7LrC;;;AA8LE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,oBAAoBE,OAAAA,CAAAA;AACnC,IAAA,IAAA,CAAKC,IAAAA,GAAO,iBAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMK,gBAAAA,CAAgBJ,SAAS,CAAA;AACvD,EAAA;AACF;AAKO,IAAMK,kBAAAA,GAAN,MAAMA,mBAAAA,SAA2Bb,QAAAA,CAAAA;EAxMxC;;;AAyME,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,kBAAkBE,OAAAA,CAAAA;AACjC,IAAA,IAAA,CAAKC,IAAAA,GAAO,oBAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMM,mBAAAA,CAAmBL,SAAS,CAAA;AAC1D,EAAA;AACF;AAKO,IAAMM,YAAAA,GAAN,MAAMA,aAAAA,SAAqBd,QAAAA,CAAAA;EAnNlC;;;AAoNE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,iBAAiBE,OAAAA,CAAAA;AAChC,IAAA,IAAA,CAAKC,IAAAA,GAAO,cAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMO,aAAAA,CAAaN,SAAS,CAAA;AACpD,EAAA;AACF;AAKO,IAAMO,YAAAA,GAAN,MAAMA,aAAAA,SAAqBf,QAAAA,CAAAA;EA9NlC;;;AA+NE,EAAA,WAAA,CAAYE,SAAiBE,OAAAA,EAAmC;AAC9D,IAAA,KAAA,CAAMF,OAAAA,EAAS,iBAAiBE,OAAAA,CAAAA;AAChC,IAAA,IAAA,CAAKC,IAAAA,GAAO,cAAA;AACZC,IAAAA,MAAAA,CAAOC,cAAAA,CAAe,IAAA,EAAMQ,aAAAA,CAAaP,SAAS,CAAA;AACpD,EAAA;AACF","file":"chunk-QGBXVNRM.js","sourcesContent":["/**\n * Core type definitions for Burdenoff Backend SDK\n */\n\nimport type { ServiceLayer } from './layer.js';\n\n/**\n * SDK Configuration\n */\nexport interface SDKConfig {\n /** Workspaces API GraphQL endpoint (should point to wspace-int-gateway for M2M calls) */\n workspacesApiUrl: string;\n /**\n * Tenant API GraphQL endpoint for cross-layer (wspace → global) M2M calls.\n * Cross-layer traffic must go through the **public** global gateway\n * (`GLOBAL_PUBLIC_GATEWAY_URL`), not the internal gateway — ACA internal\n * ingress is environment-scoped and does not route across the wspace/global\n * environment boundary.\n */\n tenantApiUrl?: string;\n /**\n * Calling service's layer (`wspace` | `global`). Used to construct the\n * cross-layer GatewayClient. Defaults to the `SERVICE_LAYER` env var, then\n * `wspace`.\n */\n serviceLayer?: ServiceLayer;\n /**\n * Calling service name. Used as the cross-layer GatewayClient service identity\n * and the `sub` of the minted service JWT. Defaults to the `SERVICE_NAME` env\n * var, then `burdenoff-be-sdk`.\n */\n serviceName?: string;\n /** Default timeout for API requests in milliseconds */\n timeout?: number;\n /** Retry configuration */\n retry?: RetryConfig;\n /** Logger configuration */\n logger?: LoggerConfig;\n /** Event queue configuration (Redis Streams). Reads `REDIS_URL` when present. */\n eventQueue?: EventQueueSDKConfig;\n /** Temporal module type (workspace or tenant) */\n temporalModuleType?: 'workspace' | 'tenant';\n /** Temporal server address */\n temporalAddress?: string;\n /** Temporal namespace */\n temporalNamespace?: string;\n /** Temporal task queue */\n temporalTaskQueue?: string;\n /** Auto-connect to Temporal (default: true) */\n temporalAutoConnect?: boolean;\n}\n\n/**\n * Retry configuration for API requests\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts */\n maxAttempts: number;\n /** Initial retry delay in milliseconds */\n initialDelay: number;\n /** Maximum retry delay in milliseconds */\n maxDelay: number;\n /** Backoff multiplier */\n backoffMultiplier: number;\n}\n\n/**\n * Logger configuration\n */\nexport interface LoggerConfig {\n /** Log level */\n level: 'debug' | 'info' | 'warn' | 'error';\n /** Enable console output */\n console: boolean;\n /** Custom logger transport */\n transport?: LoggerTransport;\n}\n\n/**\n * Custom logger transport interface\n */\nexport interface LoggerTransport {\n log(level: string, message: string, meta?: Record<string, unknown>): void;\n}\n\n/**\n * Actor type for RBAC authorization\n * - user: Direct user request (via frontend)\n * - app: OAuth app acting on behalf of user\n * - service: M2M service-to-service communication\n * - super_admin: Platform administration (bypasses all checks)\n */\nexport type ActorType = 'user' | 'app' | 'service' | 'super_admin';\n\n/**\n * Request context passed with every SDK operation\n */\nexport interface SDKContext {\n /** Actor ID - identifies the entity making the request */\n actorId?: string;\n /** Actor type - determines authorization behavior */\n actorType?: ActorType;\n /** Workspace ID */\n workspaceId?: string;\n /** Tenant ID */\n tenantId?: string;\n /** Project ID */\n projectId?: string;\n /** App ID */\n appId?: string;\n /** JWT token for authentication */\n token?: string;\n /** Product ID */\n productId?: string;\n /**\n * Cloudflare Access service-token client id, captured from the inbound\n * `cf-access-client-id` header. Re-presented on cross-layer outbound calls so\n * the peer layer's CF-Access-gated public gateway admits the request.\n */\n cfAccessClientId?: string;\n /**\n * Cloudflare Access service-token client secret, from `cf-access-client-secret`.\n * Sensitive — redacted by the SDK logger's secret pattern; never log directly.\n */\n cfAccessClientSecret?: string;\n /**\n * Cloudflare Access USER-SSO credential — the `CF_Authorization` cookie value,\n * forwarded by the public gateway as `x-cf-authorization`. On alpha a real user\n * request carries this (not the service-token pair); re-presented as the\n * `CF_Authorization` cookie on cross-layer outbound calls so the peer layer's\n * CF-Access-gated public gateway (same CF-Access app / shared AUD) admits it.\n * Sensitive — never log directly. See BOFF-2929.\n */\n cfAuthorization?: string;\n /** Additional custom context */\n [key: string]: unknown;\n}\n\n/**\n * SDK Error base class\n */\nexport class SDKError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: Record<string, unknown>\n ) {\n super(message);\n this.name = 'SDKError';\n Object.setPrototypeOf(this, SDKError.prototype);\n }\n}\n\n/**\n * Permission denied error\n */\nexport class PermissionDeniedError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'PERMISSION_DENIED', details);\n this.name = 'PermissionDeniedError';\n Object.setPrototypeOf(this, PermissionDeniedError.prototype);\n }\n}\n\n/**\n * Authentication error\n */\nexport class AuthenticationError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'AUTHENTICATION_ERROR', details);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * API error from backend\n */\nexport class APIError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'API_ERROR', details);\n this.name = 'APIError';\n Object.setPrototypeOf(this, APIError.prototype);\n }\n}\n\n/**\n * Validation error\n */\nexport class ValidationError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'VALIDATION_ERROR', details);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Quota exceeded error\n */\nexport class QuotaExceededError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'QUOTA_EXCEEDED', details);\n this.name = 'QuotaExceededError';\n Object.setPrototypeOf(this, QuotaExceededError.prototype);\n }\n}\n\n/**\n * Network error\n */\nexport class NetworkError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'NETWORK_ERROR', details);\n this.name = 'NetworkError';\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n\n/**\n * Timeout error\n */\nexport class TimeoutError extends SDKError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'TIMEOUT_ERROR', details);\n this.name = 'TimeoutError';\n Object.setPrototypeOf(this, TimeoutError.prototype);\n }\n}\n\n/**\n * Event queue SDK configuration (Redis Streams backend).\n *\n * The publisher is created via `createPublisher()` from\n * `@burdenoff/be-sdk/event-queue` and reads `REDIS_URL` from the env.\n * The fields here are for client-name + reconnect tuning only.\n */\nexport interface EventQueueSDKConfig {\n /** Redis Streams server URL. Overrides `REDIS_URL` env if set. */\n url?: string;\n /** Optional password for TLS/auth */\n password?: string;\n /** Maximum reconnect attempts (default: -1 = infinite) */\n maxReconnectAttempts?: number;\n /** Reconnect time wait in milliseconds (default: 2000) */\n reconnectTimeWait?: number;\n /** Client name for logging (default: service name) */\n name?: string;\n /** Auto-connect on SDK initialization (default: true) */\n autoConnect?: boolean;\n}\n\n/**\n * Event queue health status\n */\nexport interface EventQueueHealthStatus {\n status: 'healthy' | 'unhealthy' | 'not_configured';\n connected: boolean;\n url?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Temporal health status\n */\nexport interface TemporalHealthStatus {\n status: 'healthy' | 'unhealthy' | 'not_configured';\n connected: boolean;\n address?: string;\n namespace?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Overall SDK health status\n */\nexport interface SDKHealthStatus {\n eventQueue: EventQueueHealthStatus;\n temporal: TemporalHealthStatus;\n}\n\n/**\n * SDK shutdown options\n */\nexport interface SDKShutdownOptions {\n /** Shutdown timeout in milliseconds (default: 5000) */\n timeout?: number;\n /** Force shutdown even if timeout is exceeded (default: true) */\n force?: boolean;\n}\n\n// ============================================================================\n// Layer Types Export\n// ============================================================================\n\n/**\n * Re-export all layer-related types for convenience\n */\nexport type {\n ServiceLayer,\n LayerConfig,\n LayerGatewayConfig,\n LayerResourceUrls,\n LayerFullConfig,\n GatewayRoutingConfig,\n CircuitBreakerConfig,\n GatewayRetryConfig,\n GatewayRequestHeaders,\n} from './layer.js';\n\nexport {\n DEFAULT_GATEWAY_ROUTING,\n DEFAULT_LAYER_RESOURCES,\n DEFAULT_LAYER_STREAMS,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n DEFAULT_GATEWAY_RETRY_CONFIG,\n} from './layer.js';\n"]}
|
|
@@ -159,5 +159,5 @@ function pickTraceFields() {
|
|
|
159
159
|
__name(pickTraceFields, "pickTraceFields");
|
|
160
160
|
|
|
161
161
|
export { DEFAULT_REDACT_PATHS, createPinoLogger };
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
163
|
-
//# sourceMappingURL=chunk-
|
|
162
|
+
//# sourceMappingURL=chunk-RNR45JQH.js.map
|
|
163
|
+
//# sourceMappingURL=chunk-RNR45JQH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/telemetry/pino-redaction.ts","../src/telemetry/pino-logger.ts"],"names":["DEFAULT_REDACT_PATHS","Object","freeze","tryLoadPino","mod","candidate","default","factory","destination","makeDest","sync","undefined","PinoAdapter","BaseLogger","pino","level","console","debug","message","args","mergeArgs","info","warn","error","log","trace","out","arg","Error","stack","name","assign","createPinoLogger","cfg","loader","fallback","env","setContext","service","serviceName","version","redactPaths","dest","base","redact","paths","censor","remove","timestamp","Date","toISOString","mixin","pickTraceFields","api","globalThis","__otelApi","anyApi","span","getActiveSpan","ctx","spanContext","trace_id","traceId","span_id","spanId","trace_flags","traceFlags","toString","padStart"],"mappings":"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","file":"chunk-NRSIT4VI.js","sourcesContent":["/**\n * Default Pino redact paths — covers the Burdenoff authorization +\n * gateway header + NATS/GraphQL payload surface area.\n *\n * Services can extend this list via their own Pino options.\n */\nexport const DEFAULT_REDACT_PATHS: readonly string[] = Object.freeze([\n 'password',\n '*.password',\n '*.*.password',\n 'token',\n 'authToken',\n 'workspaceToken',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'apiKey',\n '*.apiKey',\n 'authorization',\n 'req.headers.authorization',\n 'req.headers[\"x-workspace-authorization\"]',\n 'req.headers.cookie',\n 'res.headers[\"set-cookie\"]',\n 'params.*.password',\n 'params.*.token',\n 'params.*.secret',\n]);\n","/**\n * Pino logger wrapper that emits structured JSON with trace/span correlation\n * fields. Falls back to the SDK's built-in Logger when `pino` isn't installed\n * so services without the peer dep still compile and run.\n *\n * # ACA stdout compatibility\n *\n * In Azure Container Apps the ContainerAppConsoleLogs agent captures\n * `console.log` / `process.stdout.write` output but does *not* reliably\n * capture Pino's default async sonic-boom destination (which writes to fd 1\n * through Node.js `fs.writeSync` / `fs.write`). This means every log line\n * produced through Pino's default destination is invisible in Log Analytics.\n *\n * The fix mirrors what the gateway's own `logger.ts` does in production:\n * pass `pino.destination({ sync: true })` as the second argument to the\n * Pino factory so writes are synchronous and go through a path that ACA\n * captures correctly.\n */\nimport { Logger as BaseLogger } from '../logger/index.js';\nimport { DEFAULT_REDACT_PATHS } from './pino-redaction.js';\n\nexport interface PinoLoggerConfig {\n serviceName: string;\n env: 'alpha' | 'prod' | 'dev';\n version: string;\n /** Override Pino log level. Default: warn (prod), info (alpha/dev). */\n level?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';\n /** Extra redact paths appended to DEFAULT_REDACT_PATHS. */\n redactPaths?: string[];\n}\n\ninterface PinoLike {\n level: string;\n fatal: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n debug: (...args: unknown[]) => void;\n trace: (...args: unknown[]) => void;\n child: (bindings: Record<string, unknown>) => PinoLike;\n}\n\n/**\n * Loaded Pino module: factory function + a synchronous destination factory.\n * In ACA, Pino's default async sonic-boom destination is invisible in Log\n * Analytics, so we always use a sync destination.\n */\ninterface PinoLoader {\n factory: (opts: unknown, dest?: unknown) => PinoLike;\n makeDest: () => unknown;\n}\n\nasync function tryLoadPino(): Promise<PinoLoader | null> {\n try {\n const mod = (await import('pino')) as unknown as { default?: unknown };\n const candidate = (mod.default ?? mod) as unknown;\n if (typeof candidate === 'function') {\n const factory = candidate as (opts: unknown, dest?: unknown) => PinoLike;\n const destination = (factory as unknown as Record<string, unknown>)?.['destination'] as\n | ((opts?: { sync?: boolean }) => unknown)\n | undefined;\n const makeDest =\n typeof destination === 'function' ? () => destination({ sync: true }) : () => undefined;\n return { factory, makeDest };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nclass PinoAdapter extends BaseLogger {\n constructor(private readonly pino: PinoLike) {\n super({\n level: (pino.level as BaseLogger['getLevel'] extends () => infer L ? L : never) ?? 'info',\n console: false,\n });\n }\n\n public override debug(message: string, ...args: unknown[]): void {\n this.pino.debug(this.mergeArgs(args), message);\n }\n public override info(message: string, ...args: unknown[]): void {\n this.pino.info(this.mergeArgs(args), message);\n }\n public override warn(message: string, ...args: unknown[]): void {\n this.pino.warn(this.mergeArgs(args), message);\n }\n public override error(message: string, ...args: unknown[]): void {\n this.pino.error(this.mergeArgs(args), message);\n }\n public override log(message: string, ...args: unknown[]): void {\n this.pino.info(this.mergeArgs(args), message);\n }\n public override trace(message: string, ...args: unknown[]): void {\n this.pino.trace(this.mergeArgs(args), message);\n }\n\n private mergeArgs(args: unknown[]): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const arg of args) {\n if (arg instanceof Error) {\n out['err'] = { message: arg.message, stack: arg.stack, name: arg.name };\n } else if (arg && typeof arg === 'object') {\n Object.assign(out, arg as Record<string, unknown>);\n } else if (arg !== undefined && arg !== null) {\n out['data'] = arg;\n }\n }\n return out;\n }\n}\n\n/**\n * Build a logger instance. Returns a Pino-backed adapter when `pino` is\n * installed; otherwise falls back to the SDK's built-in JSON Logger.\n *\n * Services call this through `bootstrapTelemetry`, not directly.\n */\nexport async function createPinoLogger(cfg: PinoLoggerConfig): Promise<BaseLogger> {\n const loader = await tryLoadPino();\n if (!loader) {\n // No pino — fall back to the built-in Logger with console output.\n const fallback = new BaseLogger({ level: cfg.env === 'prod' ? 'warn' : 'info', console: true });\n fallback.setContext({\n service: cfg.serviceName,\n env: cfg.env,\n version: cfg.version,\n });\n return fallback;\n }\n\n const redactPaths = [...DEFAULT_REDACT_PATHS, ...(cfg.redactPaths ?? [])];\n const dest = loader.makeDest();\n const pino = loader.factory(\n {\n name: cfg.serviceName,\n level: cfg.level ?? (cfg.env === 'prod' ? 'warn' : 'info'),\n base: {\n service: { name: cfg.serviceName, version: cfg.version },\n env: cfg.env,\n },\n redact: {\n paths: redactPaths,\n censor: '[REDACTED]',\n remove: false,\n },\n timestamp: () => `,\"time\":\"${new Date().toISOString()}\"`,\n mixin: () => pickTraceFields(),\n },\n // Explicit sync destination so output is visible in ACA Log Analytics.\n // Without this, Pino uses its default async sonic-boom which writes to fd 1\n // but the ContainerAppConsoleLogs agent doesn't capture it on Bun.\n dest\n );\n return new PinoAdapter(pino);\n}\n\nfunction pickTraceFields(): Record<string, string> {\n try {\n // Pull active span context via @opentelemetry/api if installed.\n // Use require-style dynamic access to keep this file dep-free.\n const api = (globalThis as unknown as { __otelApi?: unknown }).__otelApi;\n if (!api) return {};\n const anyApi = api as {\n trace: {\n getActiveSpan: () => { spanContext: () => { traceId: string; spanId: string; traceFlags: number } } | undefined;\n };\n };\n const span = anyApi.trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return {\n trace_id: ctx.traceId,\n span_id: ctx.spanId,\n trace_flags: ctx.traceFlags.toString(16).padStart(2, '0'),\n };\n } catch {\n return {};\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/telemetry/pino-redaction.ts","../src/telemetry/pino-logger.ts"],"names":["DEFAULT_REDACT_PATHS","Object","freeze","tryLoadPino","mod","candidate","default","factory","destination","makeDest","sync","undefined","PinoAdapter","BaseLogger","pino","level","console","debug","message","args","mergeArgs","info","warn","error","log","trace","out","arg","Error","stack","name","assign","createPinoLogger","cfg","loader","fallback","env","setContext","service","serviceName","version","redactPaths","dest","base","redact","paths","censor","remove","timestamp","Date","toISOString","mixin","pickTraceFields","api","globalThis","__otelApi","anyApi","span","getActiveSpan","ctx","spanContext","trace_id","traceId","span_id","spanId","trace_flags","traceFlags","toString","padStart"],"mappings":"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","file":"chunk-RNR45JQH.js","sourcesContent":["/**\n * Default Pino redact paths — covers the Burdenoff authorization +\n * gateway header + NATS/GraphQL payload surface area.\n *\n * Services can extend this list via their own Pino options.\n */\nexport const DEFAULT_REDACT_PATHS: readonly string[] = Object.freeze([\n 'password',\n '*.password',\n '*.*.password',\n 'token',\n 'authToken',\n 'workspaceToken',\n 'accessToken',\n 'refreshToken',\n 'clientSecret',\n 'apiKey',\n '*.apiKey',\n 'authorization',\n 'req.headers.authorization',\n 'req.headers[\"x-workspace-authorization\"]',\n 'req.headers.cookie',\n 'res.headers[\"set-cookie\"]',\n 'params.*.password',\n 'params.*.token',\n 'params.*.secret',\n]);\n","/**\n * Pino logger wrapper that emits structured JSON with trace/span correlation\n * fields. Falls back to the SDK's built-in Logger when `pino` isn't installed\n * so services without the peer dep still compile and run.\n *\n * # ACA stdout compatibility\n *\n * In Azure Container Apps the ContainerAppConsoleLogs agent captures\n * `console.log` / `process.stdout.write` output but does *not* reliably\n * capture Pino's default async sonic-boom destination (which writes to fd 1\n * through Node.js `fs.writeSync` / `fs.write`). This means every log line\n * produced through Pino's default destination is invisible in Log Analytics.\n *\n * The fix mirrors what the gateway's own `logger.ts` does in production:\n * pass `pino.destination({ sync: true })` as the second argument to the\n * Pino factory so writes are synchronous and go through a path that ACA\n * captures correctly.\n */\nimport { Logger as BaseLogger } from '../logger/index.js';\nimport { DEFAULT_REDACT_PATHS } from './pino-redaction.js';\n\nexport interface PinoLoggerConfig {\n serviceName: string;\n env: 'alpha' | 'prod' | 'dev';\n version: string;\n /** Override Pino log level. Default: warn (prod), info (alpha/dev). */\n level?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace';\n /** Extra redact paths appended to DEFAULT_REDACT_PATHS. */\n redactPaths?: string[];\n}\n\ninterface PinoLike {\n level: string;\n fatal: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n debug: (...args: unknown[]) => void;\n trace: (...args: unknown[]) => void;\n child: (bindings: Record<string, unknown>) => PinoLike;\n}\n\n/**\n * Loaded Pino module: factory function + a synchronous destination factory.\n * In ACA, Pino's default async sonic-boom destination is invisible in Log\n * Analytics, so we always use a sync destination.\n */\ninterface PinoLoader {\n factory: (opts: unknown, dest?: unknown) => PinoLike;\n makeDest: () => unknown;\n}\n\nasync function tryLoadPino(): Promise<PinoLoader | null> {\n try {\n const mod = (await import('pino')) as unknown as { default?: unknown };\n const candidate = (mod.default ?? mod) as unknown;\n if (typeof candidate === 'function') {\n const factory = candidate as (opts: unknown, dest?: unknown) => PinoLike;\n const destination = (factory as unknown as Record<string, unknown>)?.['destination'] as\n | ((opts?: { sync?: boolean }) => unknown)\n | undefined;\n const makeDest = typeof destination === 'function' ? () => destination({ sync: true }) : () => undefined;\n return { factory, makeDest };\n }\n return null;\n } catch {\n return null;\n }\n}\n\nclass PinoAdapter extends BaseLogger {\n constructor(private readonly pino: PinoLike) {\n super({\n level: (pino.level as BaseLogger['getLevel'] extends () => infer L ? L : never) ?? 'info',\n console: false,\n });\n }\n\n public override debug(message: string, ...args: unknown[]): void {\n this.pino.debug(this.mergeArgs(args), message);\n }\n public override info(message: string, ...args: unknown[]): void {\n this.pino.info(this.mergeArgs(args), message);\n }\n public override warn(message: string, ...args: unknown[]): void {\n this.pino.warn(this.mergeArgs(args), message);\n }\n public override error(message: string, ...args: unknown[]): void {\n this.pino.error(this.mergeArgs(args), message);\n }\n public override log(message: string, ...args: unknown[]): void {\n this.pino.info(this.mergeArgs(args), message);\n }\n public override trace(message: string, ...args: unknown[]): void {\n this.pino.trace(this.mergeArgs(args), message);\n }\n\n private mergeArgs(args: unknown[]): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const arg of args) {\n if (arg instanceof Error) {\n out['err'] = { message: arg.message, stack: arg.stack, name: arg.name };\n } else if (arg && typeof arg === 'object') {\n Object.assign(out, arg as Record<string, unknown>);\n } else if (arg !== undefined && arg !== null) {\n out['data'] = arg;\n }\n }\n return out;\n }\n}\n\n/**\n * Build a logger instance. Returns a Pino-backed adapter when `pino` is\n * installed; otherwise falls back to the SDK's built-in JSON Logger.\n *\n * Services call this through `bootstrapTelemetry`, not directly.\n */\nexport async function createPinoLogger(cfg: PinoLoggerConfig): Promise<BaseLogger> {\n const loader = await tryLoadPino();\n if (!loader) {\n // No pino — fall back to the built-in Logger with console output.\n const fallback = new BaseLogger({ level: cfg.env === 'prod' ? 'warn' : 'info', console: true });\n fallback.setContext({\n service: cfg.serviceName,\n env: cfg.env,\n version: cfg.version,\n });\n return fallback;\n }\n\n const redactPaths = [...DEFAULT_REDACT_PATHS, ...(cfg.redactPaths ?? [])];\n const dest = loader.makeDest();\n const pino = loader.factory(\n {\n name: cfg.serviceName,\n level: cfg.level ?? (cfg.env === 'prod' ? 'warn' : 'info'),\n base: {\n service: { name: cfg.serviceName, version: cfg.version },\n env: cfg.env,\n },\n redact: {\n paths: redactPaths,\n censor: '[REDACTED]',\n remove: false,\n },\n timestamp: () => `,\"time\":\"${new Date().toISOString()}\"`,\n mixin: () => pickTraceFields(),\n },\n // Explicit sync destination so output is visible in ACA Log Analytics.\n // Without this, Pino uses its default async sonic-boom which writes to fd 1\n // but the ContainerAppConsoleLogs agent doesn't capture it on Bun.\n dest\n );\n return new PinoAdapter(pino);\n}\n\nfunction pickTraceFields(): Record<string, string> {\n try {\n // Pull active span context via @opentelemetry/api if installed.\n // Use require-style dynamic access to keep this file dep-free.\n const api = (globalThis as unknown as { __otelApi?: unknown }).__otelApi;\n if (!api) return {};\n const anyApi = api as {\n trace: {\n getActiveSpan: () => { spanContext: () => { traceId: string; spanId: string; traceFlags: number } } | undefined;\n };\n };\n const span = anyApi.trace.getActiveSpan();\n if (!span) return {};\n const ctx = span.spanContext();\n return {\n trace_id: ctx.traceId,\n span_id: ctx.spanId,\n trace_flags: ctx.traceFlags.toString(16).padStart(2, '0'),\n };\n } catch {\n return {};\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isTokenExpired, parseJWT } from './chunk-B7I4TABG.js';
|
|
2
|
-
import { AuthenticationError } from './chunk-
|
|
2
|
+
import { AuthenticationError } from './chunk-QGBXVNRM.js';
|
|
3
3
|
import { logger } from './chunk-CBZNP6RF.js';
|
|
4
4
|
import { __name } from './chunk-XE4OXN2W.js';
|
|
5
5
|
|
|
@@ -272,5 +272,5 @@ var AuthModule = class {
|
|
|
272
272
|
};
|
|
273
273
|
|
|
274
274
|
export { AuthCookieHelper, AuthModule };
|
|
275
|
-
//# sourceMappingURL=chunk-
|
|
276
|
-
//# sourceMappingURL=chunk-
|
|
275
|
+
//# sourceMappingURL=chunk-RXMOV3YE.js.map
|
|
276
|
+
//# sourceMappingURL=chunk-RXMOV3YE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth/cookie-helper.ts","../src/auth/index.ts"],"names":["AuthCookieHelper","ACCESS_TOKEN_COOKIE","REFRESH_TOKEN_COOKIE","PROFILE_ID_COOKIE","setAuthCookies","context","tokens","options","isProd","process","env","domain","setCookie","name","value","accessToken","maxAge","httpOnly","secure","sameSite","path","refreshToken","rememberMe","profileId","clearAuthCookies","clearOptions","cookie","undefined","responseHeaders","append","AuthModule","client","verifyToken","token","logger","debug","isTokenExpired","valid","error","query","response","Error","message","decodeToken","payload","parseJWT","mutation","info","AuthenticationError","validatePermission","userId","permission","resourceId","allowed","isTokenValid","getUserIdFromToken","user_id","getWorkspaceIdFromToken","workspace_id","getTokenClaims","willExpireSoon","withinSeconds","exp","now","Math","floor","Date"],"mappings":"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","file":"chunk-SS4XWRYR.js","sourcesContent":["/**\n * Auth Cookie Helper\n *\n * Utilities for setting secure, HttpOnly cookies for authentication.\n * Used by auth services to transition from localStorage to secure cookies.\n */\n\nimport type { GraphQLContext } from '../server/types.js';\n\nexport interface CookieOptions {\n name: string;\n value: string;\n maxAge?: number;\n domain?: string;\n path?: string;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: 'Strict' | 'Lax' | 'None';\n}\n\nexport class AuthCookieHelper {\n private static readonly ACCESS_TOKEN_COOKIE = 'bf_at';\n private static readonly REFRESH_TOKEN_COOKIE = 'bf_rt';\n private static readonly PROFILE_ID_COOKIE = 'bf_pid';\n\n /**\n * Set authentication cookies in the context response headers\n */\n public static setAuthCookies(\n context: GraphQLContext,\n tokens: { accessToken: string; refreshToken?: string },\n options: { profileId?: string; rememberMe?: boolean } = {}\n ): void {\n const isProd = process.env['NODE_ENV'] === 'production';\n const domain = process.env['COOKIE_DOMAIN'];\n\n // Access token cookie (short-lived, 15m)\n this.setCookie(context, {\n name: this.ACCESS_TOKEN_COOKIE,\n value: tokens.accessToken,\n maxAge: 15 * 60,\n httpOnly: true,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/',\n });\n\n // Refresh token cookie (long-lived, 7d or 30d)\n if (tokens.refreshToken) {\n this.setCookie(context, {\n name: this.REFRESH_TOKEN_COOKIE,\n value: tokens.refreshToken,\n maxAge: options.rememberMe ? 30 * 24 * 60 * 60 : 7 * 24 * 60 * 60,\n httpOnly: true,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/global/graphql', // Only send to global gateway for refresh\n });\n }\n\n // Profile ID cookie (not HttpOnly, needed for UI profile resolution)\n if (options.profileId) {\n this.setCookie(context, {\n name: this.PROFILE_ID_COOKIE,\n value: options.profileId,\n maxAge: 30 * 24 * 60 * 60,\n httpOnly: false,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/',\n });\n }\n }\n\n /**\n * Clear authentication cookies\n */\n public static clearAuthCookies(context: GraphQLContext): void {\n const domain = process.env['COOKIE_DOMAIN'];\n const isProd = process.env['NODE_ENV'] === 'production';\n\n const clearOptions = {\n value: '',\n maxAge: 0,\n domain,\n path: '/',\n secure: isProd,\n sameSite: 'Lax' as const,\n };\n\n this.setCookie(context, { ...clearOptions, name: this.ACCESS_TOKEN_COOKIE, httpOnly: true });\n this.setCookie(context, {\n ...clearOptions,\n name: this.REFRESH_TOKEN_COOKIE,\n httpOnly: true,\n path: '/global/graphql',\n });\n this.setCookie(context, { ...clearOptions, name: this.PROFILE_ID_COOKIE, httpOnly: false });\n }\n\n /**\n * Internal helper to set a cookie header\n */\n private static setCookie(context: GraphQLContext, options: CookieOptions): void {\n let cookie = `${options.name}=${options.value}`;\n\n if (options.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`;\n }\n\n if (options.domain) {\n cookie += `; Domain=${options.domain}`;\n }\n\n if (options.path) {\n cookie += `; Path=${options.path}`;\n }\n\n if (options.httpOnly) {\n cookie += '; HttpOnly';\n }\n\n if (options.secure) {\n cookie += '; Secure';\n }\n\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite}`;\n }\n\n context.responseHeaders.append('Set-Cookie', cookie);\n }\n}\n","/**\n * Authentication Module\n * JWT verification, token management, and user authentication\n */\n\nimport type { BurdenoffGraphQLClient } from '../client/graphql.js';\nimport type { SDKContext } from '../types/index.js';\nimport { AuthenticationError } from '../types/index.js';\nimport { logger } from '../logger/index.js';\nimport { parseJWT, isTokenExpired } from '../utils/index.js';\n\nexport { AuthCookieHelper } from './cookie-helper.js';\n\nexport interface TokenPayload {\n jti: string;\n sub: string;\n user_id: string;\n workspace_id?: string;\n org_id?: string;\n tenant_endpoint?: string;\n iat: number;\n exp: number;\n scope?: string;\n}\n\nexport interface VerifyTokenResult {\n valid: boolean;\n payload?: TokenPayload;\n error?: string;\n}\n\nexport interface RefreshTokenResult {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\nexport * from './cookie-helper.js';\n\n/**\n * Authentication Module\n */\nexport class AuthModule {\n constructor(\n private client: BurdenoffGraphQLClient,\n private context: SDKContext\n ) {}\n\n /**\n * Verify JWT token\n */\n public async verifyToken(token: string): Promise<VerifyTokenResult> {\n logger.debug('[Auth] Verifying token');\n\n // First check if token is expired locally\n if (isTokenExpired(token)) {\n return {\n valid: false,\n error: 'Token expired',\n };\n }\n\n const query = `\n query VerifyToken($token: String!) {\n verifyToken(token: $token) {\n valid\n payload\n error\n }\n }\n `;\n\n try {\n const response = await this.client.query<{ verifyToken: VerifyTokenResult }>(query, { token }, this.context);\n\n logger.debug('[Auth] Token verification result', { valid: response.verifyToken.valid });\n\n return response.verifyToken;\n } catch (error) {\n logger.error('[Auth] Token verification failed', { error });\n return {\n valid: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Decode token without verification\n */\n public decodeToken(token: string): TokenPayload | null {\n const payload = parseJWT(token);\n if (!payload) {\n return null;\n }\n return payload as unknown as TokenPayload;\n }\n\n /**\n * Refresh access token using refresh token\n */\n public async refreshToken(refreshToken: string): Promise<RefreshTokenResult> {\n logger.debug('[Auth] Refreshing token');\n\n const mutation = `\n mutation RefreshToken($refreshToken: String!) {\n refreshToken(refreshToken: $refreshToken) {\n accessToken\n refreshToken\n expiresIn\n }\n }\n `;\n\n try {\n const response = await this.client.query<{ refreshToken: RefreshTokenResult }>(\n mutation,\n { refreshToken },\n this.context\n );\n\n logger.info('[Auth] Token refreshed successfully');\n\n return response.refreshToken;\n } catch (error) {\n logger.error('[Auth] Token refresh failed', { error });\n throw new AuthenticationError('Failed to refresh token', {\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n\n /**\n * Validate user has specific permission\n */\n public async validatePermission(userId: string, permission: string, resourceId?: string): Promise<boolean> {\n logger.debug('[Auth] Validating permission', { userId, permission, resourceId });\n\n const query = `\n query ValidatePermission($userId: ID!, $permission: String!, $resourceId: ID) {\n validatePermission(userId: $userId, permission: $permission, resourceId: $resourceId) {\n allowed\n }\n }\n `;\n\n try {\n const response = await this.client.query<{\n validatePermission: { allowed: boolean };\n }>(query, { userId, permission, resourceId }, this.context);\n\n logger.debug('[Auth] Permission validation result', {\n allowed: response.validatePermission.allowed,\n });\n\n return response.validatePermission.allowed;\n } catch (error) {\n logger.error('[Auth] Permission validation failed', { error });\n return false;\n }\n }\n\n /**\n * Check if token is valid and not expired\n */\n public isTokenValid(token: string): boolean {\n return !isTokenExpired(token);\n }\n\n /**\n * Get user ID from token\n */\n public getUserIdFromToken(token: string): string | null {\n const payload = this.decodeToken(token);\n return payload?.user_id || null;\n }\n\n /**\n * Get workspace ID from token\n */\n public getWorkspaceIdFromToken(token: string): string | null {\n const payload = this.decodeToken(token);\n return payload?.workspace_id || null;\n }\n\n /**\n * Extract all claims from token\n */\n public getTokenClaims(token: string): Record<string, unknown> | null {\n const payload = parseJWT(token);\n return payload;\n }\n\n /**\n * Check if token will expire soon (within given seconds)\n */\n public willExpireSoon(token: string, withinSeconds: number = 300): boolean {\n const payload = this.decodeToken(token);\n if (!payload || typeof payload.exp !== 'number') {\n return true;\n }\n\n const now = Math.floor(Date.now() / 1000);\n return payload.exp - now < withinSeconds;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth/cookie-helper.ts","../src/auth/index.ts"],"names":["AuthCookieHelper","ACCESS_TOKEN_COOKIE","REFRESH_TOKEN_COOKIE","PROFILE_ID_COOKIE","setAuthCookies","context","tokens","options","isProd","process","env","domain","setCookie","name","value","accessToken","maxAge","httpOnly","secure","sameSite","path","refreshToken","rememberMe","profileId","clearAuthCookies","clearOptions","cookie","undefined","responseHeaders","append","AuthModule","client","verifyToken","token","logger","debug","isTokenExpired","valid","error","query","response","Error","message","decodeToken","payload","parseJWT","mutation","info","AuthenticationError","validatePermission","userId","permission","resourceId","allowed","isTokenValid","getUserIdFromToken","user_id","getWorkspaceIdFromToken","workspace_id","getTokenClaims","willExpireSoon","withinSeconds","exp","now","Math","floor","Date"],"mappings":"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","file":"chunk-RXMOV3YE.js","sourcesContent":["/**\n * Auth Cookie Helper\n *\n * Utilities for setting secure, HttpOnly cookies for authentication.\n * Used by auth services to transition from localStorage to secure cookies.\n */\n\nimport type { GraphQLContext } from '../server/types.js';\n\nexport interface CookieOptions {\n name: string;\n value: string;\n maxAge?: number;\n domain?: string;\n path?: string;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: 'Strict' | 'Lax' | 'None';\n}\n\nexport class AuthCookieHelper {\n private static readonly ACCESS_TOKEN_COOKIE = 'bf_at';\n private static readonly REFRESH_TOKEN_COOKIE = 'bf_rt';\n private static readonly PROFILE_ID_COOKIE = 'bf_pid';\n\n /**\n * Set authentication cookies in the context response headers\n */\n public static setAuthCookies(\n context: GraphQLContext,\n tokens: { accessToken: string; refreshToken?: string },\n options: { profileId?: string; rememberMe?: boolean } = {}\n ): void {\n const isProd = process.env['NODE_ENV'] === 'production';\n const domain = process.env['COOKIE_DOMAIN'];\n\n // Access token cookie (short-lived, 15m)\n this.setCookie(context, {\n name: this.ACCESS_TOKEN_COOKIE,\n value: tokens.accessToken,\n maxAge: 15 * 60,\n httpOnly: true,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/',\n });\n\n // Refresh token cookie (long-lived, 7d or 30d)\n if (tokens.refreshToken) {\n this.setCookie(context, {\n name: this.REFRESH_TOKEN_COOKIE,\n value: tokens.refreshToken,\n maxAge: options.rememberMe ? 30 * 24 * 60 * 60 : 7 * 24 * 60 * 60,\n httpOnly: true,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/global/graphql', // Only send to global gateway for refresh\n });\n }\n\n // Profile ID cookie (not HttpOnly, needed for UI profile resolution)\n if (options.profileId) {\n this.setCookie(context, {\n name: this.PROFILE_ID_COOKIE,\n value: options.profileId,\n maxAge: 30 * 24 * 60 * 60,\n httpOnly: false,\n secure: isProd,\n sameSite: 'Lax',\n domain,\n path: '/',\n });\n }\n }\n\n /**\n * Clear authentication cookies\n */\n public static clearAuthCookies(context: GraphQLContext): void {\n const domain = process.env['COOKIE_DOMAIN'];\n const isProd = process.env['NODE_ENV'] === 'production';\n\n const clearOptions = {\n value: '',\n maxAge: 0,\n domain,\n path: '/',\n secure: isProd,\n sameSite: 'Lax' as const,\n };\n\n this.setCookie(context, { ...clearOptions, name: this.ACCESS_TOKEN_COOKIE, httpOnly: true });\n this.setCookie(context, {\n ...clearOptions,\n name: this.REFRESH_TOKEN_COOKIE,\n httpOnly: true,\n path: '/global/graphql',\n });\n this.setCookie(context, { ...clearOptions, name: this.PROFILE_ID_COOKIE, httpOnly: false });\n }\n\n /**\n * Internal helper to set a cookie header\n */\n private static setCookie(context: GraphQLContext, options: CookieOptions): void {\n let cookie = `${options.name}=${options.value}`;\n\n if (options.maxAge !== undefined) {\n cookie += `; Max-Age=${options.maxAge}`;\n }\n\n if (options.domain) {\n cookie += `; Domain=${options.domain}`;\n }\n\n if (options.path) {\n cookie += `; Path=${options.path}`;\n }\n\n if (options.httpOnly) {\n cookie += '; HttpOnly';\n }\n\n if (options.secure) {\n cookie += '; Secure';\n }\n\n if (options.sameSite) {\n cookie += `; SameSite=${options.sameSite}`;\n }\n\n context.responseHeaders.append('Set-Cookie', cookie);\n }\n}\n","/**\n * Authentication Module\n * JWT verification, token management, and user authentication\n */\n\nimport type { BurdenoffGraphQLClient } from '../client/graphql.js';\nimport type { SDKContext } from '../types/index.js';\nimport { AuthenticationError } from '../types/index.js';\nimport { logger } from '../logger/index.js';\nimport { parseJWT, isTokenExpired } from '../utils/index.js';\n\nexport { AuthCookieHelper } from './cookie-helper.js';\n\nexport interface TokenPayload {\n jti: string;\n sub: string;\n user_id: string;\n workspace_id?: string;\n org_id?: string;\n tenant_endpoint?: string;\n iat: number;\n exp: number;\n scope?: string;\n}\n\nexport interface VerifyTokenResult {\n valid: boolean;\n payload?: TokenPayload;\n error?: string;\n}\n\nexport interface RefreshTokenResult {\n accessToken: string;\n refreshToken: string;\n expiresIn: number;\n}\n\nexport * from './cookie-helper.js';\n\n/**\n * Authentication Module\n */\nexport class AuthModule {\n constructor(\n private client: BurdenoffGraphQLClient,\n private context: SDKContext\n ) {}\n\n /**\n * Verify JWT token\n */\n public async verifyToken(token: string): Promise<VerifyTokenResult> {\n logger.debug('[Auth] Verifying token');\n\n // First check if token is expired locally\n if (isTokenExpired(token)) {\n return {\n valid: false,\n error: 'Token expired',\n };\n }\n\n const query = `\n query VerifyToken($token: String!) {\n verifyToken(token: $token) {\n valid\n payload\n error\n }\n }\n `;\n\n try {\n const response = await this.client.query<{ verifyToken: VerifyTokenResult }>(query, { token }, this.context);\n\n logger.debug('[Auth] Token verification result', { valid: response.verifyToken.valid });\n\n return response.verifyToken;\n } catch (error) {\n logger.error('[Auth] Token verification failed', { error });\n return {\n valid: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Decode token without verification\n */\n public decodeToken(token: string): TokenPayload | null {\n const payload = parseJWT(token);\n if (!payload) {\n return null;\n }\n return payload as unknown as TokenPayload;\n }\n\n /**\n * Refresh access token using refresh token\n */\n public async refreshToken(refreshToken: string): Promise<RefreshTokenResult> {\n logger.debug('[Auth] Refreshing token');\n\n const mutation = `\n mutation RefreshToken($refreshToken: String!) {\n refreshToken(refreshToken: $refreshToken) {\n accessToken\n refreshToken\n expiresIn\n }\n }\n `;\n\n try {\n const response = await this.client.query<{ refreshToken: RefreshTokenResult }>(\n mutation,\n { refreshToken },\n this.context\n );\n\n logger.info('[Auth] Token refreshed successfully');\n\n return response.refreshToken;\n } catch (error) {\n logger.error('[Auth] Token refresh failed', { error });\n throw new AuthenticationError('Failed to refresh token', {\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n\n /**\n * Validate user has specific permission\n */\n public async validatePermission(userId: string, permission: string, resourceId?: string): Promise<boolean> {\n logger.debug('[Auth] Validating permission', { userId, permission, resourceId });\n\n const query = `\n query ValidatePermission($userId: ID!, $permission: String!, $resourceId: ID) {\n validatePermission(userId: $userId, permission: $permission, resourceId: $resourceId) {\n allowed\n }\n }\n `;\n\n try {\n const response = await this.client.query<{\n validatePermission: { allowed: boolean };\n }>(query, { userId, permission, resourceId }, this.context);\n\n logger.debug('[Auth] Permission validation result', {\n allowed: response.validatePermission.allowed,\n });\n\n return response.validatePermission.allowed;\n } catch (error) {\n logger.error('[Auth] Permission validation failed', { error });\n return false;\n }\n }\n\n /**\n * Check if token is valid and not expired\n */\n public isTokenValid(token: string): boolean {\n return !isTokenExpired(token);\n }\n\n /**\n * Get user ID from token\n */\n public getUserIdFromToken(token: string): string | null {\n const payload = this.decodeToken(token);\n return payload?.user_id || null;\n }\n\n /**\n * Get workspace ID from token\n */\n public getWorkspaceIdFromToken(token: string): string | null {\n const payload = this.decodeToken(token);\n return payload?.workspace_id || null;\n }\n\n /**\n * Extract all claims from token\n */\n public getTokenClaims(token: string): Record<string, unknown> | null {\n const payload = parseJWT(token);\n return payload;\n }\n\n /**\n * Check if token will expire soon (within given seconds)\n */\n public willExpireSoon(token: string, withinSeconds: number = 300): boolean {\n const payload = this.decodeToken(token);\n if (!payload || typeof payload.exp !== 'number') {\n return true;\n }\n\n const now = Math.floor(Date.now() / 1000);\n return payload.exp - now < withinSeconds;\n }\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|
|
3
3
|
|
|
4
4
|
var asyncLocalStorage = new AsyncLocalStorage();
|
|
5
5
|
var cfAccessFallback;
|
|
6
|
+
var cfAuthorizationFallback;
|
|
6
7
|
var ContextManager = class {
|
|
7
8
|
static {
|
|
8
9
|
__name(this, "ContextManager");
|
|
@@ -23,8 +24,8 @@ var ContextManager = class {
|
|
|
23
24
|
...currentContext,
|
|
24
25
|
...context
|
|
25
26
|
});
|
|
26
|
-
if (context.cfAccessClientId && context.cfAccessClientSecret) {
|
|
27
|
-
this.setCfAccessContext(context.cfAccessClientId, context.cfAccessClientSecret);
|
|
27
|
+
if (context.cfAccessClientId && context.cfAccessClientSecret || context.cfAuthorization) {
|
|
28
|
+
this.setCfAccessContext(context.cfAccessClientId, context.cfAccessClientSecret, context.cfAuthorization);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
@@ -41,13 +42,16 @@ var ContextManager = class {
|
|
|
41
42
|
* so "remember, never clear on absence" is both safe and correct. Use
|
|
42
43
|
* `clearContext()` to reset (shutdown / tests).
|
|
43
44
|
*/
|
|
44
|
-
static setCfAccessContext(clientId, clientSecret) {
|
|
45
|
+
static setCfAccessContext(clientId, clientSecret, cfAuthorization) {
|
|
45
46
|
if (clientId && clientSecret) {
|
|
46
47
|
cfAccessFallback = {
|
|
47
48
|
clientId,
|
|
48
49
|
clientSecret
|
|
49
50
|
};
|
|
50
51
|
}
|
|
52
|
+
if (cfAuthorization) {
|
|
53
|
+
cfAuthorizationFallback = cfAuthorization;
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Run function with specific context
|
|
@@ -68,6 +72,7 @@ var ContextManager = class {
|
|
|
68
72
|
static clearContext() {
|
|
69
73
|
asyncLocalStorage.disable();
|
|
70
74
|
cfAccessFallback = void 0;
|
|
75
|
+
cfAuthorizationFallback = void 0;
|
|
71
76
|
}
|
|
72
77
|
/**
|
|
73
78
|
* Get actor ID from context
|
|
@@ -141,7 +146,14 @@ var ContextManager = class {
|
|
|
141
146
|
return this.getContext()?.cfAccessClientSecret ?? cfAccessFallback?.clientSecret;
|
|
142
147
|
}
|
|
143
148
|
/**
|
|
144
|
-
*
|
|
149
|
+
* Get the Cloudflare Access user-SSO cookie value (`CF_Authorization`) — ALS
|
|
150
|
+
* first, then the module-level fallback (see `cfAuthorizationFallback`).
|
|
151
|
+
*/
|
|
152
|
+
static getCfAuthorization() {
|
|
153
|
+
return this.getContext()?.cfAuthorization ?? cfAuthorizationFallback;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Build the Cloudflare Access credentials to re-present on a
|
|
145
157
|
* cross-layer outbound call.
|
|
146
158
|
*
|
|
147
159
|
* Cross-layer calls (wspace ↔ global) traverse the peer layer's PUBLIC
|
|
@@ -157,15 +169,18 @@ var ContextManager = class {
|
|
|
157
169
|
* CF-Access-gated and has no CF service token).
|
|
158
170
|
*/
|
|
159
171
|
static getCfAccessHeaders() {
|
|
172
|
+
const headers = {};
|
|
160
173
|
const clientId = this.getCfAccessClientId();
|
|
161
174
|
const clientSecret = this.getCfAccessClientSecret();
|
|
162
175
|
if (clientId && clientSecret) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
176
|
+
headers["cf-access-client-id"] = clientId;
|
|
177
|
+
headers["cf-access-client-secret"] = clientSecret;
|
|
178
|
+
}
|
|
179
|
+
const cfAuthorization = this.getCfAuthorization();
|
|
180
|
+
if (cfAuthorization) {
|
|
181
|
+
headers["cookie"] = `CF_Authorization=${cfAuthorization}`;
|
|
167
182
|
}
|
|
168
|
-
return
|
|
183
|
+
return headers;
|
|
169
184
|
}
|
|
170
185
|
/**
|
|
171
186
|
* Update specific context fields
|
|
@@ -206,5 +221,5 @@ var ContextManager = class {
|
|
|
206
221
|
};
|
|
207
222
|
|
|
208
223
|
export { ContextManager };
|
|
209
|
-
//# sourceMappingURL=chunk-
|
|
210
|
-
//# sourceMappingURL=chunk-
|
|
224
|
+
//# sourceMappingURL=chunk-RZOFS2BQ.js.map
|
|
225
|
+
//# sourceMappingURL=chunk-RZOFS2BQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/index.ts"],"names":["asyncLocalStorage","AsyncLocalStorage","cfAccessFallback","cfAuthorizationFallback","ContextManager","getContext","getStore","setContext","context","currentContext","enterWith","cfAccessClientId","cfAccessClientSecret","cfAuthorization","setCfAccessContext","clientId","clientSecret","withContext","fn","run","withContextSync","clearContext","disable","undefined","getActorId","actorId","getUser","userId","getWorkspace","workspaceId","getTenant","tenantId","getOrganization","organizationId","getProject","projectId","getApp","appId","getToken","token","getProduct","productId","getCfAccessClientId","getCfAccessClientSecret","getCfAuthorization","getCfAccessHeaders","headers","updateContext","updates","current","hasContext","getCustomField","key","setCustomField","value"],"mappings":"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","file":"chunk-RZOFS2BQ.js","sourcesContent":["/**\n * Context Module\n * Manages request-scoped context using AsyncLocalStorage\n * Thread-safe context propagation for async operations\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { SDKContext } from '../types/index.js';\n\nconst asyncLocalStorage = new AsyncLocalStorage<SDKContext>();\n\n/**\n * Module-level fallback for the Cloudflare Access service token.\n *\n * WHY THIS EXISTS — read before \"simplifying\" it away:\n * `asyncLocalStorage.enterWith()` (used by `setContext`) does NOT reliably\n * propagate across every async boundary under Bun + GraphQL Yoga — the same\n * limitation documented for the Prisma workspace context store\n * (see `src/prisma/workspace-extension.ts`). On top of that, the `createServer`\n * request path (`src/server/context.ts` `buildContext`) populates the *Prisma*\n * workspace store, not this `ContextManager` ALS at all. So a resolver-initiated\n * cross-layer call would otherwise read an empty context and attach no CF creds,\n * making the propagation a silent no-op on alpha.\n *\n * Unlike workspaceId/actorId (which differ per request and would race in a\n * module-level singleton), the CF Access service token is effectively\n * CONSTANT per environment — it's a single service-token pair fronting the\n * gateway. So caching it module-side is race-safe: concurrent requests all\n * carry the same value. It is only ever set when a request actually presents\n * the headers (gated upstream on `cf-ray`), and stays unset in dev/prod where\n * no CF Access sits in front of the gateways.\n */\nlet cfAccessFallback: { clientId: string; clientSecret: string } | undefined;\n\n/**\n * Module-level fallback for the CF Access USER-SSO cookie (`CF_Authorization`),\n * mirroring `cfAccessFallback` for the same Bun+Yoga ALS-unreliability reason.\n * Unlike the service-token pair this is per-USER and ~1h TTL — but for passing\n * the CF edge any valid member cookie suffices (app-layer identity travels in\n * `x-actor-*`). Refreshed by every inbound CF request, so an active fleet keeps\n * a live value. See BOFF-2929.\n */\nlet cfAuthorizationFallback: string | undefined;\n\n/**\n * Context Manager for handling request-scoped context\n */\nexport class ContextManager {\n /**\n * Get current context from async local storage\n */\n public static getContext(): SDKContext | undefined {\n return asyncLocalStorage.getStore();\n }\n\n /**\n * Set context for current async scope\n * Merges with existing context if present\n */\n public static setContext(context: SDKContext): void {\n const currentContext = this.getContext() || {};\n asyncLocalStorage.enterWith({ ...currentContext, ...context });\n // Mirror CF creds to the module-level fallback so cross-layer calls can\n // recover them even when enterWith() doesn't reach the resolver chain.\n if ((context.cfAccessClientId && context.cfAccessClientSecret) || context.cfAuthorization) {\n this.setCfAccessContext(context.cfAccessClientId, context.cfAccessClientSecret, context.cfAuthorization);\n }\n }\n\n /**\n * Record the Cloudflare Access service token for the current process.\n *\n * Call this from every request-context builder that may receive CF headers\n * (the SDK `createServer` `buildContext` and the bootstrap HTTP middleware\n * both do). Stored in a module-level fallback because `enterWith()` is not a\n * reliable carrier under Bun + Yoga — see the `cfAccessFallback` note above.\n *\n * Deliberately a NO-OP when either cred is absent: `buildContext` runs on\n * every request, so clearing here on a tokenless request could wipe the value\n * a concurrent CF request just stored. The token is constant per environment,\n * so \"remember, never clear on absence\" is both safe and correct. Use\n * `clearContext()` to reset (shutdown / tests).\n */\n public static setCfAccessContext(clientId?: string, clientSecret?: string, cfAuthorization?: string): void {\n if (clientId && clientSecret) {\n cfAccessFallback = { clientId, clientSecret };\n }\n // CF Access user-SSO cookie — remember, never clear on absence (same policy\n // as the service token). Present on real alpha user requests; kept live by\n // ongoing traffic. See BOFF-2929.\n if (cfAuthorization) {\n cfAuthorizationFallback = cfAuthorization;\n }\n }\n\n /**\n * Run function with specific context\n * Context is isolated to this execution and its children\n */\n public static async withContext<T>(context: SDKContext, fn: () => Promise<T>): Promise<T> {\n return asyncLocalStorage.run(context, fn);\n }\n\n /**\n * Run synchronous function with specific context\n */\n public static withContextSync<T>(context: SDKContext, fn: () => T): T {\n return asyncLocalStorage.run(context, fn);\n }\n\n /**\n * Clear current context (including the module-level CF Access fallback).\n */\n public static clearContext(): void {\n asyncLocalStorage.disable();\n cfAccessFallback = undefined;\n cfAuthorizationFallback = undefined;\n }\n\n /**\n * Get actor ID from context\n */\n public static getActorId(): string | undefined {\n return this.getContext()?.actorId;\n }\n\n /**\n * Get legacy user ID from context.\n */\n public static getUser(): string | undefined {\n const context = this.getContext();\n const userId = context?.['userId'];\n return typeof userId === 'string' ? userId : context?.actorId;\n }\n\n /**\n * Get workspace ID from context\n */\n public static getWorkspace(): string | undefined {\n return this.getContext()?.workspaceId;\n }\n\n /**\n * Get tenant ID from context\n */\n public static getTenant(): string | undefined {\n return this.getContext()?.tenantId;\n }\n\n /**\n * Get legacy organization ID from context.\n */\n public static getOrganization(): string | undefined {\n const organizationId = this.getContext()?.['organizationId'];\n return typeof organizationId === 'string' ? organizationId : undefined;\n }\n\n /**\n * Get project ID from context\n */\n public static getProject(): string | undefined {\n return this.getContext()?.projectId;\n }\n\n /**\n * Get app ID from context\n */\n public static getApp(): string | undefined {\n return this.getContext()?.appId;\n }\n\n /**\n * Get JWT token from context\n */\n public static getToken(): string | undefined {\n return this.getContext()?.token;\n }\n\n /**\n * Get product ID from context\n */\n public static getProduct(): string | undefined {\n return this.getContext()?.productId;\n }\n\n /**\n * Get the Cloudflare Access service-token client id (ALS first, then the\n * module-level fallback — see `cfAccessFallback`).\n */\n public static getCfAccessClientId(): string | undefined {\n return this.getContext()?.cfAccessClientId ?? cfAccessFallback?.clientId;\n }\n\n /**\n * Get the Cloudflare Access service-token client secret (ALS first, then the\n * module-level fallback — see `cfAccessFallback`).\n */\n public static getCfAccessClientSecret(): string | undefined {\n return this.getContext()?.cfAccessClientSecret ?? cfAccessFallback?.clientSecret;\n }\n\n /**\n * Get the Cloudflare Access user-SSO cookie value (`CF_Authorization`) — ALS\n * first, then the module-level fallback (see `cfAuthorizationFallback`).\n */\n public static getCfAuthorization(): string | undefined {\n return this.getContext()?.cfAuthorization ?? cfAuthorizationFallback;\n }\n\n /**\n * Build the Cloudflare Access credentials to re-present on a\n * cross-layer outbound call.\n *\n * Cross-layer calls (wspace ↔ global) traverse the peer layer's PUBLIC\n * gateway, which on alpha sits behind Cloudflare Access. Without the CF\n * service token the request is rejected at the CF edge before reaching the\n * gateway. We reuse the *inbound* request's CF creds (propagated by the\n * public gateway into request-scoped context) so the cross-layer leg passes.\n *\n * Inbound-only: sourced from the request context (AsyncLocalStorage first,\n * module-level fallback second — see `cfAccessFallback`). Returns `{}` unless\n * BOTH creds are present — so it is a no-op in environments without CF in\n * front of the gateways (local dev, and production, which is not\n * CF-Access-gated and has no CF service token).\n */\n public static getCfAccessHeaders(): Record<string, string> {\n const headers: Record<string, string> = {};\n\n // Service-token pair (machine callers). Present only when a service token\n // fronted the inbound call — not on browser user requests.\n const clientId = this.getCfAccessClientId();\n const clientSecret = this.getCfAccessClientSecret();\n if (clientId && clientSecret) {\n headers['cf-access-client-id'] = clientId;\n headers['cf-access-client-secret'] = clientSecret;\n }\n\n // User-SSO cookie. On alpha a real user request carries the CF_Authorization\n // cookie rather than the service-token pair; re-present it as the cookie so\n // the peer layer's CF Access (same app / shared AUD) admits the cross-layer\n // call. See BOFF-2929.\n const cfAuthorization = this.getCfAuthorization();\n if (cfAuthorization) {\n headers['cookie'] = `CF_Authorization=${cfAuthorization}`;\n }\n\n return headers;\n }\n\n /**\n * Update specific context fields\n */\n public static updateContext(updates: Partial<SDKContext>): void {\n const current = this.getContext();\n if (current) {\n this.setContext({ ...current, ...updates });\n } else {\n this.setContext(updates);\n }\n }\n\n /**\n * Check if context exists\n */\n public static hasContext(): boolean {\n return this.getContext() !== undefined;\n }\n\n /**\n * Get custom field from context\n */\n public static getCustomField(key: string): unknown {\n return this.getContext()?.[key];\n }\n\n /**\n * Set custom field in context\n */\n public static setCustomField(key: string, value: unknown): void {\n const current = this.getContext() || {};\n this.setContext({ ...current, [key]: value });\n }\n}\n\n/**\n * Default export for convenience\n */\nexport const contextManager = ContextManager;\n\n/**\n * Helper function to extract context from HTTP request\n */\nexport function extractContextFromRequest(req: {\n headers: Record<string, string | string[] | undefined>;\n user?: { user_id?: string; workspace_id?: string; tenant_endpoint?: string };\n}): SDKContext {\n const context: SDKContext = {};\n\n // Extract from user object (if JWT already verified)\n if (req.user) {\n context.actorId = req.user.user_id;\n context.workspaceId = req.user.workspace_id;\n context.tenantId = req.user.tenant_endpoint;\n }\n\n // Extract from headers\n const getHeader = (key: string): string | undefined => {\n const value = req.headers[key.toLowerCase()];\n return Array.isArray(value) ? value[0] : value;\n };\n\n const authHeader = getHeader('authorization');\n if (authHeader) {\n context.token = authHeader.replace('Bearer ', '');\n }\n\n const workspaceId = getHeader('x-workspace-id');\n if (workspaceId && !context.workspaceId) {\n context.workspaceId = workspaceId;\n }\n\n const productId = getHeader('x-product-id');\n if (productId) {\n context.productId = productId;\n }\n\n const projectId = getHeader('x-project-id');\n if (projectId) {\n context.projectId = projectId;\n }\n\n const appId = getHeader('x-app-id');\n if (appId) {\n context.appId = appId;\n }\n\n // Cloudflare Access service token. Forwarded to subgraphs by the public\n // gateways only when the request actually transited CF (cf-ray present), so\n // we can capture whatever arrives here. Re-presented on cross-layer outbound\n // calls — see ContextManager.getCfAccessHeaders.\n const cfAccessClientId = getHeader('cf-access-client-id');\n if (cfAccessClientId) {\n context.cfAccessClientId = cfAccessClientId;\n }\n\n const cfAccessClientSecret = getHeader('cf-access-client-secret');\n if (cfAccessClientSecret) {\n context.cfAccessClientSecret = cfAccessClientSecret;\n }\n\n // CF Access user-SSO cookie, forwarded by the public gateway as\n // x-cf-authorization. Re-presented as the CF_Authorization cookie on\n // cross-layer calls (see getCfAccessHeaders). See BOFF-2929.\n const cfAuthorization = getHeader('x-cf-authorization');\n if (cfAuthorization) {\n context.cfAuthorization = cfAuthorization;\n }\n\n return context;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ValidationError, PermissionDeniedError } from './chunk-
|
|
1
|
+
import { ValidationError, PermissionDeniedError } from './chunk-QGBXVNRM.js';
|
|
2
2
|
import { DEFAULT_GATEWAY_ROUTING, DEFAULT_LAYER_RESOURCES } from './chunk-YFOHAHZE.js';
|
|
3
3
|
import { logger } from './chunk-CBZNP6RF.js';
|
|
4
4
|
import { __name } from './chunk-XE4OXN2W.js';
|
|
@@ -1491,5 +1491,5 @@ var RBACModule = class {
|
|
|
1491
1491
|
};
|
|
1492
1492
|
|
|
1493
1493
|
export { RBACClient, RBACModule, createRBACClientFromEnv, createRBACDirective, createRBACDirectiveForLayer, createRBACDirectiveFromEnv };
|
|
1494
|
-
//# sourceMappingURL=chunk-
|
|
1495
|
-
//# sourceMappingURL=chunk-
|
|
1494
|
+
//# sourceMappingURL=chunk-S32JOMMW.js.map
|
|
1495
|
+
//# sourceMappingURL=chunk-S32JOMMW.js.map
|