@liteguard/core 0.2.20260314 → 0.3.20260315

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/evaluation.ts","../src/client-base.ts"],"sourcesContent":["export { BaseLiteguardClient, LiteguardScope } from './client-base.js';\nexport type { FlushOptions, LiteguardChangeListener, ScopedOptions } from './client-base.js';\nexport { evaluateGuard } from './evaluation.js';\nexport type {\n ExecutionAdapter,\n ExecutionState,\n MeasurementAdapter,\n RequestScopeAdapter,\n RequestScopeStore,\n RuntimeAdapter,\n} from './runtime.js';\nexport type {\n GetGuardsRequest,\n GetGuardsResponse,\n Guard,\n GuardCheckPerformance,\n GuardExecutionPerformance,\n ClientOptions,\n Options,\n Operator,\n Properties,\n PropertyValue,\n ProtectedContext,\n Rule,\n SendUnadoptedGuardsRequest,\n SendUnadoptedGuardsResponse,\n Signal,\n SignalPerformance,\n TraceContext,\n} from './types.js';\n","import type { Guard, Properties, PropertyValue, Rule } from './types.js';\n\n/**\n * Evaluate all rules for a guard against the given properties.\n *\n * Rules are checked in order; the first matching, enabled rule wins and its\n * `result` is returned. If no rule matches, the guard's `defaultValue` is\n * returned instead.\n *\n * @param guard - The guard definition containing rules and a default value.\n * @param properties - The property bag to evaluate each rule against.\n * @returns `true` if the guard is open (traffic should proceed), `false` otherwise.\n */\nexport function evaluateGuard(guard: Guard, properties: Properties): boolean {\n for (const rule of guard.rules) {\n if (!rule.enabled) {\n continue;\n }\n if (matchesRule(rule, properties)) {\n return rule.result;\n }\n }\n return guard.defaultValue;\n}\n\n/**\n * Return `true` when a single rule matches the provided property bag.\n *\n * Missing properties, empty rule value lists, and invalid regex patterns are\n * treated as non-matches so evaluation can continue to the next rule.\n *\n * @param rule - Rule to test.\n * @param properties - Property bag supplied to the guard evaluation.\n * @returns `true` when the rule matches, otherwise `false`.\n */\nfunction matchesRule(rule: Rule, properties: Properties): boolean {\n const raw = properties[rule.propertyName];\n if (raw === undefined) {\n return false;\n }\n if (rule.values.length === 0) {\n return false;\n }\n\n switch (rule.operator) {\n case 'EQUALS':\n return valuesEqual(raw, rule.values[0]);\n case 'NOT_EQUALS':\n return !valuesEqual(raw, rule.values[0]);\n case 'IN':\n return rule.values.some((value) => valuesEqual(raw, value));\n case 'NOT_IN':\n return rule.values.every((value) => !valuesEqual(raw, value));\n case 'REGEX': {\n const pattern = String(rule.values[0] ?? '');\n try {\n return new RegExp(pattern).test(String(raw));\n } catch {\n return false;\n }\n }\n case 'GT':\n return (compareOrdered(raw, rule.values[0]) ?? 0) > 0;\n case 'GTE':\n return (compareOrdered(raw, rule.values[0]) ?? -1) >= 0;\n case 'LT':\n return (compareOrdered(raw, rule.values[0]) ?? 0) < 0;\n case 'LTE':\n return (compareOrdered(raw, rule.values[0]) ?? 1) <= 0;\n default:\n return false;\n }\n}\n\n/**\n * Determine whether two Liteguard property values are equal without mixed-type\n * coercion.\n *\n * @param a - Actual property value from the request scope.\n * @param b - Rule value to compare against.\n * @returns `true` when the values are equal and of the same supported kind.\n */\nfunction valuesEqual(a: PropertyValue, b: PropertyValue | undefined): boolean {\n if (b === undefined) {\n return false;\n }\n if (typeof a === 'boolean' || typeof b === 'boolean') {\n return typeof a === 'boolean' && typeof b === 'boolean' && a === b;\n }\n if (typeof a === 'string' || typeof b === 'string') {\n return typeof a === 'string' && typeof b === 'string' && a === b;\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b;\n }\n\n return false;\n}\n\n/**\n * Compare two Liteguard property values for ordered operators without\n * mixed-type coercion.\n *\n * @param a - Actual property value from the request scope.\n * @param b - Rule value to compare against.\n * @returns A negative number when `a < b`, zero when equal, a positive number\n * when `a > b`, or `undefined` when the values are not comparable.\n */\nfunction compareOrdered(a: PropertyValue, b: PropertyValue | undefined): number | undefined {\n if (b === undefined) {\n return undefined;\n }\n if (typeof a === 'string' || typeof b === 'string') {\n if (typeof a !== 'string' || typeof b !== 'string') {\n return undefined;\n }\n return a.localeCompare(b);\n }\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n return undefined;\n}\n","import { evaluateGuard } from './evaluation.js';\nimport type {\n GetGuardsRequest,\n Guard,\n ClientOptions,\n Options,\n Properties,\n ProtectedContext,\n SendUnadoptedGuardsRequest,\n Signal,\n} from './types.js';\nimport type { ExecutionState, RequestScopeStore, RuntimeAdapter } from './runtime.js';\n\nconst DEFAULT_BACKEND_URL = 'https://api.liteguard.io';\nconst DEFAULT_REFRESH_RATE_S = 30;\nconst DEFAULT_FLUSH_RATE_S = 10;\nconst DEFAULT_FLUSH_SIZE = 500;\nconst DEFAULT_HTTP_TIMEOUT_S = 4;\nconst DEFAULT_FLUSH_BUFFER_MULTIPLIER = 4;\nconst DEFAULT_QUIET = true;\nconst PUBLIC_BUNDLE_KEY = '';\n\n/** Callback invoked whenever the guard bundle is refreshed from the backend. */\nexport type LiteguardChangeListener = () => void;\n\n/** Options for {@link BaseLiteguardClient.flushSignals}. */\nexport type FlushOptions = {\n /**\n * When `true`, the underlying `fetch` request is sent with the\n * [`keepalive`](https://developer.mozilla.org/en-US/docs/Web/API/Request/keepalive)\n * flag so it survives page navigation. Set this when flushing during\n * `visibilitychange` or `pagehide` in the browser.\n */\n keepalive?: boolean;\n};\n\n/**\n * Per-call options accepted by guard-evaluation methods. Extends the\n * standard {@link Options} with an optional explicit {@link LiteguardScope}.\n */\nexport type ScopedOptions = Partial<Options> & {\n /**\n * Explicit scope to evaluate against. When omitted the client resolves the\n * scope from the current async context or falls back to the default scope.\n */\n scope?: LiteguardScope;\n};\n\ntype GuardBundle = {\n key: string;\n guards: Map<string, Guard>;\n ready: boolean;\n etag: string;\n protectedContext: ProtectedContext | null;\n refreshRateSeconds: number;\n};\n\ntype ScopeSnapshot = {\n properties: Properties;\n protectedBundleKey: string;\n protectedContext: ProtectedContext | null;\n};\n\ntype BufferedSignalInput = {\n guardName: string;\n result: boolean;\n properties: Properties;\n callsiteId: string;\n kind: 'guard_check' | 'guard_execution';\n measurement?: Signal['measurement'];\n parentSignalIdOverride?: string;\n};\n\n/**\n * Return a positive numeric option value, or the provided default when the\n * input is missing or invalid.\n */\nfunction positiveNumberOrDefault(value: number | undefined, fallback: number): number {\n return value !== undefined && value > 0 ? value : fallback;\n}\n\n/**\n * Return a trimmed string option when present, otherwise the provided default.\n */\nfunction nonEmptyStringOrDefault(value: string | undefined, fallback: string): string {\n return value !== undefined && value.trim() !== '' ? value : fallback;\n}\n\n/** Return a shallow copy of a property bag so scopes stay immutable. */\nfunction cloneProperties(properties: Properties): Properties {\n return { ...properties };\n}\n\n/**\n * Clone a protected-context payload so callers cannot mutate cached bundle\n * state through shared object references.\n */\nfunction cloneProtectedContext(protectedContext: ProtectedContext | null): ProtectedContext | null {\n if (!protectedContext) {\n return null;\n }\n return {\n signature: protectedContext.signature,\n properties: { ...(protectedContext.properties ?? {}) },\n };\n}\n\n/**\n * Build a stable cache key for a protected-context guard bundle.\n *\n * The key includes the signature and sorted property pairs so equivalent\n * contexts reuse the same fetched bundle regardless of object key order.\n */\nfunction protectedContextCacheKey(protectedContext: ProtectedContext | null): string {\n if (!protectedContext) {\n return PUBLIC_BUNDLE_KEY;\n }\n const keys = Object.keys(protectedContext.properties ?? {}).sort();\n const parts = [protectedContext.signature, ''];\n for (const key of keys) {\n parts.push(`${key}=${protectedContext.properties?.[key] ?? ''}`);\n }\n return parts.join('\\x00');\n}\n\n/**\n * Detect promise-like values so scope cleanup can be deferred until async\n * work settles.\n */\nfunction isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {\n return typeof value === 'object' && value !== null && 'then' in value;\n}\n\nfunction asyncContextUnsupportedError(operation: string): Error {\n return new Error(\n `[liteguard] ${operation} cannot cross await boundaries in this runtime. ` +\n 'Use an explicit LiteguardScope and call scope methods directly after each await.',\n );\n}\n\nlet signalCounter = 0;\n\n/**\n * An immutable snapshot of evaluation context (properties and optional\n * protected context) that can be passed to guard-evaluation methods.\n *\n * Scopes are created via {@link BaseLiteguardClient.createScope} or by\n * deriving from an existing scope with methods like {@link withProperties}.\n * They are cheap to create and safe to share across async boundaries.\n */\nexport class LiteguardScope {\n private readonly client: BaseLiteguardClient;\n private readonly snapshot: ScopeSnapshot;\n\n /** @internal — use {@link BaseLiteguardClient.createScope} instead. */\n constructor(client: BaseLiteguardClient, snapshot: ScopeSnapshot) {\n this.client = client;\n this.snapshot = {\n properties: cloneProperties(snapshot.properties),\n protectedBundleKey: snapshot.protectedBundleKey,\n protectedContext: cloneProtectedContext(snapshot.protectedContext),\n };\n }\n\n /**\n * Derive a new scope with the given properties merged on top of this\n * scope's existing properties. Does not mutate the current scope.\n *\n * @param properties - Key/value pairs to merge into the new scope.\n * @returns A new {@link LiteguardScope} with the merged properties.\n */\n withProperties(properties: Properties): LiteguardScope {\n return this.client._deriveScope(this, {\n properties: {\n ...this.snapshot.properties,\n ...properties,\n },\n });\n }\n\n /**\n * Alias for {@link withProperties} — derives a new scope with additional properties.\n *\n * @param properties - Key/value pairs to add.\n * @returns A new {@link LiteguardScope} with the merged properties.\n */\n addProperties(properties: Properties): LiteguardScope {\n return this.withProperties(properties);\n }\n\n /**\n * Derive a new scope with the specified property keys removed.\n *\n * @param names - Property keys to remove.\n * @returns A new {@link LiteguardScope} without the listed properties.\n */\n clearProperties(names: string[]): LiteguardScope {\n const next = cloneProperties(this.snapshot.properties);\n for (const name of names) {\n delete next[name];\n }\n return this.client._deriveScope(this, { properties: next });\n }\n\n /**\n * Derive a new scope with all properties removed.\n *\n * @returns A new {@link LiteguardScope} with an empty property bag.\n */\n resetProperties(): LiteguardScope {\n return this.client._deriveScope(this, { properties: {} });\n }\n\n /**\n * Derive a new scope bound to the supplied protected context. The client\n * will fetch (or reuse) a guard bundle specific to this context.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @returns A new {@link LiteguardScope} bound to the protected context.\n */\n async bindProtectedContext(protectedContext: ProtectedContext): Promise<LiteguardScope> {\n return await this.client._bindProtectedContextToScope(this, protectedContext);\n }\n\n /**\n * Derive a new scope with protected context cleared, reverting to the\n * public guard bundle.\n *\n * @returns A new {@link LiteguardScope} using the public guard bundle.\n */\n clearProtectedContext(): LiteguardScope {\n return this.client._deriveScope(this, {\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Check whether the named guard is open within this scope. Buffers a\n * `guard_check` telemetry signal.\n *\n * @param name - Guard name (e.g. `\"payments.checkout\"`).\n * @param options - Optional per-call overrides (extra properties, fallback).\n * @returns `true` if the guard is open, `false` otherwise.\n */\n isOpen(name: string, options: Partial<Options> = {}): boolean {\n return this.client.isOpen(name, { ...options, scope: this });\n }\n\n /**\n * Check whether the named guard is open without emitting a telemetry\n * signal or consuming a rate-limit slot. Useful in hot paths or render\n * loops where you only need the boolean result.\n *\n * @param name - Guard name to evaluate.\n * @param options - Optional per-call overrides.\n * @returns `true` if the guard is open, `false` otherwise.\n */\n peekIsOpen(name: string, options: Partial<Options> = {}): boolean {\n return this.client.peekIsOpen(name, { ...options, scope: this });\n }\n\n /**\n * Evaluate the guard and, if open, call `fn` within a correlated execution\n * scope. Returns `fn`'s result when open, or `undefined` when closed.\n *\n * Emits both a `guard_check` and a `guard_execution` telemetry signal so\n * you can measure the guarded code path.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Synchronous function to invoke when the guard is open.\n * @param options - Optional per-call overrides.\n * @returns The return value of `fn`, or `undefined` if the guard is closed.\n */\n executeIfOpen<T>(name: string, fn: () => T, options: Partial<Options> = {}): T | undefined {\n return this.client.executeIfOpen(name, fn, { ...options, scope: this });\n }\n\n /**\n * Async variant of {@link executeIfOpen}. Evaluates the guard and, if open,\n * awaits `fn` within a correlated execution scope.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Async function to invoke when the guard is open.\n * @param options - Optional per-call overrides.\n * @returns The resolved value of `fn`, or `undefined` if the guard is closed.\n */\n async executeIfOpenAsync<T>(\n name: string,\n fn: () => Promise<T>,\n options: Partial<Options> = {},\n ): Promise<T | undefined> {\n return await this.client.executeIfOpenAsync(name, fn, { ...options, scope: this });\n }\n\n /**\n * Run `fn` with this scope as the active scope for all guard checks made\n * during its execution.\n *\n * @param fn - Callback to execute within this scope.\n * @returns The return value of `fn`.\n */\n run<T>(fn: () => T): T {\n return this.client.runWithScope(this, fn);\n }\n\n /**\n * Run `fn` with this scope active **and** inside a Liteguard execution\n * context so that all guard signals share a common execution ID.\n *\n * @param fn - Callback to execute.\n * @returns The return value of `fn`.\n */\n withExecution<T>(fn: () => T): T {\n return this.client.runWithScope(this, () => this.client.withExecution(fn));\n }\n\n /**\n * Return a shallow copy of this scope's properties.\n *\n * @returns A plain object of property key/value pairs.\n */\n getProperties(): Properties {\n return cloneProperties(this.snapshot.properties);\n }\n\n /**\n * Return the protected context bound to this scope, or `null` if none.\n *\n * @returns A copy of the protected context, or `null`.\n */\n getProtectedContext(): ProtectedContext | null {\n return cloneProtectedContext(this.snapshot.protectedContext);\n }\n\n _getBundleKey(): string {\n return this.snapshot.protectedBundleKey;\n }\n\n _getSnapshot(): ScopeSnapshot {\n return {\n properties: cloneProperties(this.snapshot.properties),\n protectedBundleKey: this.snapshot.protectedBundleKey,\n protectedContext: cloneProtectedContext(this.snapshot.protectedContext),\n };\n }\n\n _belongsTo(client: BaseLiteguardClient): boolean {\n return this.client === client;\n }\n}\n\n/**\n * Core Liteguard client that evaluates guards, buffers telemetry signals,\n * and manages background refresh and flush cycles.\n *\n * This is the platform-agnostic base class. Use the platform-specific\n * `LiteguardClient` from `@liteguard/liteguard-node` or\n * `@liteguard/liteguard-browser` unless you are providing a custom\n * {@link RuntimeAdapter}.\n */\nexport class BaseLiteguardClient {\n private readonly projectClientKeyId: string;\n private readonly runtime: RuntimeAdapter;\n private readonly options: Required<ClientOptions>;\n\n private readonly bundles = new Map<string, GuardBundle>();\n private defaultScope: LiteguardScope;\n\n private signalBuffer: Signal[] = [];\n private droppedSignalsPending = 0;\n private readonly reportedUnadoptedGuards = new Set<string>();\n private readonly pendingUnadoptedGuards = new Set<string>();\n private refreshTimer: unknown = null;\n private flushTimer: unknown = null;\n private lifecycleCleanup: (() => void) | null = null;\n private currentRefreshRateSeconds: number;\n private readonly rateLimitState: Map<string, { windowStart: number; count: number }> = new Map();\n private readonly listeners = new Set<LiteguardChangeListener>();\n\n /**\n * Create a new Liteguard client.\n *\n * Call {@link start} after construction to fetch the initial guard bundle\n * and begin background timers.\n *\n * @param runtime - Platform adapter providing timers, fetch, and context storage.\n * @param projectClientKeyId - Your project client key ID from the Liteguard dashboard.\n * @param options - Optional SDK configuration overrides.\n */\n constructor(runtime: RuntimeAdapter, projectClientKeyId: string, options: ClientOptions = {}) {\n this.runtime = runtime;\n this.projectClientKeyId = projectClientKeyId;\n this.options = {\n environment: options.environment ?? '',\n fallback: options.fallback ?? false,\n refreshRateSeconds: positiveNumberOrDefault(options.refreshRateSeconds, DEFAULT_REFRESH_RATE_S),\n flushRateSeconds: positiveNumberOrDefault(options.flushRateSeconds, DEFAULT_FLUSH_RATE_S),\n flushSize: positiveNumberOrDefault(options.flushSize, DEFAULT_FLUSH_SIZE),\n httpTimeoutSeconds: positiveNumberOrDefault(options.httpTimeoutSeconds, DEFAULT_HTTP_TIMEOUT_S),\n flushBufferMultiplier: positiveNumberOrDefault(\n options.flushBufferMultiplier,\n DEFAULT_FLUSH_BUFFER_MULTIPLIER,\n ),\n disableMeasurement: options.disableMeasurement ?? false,\n backendUrl: nonEmptyStringOrDefault(options.backendUrl, DEFAULT_BACKEND_URL),\n quiet: options.quiet ?? DEFAULT_QUIET,\n };\n this.currentRefreshRateSeconds = this.options.refreshRateSeconds;\n this.bundles.set(PUBLIC_BUNDLE_KEY, this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null));\n this.defaultScope = new LiteguardScope(this, {\n properties: {},\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Fetch the initial public guard bundle and start background refresh and\n * flush timers. Must be called once after construction before using the\n * client in production.\n */\n async start(): Promise<void> {\n await this.fetchGuardsForBundle(PUBLIC_BUNDLE_KEY);\n this.scheduleNextRefresh();\n\n this.flushTimer = this.runtime.setInterval(() => {\n void this.flushSignals();\n }, this.options.flushRateSeconds * 1_000);\n this.runtime.detachTimer?.(this.flushTimer);\n\n this.lifecycleCleanup = this.runtime.installLifecycleHooks?.({\n flushKeepalive: () => {\n void this.flushSignals({ keepalive: true });\n },\n }) ?? null;\n }\n\n /**\n * Stop all background timers and flush remaining buffered signals.\n * After calling `shutdown` the client should not be used further.\n */\n async shutdown(): Promise<void> {\n if (this.refreshTimer !== null) {\n this.runtime.clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n if (this.flushTimer !== null) {\n this.runtime.clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n this.lifecycleCleanup?.();\n this.lifecycleCleanup = null;\n await this.flushSignals();\n }\n\n /**\n * Returns `true` once the initial public guard bundle has been fetched.\n */\n isReady(): boolean {\n return this.getBundle(PUBLIC_BUNDLE_KEY)?.ready ?? false;\n }\n\n /**\n * Register a listener that is called whenever the guard bundle is refreshed.\n *\n * @param listener - Callback invoked on each guard bundle update.\n * @returns An unsubscribe function — call it to remove the listener.\n *\n * @example\n * ```ts\n * const unsubscribe = client.subscribe(() => {\n * console.log('Guards refreshed');\n * });\n * // later…\n * unsubscribe();\n * ```\n */\n subscribe(listener: LiteguardChangeListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Create a new request scope with optional initial properties. The scope\n * uses the public guard bundle by default; call\n * {@link LiteguardScope.bindProtectedContext} on the returned scope to\n * attach signed user context.\n *\n * @param properties - Initial property key/value pairs for the scope.\n * @returns A new {@link LiteguardScope}.\n *\n * @example\n * ```ts\n * const scope = client.createScope({ plan: 'enterprise' });\n * scope.isOpen('feature.beta'); // evaluates with plan=enterprise\n * ```\n */\n createScope(properties: Properties = {}): LiteguardScope {\n return new LiteguardScope(this, {\n properties: cloneProperties(properties),\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Return the currently active request scope, or the shared default scope\n * when no request-local scope is active.\n *\n * On Node.js, the active scope is resolved from `AsyncLocalStorage` so\n * each concurrent request can carry its own context automatically.\n *\n * @returns The active {@link LiteguardScope}.\n */\n getActiveScope(): LiteguardScope {\n const active = this.getRuntimeScope();\n return active ?? this.defaultScope;\n }\n\n /**\n * Run `fn` inside the given request scope. All guard checks performed\n * during `fn` will use the properties and protected context of `scope`.\n *\n * @param scope - The scope to activate.\n * @param fn - Callback to execute within the scope.\n * @returns The return value of `fn`.\n */\n runWithScope<T>(scope: LiteguardScope, fn: () => T): T {\n const resolvedScope = this.resolveScope(scope);\n const adapter = this.runtime.requestScope;\n if (adapter) {\n const result = adapter.run({ currentScope: resolvedScope }, fn);\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('scope.run(), client.runWithScope(), and property-scoped callbacks');\n }\n return result;\n }\n\n const previousScope = this.defaultScope;\n this.defaultScope = resolvedScope;\n try {\n const result = fn();\n if (isPromiseLike(result)) {\n if (!this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('scope.run(), client.runWithScope(), and property-scoped callbacks');\n }\n return Promise.resolve(result).finally(() => {\n this.defaultScope = previousScope;\n }) as T;\n }\n this.defaultScope = previousScope;\n return result;\n } catch (error) {\n this.defaultScope = previousScope;\n throw error;\n }\n }\n\n /**\n * Convenience helper that derives a scope from the current active scope,\n * merges in the supplied properties, and runs `fn` inside it.\n *\n * @param properties - Key/value pairs to add for the duration of `fn`.\n * @param fn - Callback to execute within the scoped context.\n * @returns The return value of `fn`.\n *\n * @example\n * ```ts\n * const result = client.withProperties({ userId: '42' }, () => {\n * return client.isOpen('feature.beta');\n * });\n * ```\n */\n withProperties<T>(properties: Properties, fn: () => T): T {\n const scope = this.getActiveScope().withProperties(properties);\n return this.runWithScope(scope, fn);\n }\n\n /**\n * Bind protected context to a derived scope and run `fn` inside it.\n * Fetches (or reuses) a guard bundle specific to this context before\n * executing `fn`.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @param fn - Callback to execute within the protected scope.\n * @returns The (awaited) return value of `fn`.\n */\n async withProtectedContext<T>(\n protectedContext: ProtectedContext,\n fn: () => T | Promise<T>,\n ): Promise<Awaited<T>> {\n const scope = await this.getActiveScope().bindProtectedContext(protectedContext);\n return await Promise.resolve(this.runWithScope(scope, fn));\n }\n\n /**\n * Run `fn` inside a Liteguard execution scope so that all guard signals\n * emitted during `fn` share a common execution ID for correlated\n * telemetry. If an execution scope is already active, `fn` is called\n * directly without creating a new one.\n *\n * @param fn - Callback to execute within the execution scope.\n * @returns The return value of `fn`.\n */\n withExecution<T>(fn: () => T): T {\n const existing = this.runtime.execution.getStore();\n if (existing) {\n const result = fn();\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('withExecution()');\n }\n return result;\n }\n const result = this.runtime.execution.run({ executionId: nextSignalId(), sequenceNumber: 0 }, fn);\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('withExecution()');\n }\n return result;\n }\n\n /**\n * Return `true` if the named guard is open for the resolved request scope.\n * Buffers a `guard_check` telemetry signal and consumes a rate-limit slot\n * when applicable.\n *\n * Guards that have not yet been adopted on the Liteguard dashboard always\n * return `true`, allowing you to instrument code before enabling the guard.\n *\n * @param name - Guard name (e.g. `\"payments.checkout\"`).\n * @param callOptions - Optional per-call overrides (extra properties, fallback, scope).\n * @returns `true` if the guard is open, `false` otherwise.\n */\n isOpen(name: string, callOptions: ScopedOptions = {}): boolean {\n return this.evaluateIsOpen(name, callOptions, true);\n }\n\n /**\n * Signal-free variant of {@link isOpen}. Returns the same boolean result\n * but does **not** emit a telemetry signal or consume a rate-limit slot.\n * Ideal for render loops or other hot paths where you only need the\n * boolean value.\n *\n * @param name - Guard name to evaluate.\n * @param callOptions - Optional per-call overrides.\n * @returns `true` if the guard is open, `false` otherwise.\n */\n peekIsOpen(name: string, callOptions: ScopedOptions = {}): boolean {\n return this.evaluateIsOpen(name, callOptions, false);\n }\n\n private evaluateIsOpen(name: string, callOptions: ScopedOptions, emitSignal: boolean): boolean {\n const scope = this.resolveScope(callOptions.scope);\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const fallback = options.fallback ?? this.options.fallback;\n const bundle = this.bundleForScope(scope);\n\n if (!bundle.ready) {\n return fallback;\n }\n\n const guard = bundle.guards.get(name);\n if (!guard) {\n this.recordUnadoptedGuard(name);\n return true;\n }\n if (!guard.adopted) {\n this.recordUnadoptedGuard(name);\n return true;\n }\n\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n let result = evaluateGuard(guard, props);\n if (result && guard.rateLimitPerMinute > 0) {\n result = emitSignal\n ? this.checkRateLimit(name, guard.rateLimitPerMinute, guard.rateLimitProperties, props)\n : this.wouldPassRateLimit(name, guard.rateLimitPerMinute, guard.rateLimitProperties, props);\n }\n\n if (emitSignal) {\n this.bufferSignal({\n guardName: name,\n result,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_check',\n measurement: this.isMeasurementEnabled(guard, options)\n ? this.runtime.measurement.captureGuardCheck()\n : undefined,\n });\n }\n\n return result;\n }\n\n /**\n * Evaluate the guard and, if open, call `fn` inside a correlated execution\n * scope. Returns `fn`'s result when the guard is open, or `undefined` when\n * closed. Emits correlated `guard_check` and `guard_execution` signals.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Synchronous function to invoke when the guard is open.\n * @param callOptions - Optional per-call overrides.\n * @returns The return value of `fn`, or `undefined` if the guard is closed.\n *\n * @example\n * ```ts\n * const banner = client.executeIfOpen('promo.banner', () => {\n * return renderPromoBanner();\n * });\n * ```\n */\n executeIfOpen<T>(name: string, fn: () => T, callOptions: ScopedOptions = {}): T | undefined {\n const scope = this.resolveScope(callOptions.scope);\n return this.runWithScope(scope, () =>\n this.withExecution(() => {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n if (!this.isOpen(name, { ...options, scope })) {\n return undefined;\n }\n if (!guard?.adopted) {\n return fn();\n }\n\n const guardCheckSignalId = this.runtime.execution.getStore()?.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = fn();\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n return value;\n } catch (error) {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n throw error;\n }\n }),\n );\n }\n\n /**\n * Async variant of {@link executeIfOpen}. Evaluates the guard and, if\n * open, awaits `fn` inside a correlated execution scope.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Async function to invoke when the guard is open.\n * @param callOptions - Optional per-call overrides.\n * @returns The resolved value of `fn`, or `undefined` if the guard is closed.\n */\n async executeIfOpenAsync<T>(\n name: string,\n fn: () => Promise<T>,\n callOptions: ScopedOptions = {},\n ): Promise<T | undefined> {\n const scope = this.resolveScope(callOptions.scope);\n if (!this.runtime.supportsAsyncContextPropagation) {\n return await this.executeIfOpenAsyncWithoutAsyncContext(name, scope, fn, callOptions);\n }\n return await this.runWithScope(scope, () =>\n this.withExecution(async () => {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n if (!this.isOpen(name, { ...options, scope })) {\n return undefined;\n }\n if (!guard?.adopted) {\n return await fn();\n }\n\n const guardCheckSignalId = this.runtime.execution.getStore()?.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = await fn();\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n return value;\n } catch (error) {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n throw error;\n }\n }),\n );\n }\n\n private async executeIfOpenAsyncWithoutAsyncContext<T>(\n name: string,\n scope: LiteguardScope,\n fn: () => Promise<T>,\n callOptions: ScopedOptions,\n ): Promise<T | undefined> {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n const executionState = this.runtime.execution.getStore() ?? { executionId: nextSignalId(), sequenceNumber: 0 };\n\n const isOpen = this.runWithExplicitExecutionState(executionState, () => this.isOpen(name, { ...options, scope }));\n if (!isOpen) {\n return undefined;\n }\n if (!guard?.adopted) {\n return await fn();\n }\n\n const guardCheckSignalId = executionState.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = await fn();\n this.runWithExplicitExecutionState(executionState, () => {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n });\n return value;\n } catch (error) {\n this.runWithExplicitExecutionState(executionState, () => {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n });\n throw error;\n }\n }\n\n /**\n * Merge `properties` into the active scope's property bag and persist\n * the derived scope as the new active scope. Subsequent {@link isOpen}\n * calls will include these properties unless overridden per-call.\n *\n * @param properties - Key/value pairs to add.\n * @returns The new active {@link LiteguardScope}.\n */\n addProperties(properties: Properties): LiteguardScope {\n const nextScope = this.getActiveScope().withProperties(properties);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Remove the given property keys from the active scope and persist the\n * resulting scope.\n *\n * @param names - Property keys to remove.\n * @returns The new active {@link LiteguardScope}.\n */\n clearProperties(names: string[]): LiteguardScope {\n const nextScope = this.getActiveScope().clearProperties(names);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Remove all properties from the active scope and persist the resulting\n * scope.\n *\n * @returns The new active {@link LiteguardScope} with an empty property bag.\n */\n resetProperties(): LiteguardScope {\n const nextScope = this.getActiveScope().resetProperties();\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Bind protected context to the active scope and persist the derived\n * scope. Fetches (or reuses) a guard bundle specific to this context.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @returns The new active {@link LiteguardScope}.\n */\n async bindProtectedContext(protectedContext: ProtectedContext): Promise<LiteguardScope> {\n const nextScope = await this.getActiveScope().bindProtectedContext(protectedContext);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Clear protected context from the active scope and revert to the\n * public guard bundle.\n *\n * @returns The new active {@link LiteguardScope}.\n */\n clearProtectedContext(): LiteguardScope {\n const nextScope = this.getActiveScope().clearProtectedContext();\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Immediately transmit all buffered telemetry signals to the backend. Also\n * flushes any pending unadopted-guard reports.\n *\n * Call this before process exit or page unload to avoid losing signals.\n *\n * @param options - Flush options (e.g. `{ keepalive: true }` for page unload).\n */\n async flushSignals(options: FlushOptions = {}): Promise<void> {\n const signalBatch = this.signalBuffer.splice(0);\n const unadoptedGuardNames = [...this.pendingUnadoptedGuards];\n this.pendingUnadoptedGuards.clear();\n\n if (signalBatch.length > 0) {\n try {\n const url = new URL('/api/v1/signals', this.options.backendUrl);\n const body = JSON.stringify({\n projectClientKeyId: this.projectClientKeyId,\n environment: this.options.environment,\n signals: signalBatch,\n });\n await this.post(url, body, options.keepalive);\n } catch (error) {\n this.log('[liteguard] Signal flush failed', error);\n this.signalBuffer.unshift(...signalBatch);\n const maxBuf = this.options.flushSize * this.options.flushBufferMultiplier;\n if (this.signalBuffer.length > maxBuf) {\n this.droppedSignalsPending += this.signalBuffer.length - maxBuf;\n this.signalBuffer.length = maxBuf;\n }\n }\n }\n\n if (unadoptedGuardNames.length > 0) {\n try {\n const url = new URL('/api/v1/unadopted-guards', this.options.backendUrl);\n const requestBody: SendUnadoptedGuardsRequest = {\n projectClientKeyId: this.projectClientKeyId,\n environment: this.options.environment,\n guardNames: unadoptedGuardNames,\n };\n await this.post(url, JSON.stringify(requestBody), options.keepalive);\n } catch (error) {\n this.log('[liteguard] Unadopted guard flush failed', error);\n for (const guardName of unadoptedGuardNames) {\n this.pendingUnadoptedGuards.add(guardName);\n }\n }\n }\n }\n\n /** Notify subscribers that client-visible state has changed. */\n protected emitChange(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n /** @internal Derive a new immutable scope from an existing scope snapshot. */\n _deriveScope(scope: LiteguardScope, patch: Partial<ScopeSnapshot>): LiteguardScope {\n const resolved = this.resolveScope(scope);\n const snapshot = resolved._getSnapshot();\n return new LiteguardScope(this, {\n properties: patch.properties ? cloneProperties(patch.properties) : snapshot.properties,\n protectedBundleKey: patch.protectedBundleKey ?? snapshot.protectedBundleKey,\n protectedContext:\n patch.protectedContext === undefined\n ? snapshot.protectedContext\n : cloneProtectedContext(patch.protectedContext),\n });\n }\n\n /** @internal Bind protected context to a scope and ensure its bundle is loaded. */\n async _bindProtectedContextToScope(\n scope: LiteguardScope,\n protectedContext: ProtectedContext,\n ): Promise<LiteguardScope> {\n this.resolveScope(scope);\n const clonedProtectedContext = cloneProtectedContext(protectedContext);\n const bundleKey = await this.ensureBundleForProtectedContext(clonedProtectedContext);\n return new LiteguardScope(this, {\n properties: scope.getProperties(),\n protectedBundleKey: bundleKey,\n protectedContext: clonedProtectedContext,\n });\n }\n\n /** Persist a derived scope into the current request-local or default scope slot. */\n private replaceCurrentScope(nextScope: LiteguardScope): LiteguardScope {\n const resolved = this.resolveScope(nextScope);\n const store = this.runtime.requestScope?.getStore();\n if (store) {\n store.currentScope = resolved;\n return resolved;\n }\n\n this.defaultScope = resolved;\n this.emitChange();\n return resolved;\n }\n\n /** Resolve the active runtime scope store into a client-owned scope instance. */\n private getRuntimeScope(): LiteguardScope | null {\n const store = this.runtime.requestScope?.getStore();\n if (!store) {\n return null;\n }\n const scope = store.currentScope;\n if (scope instanceof LiteguardScope && scope._belongsTo(this)) {\n return scope;\n }\n return null;\n }\n\n /** Validate and resolve the scope used for a client operation. */\n private resolveScope(scope?: LiteguardScope): LiteguardScope {\n const resolved = scope ?? this.getRuntimeScope() ?? this.defaultScope;\n if (!resolved._belongsTo(this)) {\n throw new Error('[liteguard] Scope belongs to a different Liteguard client.');\n }\n return resolved;\n }\n\n /** Queue a one-time report for a guard that exists only in application code. */\n private recordUnadoptedGuard(name: string): void {\n if (!this.reportedUnadoptedGuards.has(name)) {\n this.reportedUnadoptedGuards.add(name);\n this.pendingUnadoptedGuards.add(name);\n }\n }\n\n /** Schedule the next periodic guard refresh using the current refresh interval. */\n private scheduleNextRefresh(): void {\n if (this.refreshTimer !== null) {\n this.runtime.clearTimeout(this.refreshTimer);\n }\n this.refreshTimer = this.runtime.setTimeout(() => {\n void this.runRefreshCycle();\n }, this.currentRefreshRateSeconds * 1_000);\n this.runtime.detachTimer?.(this.refreshTimer);\n }\n\n /** Refresh every cached bundle once, then reschedule the next cycle. */\n private async runRefreshCycle(): Promise<void> {\n if (this.refreshTimer === null) {\n return;\n }\n const bundleKeys = [...this.bundles.keys()].sort();\n for (const bundleKey of bundleKeys) {\n await this.fetchGuardsForBundle(bundleKey);\n }\n if (this.refreshTimer !== null) {\n this.scheduleNextRefresh();\n }\n }\n\n /** Create an empty bundle placeholder before the first successful fetch. */\n private createEmptyBundle(bundleKey: string, protectedContext: ProtectedContext | null): GuardBundle {\n return {\n key: bundleKey,\n guards: new Map(),\n ready: false,\n etag: '',\n protectedContext: cloneProtectedContext(protectedContext),\n refreshRateSeconds: this.options.refreshRateSeconds,\n };\n }\n\n /** Return a cached bundle by key, if present. */\n private getBundle(bundleKey: string): GuardBundle | undefined {\n return this.bundles.get(bundleKey);\n }\n\n /** Resolve the bundle used for a scope, falling back to the public bundle. */\n private bundleForScope(scope: LiteguardScope): GuardBundle {\n return this.getBundle(scope._getBundleKey())\n ?? this.getBundle(PUBLIC_BUNDLE_KEY)\n ?? this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null);\n }\n\n /** Ensure the bundle for a protected context exists locally and is ready. */\n private async ensureBundleForProtectedContext(protectedContext: ProtectedContext | null): Promise<string> {\n const bundleKey = protectedContextCacheKey(protectedContext);\n const existing = this.getBundle(bundleKey);\n if (existing?.ready) {\n return bundleKey;\n }\n if (!existing) {\n this.bundles.set(bundleKey, this.createEmptyBundle(bundleKey, protectedContext));\n }\n await this.fetchGuardsForBundle(bundleKey);\n return bundleKey;\n }\n\n /** Use the shortest bundle refresh rate so all cached bundles stay current. */\n private recomputeRefreshInterval(): void {\n let next = 0;\n for (const bundle of this.bundles.values()) {\n if (bundle.refreshRateSeconds <= 0) {\n continue;\n }\n if (next === 0 || bundle.refreshRateSeconds < next) {\n next = bundle.refreshRateSeconds;\n }\n }\n if (next <= 0) {\n next = this.options.refreshRateSeconds;\n }\n if (next <= 0) {\n next = DEFAULT_REFRESH_RATE_S;\n }\n this.currentRefreshRateSeconds = next;\n }\n\n /** Fetch the latest guard bundle for a bundle key, respecting ETags and timeouts. */\n private async fetchGuardsForBundle(bundleKey: string): Promise<void> {\n const bundle = this.getBundle(bundleKey) ?? this.getBundle(PUBLIC_BUNDLE_KEY);\n const etag = bundle?.etag ?? '';\n const protectedContext = cloneProtectedContext(bundle?.protectedContext ?? null);\n const { signal, cleanup } = this.createTimeoutSignal(this.options.httpTimeoutSeconds * 1_000);\n try {\n const url = new URL('/api/v1/guards', this.options.backendUrl);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.projectClientKeyId}`,\n 'Content-Type': 'application/json',\n };\n if (etag) {\n headers['If-None-Match'] = etag;\n }\n const requestBody: GetGuardsRequest = {\n projectClientKeyId: this.projectClientKeyId,\n environment: this.options.environment,\n ...(protectedContext ? { protectedContext } : {}),\n };\n\n const res = await this.runtime.fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n signal,\n });\n\n if (res.status === 304) {\n return;\n }\n if (!res.ok) {\n await res.text();\n this.log(`[liteguard] Guard fetch failed: ${res.status} ${res.statusText}`);\n return;\n }\n\n const body = (await res.json()) as {\n guards: Guard[];\n refreshRateSeconds?: number;\n etag?: string;\n };\n\n const guards = new Map<string, Guard>();\n for (const guard of body.guards) {\n guards.set(guard.name, guard);\n }\n\n const nextBundle = this.getBundle(bundleKey) ?? this.createEmptyBundle(bundleKey, protectedContext);\n nextBundle.guards = guards;\n nextBundle.ready = true;\n nextBundle.etag = body.etag ?? '';\n nextBundle.protectedContext = cloneProtectedContext(protectedContext);\n const previousRefreshRateSeconds = this.currentRefreshRateSeconds;\n nextBundle.refreshRateSeconds = positiveNumberOrDefault(\n body.refreshRateSeconds,\n this.options.refreshRateSeconds,\n );\n this.bundles.set(bundleKey, nextBundle);\n this.recomputeRefreshInterval();\n if (this.refreshTimer !== null && this.currentRefreshRateSeconds !== previousRefreshRateSeconds) {\n this.scheduleNextRefresh();\n }\n this.emitChange();\n } catch (error) {\n this.log('[liteguard] Guard fetch error:', error);\n } finally {\n cleanup();\n }\n }\n\n /** Create an `AbortSignal` that cancels an HTTP request after `timeoutMs`. */\n private createTimeoutSignal(timeoutMs: number): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const handle = this.runtime.setTimeout(() => {\n controller.abort();\n }, timeoutMs);\n this.runtime.detachTimer?.(handle);\n return {\n signal: controller.signal,\n cleanup: () => {\n this.runtime.clearTimeout(handle);\n },\n };\n }\n\n /** Send a JSON POST request to the Liteguard backend with the SDK defaults applied. */\n private async post(url: URL, body: string, keepalive = false): Promise<void> {\n const { signal, cleanup } = this.createTimeoutSignal(this.options.httpTimeoutSeconds * 1_000);\n try {\n const res = await this.runtime.fetch(url.toString(), {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.projectClientKeyId}`,\n 'Content-Type': 'application/json',\n ...(this.options.environment && { 'X-Liteguard-Environment': this.options.environment }),\n },\n body,\n signal,\n keepalive,\n });\n if (!res.ok) {\n throw new Error(`${res.status} ${res.statusText}`);\n }\n } finally {\n cleanup();\n }\n }\n\n /** Buffer a telemetry signal and trigger an eager flush when the batch is full. */\n private bufferSignal(input: BufferedSignalInput): Signal {\n const metadata = this.nextSignalMetadata(input.parentSignalIdOverride);\n const signal: Signal = {\n guardName: input.guardName,\n result: input.result,\n properties: { ...input.properties },\n timestampMs: this.runtime.now(),\n signalId: metadata.signalId,\n executionId: metadata.executionId,\n ...(metadata.parentSignalId ? { parentSignalId: metadata.parentSignalId } : {}),\n sequenceNumber: metadata.sequenceNumber,\n callsiteId: input.callsiteId,\n kind: input.kind,\n droppedSignalsSinceLast: this.takeDroppedSignals(),\n ...(input.measurement ? { measurement: input.measurement } : {}),\n };\n if (this.signalBuffer.length >= this.maxBufferSize()) {\n this.signalBuffer.shift();\n this.droppedSignalsPending += 1;\n }\n this.signalBuffer.push(signal);\n if (this.signalBuffer.length >= this.options.flushSize) {\n void this.flushSignals();\n }\n return signal;\n }\n\n /** Allocate signal correlation metadata for the current execution context. */\n private nextSignalMetadata(parentSignalIdOverride?: string): {\n signalId: string;\n executionId: string;\n parentSignalId?: string;\n sequenceNumber: number;\n } {\n const state = this.runtime.execution.getStore();\n const signalId = nextSignalId();\n if (!state) {\n return {\n signalId,\n executionId: nextSignalId(),\n sequenceNumber: 1,\n };\n }\n\n state.sequenceNumber += 1;\n const parentSignalId = parentSignalIdOverride ?? state.lastSignalId;\n state.lastSignalId = signalId;\n return {\n signalId,\n executionId: state.executionId,\n ...(parentSignalId ? { parentSignalId } : {}),\n sequenceNumber: state.sequenceNumber,\n };\n }\n\n private runWithExplicitExecutionState<T>(state: ExecutionState, fn: () => T): T {\n const existing = this.runtime.execution.getStore();\n if (existing === state) {\n return fn();\n }\n return this.runtime.execution.run(state, fn);\n }\n\n /** Capture the first external stack frame so telemetry can identify the call site. */\n private captureCallsiteId(): string {\n const err = new Error();\n const stack = err.stack?.split('\\n').slice(1) ?? [];\n const frame = stack.find((line) => {\n if (line.includes('node:internal')) return false;\n return !this.runtime.internalStackMarkers.some((marker) => line.includes(marker));\n });\n return frame?.trim().replace(/^at\\s+/, '') || 'unknown';\n }\n\n /** Build the in-memory rate-limit bucket key for a guard and property set. */\n private rateLimitBucketKey(name: string, rateLimitProperties: string[], props: Properties): string {\n if (rateLimitProperties.length === 0) return name;\n const parts = rateLimitProperties.map((property) => `${property}=${String(props[property] ?? '')}`);\n return `${name}\\x00${parts.join('\\x00')}`;\n }\n\n private checkRateLimit(\n name: string,\n limitPerMinute: number,\n rateLimitProperties: string[],\n props: Properties,\n ): boolean {\n const now = this.runtime.monotonicNow();\n const key = this.rateLimitBucketKey(name, rateLimitProperties, props);\n const state = this.rateLimitState.get(key) ?? { windowStart: now, count: 0 };\n if (now - state.windowStart >= 60_000) {\n state.windowStart = now;\n state.count = 0;\n }\n if (state.count >= limitPerMinute) {\n this.rateLimitState.set(key, state);\n return false;\n }\n state.count += 1;\n this.rateLimitState.set(key, state);\n return true;\n }\n\n /** Check whether a guard would pass the current rate-limit window without consuming a slot. */\n private wouldPassRateLimit(\n name: string,\n limitPerMinute: number,\n rateLimitProperties: string[],\n props: Properties,\n ): boolean {\n const now = this.runtime.monotonicNow();\n const key = this.rateLimitBucketKey(name, rateLimitProperties, props);\n const state = this.rateLimitState.get(key);\n if (!state) {\n return true;\n }\n if (now - state.windowStart >= 60_000) {\n return true;\n }\n return state.count < limitPerMinute;\n }\n\n /** Decide whether performance measurement should be captured for this guard call. */\n private isMeasurementEnabled(guard: Guard, options: Options): boolean {\n if (this.options.disableMeasurement) {\n return false;\n }\n if (options.disableMeasurement) {\n return false;\n }\n if (guard.disableMeasurement === true) {\n return false;\n }\n return true;\n }\n\n /** Return the hard cap for the in-memory signal buffer after flush failures. */\n private maxBufferSize(): number {\n return this.options.flushSize * this.options.flushBufferMultiplier;\n }\n\n /** Drain and reset the count of dropped signals to attach to the next emitted signal. */\n private takeDroppedSignals(): number {\n const dropped = this.droppedSignalsPending;\n this.droppedSignalsPending = 0;\n return dropped;\n }\n\n /** Write a warning only when quiet mode is disabled. */\n private log(...args: unknown[]): void {\n if (!this.options.quiet) {\n console.warn(...args);\n }\n }\n\n /** @internal Test helper for swapping the public guard bundle in memory. */\n _setGuards(guards: Guard[]): void {\n const bundle = this.getBundle(PUBLIC_BUNDLE_KEY) ?? this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null);\n bundle.guards = new Map(guards.map((guard) => [guard.name, guard]));\n bundle.ready = true;\n bundle.etag = '';\n this.bundles.set(PUBLIC_BUNDLE_KEY, bundle);\n this.emitChange();\n }\n\n /** @internal Test helper for swapping a protected-context bundle in memory. */\n _setProtectedGuards(protectedContext: ProtectedContext, guards: Guard[]): void {\n const key = protectedContextCacheKey(protectedContext);\n const bundle = this.getBundle(key) ?? this.createEmptyBundle(key, protectedContext);\n bundle.guards = new Map(guards.map((guard) => [guard.name, guard]));\n bundle.ready = true;\n bundle.etag = '';\n bundle.protectedContext = cloneProtectedContext(protectedContext);\n this.bundles.set(key, bundle);\n this.recomputeRefreshInterval();\n this.emitChange();\n }\n\n /** @internal Clear all rate-limit counters, or only those for a specific guard. */\n _resetRateLimitState(name?: string): void {\n if (name !== undefined) {\n this.rateLimitState.delete(name);\n const prefix = name + '\\x00';\n for (const key of this.rateLimitState.keys()) {\n if (key.startsWith(prefix)) {\n this.rateLimitState.delete(key);\n }\n }\n return;\n }\n this.rateLimitState.clear();\n }\n\n /** @internal Return the default scope properties for tests and diagnostics. */\n _getContext(): Properties {\n return this.defaultScope.getProperties();\n }\n\n /** @internal Return the default scope protected context for tests and diagnostics. */\n _getProtectedContext(): ProtectedContext | null {\n return this.defaultScope.getProtectedContext();\n }\n\n /** @internal Return the active refresh cadence for tests and diagnostics. */\n _getRefreshRateSeconds(): number {\n return this.currentRefreshRateSeconds;\n }\n\n /** @internal Return a cloned view of the buffered signals for tests and diagnostics. */\n _getSignalBuffer(): Signal[] {\n return this.signalBuffer.map((signal) => ({\n ...signal,\n ...(signal.properties ? { properties: { ...signal.properties } } : {}),\n }));\n }\n\n /** @internal Return the sorted set of queued unadopted guards. */\n _getPendingUnadoptedGuards(): string[] {\n return [...this.pendingUnadoptedGuards].sort();\n }\n\n /** @internal Return the currently cached bundle keys. */\n _getBundleKeys(): string[] {\n return [...this.bundles.keys()].sort();\n }\n\n /** @internal Expose the request-scope store for tests. */\n _getRequestScopeStore(): RequestScopeStore | undefined {\n return this.runtime.requestScope?.getStore();\n }\n}\n\n/** Generate a lightweight unique identifier for signals and executions. */\nfunction nextSignalId(): string {\n signalCounter += 1;\n return `${Date.now().toString(36)}-${signalCounter.toString(36)}`;\n}\n"],"mappings":"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","names":["result"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/evaluation.ts","../src/client-base.ts"],"sourcesContent":["export { BaseLiteguardClient, LiteguardScope } from './client-base.js';\nexport type { FlushOptions, LiteguardChangeListener, ScopedOptions } from './client-base.js';\nexport { evaluateGuard } from './evaluation.js';\nexport type {\n ExecutionAdapter,\n ExecutionState,\n MeasurementAdapter,\n RequestScopeAdapter,\n RequestScopeStore,\n RuntimeAdapter,\n} from './runtime.js';\nexport type {\n GetGuardsRequest,\n GetGuardsResponse,\n Guard,\n GuardCheckPerformance,\n GuardExecutionPerformance,\n ClientOptions,\n Options,\n Operator,\n Properties,\n PropertyValue,\n ProtectedContext,\n Rule,\n UnadoptedGuardObservation,\n SendUnadoptedGuardsRequest,\n SendUnadoptedGuardsResponse,\n Signal,\n SignalPerformance,\n TraceContext,\n} from './types.js';\n","import type { Guard, Properties, PropertyValue, Rule } from './types.js';\n\n/**\n * Evaluate all rules for a guard against the given properties.\n *\n * Rules are checked in order; the first matching, enabled rule wins and its\n * `result` is returned. If no rule matches, the guard's `defaultValue` is\n * returned instead.\n *\n * @param guard - The guard definition containing rules and a default value.\n * @param properties - The property bag to evaluate each rule against.\n * @returns `true` if the guard is open (traffic should proceed), `false` otherwise.\n */\nexport function evaluateGuard(guard: Guard, properties: Properties): boolean {\n for (const rule of guard.rules) {\n if (!rule.enabled) {\n continue;\n }\n if (matchesRule(rule, properties)) {\n return rule.result;\n }\n }\n return guard.defaultValue;\n}\n\n/**\n * Return `true` when a single rule matches the provided property bag.\n *\n * Missing properties, empty rule value lists, and invalid regex patterns are\n * treated as non-matches so evaluation can continue to the next rule.\n *\n * @param rule - Rule to test.\n * @param properties - Property bag supplied to the guard evaluation.\n * @returns `true` when the rule matches, otherwise `false`.\n */\nfunction matchesRule(rule: Rule, properties: Properties): boolean {\n const raw = properties[rule.propertyName];\n if (raw === undefined) {\n return false;\n }\n if (rule.values.length === 0) {\n return false;\n }\n\n switch (rule.operator) {\n case 'EQUALS':\n return valuesEqual(raw, rule.values[0]);\n case 'NOT_EQUALS':\n return !valuesEqual(raw, rule.values[0]);\n case 'IN':\n return rule.values.some((value) => valuesEqual(raw, value));\n case 'NOT_IN':\n return rule.values.every((value) => !valuesEqual(raw, value));\n case 'REGEX': {\n const pattern = String(rule.values[0] ?? '');\n try {\n return new RegExp(pattern).test(String(raw));\n } catch {\n return false;\n }\n }\n case 'GT':\n return (compareOrdered(raw, rule.values[0]) ?? 0) > 0;\n case 'GTE':\n return (compareOrdered(raw, rule.values[0]) ?? -1) >= 0;\n case 'LT':\n return (compareOrdered(raw, rule.values[0]) ?? 0) < 0;\n case 'LTE':\n return (compareOrdered(raw, rule.values[0]) ?? 1) <= 0;\n default:\n return false;\n }\n}\n\n/**\n * Determine whether two Liteguard property values are equal without mixed-type\n * coercion.\n *\n * @param a - Actual property value from the request scope.\n * @param b - Rule value to compare against.\n * @returns `true` when the values are equal and of the same supported kind.\n */\nfunction valuesEqual(a: PropertyValue, b: PropertyValue | undefined): boolean {\n if (b === undefined) {\n return false;\n }\n if (typeof a === 'boolean' || typeof b === 'boolean') {\n return typeof a === 'boolean' && typeof b === 'boolean' && a === b;\n }\n if (typeof a === 'string' || typeof b === 'string') {\n return typeof a === 'string' && typeof b === 'string' && a === b;\n }\n\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b;\n }\n\n return false;\n}\n\n/**\n * Compare two Liteguard property values for ordered operators without\n * mixed-type coercion.\n *\n * @param a - Actual property value from the request scope.\n * @param b - Rule value to compare against.\n * @returns A negative number when `a < b`, zero when equal, a positive number\n * when `a > b`, or `undefined` when the values are not comparable.\n */\nfunction compareOrdered(a: PropertyValue, b: PropertyValue | undefined): number | undefined {\n if (b === undefined) {\n return undefined;\n }\n if (typeof a === 'string' || typeof b === 'string') {\n if (typeof a !== 'string' || typeof b !== 'string') {\n return undefined;\n }\n return a.localeCompare(b);\n }\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n return undefined;\n}\n","import { evaluateGuard } from './evaluation.js';\nimport type {\n GetGuardsRequest,\n Guard,\n ClientOptions,\n Options,\n Properties,\n ProtectedContext,\n SendUnadoptedGuardsRequest,\n Signal,\n UnadoptedGuardObservation,\n} from './types.js';\nimport type { ExecutionState, RequestScopeStore, RuntimeAdapter } from './runtime.js';\n\nconst DEFAULT_BACKEND_URL = 'https://api.liteguard.io';\nconst DEFAULT_REFRESH_RATE_S = 30;\nconst DEFAULT_FLUSH_RATE_S = 10;\nconst DEFAULT_FLUSH_SIZE = 500;\nconst DEFAULT_HTTP_TIMEOUT_S = 4;\nconst DEFAULT_FLUSH_BUFFER_MULTIPLIER = 4;\nconst DEFAULT_QUIET = true;\nconst PUBLIC_BUNDLE_KEY = '';\n\n/** Callback invoked whenever the guard bundle is refreshed from the backend. */\nexport type LiteguardChangeListener = () => void;\n\n/** Options for {@link BaseLiteguardClient.flushSignals}. */\nexport type FlushOptions = {\n /**\n * When `true`, the underlying `fetch` request is sent with the\n * [`keepalive`](https://developer.mozilla.org/en-US/docs/Web/API/Request/keepalive)\n * flag so it survives page navigation. Set this when flushing during\n * `visibilitychange` or `pagehide` in the browser.\n */\n keepalive?: boolean;\n};\n\n/**\n * Per-call options accepted by guard-evaluation methods. Extends the\n * standard {@link Options} with an optional explicit {@link LiteguardScope}.\n */\nexport type ScopedOptions = Partial<Options> & {\n /**\n * Explicit scope to evaluate against. When omitted the client resolves the\n * scope from the current async context or falls back to the default scope.\n */\n scope?: LiteguardScope;\n};\n\ntype GuardBundle = {\n key: string;\n guards: Map<string, Guard>;\n ready: boolean;\n etag: string;\n protectedContext: ProtectedContext | null;\n refreshRateSeconds: number;\n};\n\ntype ScopeSnapshot = {\n properties: Properties;\n protectedBundleKey: string;\n protectedContext: ProtectedContext | null;\n};\n\ntype PendingUnadoptedGuardObservation = Omit<UnadoptedGuardObservation, 'estimatedChecksPerMinute'>;\n\ntype BufferedSignalInput = {\n guardName: string;\n result: boolean;\n properties: Properties;\n callsiteId: string;\n kind: 'guard_check' | 'guard_execution';\n measurement?: Signal['measurement'];\n parentSignalIdOverride?: string;\n};\n\n/**\n * Return a positive numeric option value, or the provided default when the\n * input is missing or invalid.\n */\nfunction positiveNumberOrDefault(value: number | undefined, fallback: number): number {\n return value !== undefined && value > 0 ? value : fallback;\n}\n\n/**\n * Return a trimmed string option when present, otherwise the provided default.\n */\nfunction nonEmptyStringOrDefault(value: string | undefined, fallback: string): string {\n return value !== undefined && value.trim() !== '' ? value : fallback;\n}\n\n/** Return a shallow copy of a property bag so scopes stay immutable. */\nfunction cloneProperties(properties: Properties): Properties {\n return { ...properties };\n}\n\n/**\n * Clone a protected-context payload so callers cannot mutate cached bundle\n * state through shared object references.\n */\nfunction cloneProtectedContext(protectedContext: ProtectedContext | null): ProtectedContext | null {\n if (!protectedContext) {\n return null;\n }\n return {\n signature: protectedContext.signature,\n properties: { ...(protectedContext.properties ?? {}) },\n };\n}\n\n/**\n * Build a stable cache key for a protected-context guard bundle.\n *\n * The key includes the signature and sorted property pairs so equivalent\n * contexts reuse the same fetched bundle regardless of object key order.\n */\nfunction protectedContextCacheKey(protectedContext: ProtectedContext | null): string {\n if (!protectedContext) {\n return PUBLIC_BUNDLE_KEY;\n }\n const keys = Object.keys(protectedContext.properties ?? {}).sort();\n const parts = [protectedContext.signature, ''];\n for (const key of keys) {\n parts.push(`${key}=${protectedContext.properties?.[key] ?? ''}`);\n }\n return parts.join('\\x00');\n}\n\n/**\n * Detect promise-like values so scope cleanup can be deferred until async\n * work settles.\n */\nfunction isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {\n return typeof value === 'object' && value !== null && 'then' in value;\n}\n\nfunction asyncContextUnsupportedError(operation: string): Error {\n return new Error(\n `[liteguard] ${operation} cannot cross await boundaries in this runtime. ` +\n 'Use an explicit LiteguardScope and call scope methods directly after each await.',\n );\n}\n\nlet signalCounter = 0;\n\n/**\n * An immutable snapshot of evaluation context (properties and optional\n * protected context) that can be passed to guard-evaluation methods.\n *\n * Scopes are created via {@link BaseLiteguardClient.createScope} or by\n * deriving from an existing scope with methods like {@link withProperties}.\n * They are cheap to create and safe to share across async boundaries.\n */\nexport class LiteguardScope {\n private readonly client: BaseLiteguardClient;\n private readonly snapshot: ScopeSnapshot;\n\n /** @internal use {@link BaseLiteguardClient.createScope} instead. */\n constructor(client: BaseLiteguardClient, snapshot: ScopeSnapshot) {\n this.client = client;\n this.snapshot = {\n properties: cloneProperties(snapshot.properties),\n protectedBundleKey: snapshot.protectedBundleKey,\n protectedContext: cloneProtectedContext(snapshot.protectedContext),\n };\n }\n\n /**\n * Derive a new scope with the given properties merged on top of this\n * scope's existing properties. Does not mutate the current scope.\n *\n * @param properties - Key/value pairs to merge into the new scope.\n * @returns A new {@link LiteguardScope} with the merged properties.\n */\n withProperties(properties: Properties): LiteguardScope {\n return this.client._deriveScope(this, {\n properties: {\n ...this.snapshot.properties,\n ...properties,\n },\n });\n }\n\n /**\n * Alias for {@link withProperties} derives a new scope with additional properties.\n *\n * @param properties - Key/value pairs to add.\n * @returns A new {@link LiteguardScope} with the merged properties.\n */\n addProperties(properties: Properties): LiteguardScope {\n return this.withProperties(properties);\n }\n\n /**\n * Derive a new scope with the specified property keys removed.\n *\n * @param names - Property keys to remove.\n * @returns A new {@link LiteguardScope} without the listed properties.\n */\n clearProperties(names: string[]): LiteguardScope {\n const next = cloneProperties(this.snapshot.properties);\n for (const name of names) {\n delete next[name];\n }\n return this.client._deriveScope(this, { properties: next });\n }\n\n /**\n * Derive a new scope with all properties removed.\n *\n * @returns A new {@link LiteguardScope} with an empty property bag.\n */\n resetProperties(): LiteguardScope {\n return this.client._deriveScope(this, { properties: {} });\n }\n\n /**\n * Derive a new scope bound to the supplied protected context. The client\n * will fetch (or reuse) a guard bundle specific to this context.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @returns A new {@link LiteguardScope} bound to the protected context.\n */\n async bindProtectedContext(protectedContext: ProtectedContext): Promise<LiteguardScope> {\n return await this.client._bindProtectedContextToScope(this, protectedContext);\n }\n\n /**\n * Derive a new scope with protected context cleared, reverting to the\n * public guard bundle.\n *\n * @returns A new {@link LiteguardScope} using the public guard bundle.\n */\n clearProtectedContext(): LiteguardScope {\n return this.client._deriveScope(this, {\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Check whether the named guard is open within this scope. Buffers a\n * `guard_check` telemetry signal.\n *\n * @param name - Guard name (e.g. `\"payments.checkout\"`).\n * @param options - Optional per-call overrides (extra properties, fallback).\n * @returns `true` if the guard is open, `false` otherwise.\n */\n isOpen(name: string, options: Partial<Options> = {}): boolean {\n return this.client.isOpen(name, { ...options, scope: this });\n }\n\n /**\n * Check whether the named guard is open without emitting a telemetry\n * signal or consuming a rate-limit slot. Useful in hot paths or render\n * loops where you only need the boolean result.\n *\n * @param name - Guard name to evaluate.\n * @param options - Optional per-call overrides.\n * @returns `true` if the guard is open, `false` otherwise.\n */\n peekIsOpen(name: string, options: Partial<Options> = {}): boolean {\n return this.client.peekIsOpen(name, { ...options, scope: this });\n }\n\n /**\n * Evaluate the guard and, if open, call `fn` within a correlated execution\n * scope. Returns `fn`'s result when open, or `undefined` when closed.\n *\n * Emits both a `guard_check` and a `guard_execution` telemetry signal so\n * you can measure the guarded code path.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Synchronous function to invoke when the guard is open.\n * @param options - Optional per-call overrides.\n * @returns The return value of `fn`, or `undefined` if the guard is closed.\n */\n executeIfOpen<T>(name: string, fn: () => T, options: Partial<Options> = {}): T | undefined {\n return this.client.executeIfOpen(name, fn, { ...options, scope: this });\n }\n\n /**\n * Async variant of {@link executeIfOpen}. Evaluates the guard and, if open,\n * awaits `fn` within a correlated execution scope.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Async function to invoke when the guard is open.\n * @param options - Optional per-call overrides.\n * @returns The resolved value of `fn`, or `undefined` if the guard is closed.\n */\n async executeIfOpenAsync<T>(\n name: string,\n fn: () => Promise<T>,\n options: Partial<Options> = {},\n ): Promise<T | undefined> {\n return await this.client.executeIfOpenAsync(name, fn, { ...options, scope: this });\n }\n\n /**\n * Run `fn` with this scope as the active scope for all guard checks made\n * during its execution.\n *\n * @param fn - Callback to execute within this scope.\n * @returns The return value of `fn`.\n */\n run<T>(fn: () => T): T {\n return this.client.runWithScope(this, fn);\n }\n\n /**\n * Run `fn` with this scope active **and** inside a Liteguard execution\n * context so that all guard signals share a common execution ID.\n *\n * @param fn - Callback to execute.\n * @returns The return value of `fn`.\n */\n withExecution<T>(fn: () => T): T {\n return this.client.runWithScope(this, () => this.client.withExecution(fn));\n }\n\n /**\n * Return a shallow copy of this scope's properties.\n *\n * @returns A plain object of property key/value pairs.\n */\n getProperties(): Properties {\n return cloneProperties(this.snapshot.properties);\n }\n\n /**\n * Return the protected context bound to this scope, or `null` if none.\n *\n * @returns A copy of the protected context, or `null`.\n */\n getProtectedContext(): ProtectedContext | null {\n return cloneProtectedContext(this.snapshot.protectedContext);\n }\n\n _getBundleKey(): string {\n return this.snapshot.protectedBundleKey;\n }\n\n _getSnapshot(): ScopeSnapshot {\n return {\n properties: cloneProperties(this.snapshot.properties),\n protectedBundleKey: this.snapshot.protectedBundleKey,\n protectedContext: cloneProtectedContext(this.snapshot.protectedContext),\n };\n }\n\n _belongsTo(client: BaseLiteguardClient): boolean {\n return this.client === client;\n }\n}\n\n/**\n * Core Liteguard client that evaluates guards, buffers telemetry signals,\n * and manages background refresh and flush cycles.\n *\n * This is the platform-agnostic base class. Use the platform-specific\n * `LiteguardClient` from `@liteguard/liteguard-node` or\n * `@liteguard/liteguard-browser` unless you are providing a custom\n * {@link RuntimeAdapter}.\n */\nexport class BaseLiteguardClient {\n private readonly projectClientToken: string;\n private readonly runtime: RuntimeAdapter;\n private readonly options: Required<ClientOptions>;\n\n private readonly bundles = new Map<string, GuardBundle>();\n private defaultScope: LiteguardScope;\n\n private signalBuffer: Signal[] = [];\n private droppedSignalsPending = 0;\n private readonly pendingUnadoptedGuards = new Map<string, PendingUnadoptedGuardObservation>();\n private refreshTimer: unknown = null;\n private flushTimer: unknown = null;\n private lifecycleCleanup: (() => void) | null = null;\n private currentRefreshRateSeconds: number;\n private readonly rateLimitState: Map<string, { windowStart: number; count: number }> = new Map();\n private readonly listeners = new Set<LiteguardChangeListener>();\n\n /**\n * Create a new Liteguard client.\n *\n * Call {@link start} after construction to fetch the initial guard bundle\n * and begin background timers.\n *\n * @param runtime - Platform adapter providing timers, fetch, and context storage.\n * @param projectClientToken - Your project client token from the Liteguard dashboard.\n * @param options - Optional SDK configuration overrides.\n */\n constructor(runtime: RuntimeAdapter, projectClientToken: string, options: ClientOptions = {}) {\n this.runtime = runtime;\n this.projectClientToken = projectClientToken;\n this.options = {\n environment: options.environment ?? '',\n fallback: options.fallback ?? false,\n refreshRateSeconds: positiveNumberOrDefault(options.refreshRateSeconds, DEFAULT_REFRESH_RATE_S),\n flushRateSeconds: positiveNumberOrDefault(options.flushRateSeconds, DEFAULT_FLUSH_RATE_S),\n flushSize: positiveNumberOrDefault(options.flushSize, DEFAULT_FLUSH_SIZE),\n httpTimeoutSeconds: positiveNumberOrDefault(options.httpTimeoutSeconds, DEFAULT_HTTP_TIMEOUT_S),\n flushBufferMultiplier: positiveNumberOrDefault(\n options.flushBufferMultiplier,\n DEFAULT_FLUSH_BUFFER_MULTIPLIER,\n ),\n disableMeasurement: options.disableMeasurement ?? false,\n backendUrl: nonEmptyStringOrDefault(options.backendUrl, DEFAULT_BACKEND_URL),\n quiet: options.quiet ?? DEFAULT_QUIET,\n };\n this.currentRefreshRateSeconds = this.options.refreshRateSeconds;\n this.bundles.set(PUBLIC_BUNDLE_KEY, this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null));\n this.defaultScope = new LiteguardScope(this, {\n properties: {},\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Fetch the initial public guard bundle and start background refresh and\n * flush timers. Must be called once after construction before using the\n * client in production.\n */\n async start(): Promise<void> {\n await this.fetchGuardsForBundle(PUBLIC_BUNDLE_KEY);\n this.scheduleNextRefresh();\n\n this.flushTimer = this.runtime.setInterval(() => {\n void this.flushSignals();\n }, this.options.flushRateSeconds * 1_000);\n this.runtime.detachTimer?.(this.flushTimer);\n\n this.lifecycleCleanup = this.runtime.installLifecycleHooks?.({\n flushKeepalive: () => {\n void this.flushSignals({ keepalive: true });\n },\n }) ?? null;\n }\n\n /**\n * Stop all background timers and flush remaining buffered signals.\n * After calling `shutdown` the client should not be used further.\n */\n async shutdown(): Promise<void> {\n if (this.refreshTimer !== null) {\n this.runtime.clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n if (this.flushTimer !== null) {\n this.runtime.clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n this.lifecycleCleanup?.();\n this.lifecycleCleanup = null;\n await this.flushSignals();\n }\n\n /**\n * Returns `true` once the initial public guard bundle has been fetched.\n */\n isReady(): boolean {\n return this.getBundle(PUBLIC_BUNDLE_KEY)?.ready ?? false;\n }\n\n /**\n * Register a listener that is called whenever the guard bundle is refreshed.\n *\n * @param listener - Callback invoked on each guard bundle update.\n * @returns An unsubscribe function. Call it to remove the listener.\n *\n * @example\n * ```ts\n * const unsubscribe = client.subscribe(() => {\n * console.log('Guards refreshed');\n * });\n * // later…\n * unsubscribe();\n * ```\n */\n subscribe(listener: LiteguardChangeListener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Create a new request scope with optional initial properties. The scope\n * uses the public guard bundle by default; call\n * {@link LiteguardScope.bindProtectedContext} on the returned scope to\n * attach signed user context.\n *\n * @param properties - Initial property key/value pairs for the scope.\n * @returns A new {@link LiteguardScope}.\n *\n * @example\n * ```ts\n * const scope = client.createScope({ plan: 'enterprise' });\n * scope.isOpen('feature.beta'); // evaluates with plan=enterprise\n * ```\n */\n createScope(properties: Properties = {}): LiteguardScope {\n return new LiteguardScope(this, {\n properties: cloneProperties(properties),\n protectedBundleKey: PUBLIC_BUNDLE_KEY,\n protectedContext: null,\n });\n }\n\n /**\n * Return the currently active request scope, or the shared default scope\n * when no request-local scope is active.\n *\n * On Node.js, the active scope is resolved from `AsyncLocalStorage` so\n * each concurrent request can carry its own context automatically.\n *\n * @returns The active {@link LiteguardScope}.\n */\n getActiveScope(): LiteguardScope {\n const active = this.getRuntimeScope();\n return active ?? this.defaultScope;\n }\n\n /**\n * Run `fn` inside the given request scope. All guard checks performed\n * during `fn` will use the properties and protected context of `scope`.\n *\n * @param scope - The scope to activate.\n * @param fn - Callback to execute within the scope.\n * @returns The return value of `fn`.\n */\n runWithScope<T>(scope: LiteguardScope, fn: () => T): T {\n const resolvedScope = this.resolveScope(scope);\n const adapter = this.runtime.requestScope;\n if (adapter) {\n const result = adapter.run({ currentScope: resolvedScope }, fn);\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('scope.run(), client.runWithScope(), and property-scoped callbacks');\n }\n return result;\n }\n\n const previousScope = this.defaultScope;\n this.defaultScope = resolvedScope;\n try {\n const result = fn();\n if (isPromiseLike(result)) {\n if (!this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('scope.run(), client.runWithScope(), and property-scoped callbacks');\n }\n return Promise.resolve(result).finally(() => {\n this.defaultScope = previousScope;\n }) as T;\n }\n this.defaultScope = previousScope;\n return result;\n } catch (error) {\n this.defaultScope = previousScope;\n throw error;\n }\n }\n\n /**\n * Convenience helper that derives a scope from the current active scope,\n * merges in the supplied properties, and runs `fn` inside it.\n *\n * @param properties - Key/value pairs to add for the duration of `fn`.\n * @param fn - Callback to execute within the scoped context.\n * @returns The return value of `fn`.\n *\n * @example\n * ```ts\n * const result = client.withProperties({ userId: '42' }, () => {\n * return client.isOpen('feature.beta');\n * });\n * ```\n */\n withProperties<T>(properties: Properties, fn: () => T): T {\n const scope = this.getActiveScope().withProperties(properties);\n return this.runWithScope(scope, fn);\n }\n\n /**\n * Bind protected context to a derived scope and run `fn` inside it.\n * Fetches (or reuses) a guard bundle specific to this context before\n * executing `fn`.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @param fn - Callback to execute within the protected scope.\n * @returns The (awaited) return value of `fn`.\n */\n async withProtectedContext<T>(\n protectedContext: ProtectedContext,\n fn: () => T | Promise<T>,\n ): Promise<Awaited<T>> {\n const scope = await this.getActiveScope().bindProtectedContext(protectedContext);\n return await Promise.resolve(this.runWithScope(scope, fn));\n }\n\n /**\n * Run `fn` inside a Liteguard execution scope so that all guard signals\n * emitted during `fn` share a common execution ID for correlated\n * telemetry. If an execution scope is already active, `fn` is called\n * directly without creating a new one.\n *\n * @param fn - Callback to execute within the execution scope.\n * @returns The return value of `fn`.\n */\n withExecution<T>(fn: () => T): T {\n const existing = this.runtime.execution.getStore();\n if (existing) {\n const result = fn();\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('withExecution()');\n }\n return result;\n }\n const result = this.runtime.execution.run({ executionId: nextSignalId(), sequenceNumber: 0 }, fn);\n if (isPromiseLike(result) && !this.runtime.supportsAsyncContextPropagation) {\n throw asyncContextUnsupportedError('withExecution()');\n }\n return result;\n }\n\n /**\n * Return `true` if the named guard is open for the resolved request scope.\n * Buffers a `guard_check` telemetry signal and consumes a rate-limit slot\n * when applicable.\n *\n * Guards that have not yet been adopted on the Liteguard dashboard always\n * return `true`, allowing you to instrument code before enabling the guard.\n *\n * @param name - Guard name (e.g. `\"payments.checkout\"`).\n * @param callOptions - Optional per-call overrides (extra properties, fallback, scope).\n * @returns `true` if the guard is open, `false` otherwise.\n */\n isOpen(name: string, callOptions: ScopedOptions = {}): boolean {\n return this.evaluateIsOpen(name, callOptions, true);\n }\n\n /**\n * Signal-free variant of {@link isOpen}. Returns the same boolean result\n * but does **not** emit a telemetry signal or consume a rate-limit slot.\n * Ideal for render loops or other hot paths where you only need the\n * boolean value.\n *\n * @param name - Guard name to evaluate.\n * @param callOptions - Optional per-call overrides.\n * @returns `true` if the guard is open, `false` otherwise.\n */\n peekIsOpen(name: string, callOptions: ScopedOptions = {}): boolean {\n return this.evaluateIsOpen(name, callOptions, false);\n }\n\n private evaluateIsOpen(name: string, callOptions: ScopedOptions, emitSignal: boolean): boolean {\n const scope = this.resolveScope(callOptions.scope);\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const fallback = options.fallback ?? this.options.fallback;\n const bundle = this.bundleForScope(scope);\n\n if (!bundle.ready) {\n return fallback;\n }\n\n const guard = bundle.guards.get(name);\n if (!guard) {\n this.recordUnadoptedGuard(name);\n return true;\n }\n if (!guard.adopted) {\n this.recordUnadoptedGuard(name);\n return true;\n }\n\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n let result = evaluateGuard(guard, props);\n if (result && guard.rateLimitPerMinute > 0) {\n result = emitSignal\n ? this.checkRateLimit(name, guard.rateLimitPerMinute, guard.rateLimitProperties, props)\n : this.wouldPassRateLimit(name, guard.rateLimitPerMinute, guard.rateLimitProperties, props);\n }\n\n if (emitSignal) {\n this.bufferSignal({\n guardName: name,\n result,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_check',\n measurement: this.isMeasurementEnabled(guard, options)\n ? this.runtime.measurement.captureGuardCheck()\n : undefined,\n });\n }\n\n return result;\n }\n\n /**\n * Evaluate the guard and, if open, call `fn` inside a correlated execution\n * scope. Returns `fn`'s result when the guard is open, or `undefined` when\n * closed. Emits correlated `guard_check` and `guard_execution` signals.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Synchronous function to invoke when the guard is open.\n * @param callOptions - Optional per-call overrides.\n * @returns The return value of `fn`, or `undefined` if the guard is closed.\n *\n * @example\n * ```ts\n * const banner = client.executeIfOpen('promo.banner', () => {\n * return renderPromoBanner();\n * });\n * ```\n */\n executeIfOpen<T>(name: string, fn: () => T, callOptions: ScopedOptions = {}): T | undefined {\n const scope = this.resolveScope(callOptions.scope);\n return this.runWithScope(scope, () =>\n this.withExecution(() => {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n if (!this.isOpen(name, { ...options, scope })) {\n return undefined;\n }\n if (!guard?.adopted) {\n return fn();\n }\n\n const guardCheckSignalId = this.runtime.execution.getStore()?.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = fn();\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n return value;\n } catch (error) {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n throw error;\n }\n }),\n );\n }\n\n /**\n * Async variant of {@link executeIfOpen}. Evaluates the guard and, if\n * open, awaits `fn` inside a correlated execution scope.\n *\n * @param name - Guard name to evaluate.\n * @param fn - Async function to invoke when the guard is open.\n * @param callOptions - Optional per-call overrides.\n * @returns The resolved value of `fn`, or `undefined` if the guard is closed.\n */\n async executeIfOpenAsync<T>(\n name: string,\n fn: () => Promise<T>,\n callOptions: ScopedOptions = {},\n ): Promise<T | undefined> {\n const scope = this.resolveScope(callOptions.scope);\n if (!this.runtime.supportsAsyncContextPropagation) {\n return await this.executeIfOpenAsyncWithoutAsyncContext(name, scope, fn, callOptions);\n }\n return await this.runWithScope(scope, () =>\n this.withExecution(async () => {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n\n if (!this.isOpen(name, { ...options, scope })) {\n return undefined;\n }\n if (!guard?.adopted) {\n return await fn();\n }\n\n const guardCheckSignalId = this.runtime.execution.getStore()?.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = await fn();\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n return value;\n } catch (error) {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n throw error;\n }\n }),\n );\n }\n\n private async executeIfOpenAsyncWithoutAsyncContext<T>(\n name: string,\n scope: LiteguardScope,\n fn: () => Promise<T>,\n callOptions: ScopedOptions,\n ): Promise<T | undefined> {\n const options: Options = {\n disableMeasurement: false,\n ...callOptions,\n };\n const bundle = this.bundleForScope(scope);\n const guard = bundle.guards.get(name);\n const props: Properties = {\n ...scope.getProperties(),\n ...(options.properties ?? {}),\n };\n const executionState = this.runtime.execution.getStore() ?? { executionId: nextSignalId(), sequenceNumber: 0 };\n\n const isOpen = this.runWithExplicitExecutionState(executionState, () => this.isOpen(name, { ...options, scope }));\n if (!isOpen) {\n return undefined;\n }\n if (!guard?.adopted) {\n return await fn();\n }\n\n const guardCheckSignalId = executionState.lastSignalId;\n const measurementEnabled = this.isMeasurementEnabled(guard, options);\n const start = measurementEnabled ? this.runtime.measurement.beginGuardExecution() : undefined;\n\n try {\n const value = await fn();\n this.runWithExplicitExecutionState(executionState, () => {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, true)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n });\n return value;\n } catch (error) {\n this.runWithExplicitExecutionState(executionState, () => {\n this.bufferSignal({\n guardName: name,\n result: true,\n properties: props,\n callsiteId: this.captureCallsiteId(),\n kind: 'guard_execution',\n measurement: measurementEnabled\n ? this.runtime.measurement.captureGuardExecution(start, false, error)\n : undefined,\n parentSignalIdOverride: guardCheckSignalId,\n });\n });\n throw error;\n }\n }\n\n /**\n * Merge `properties` into the active scope's property bag and persist\n * the derived scope as the new active scope. Subsequent {@link isOpen}\n * calls will include these properties unless overridden per-call.\n *\n * @param properties - Key/value pairs to add.\n * @returns The new active {@link LiteguardScope}.\n */\n addProperties(properties: Properties): LiteguardScope {\n const nextScope = this.getActiveScope().withProperties(properties);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Remove the given property keys from the active scope and persist the\n * resulting scope.\n *\n * @param names - Property keys to remove.\n * @returns The new active {@link LiteguardScope}.\n */\n clearProperties(names: string[]): LiteguardScope {\n const nextScope = this.getActiveScope().clearProperties(names);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Remove all properties from the active scope and persist the resulting\n * scope.\n *\n * @returns The new active {@link LiteguardScope} with an empty property bag.\n */\n resetProperties(): LiteguardScope {\n const nextScope = this.getActiveScope().resetProperties();\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Bind protected context to the active scope and persist the derived\n * scope. Fetches (or reuses) a guard bundle specific to this context.\n *\n * @param protectedContext - Signed context bundle from your auth backend.\n * @returns The new active {@link LiteguardScope}.\n */\n async bindProtectedContext(protectedContext: ProtectedContext): Promise<LiteguardScope> {\n const nextScope = await this.getActiveScope().bindProtectedContext(protectedContext);\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Clear protected context from the active scope and revert to the\n * public guard bundle.\n *\n * @returns The new active {@link LiteguardScope}.\n */\n clearProtectedContext(): LiteguardScope {\n const nextScope = this.getActiveScope().clearProtectedContext();\n return this.replaceCurrentScope(nextScope);\n }\n\n /**\n * Immediately transmit all buffered telemetry signals to the backend. Also\n * flushes any pending unadopted-guard observations.\n *\n * Call this before process exit or page unload to avoid losing signals.\n *\n * @param options - Flush options (e.g. `{ keepalive: true }` for page unload).\n */\n async flushSignals(options: FlushOptions = {}): Promise<void> {\n const signalBatch = this.signalBuffer.splice(0);\n const unadoptedObservations = [...this.pendingUnadoptedGuards.values()]\n .sort((a, b) => a.guardName.localeCompare(b.guardName))\n .map((observation) => this.toUnadoptedGuardObservation(observation));\n this.pendingUnadoptedGuards.clear();\n\n if (signalBatch.length > 0) {\n try {\n const url = new URL('/api/v1/signals', this.options.backendUrl);\n const body = JSON.stringify({\n projectClientToken: this.projectClientToken,\n environment: this.options.environment,\n signals: signalBatch,\n });\n await this.post(url, body, options.keepalive);\n } catch (error) {\n this.log('[liteguard] Signal flush failed', error);\n this.signalBuffer.unshift(...signalBatch);\n const maxBuf = this.options.flushSize * this.options.flushBufferMultiplier;\n if (this.signalBuffer.length > maxBuf) {\n this.droppedSignalsPending += this.signalBuffer.length - maxBuf;\n this.signalBuffer.length = maxBuf;\n }\n }\n }\n\n if (unadoptedObservations.length > 0) {\n try {\n const url = new URL('/api/v1/unadopted-guards', this.options.backendUrl);\n const requestBody: SendUnadoptedGuardsRequest = {\n projectClientToken: this.projectClientToken,\n environment: this.options.environment,\n observations: unadoptedObservations,\n };\n await this.post(url, JSON.stringify(requestBody), options.keepalive);\n } catch (error) {\n this.log('[liteguard] Unadopted guard flush failed', error);\n for (const observation of unadoptedObservations) {\n this.mergePendingUnadoptedObservation(observation);\n }\n }\n }\n }\n\n /** Notify subscribers that client-visible state has changed. */\n protected emitChange(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n /** @internal Derive a new immutable scope from an existing scope snapshot. */\n _deriveScope(scope: LiteguardScope, patch: Partial<ScopeSnapshot>): LiteguardScope {\n const resolved = this.resolveScope(scope);\n const snapshot = resolved._getSnapshot();\n return new LiteguardScope(this, {\n properties: patch.properties ? cloneProperties(patch.properties) : snapshot.properties,\n protectedBundleKey: patch.protectedBundleKey ?? snapshot.protectedBundleKey,\n protectedContext:\n patch.protectedContext === undefined\n ? snapshot.protectedContext\n : cloneProtectedContext(patch.protectedContext),\n });\n }\n\n /** @internal Bind protected context to a scope and ensure its bundle is loaded. */\n async _bindProtectedContextToScope(\n scope: LiteguardScope,\n protectedContext: ProtectedContext,\n ): Promise<LiteguardScope> {\n this.resolveScope(scope);\n const clonedProtectedContext = cloneProtectedContext(protectedContext);\n const bundleKey = await this.ensureBundleForProtectedContext(clonedProtectedContext);\n return new LiteguardScope(this, {\n properties: scope.getProperties(),\n protectedBundleKey: bundleKey,\n protectedContext: clonedProtectedContext,\n });\n }\n\n /** Persist a derived scope into the current request-local or default scope slot. */\n private replaceCurrentScope(nextScope: LiteguardScope): LiteguardScope {\n const resolved = this.resolveScope(nextScope);\n const store = this.runtime.requestScope?.getStore();\n if (store) {\n store.currentScope = resolved;\n return resolved;\n }\n\n this.defaultScope = resolved;\n this.emitChange();\n return resolved;\n }\n\n /** Resolve the active runtime scope store into a client-owned scope instance. */\n private getRuntimeScope(): LiteguardScope | null {\n const store = this.runtime.requestScope?.getStore();\n if (!store) {\n return null;\n }\n const scope = store.currentScope;\n if (scope instanceof LiteguardScope && scope._belongsTo(this)) {\n return scope;\n }\n return null;\n }\n\n /** Validate and resolve the scope used for a client operation. */\n private resolveScope(scope?: LiteguardScope): LiteguardScope {\n const resolved = scope ?? this.getRuntimeScope() ?? this.defaultScope;\n if (!resolved._belongsTo(this)) {\n throw new Error('[liteguard] Scope belongs to a different Liteguard client.');\n }\n return resolved;\n }\n\n /** Queue a one-time report for a guard that exists only in application code. */\n private recordUnadoptedGuard(name: string): void {\n const now = Date.now();\n const existing = this.pendingUnadoptedGuards.get(name);\n if (existing) {\n existing.lastSeenMs = now;\n existing.checkCount += 1;\n return;\n }\n\n this.pendingUnadoptedGuards.set(name, {\n guardName: name,\n firstSeenMs: now,\n lastSeenMs: now,\n checkCount: 1,\n });\n }\n\n private mergePendingUnadoptedObservation(observation: UnadoptedGuardObservation): void {\n const existing = this.pendingUnadoptedGuards.get(observation.guardName);\n if (!existing) {\n this.pendingUnadoptedGuards.set(observation.guardName, {\n guardName: observation.guardName,\n firstSeenMs: observation.firstSeenMs,\n lastSeenMs: observation.lastSeenMs,\n checkCount: observation.checkCount,\n });\n return;\n }\n\n existing.firstSeenMs = Math.min(existing.firstSeenMs, observation.firstSeenMs);\n existing.lastSeenMs = Math.max(existing.lastSeenMs, observation.lastSeenMs);\n existing.checkCount += observation.checkCount;\n }\n\n private toUnadoptedGuardObservation(\n observation: PendingUnadoptedGuardObservation,\n ): UnadoptedGuardObservation {\n return {\n ...observation,\n estimatedChecksPerMinute: estimateChecksPerMinute(\n observation.firstSeenMs,\n observation.lastSeenMs,\n observation.checkCount,\n ),\n };\n }\n\n /** Schedule the next periodic guard refresh using the current refresh interval. */\n private scheduleNextRefresh(): void {\n if (this.refreshTimer !== null) {\n this.runtime.clearTimeout(this.refreshTimer);\n }\n this.refreshTimer = this.runtime.setTimeout(() => {\n void this.runRefreshCycle();\n }, this.currentRefreshRateSeconds * 1_000);\n this.runtime.detachTimer?.(this.refreshTimer);\n }\n\n /** Refresh every cached bundle once, then reschedule the next cycle. */\n private async runRefreshCycle(): Promise<void> {\n if (this.refreshTimer === null) {\n return;\n }\n const bundleKeys = [...this.bundles.keys()].sort();\n for (const bundleKey of bundleKeys) {\n await this.fetchGuardsForBundle(bundleKey);\n }\n if (this.refreshTimer !== null) {\n this.scheduleNextRefresh();\n }\n }\n\n /** Create an empty bundle placeholder before the first successful fetch. */\n private createEmptyBundle(bundleKey: string, protectedContext: ProtectedContext | null): GuardBundle {\n return {\n key: bundleKey,\n guards: new Map(),\n ready: false,\n etag: '',\n protectedContext: cloneProtectedContext(protectedContext),\n refreshRateSeconds: this.options.refreshRateSeconds,\n };\n }\n\n /** Return a cached bundle by key, if present. */\n private getBundle(bundleKey: string): GuardBundle | undefined {\n return this.bundles.get(bundleKey);\n }\n\n /** Resolve the bundle used for a scope, falling back to the public bundle. */\n private bundleForScope(scope: LiteguardScope): GuardBundle {\n return this.getBundle(scope._getBundleKey())\n ?? this.getBundle(PUBLIC_BUNDLE_KEY)\n ?? this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null);\n }\n\n /** Ensure the bundle for a protected context exists locally and is ready. */\n private async ensureBundleForProtectedContext(protectedContext: ProtectedContext | null): Promise<string> {\n const bundleKey = protectedContextCacheKey(protectedContext);\n const existing = this.getBundle(bundleKey);\n if (existing?.ready) {\n return bundleKey;\n }\n if (!existing) {\n this.bundles.set(bundleKey, this.createEmptyBundle(bundleKey, protectedContext));\n }\n await this.fetchGuardsForBundle(bundleKey);\n return bundleKey;\n }\n\n /** Use the shortest bundle refresh rate so all cached bundles stay current. */\n private recomputeRefreshInterval(): void {\n let next = 0;\n for (const bundle of this.bundles.values()) {\n if (bundle.refreshRateSeconds <= 0) {\n continue;\n }\n if (next === 0 || bundle.refreshRateSeconds < next) {\n next = bundle.refreshRateSeconds;\n }\n }\n if (next <= 0) {\n next = this.options.refreshRateSeconds;\n }\n if (next <= 0) {\n next = DEFAULT_REFRESH_RATE_S;\n }\n this.currentRefreshRateSeconds = next;\n }\n\n /** Fetch the latest guard bundle for a bundle key, respecting ETags and timeouts. */\n private async fetchGuardsForBundle(bundleKey: string): Promise<void> {\n const bundle = this.getBundle(bundleKey) ?? this.getBundle(PUBLIC_BUNDLE_KEY);\n const etag = bundle?.etag ?? '';\n const protectedContext = cloneProtectedContext(bundle?.protectedContext ?? null);\n const { signal, cleanup } = this.createTimeoutSignal(this.options.httpTimeoutSeconds * 1_000);\n try {\n const url = new URL('/api/v1/guards', this.options.backendUrl);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.projectClientToken}`,\n 'Content-Type': 'application/json',\n };\n if (etag) {\n headers['If-None-Match'] = etag;\n }\n const requestBody: GetGuardsRequest = {\n projectClientToken: this.projectClientToken,\n environment: this.options.environment,\n ...(protectedContext ? { protectedContext } : {}),\n };\n\n const res = await this.runtime.fetch(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n signal,\n });\n\n if (res.status === 304) {\n return;\n }\n if (!res.ok) {\n await res.text();\n this.log(`[liteguard] Guard fetch failed: ${res.status} ${res.statusText}`);\n return;\n }\n\n const body = (await res.json()) as {\n guards: Guard[];\n refreshRateSeconds?: number;\n etag?: string;\n };\n\n const guards = new Map<string, Guard>();\n for (const guard of body.guards) {\n guards.set(guard.name, guard);\n }\n\n const nextBundle = this.getBundle(bundleKey) ?? this.createEmptyBundle(bundleKey, protectedContext);\n nextBundle.guards = guards;\n nextBundle.ready = true;\n nextBundle.etag = body.etag ?? '';\n nextBundle.protectedContext = cloneProtectedContext(protectedContext);\n const previousRefreshRateSeconds = this.currentRefreshRateSeconds;\n nextBundle.refreshRateSeconds = positiveNumberOrDefault(\n body.refreshRateSeconds,\n this.options.refreshRateSeconds,\n );\n this.bundles.set(bundleKey, nextBundle);\n this.recomputeRefreshInterval();\n if (this.refreshTimer !== null && this.currentRefreshRateSeconds !== previousRefreshRateSeconds) {\n this.scheduleNextRefresh();\n }\n this.emitChange();\n } catch (error) {\n this.log('[liteguard] Guard fetch error:', error);\n } finally {\n cleanup();\n }\n }\n\n /** Create an `AbortSignal` that cancels an HTTP request after `timeoutMs`. */\n private createTimeoutSignal(timeoutMs: number): { signal: AbortSignal; cleanup: () => void } {\n const controller = new AbortController();\n const handle = this.runtime.setTimeout(() => {\n controller.abort();\n }, timeoutMs);\n this.runtime.detachTimer?.(handle);\n return {\n signal: controller.signal,\n cleanup: () => {\n this.runtime.clearTimeout(handle);\n },\n };\n }\n\n /** Send a JSON POST request to the Liteguard backend with the SDK defaults applied. */\n private async post(url: URL, body: string, keepalive = false): Promise<void> {\n const { signal, cleanup } = this.createTimeoutSignal(this.options.httpTimeoutSeconds * 1_000);\n try {\n const res = await this.runtime.fetch(url.toString(), {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.projectClientToken}`,\n 'Content-Type': 'application/json',\n ...(this.options.environment && { 'X-Liteguard-Environment': this.options.environment }),\n },\n body,\n signal,\n keepalive,\n });\n if (!res.ok) {\n throw new Error(`${res.status} ${res.statusText}`);\n }\n } finally {\n cleanup();\n }\n }\n\n /** Buffer a telemetry signal and trigger an eager flush when the batch is full. */\n private bufferSignal(input: BufferedSignalInput): Signal {\n const metadata = this.nextSignalMetadata(input.parentSignalIdOverride);\n const signal: Signal = {\n guardName: input.guardName,\n result: input.result,\n properties: { ...input.properties },\n timestampMs: this.runtime.now(),\n signalId: metadata.signalId,\n executionId: metadata.executionId,\n ...(metadata.parentSignalId ? { parentSignalId: metadata.parentSignalId } : {}),\n sequenceNumber: metadata.sequenceNumber,\n callsiteId: input.callsiteId,\n kind: input.kind,\n droppedSignalsSinceLast: this.takeDroppedSignals(),\n ...(input.measurement ? { measurement: input.measurement } : {}),\n };\n if (this.signalBuffer.length >= this.maxBufferSize()) {\n this.signalBuffer.shift();\n this.droppedSignalsPending += 1;\n }\n this.signalBuffer.push(signal);\n if (this.signalBuffer.length >= this.options.flushSize) {\n void this.flushSignals();\n }\n return signal;\n }\n\n /** Allocate signal correlation metadata for the current execution context. */\n private nextSignalMetadata(parentSignalIdOverride?: string): {\n signalId: string;\n executionId: string;\n parentSignalId?: string;\n sequenceNumber: number;\n } {\n const state = this.runtime.execution.getStore();\n const signalId = nextSignalId();\n if (!state) {\n return {\n signalId,\n executionId: nextSignalId(),\n sequenceNumber: 1,\n };\n }\n\n state.sequenceNumber += 1;\n const parentSignalId = parentSignalIdOverride ?? state.lastSignalId;\n state.lastSignalId = signalId;\n return {\n signalId,\n executionId: state.executionId,\n ...(parentSignalId ? { parentSignalId } : {}),\n sequenceNumber: state.sequenceNumber,\n };\n }\n\n private runWithExplicitExecutionState<T>(state: ExecutionState, fn: () => T): T {\n const existing = this.runtime.execution.getStore();\n if (existing === state) {\n return fn();\n }\n return this.runtime.execution.run(state, fn);\n }\n\n /** Capture the first external stack frame so telemetry can identify the call site. */\n private captureCallsiteId(): string {\n const err = new Error();\n const stack = err.stack?.split('\\n').slice(1) ?? [];\n const frame = stack.find((line) => {\n if (line.includes('node:internal')) return false;\n return !this.runtime.internalStackMarkers.some((marker) => line.includes(marker));\n });\n return frame?.trim().replace(/^at\\s+/, '') || 'unknown';\n }\n\n /** Build the in-memory rate-limit bucket key for a guard and property set. */\n private rateLimitBucketKey(name: string, rateLimitProperties: string[], props: Properties): string {\n if (rateLimitProperties.length === 0) return name;\n const parts = rateLimitProperties.map((property) => `${property}=${String(props[property] ?? '')}`);\n return `${name}\\x00${parts.join('\\x00')}`;\n }\n\n private checkRateLimit(\n name: string,\n limitPerMinute: number,\n rateLimitProperties: string[],\n props: Properties,\n ): boolean {\n const now = this.runtime.monotonicNow();\n const key = this.rateLimitBucketKey(name, rateLimitProperties, props);\n const state = this.rateLimitState.get(key) ?? { windowStart: now, count: 0 };\n if (now - state.windowStart >= 60_000) {\n state.windowStart = now;\n state.count = 0;\n }\n if (state.count >= limitPerMinute) {\n this.rateLimitState.set(key, state);\n return false;\n }\n state.count += 1;\n this.rateLimitState.set(key, state);\n return true;\n }\n\n /** Check whether a guard would pass the current rate-limit window without consuming a slot. */\n private wouldPassRateLimit(\n name: string,\n limitPerMinute: number,\n rateLimitProperties: string[],\n props: Properties,\n ): boolean {\n const now = this.runtime.monotonicNow();\n const key = this.rateLimitBucketKey(name, rateLimitProperties, props);\n const state = this.rateLimitState.get(key);\n if (!state) {\n return true;\n }\n if (now - state.windowStart >= 60_000) {\n return true;\n }\n return state.count < limitPerMinute;\n }\n\n /** Decide whether performance measurement should be captured for this guard call. */\n private isMeasurementEnabled(guard: Guard, options: Options): boolean {\n if (this.options.disableMeasurement) {\n return false;\n }\n if (options.disableMeasurement) {\n return false;\n }\n if (guard.disableMeasurement === true) {\n return false;\n }\n return true;\n }\n\n /** Return the hard cap for the in-memory signal buffer after flush failures. */\n private maxBufferSize(): number {\n return this.options.flushSize * this.options.flushBufferMultiplier;\n }\n\n /** Drain and reset the count of dropped signals to attach to the next emitted signal. */\n private takeDroppedSignals(): number {\n const dropped = this.droppedSignalsPending;\n this.droppedSignalsPending = 0;\n return dropped;\n }\n\n /** Write a warning only when quiet mode is disabled. */\n private log(...args: unknown[]): void {\n if (!this.options.quiet) {\n console.warn(...args);\n }\n }\n\n /** @internal Test helper for swapping the public guard bundle in memory. */\n _setGuards(guards: Guard[]): void {\n const bundle = this.getBundle(PUBLIC_BUNDLE_KEY) ?? this.createEmptyBundle(PUBLIC_BUNDLE_KEY, null);\n bundle.guards = new Map(guards.map((guard) => [guard.name, guard]));\n bundle.ready = true;\n bundle.etag = '';\n this.bundles.set(PUBLIC_BUNDLE_KEY, bundle);\n this.emitChange();\n }\n\n /** @internal Test helper for swapping a protected-context bundle in memory. */\n _setProtectedGuards(protectedContext: ProtectedContext, guards: Guard[]): void {\n const key = protectedContextCacheKey(protectedContext);\n const bundle = this.getBundle(key) ?? this.createEmptyBundle(key, protectedContext);\n bundle.guards = new Map(guards.map((guard) => [guard.name, guard]));\n bundle.ready = true;\n bundle.etag = '';\n bundle.protectedContext = cloneProtectedContext(protectedContext);\n this.bundles.set(key, bundle);\n this.recomputeRefreshInterval();\n this.emitChange();\n }\n\n /** @internal Clear all rate-limit counters, or only those for a specific guard. */\n _resetRateLimitState(name?: string): void {\n if (name !== undefined) {\n this.rateLimitState.delete(name);\n const prefix = name + '\\x00';\n for (const key of this.rateLimitState.keys()) {\n if (key.startsWith(prefix)) {\n this.rateLimitState.delete(key);\n }\n }\n return;\n }\n this.rateLimitState.clear();\n }\n\n /** @internal Return the default scope properties for tests and diagnostics. */\n _getContext(): Properties {\n return this.defaultScope.getProperties();\n }\n\n /** @internal Return the default scope protected context for tests and diagnostics. */\n _getProtectedContext(): ProtectedContext | null {\n return this.defaultScope.getProtectedContext();\n }\n\n /** @internal Return the active refresh cadence for tests and diagnostics. */\n _getRefreshRateSeconds(): number {\n return this.currentRefreshRateSeconds;\n }\n\n /** @internal Return a cloned view of the buffered signals for tests and diagnostics. */\n _getSignalBuffer(): Signal[] {\n return this.signalBuffer.map((signal) => ({\n ...signal,\n ...(signal.properties ? { properties: { ...signal.properties } } : {}),\n }));\n }\n\n /** @internal Return the sorted set of queued unadopted guard observations. */\n _getPendingUnadoptedGuards(): UnadoptedGuardObservation[] {\n return [...this.pendingUnadoptedGuards.values()]\n .sort((a, b) => a.guardName.localeCompare(b.guardName))\n .map((observation) => this.toUnadoptedGuardObservation(observation));\n }\n\n /** @internal Return the currently cached bundle keys. */\n _getBundleKeys(): string[] {\n return [...this.bundles.keys()].sort();\n }\n\n /** @internal Expose the request-scope store for tests. */\n _getRequestScopeStore(): RequestScopeStore | undefined {\n return this.runtime.requestScope?.getStore();\n }\n}\n\n/** Generate a lightweight unique identifier for signals and executions. */\nfunction nextSignalId(): string {\n signalCounter += 1;\n return `${Date.now().toString(36)}-${signalCounter.toString(36)}`;\n}\n\nfunction estimateChecksPerMinute(firstSeenMs: number, lastSeenMs: number, checkCount: number): number {\n if (checkCount <= 0) {\n return 0;\n }\n const windowMs = Math.max(1000, lastSeenMs - firstSeenMs);\n return (checkCount * 60000) / windowMs;\n}\n"],"mappings":"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","names":["result"]}
package/dist/index.mjs CHANGED
@@ -128,7 +128,7 @@ var signalCounter = 0;
128
128
  var LiteguardScope = class {
129
129
  client;
130
130
  snapshot;
131
- /** @internal use {@link BaseLiteguardClient.createScope} instead. */
131
+ /** @internal use {@link BaseLiteguardClient.createScope} instead. */
132
132
  constructor(client, snapshot) {
133
133
  this.client = client;
134
134
  this.snapshot = {
@@ -153,7 +153,7 @@ var LiteguardScope = class {
153
153
  });
154
154
  }
155
155
  /**
156
- * Alias for {@link withProperties} derives a new scope with additional properties.
156
+ * Alias for {@link withProperties} derives a new scope with additional properties.
157
157
  *
158
158
  * @param properties - Key/value pairs to add.
159
159
  * @returns A new {@link LiteguardScope} with the merged properties.
@@ -305,15 +305,14 @@ var LiteguardScope = class {
305
305
  }
306
306
  };
307
307
  var BaseLiteguardClient = class {
308
- projectClientKeyId;
308
+ projectClientToken;
309
309
  runtime;
310
310
  options;
311
311
  bundles = /* @__PURE__ */ new Map();
312
312
  defaultScope;
313
313
  signalBuffer = [];
314
314
  droppedSignalsPending = 0;
315
- reportedUnadoptedGuards = /* @__PURE__ */ new Set();
316
- pendingUnadoptedGuards = /* @__PURE__ */ new Set();
315
+ pendingUnadoptedGuards = /* @__PURE__ */ new Map();
317
316
  refreshTimer = null;
318
317
  flushTimer = null;
319
318
  lifecycleCleanup = null;
@@ -327,12 +326,12 @@ var BaseLiteguardClient = class {
327
326
  * and begin background timers.
328
327
  *
329
328
  * @param runtime - Platform adapter providing timers, fetch, and context storage.
330
- * @param projectClientKeyId - Your project client key ID from the Liteguard dashboard.
329
+ * @param projectClientToken - Your project client token from the Liteguard dashboard.
331
330
  * @param options - Optional SDK configuration overrides.
332
331
  */
333
- constructor(runtime, projectClientKeyId, options = {}) {
332
+ constructor(runtime, projectClientToken, options = {}) {
334
333
  this.runtime = runtime;
335
- this.projectClientKeyId = projectClientKeyId;
334
+ this.projectClientToken = projectClientToken;
336
335
  this.options = {
337
336
  environment: options.environment ?? "",
338
337
  fallback: options.fallback ?? false,
@@ -401,7 +400,7 @@ var BaseLiteguardClient = class {
401
400
  * Register a listener that is called whenever the guard bundle is refreshed.
402
401
  *
403
402
  * @param listener - Callback invoked on each guard bundle update.
404
- * @returns An unsubscribe function call it to remove the listener.
403
+ * @returns An unsubscribe function. Call it to remove the listener.
405
404
  *
406
405
  * @example
407
406
  * ```ts
@@ -851,8 +850,8 @@ var BaseLiteguardClient = class {
851
850
  return this.replaceCurrentScope(nextScope);
852
851
  }
853
852
  /**
854
- * Immediately transmit all buffered telemetry signals to the backend. Also
855
- * flushes any pending unadopted-guard reports.
853
+ * Immediately transmit all buffered telemetry signals to the backend. Also
854
+ * flushes any pending unadopted-guard observations.
856
855
  *
857
856
  * Call this before process exit or page unload to avoid losing signals.
858
857
  *
@@ -860,13 +859,13 @@ var BaseLiteguardClient = class {
860
859
  */
861
860
  async flushSignals(options = {}) {
862
861
  const signalBatch = this.signalBuffer.splice(0);
863
- const unadoptedGuardNames = [...this.pendingUnadoptedGuards];
862
+ const unadoptedObservations = [...this.pendingUnadoptedGuards.values()].sort((a, b) => a.guardName.localeCompare(b.guardName)).map((observation) => this.toUnadoptedGuardObservation(observation));
864
863
  this.pendingUnadoptedGuards.clear();
865
864
  if (signalBatch.length > 0) {
866
865
  try {
867
866
  const url = new URL("/api/v1/signals", this.options.backendUrl);
868
867
  const body = JSON.stringify({
869
- projectClientKeyId: this.projectClientKeyId,
868
+ projectClientToken: this.projectClientToken,
870
869
  environment: this.options.environment,
871
870
  signals: signalBatch
872
871
  });
@@ -881,19 +880,19 @@ var BaseLiteguardClient = class {
881
880
  }
882
881
  }
883
882
  }
884
- if (unadoptedGuardNames.length > 0) {
883
+ if (unadoptedObservations.length > 0) {
885
884
  try {
886
885
  const url = new URL("/api/v1/unadopted-guards", this.options.backendUrl);
887
886
  const requestBody = {
888
- projectClientKeyId: this.projectClientKeyId,
887
+ projectClientToken: this.projectClientToken,
889
888
  environment: this.options.environment,
890
- guardNames: unadoptedGuardNames
889
+ observations: unadoptedObservations
891
890
  };
892
891
  await this.post(url, JSON.stringify(requestBody), options.keepalive);
893
892
  } catch (error) {
894
893
  this.log("[liteguard] Unadopted guard flush failed", error);
895
- for (const guardName of unadoptedGuardNames) {
896
- this.pendingUnadoptedGuards.add(guardName);
894
+ for (const observation of unadoptedObservations) {
895
+ this.mergePendingUnadoptedObservation(observation);
897
896
  }
898
897
  }
899
898
  }
@@ -959,10 +958,44 @@ var BaseLiteguardClient = class {
959
958
  }
960
959
  /** Queue a one-time report for a guard that exists only in application code. */
961
960
  recordUnadoptedGuard(name) {
962
- if (!this.reportedUnadoptedGuards.has(name)) {
963
- this.reportedUnadoptedGuards.add(name);
964
- this.pendingUnadoptedGuards.add(name);
961
+ const now = Date.now();
962
+ const existing = this.pendingUnadoptedGuards.get(name);
963
+ if (existing) {
964
+ existing.lastSeenMs = now;
965
+ existing.checkCount += 1;
966
+ return;
965
967
  }
968
+ this.pendingUnadoptedGuards.set(name, {
969
+ guardName: name,
970
+ firstSeenMs: now,
971
+ lastSeenMs: now,
972
+ checkCount: 1
973
+ });
974
+ }
975
+ mergePendingUnadoptedObservation(observation) {
976
+ const existing = this.pendingUnadoptedGuards.get(observation.guardName);
977
+ if (!existing) {
978
+ this.pendingUnadoptedGuards.set(observation.guardName, {
979
+ guardName: observation.guardName,
980
+ firstSeenMs: observation.firstSeenMs,
981
+ lastSeenMs: observation.lastSeenMs,
982
+ checkCount: observation.checkCount
983
+ });
984
+ return;
985
+ }
986
+ existing.firstSeenMs = Math.min(existing.firstSeenMs, observation.firstSeenMs);
987
+ existing.lastSeenMs = Math.max(existing.lastSeenMs, observation.lastSeenMs);
988
+ existing.checkCount += observation.checkCount;
989
+ }
990
+ toUnadoptedGuardObservation(observation) {
991
+ return {
992
+ ...observation,
993
+ estimatedChecksPerMinute: estimateChecksPerMinute(
994
+ observation.firstSeenMs,
995
+ observation.lastSeenMs,
996
+ observation.checkCount
997
+ )
998
+ };
966
999
  }
967
1000
  /** Schedule the next periodic guard refresh using the current refresh interval. */
968
1001
  scheduleNextRefresh() {
@@ -1047,14 +1080,14 @@ var BaseLiteguardClient = class {
1047
1080
  try {
1048
1081
  const url = new URL("/api/v1/guards", this.options.backendUrl);
1049
1082
  const headers = {
1050
- Authorization: `Bearer ${this.projectClientKeyId}`,
1083
+ Authorization: `Bearer ${this.projectClientToken}`,
1051
1084
  "Content-Type": "application/json"
1052
1085
  };
1053
1086
  if (etag) {
1054
1087
  headers["If-None-Match"] = etag;
1055
1088
  }
1056
1089
  const requestBody = {
1057
- projectClientKeyId: this.projectClientKeyId,
1090
+ projectClientToken: this.projectClientToken,
1058
1091
  environment: this.options.environment,
1059
1092
  ...protectedContext ? { protectedContext } : {}
1060
1093
  };
@@ -1120,7 +1153,7 @@ var BaseLiteguardClient = class {
1120
1153
  const res = await this.runtime.fetch(url.toString(), {
1121
1154
  method: "POST",
1122
1155
  headers: {
1123
- Authorization: `Bearer ${this.projectClientKeyId}`,
1156
+ Authorization: `Bearer ${this.projectClientToken}`,
1124
1157
  "Content-Type": "application/json",
1125
1158
  ...this.options.environment && { "X-Liteguard-Environment": this.options.environment }
1126
1159
  },
@@ -1318,9 +1351,9 @@ var BaseLiteguardClient = class {
1318
1351
  ...signal.properties ? { properties: { ...signal.properties } } : {}
1319
1352
  }));
1320
1353
  }
1321
- /** @internal Return the sorted set of queued unadopted guards. */
1354
+ /** @internal Return the sorted set of queued unadopted guard observations. */
1322
1355
  _getPendingUnadoptedGuards() {
1323
- return [...this.pendingUnadoptedGuards].sort();
1356
+ return [...this.pendingUnadoptedGuards.values()].sort((a, b) => a.guardName.localeCompare(b.guardName)).map((observation) => this.toUnadoptedGuardObservation(observation));
1324
1357
  }
1325
1358
  /** @internal Return the currently cached bundle keys. */
1326
1359
  _getBundleKeys() {
@@ -1335,6 +1368,13 @@ function nextSignalId() {
1335
1368
  signalCounter += 1;
1336
1369
  return `${Date.now().toString(36)}-${signalCounter.toString(36)}`;
1337
1370
  }
1371
+ function estimateChecksPerMinute(firstSeenMs, lastSeenMs, checkCount) {
1372
+ if (checkCount <= 0) {
1373
+ return 0;
1374
+ }
1375
+ const windowMs = Math.max(1e3, lastSeenMs - firstSeenMs);
1376
+ return checkCount * 6e4 / windowMs;
1377
+ }
1338
1378
  export {
1339
1379
  BaseLiteguardClient,
1340
1380
  LiteguardScope,