@guidelytech/flags 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/dist/chunk-2RZKAT37.js +1085 -0
- package/dist/chunk-2RZKAT37.js.map +1 -0
- package/dist/cli.cjs +186 -96
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +121 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -5
- package/dist/index.d.ts +46 -5
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util.ts","../src/dev-overrides.ts","../src/evaluator.ts","../src/crypto.ts","../src/sse.ts","../src/client.ts"],"sourcesContent":["/** Small runtime-neutral helpers shared by the flags client. No Node built-ins, no DOM at module scope. */\nimport type { Attributes, EncryptedFlagsPayload, FlagsPayload, JsonValue, RemoteEvaluationResponse } from \"./types\";\n\n/** JSON with object keys sorted: equal attribute sets give equal strings (remote-eval cache keys). */\nexport function stableStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map((v) => (v === undefined ? \"null\" : stableStringify(v))).join(\",\")}]`;\n if (value instanceof Date) return JSON.stringify(value.toISOString());\n const obj = value as Record<string, unknown>;\n return `{${Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n .map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n .join(\",\")}}`;\n}\n\nexport function isEncryptedPayload(x: unknown): x is EncryptedFlagsPayload {\n return !!x && typeof x === \"object\" && typeof (x as EncryptedFlagsPayload).encryptedPayload === \"string\";\n}\n\n/** A remote-evaluation response (`{ version, flags: { key: EvaluationResult } }`) rather than a payload. */\nexport function isRemoteResults(x: unknown): x is RemoteEvaluationResponse {\n return !!x && typeof x === \"object\" && !(\"schemaVersion\" in x) && !!(x as RemoteEvaluationResponse).flags && typeof (x as RemoteEvaluationResponse).flags === \"object\";\n}\n\nexport function isFlagsPayload(x: unknown): x is FlagsPayload {\n return !!x && typeof x === \"object\" && \"schemaVersion\" in x && !!(x as FlagsPayload).flags && typeof (x as FlagsPayload).flags === \"object\";\n}\n\n/** JSON type name used by the typed getters (`array` and `object` are distinct). */\nexport function kindOf(v: unknown): string {\n if (v === null || v === undefined) return \"null\";\n if (Array.isArray(v)) return \"array\";\n return typeof v;\n}\n\n/** Drops `undefined` values (they are not attributes). */\nexport function compact(attrs: Attributes): Attributes {\n const out: Attributes = {};\n for (const [k, v] of Object.entries(attrs)) if (v !== undefined) out[k] = v;\n return out;\n}\n\n/** `?guidely_flags=key:value,key2:\"str\"` → overrides (text as in {@link parseOverrides}; invalid text gives none). */\nexport function parseUrlOverrides(search: string, param = \"guidely_flags\"): Record<string, JsonValue> {\n let raw: string | null = null;\n try {\n raw = new URLSearchParams(search.startsWith(\"?\") ? search.slice(1) : search).get(param);\n } catch {\n return {};\n }\n return (raw && parseOverrides(raw)) || {};\n}\n\n/**\n * Overrides text (docs/feature-flags-spec.md §4.1), as held by `GUIDELY_FLAG_OVERRIDES`, an\n * overrides file or `?guidely_flags=`: a JSON object (`{\"new-checkout\":true}`) or a list\n * `key:value` separated by commas or newlines (`new-checkout:true,limit:5,theme:\"dark\"`), where each\n * value is JSON when it parses and text otherwise. `null` when the text is a malformed JSON object.\n */\nexport function parseOverrides(text: string): Record<string, JsonValue> | null {\n const raw = text.trim();\n if (raw.startsWith(\"{\")) {\n try {\n const obj = JSON.parse(raw) as unknown;\n return obj && typeof obj === \"object\" && !Array.isArray(obj) ? (obj as Record<string, JsonValue>) : null;\n } catch {\n return null;\n }\n }\n const out: Record<string, JsonValue> = {};\n // Split on top-level commas and newlines: those inside quotes, arrays or objects belong to the value.\n const items: string[] = [];\n let depth = 0;\n let quoted = false;\n let start = 0;\n for (let i = 0; i < raw.length; i++) {\n const c = raw[i];\n if (quoted) {\n if (c === \"\\\\\") i++;\n else if (c === '\"') quoted = false;\n } else if (c === '\"') quoted = true;\n else if (c === \"[\" || c === \"{\") depth++;\n else if (c === \"]\" || c === \"}\") depth = Math.max(0, depth - 1);\n else if ((c === \",\" || c === \"\\n\") && depth === 0) {\n items.push(raw.slice(start, i));\n start = i + 1;\n }\n }\n items.push(raw.slice(start));\n for (const item of items) {\n const sep = item.indexOf(\":\");\n if (sep <= 0) continue;\n const key = item.slice(0, sep).trim();\n const text = item.slice(sep + 1).trim();\n if (!key) continue;\n try {\n out[key] = JSON.parse(text) as JsonValue;\n } catch {\n out[key] = text;\n }\n }\n return out;\n}\n\nexport function isDev(): boolean {\n try {\n const p = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return !!p?.env && p.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\n/** `setTimeout` that never keeps a Node process alive. */\nexport function timer(fn: () => void, ms: number): ReturnType<typeof setTimeout> {\n const t = setTimeout(fn, ms);\n (t as unknown as { unref?: () => void }).unref?.();\n return t;\n}\n","/**\n * Dev overrides (docs/feature-flags-spec.md §4.1): flag values pinned from outside the code, so CI\n * and tests can switch flags without editing the app. Read once, when a client is created.\n * Runtime-neutral: environment variables via `process.env` (Node, Bun, Next.js server) or\n * `Deno.env`, files via `process.getBuiltinModule(\"node:fs\")` or `Deno`, and a global that test\n * runners set in the page (`page.addInitScript`).\n */\nimport type { OverrideBehaviour } from \"./client\";\nimport type { JsonValue } from \"./types\";\nimport { parseOverrides } from \"./util\";\n\nexport const OVERRIDES_ENV = \"GUIDELY_FLAG_OVERRIDES\";\nexport const OVERRIDES_FILE_ENV = \"GUIDELY_FLAG_OVERRIDES_FILE\";\nexport const OVERRIDE_BEHAVIOUR_ENV = \"GUIDELY_FLAG_OVERRIDE_BEHAVIOUR\";\nexport const OVERRIDES_GLOBAL = \"__GUIDELY_FLAG_OVERRIDES__\";\nexport const OVERRIDE_BEHAVIOUR_GLOBAL = \"__GUIDELY_FLAG_OVERRIDE_BEHAVIOUR__\";\n\nconst BEHAVIOURS: readonly OverrideBehaviour[] = [\"localOnly\", \"localOverRemote\", \"remoteOverLocal\"];\n\nexport interface DevOverrides {\n /** Merged values: file, then environment variable, then global (later wins per key). */\n values: Record<string, JsonValue>;\n /** Replaces the configured `overrideBehaviour` when set. */\n behaviour?: OverrideBehaviour;\n /** Where values or the behaviour came from, for the start-up notice. */\n sources: string[];\n /** Unreadable or malformed sources (ignored). */\n errors: string[];\n}\n\ntype Runtime = {\n process?: { env?: Record<string, string | undefined>; getBuiltinModule?: (id: string) => unknown };\n Deno?: { env?: { get(name: string): string | undefined }; readTextFileSync?(path: string): string };\n};\n\nconst runtime = () => globalThis as unknown as Runtime & Record<string, unknown>;\n\nfunction env(name: string): string | undefined {\n const g = runtime();\n try {\n const v = g.process?.env?.[name];\n if (v !== undefined) return v;\n } catch {\n // Some edge runtimes throw on unknown variables.\n }\n try {\n return g.Deno?.env?.get(name);\n } catch {\n return undefined; // Deno without --allow-env\n }\n}\n\nfunction readFile(path: string): string {\n const g = runtime();\n const fs = g.process?.getBuiltinModule?.(\"node:fs\") as { readFileSync(p: string, enc: \"utf8\"): string } | undefined;\n if (fs) return fs.readFileSync(path, \"utf8\");\n if (g.Deno?.readTextFileSync) return g.Deno.readTextFileSync(path);\n throw new Error(\"this runtime cannot read files\");\n}\n\nfunction behaviourOf(raw: unknown, source: string, out: DevOverrides): void {\n if (typeof raw !== \"string\" || !raw.trim()) return;\n const b = raw.trim() as OverrideBehaviour;\n if (BEHAVIOURS.includes(b)) {\n out.behaviour = b;\n out.sources.push(source);\n } else out.errors.push(`${source} must be localOnly, localOverRemote or remoteOverLocal (got \"${raw}\"); ignoring it.`);\n}\n\nfunction merge(text: string | undefined, source: string, out: DevOverrides): void {\n if (text === undefined || !text.trim()) return;\n const values = parseOverrides(text);\n if (!values) return void out.errors.push(`${source} is not a JSON object or a key:value list; ignoring it.`);\n if (!Object.keys(values).length) return;\n Object.assign(out.values, values);\n out.sources.push(source);\n}\n\n/** Reads every dev-override source. Never throws: problems land in `errors`. */\nexport function readDevOverrides(): DevOverrides {\n const out: DevOverrides = { values: {}, sources: [], errors: [] };\n const file = env(OVERRIDES_FILE_ENV);\n if (file?.trim()) {\n try {\n merge(readFile(file.trim()), `${OVERRIDES_FILE_ENV} (${file.trim()})`, out);\n } catch (err) {\n out.errors.push(`Could not read ${OVERRIDES_FILE_ENV} (${file.trim()}): ${err instanceof Error ? err.message : String(err)}; ignoring it.`);\n }\n }\n merge(env(OVERRIDES_ENV), OVERRIDES_ENV, out);\n const g = runtime()[OVERRIDES_GLOBAL];\n if (g && typeof g === \"object\" && !Array.isArray(g)) {\n if (Object.keys(g).length) {\n Object.assign(out.values, g as Record<string, JsonValue>);\n out.sources.push(OVERRIDES_GLOBAL);\n }\n } else if (typeof g === \"string\") merge(g, OVERRIDES_GLOBAL, out);\n else if (g !== undefined && g !== null) out.errors.push(`${OVERRIDES_GLOBAL} must be an object or overrides text; ignoring it.`);\n behaviourOf(env(OVERRIDE_BEHAVIOUR_ENV), OVERRIDE_BEHAVIOUR_ENV, out);\n behaviourOf(runtime()[OVERRIDE_BEHAVIOUR_GLOBAL], OVERRIDE_BEHAVIOUR_GLOBAL, out);\n return out;\n}\n","/** The getter surface shared by the client, `forUser()` evaluators and static (SSR) snapshots. */\nimport { evaluateFlag } from \"./evaluate\";\nimport type { Attributes, EvaluationResult, FlagsPayload, JsonValue, RemoteEvaluationResponse } from \"./types\";\nimport { isEncryptedPayload, isFlagsPayload, isRemoteResults, kindOf } from \"./util\";\n\n/** The value type a getter returns for a fallback: literals widen (`\"blue\"` → `string`), JSON types stay. */\nexport type FlagValue<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;\n\n/** An evaluation result whose `value` is the caller's fallback when the flag is missing, null or of another type. */\nexport interface FlagDetails<T = JsonValue> extends Omit<EvaluationResult, \"value\"> {\n value: T;\n /** The fallback was served (flag missing, value null or of the wrong type). */\n usedFallback: boolean;\n}\n\n/** Synchronous flag getters. They never throw: problems give the fallback. */\nexport interface FlagsEvaluator {\n /** Boolean flag value; `fallback` (default `false`) when missing or not a boolean. */\n isEnabled(key: string, fallback?: boolean): boolean;\n /** Flag value; `fallback` when missing, null or of a different JSON type than `fallback`. */\n getValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T>;\n getString(key: string, fallback: string): string;\n getNumber(key: string, fallback: number): number;\n getBoolean(key: string, fallback: boolean): boolean;\n /** Object or array value. */\n getJson<T = JsonValue>(key: string, fallback: T): T;\n /** Value plus why it was served (reason, rule, variation, experiment). */\n getDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>>;\n /** Every flag's value for these attributes (no exposures or usage are recorded). */\n getAll(): Record<string, JsonValue>;\n /** The attributes these getters evaluate with. */\n getAttributes(): Attributes;\n}\n\n/** What framework bindings read from: an evaluator that can say whether it has loaded and notify changes. */\nexport interface FlagsReader extends FlagsEvaluator {\n isReady(): boolean;\n /** Calls `listener` whenever values may have changed. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n}\n\nexport type Resolver = (key: string) => EvaluationResult;\n\nfunction typeMatches(value: unknown, fallback: unknown): boolean {\n if (value === null || value === undefined) return false;\n if (fallback === null || fallback === undefined) return true;\n return kindOf(value) === kindOf(fallback);\n}\n\n/** Builds the getters from a resolver (key → result). */\nexport function makeEvaluator(resolve: Resolver, all: () => Record<string, JsonValue>, attributes: () => Attributes): FlagsEvaluator {\n const details = <T,>(key: string, fallback: T): FlagDetails<T> => {\n const r = resolve(key);\n const ok = typeMatches(r.value, fallback);\n return { ...r, value: ok ? (r.value as T) : fallback, usedFallback: !ok };\n };\n const of = <T,>(type: string, key: string, fallback: T): T => {\n const v = resolve(key).value;\n return kindOf(v) === type ? (v as T) : fallback;\n };\n return {\n isEnabled: (key, fallback = false) => of(\"boolean\", key, fallback),\n getValue: <T extends JsonValue>(key: string, fallback: T) => details(key, fallback).value as FlagValue<T>,\n getString: (key, fallback) => of(\"string\", key, fallback),\n getNumber: (key, fallback) => of(\"number\", key, fallback),\n getBoolean: (key, fallback) => of(\"boolean\", key, fallback),\n getJson: <T,>(key: string, fallback: T) => {\n const v = resolve(key).value;\n return v !== null && typeof v === \"object\" ? (v as T) : fallback;\n },\n getDetails: details as FlagsEvaluator[\"getDetails\"],\n getAll: all,\n getAttributes: attributes,\n };\n}\n\nexport function notFound(key: string): EvaluationResult {\n return { key, value: null, reason: \"FLAG_NOT_FOUND\", inExperiment: false };\n}\n\nexport interface StaticFlagsInput {\n /** A payload (or remote-evaluation results) from `client.getPayload()` on the server. */\n payload?: FlagsPayload | RemoteEvaluationResponse | unknown;\n attributes?: Attributes;\n now?: Date | number;\n}\n\n/**\n * A network-free, read-only snapshot over a bootstrap payload. Framework bindings render with it\n * on the server and during hydration so the HTML matches; it records no exposures or usage.\n * Encrypted payloads cannot be read synchronously and give fallbacks.\n */\nexport function createStaticFlags(input: StaticFlagsInput = {}): FlagsReader {\n const attrs = { ...(input.attributes ?? {}) };\n const p = input.payload;\n const payload = isFlagsPayload(p) && !isEncryptedPayload(p) ? p : null;\n const results = !payload && isRemoteResults(p) ? p.flags : null;\n const resolve: Resolver = (key) => {\n try {\n if (payload) return evaluateFlag(payload, key, attrs, { now: input.now });\n return results?.[key] ?? notFound(key);\n } catch {\n return notFound(key);\n }\n };\n const all = () => {\n const out: Record<string, JsonValue> = {};\n for (const key of Object.keys(payload?.flags ?? results ?? {})) out[key] = resolve(key).value;\n return out;\n };\n return { ...makeEvaluator(resolve, all, () => ({ ...attrs })), isReady: () => !!(payload || results), subscribe: () => () => {} };\n}\n","/** AES-256-GCM decryption of encrypted payloads (docs/feature-flags-spec.md §1.3) through WebCrypto. */\nimport { decodeUtf8 } from \"./hash\";\n\nconst ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** Standard or URL-safe base64, padding optional. Throws on invalid input. */\nexport function base64ToBytes(input: string): Uint8Array {\n const s = input.trim().replace(/-/g, \"+\").replace(/_/g, \"/\").replace(/=+$/, \"\");\n const out = new Uint8Array(Math.floor((s.length * 3) / 4));\n let buffer = 0;\n let bits = 0;\n let n = 0;\n for (let i = 0; i < s.length; i++) {\n const v = ALPHABET.indexOf(s[i]!);\n if (v < 0) throw new Error(\"invalid base64\");\n buffer = (buffer << 6) | v;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[n++] = (buffer >> bits) & 0xff;\n }\n }\n return out.subarray(0, n);\n}\n\n/** Decrypts `\"<base64 iv>.<base64 ciphertext+tag>\"` with a base64 32-byte key; returns the UTF-8 plaintext. */\nexport async function decryptPayloadString(encrypted: string, decryptionKey: string): Promise<string> {\n const subtle = (globalThis as { crypto?: Crypto }).crypto?.subtle;\n if (!subtle) throw new Error(\"WebCrypto (crypto.subtle) is not available in this runtime; encrypted payloads need it (Node ≥ 19, browsers, edge runtimes, or a polyfill)\");\n const [ivText, dataText] = encrypted.split(\".\");\n if (!ivText || !dataText) throw new Error(\"malformed encrypted payload\");\n const key = base64ToBytes(decryptionKey);\n if (key.length !== 32) throw new Error(\"decryptionKey must be base64 of 32 bytes\");\n const cryptoKey = await subtle.importKey(\"raw\", key as BufferSource, { name: \"AES-GCM\" }, false, [\"decrypt\"]);\n const plain = await subtle.decrypt({ name: \"AES-GCM\", iv: base64ToBytes(ivText) as BufferSource }, cryptoKey, base64ToBytes(dataText) as BufferSource);\n return decodeUtf8(new Uint8Array(plain));\n}\n","/** Incremental `text/event-stream` parser for runtimes without `EventSource` (Node, Deno, Bun, edge). */\nexport function createSseParser(onEvent: (event: string, data: string) => void): { feed(chunk: string): void; end(): void } {\n let buffer = \"\";\n const dispatch = (raw: string) => {\n let event = \"message\";\n const data: string[] = [];\n for (const line of raw.split(\"\\n\")) {\n if (!line || line.startsWith(\":\")) continue; // comments (`: ping`)\n const i = line.indexOf(\":\");\n const field = i < 0 ? line : line.slice(0, i);\n const value = i < 0 ? \"\" : line.slice(i + 1).replace(/^ /, \"\");\n if (field === \"event\") event = value;\n else if (field === \"data\") data.push(value);\n }\n if (data.length) onEvent(event, data.join(\"\\n\"));\n };\n return {\n feed(chunk) {\n buffer += chunk.replace(/\\r\\n?/g, \"\\n\");\n let sep: number;\n while ((sep = buffer.indexOf(\"\\n\\n\")) >= 0) {\n dispatch(buffer.slice(0, sep));\n buffer = buffer.slice(sep + 2);\n }\n },\n end() {\n if (buffer.trim()) dispatch(buffer);\n buffer = \"\";\n },\n };\n}\n","/**\n * The feature-flag client (docs/feature-flags-spec.md §4): downloads the environment's payload (or\n * asks the server to evaluate), keeps it fresh, evaluates locally and reports usage and exposures.\n * Isomorphic: browsers, Node ≥ 18, Next.js server and edge, Deno, Bun and React Native. Uses the\n * global `fetch` (injectable) and touches no DOM or Node API at module scope.\n */\nimport { decryptPayloadString } from \"./crypto\";\nimport { readDevOverrides } from \"./dev-overrides\";\nimport { evaluateFlag, textOf } from \"./evaluate\";\nimport { makeEvaluator, notFound, type FlagDetails, type FlagValue, type FlagsEvaluator, type FlagsReader } from \"./evaluator\";\nimport { createSseParser } from \"./sse\";\nimport type { Attributes, EncryptedFlagsPayload, EvaluationResult, ExperimentAssignment, FlagEventsBatch, FlagsPayload, JsonValue, RemoteEvaluationResponse, StickyBucketStore } from \"./types\";\nimport { compact, isDev, isEncryptedPayload, isFlagsPayload, isRemoteResults, parseUrlOverrides, stableStringify, timer } from \"./util\";\n\nexport const FLAGS_SDK_NAME = \"@guidelytech/flags\";\nexport const FLAGS_SDK_VERSION = \"0.8.0\";\nexport const DEFAULT_FLAGS_API_URL = \"https://guidelytech.com\";\n\n/**\n * - `auto` — fetch at start, then every `pollIntervalSeconds`.\n * - `lazy` — fetch when a flag is read and the cache is older than `cacheTtlSeconds` (serverless, edge).\n * - `manual` — only `refresh()` fetches.\n * - `stream` — server-sent events push changes; polls while the stream is down.\n */\nexport type PollingMode = \"auto\" | \"lazy\" | \"manual\" | \"stream\";\n/** `localOnly`: never fetch; serve overrides, then the bootstrap. `localOverRemote`: overrides win. `remoteOverLocal`: overrides fill in flags missing remotely. */\nexport type OverrideBehaviour = \"localOnly\" | \"localOverRemote\" | \"remoteOverLocal\";\n\n/** Key/value storage for the last payload: `localStorage`-like or AsyncStorage-like (sync or async). */\nexport interface FlagsStorage {\n getItem(key: string): string | null | undefined | Promise<string | null | undefined>;\n setItem(key: string, value: string): void | Promise<void>;\n removeItem?(key: string): void | Promise<void>;\n}\n\nexport interface FlagsLogger {\n debug?(...args: unknown[]): void;\n warn?(...args: unknown[]): void;\n error?(...args: unknown[]): void;\n}\n\n/** What a bootstrap / `getPayload()` can hold. */\nexport type FlagsBootstrap = FlagsPayload | EncryptedFlagsPayload | RemoteEvaluationResponse;\n\nexport interface FlagsClientOptions {\n /** Environment public key `pk_…` (the `projectId` used for tracking) or, on servers only, a secret `gly_sk_…` key. */\n sdkKey?: string;\n /** Alias of `sdkKey`. */\n projectId?: string;\n /** Base URL of your Guidely deployment (default `https://guidelytech.com`). */\n apiUrl?: string;\n /** `development` | `staging` | `production`. Required with a `gly_sk_` key. */\n environment?: string;\n /** Default attributes, merged under per-call (`forUser`) attributes. */\n attributes?: Attributes;\n /**\n * Attributes supplied by a Guidely SDK integration (identity, page, device). App attributes\n * (`attributes`, `setAttributes`) are merged over them.\n */\n contextAttributes?: Attributes;\n pollingMode?: PollingMode;\n /** `auto` / `stream` fallback polling interval (default 60). */\n pollIntervalSeconds?: number;\n /** `lazy` cache lifetime (default 60). */\n cacheTtlSeconds?: number;\n /** Bootstrap: a payload (plain or encrypted) or remote-evaluation results, used immediately. */\n payload?: FlagsBootstrap | null;\n /** Bootstrap for remote evaluation: `{ version, flags }` or a map of results. */\n remoteResults?: RemoteEvaluationResponse | Record<string, EvaluationResult> | null;\n /** Base64 AES-256 key for environments that encrypt their payload. */\n decryptionKey?: string;\n /** Ask the server to evaluate (`POST /flags/evaluate`) instead of downloading rules. Needed when the environment hides rules. */\n remoteEval?: boolean;\n /** Debounce for re-evaluating remotely after attribute changes (default 50 ms). */\n remoteEvalDebounceMs?: number;\n /** Local flag values (tests, QA, kill switches) applied per `overrideBehaviour`. */\n overrides?: Record<string, JsonValue> | null;\n overrideBehaviour?: OverrideBehaviour;\n /**\n * Read dev overrides from outside the code (default `true`): `GUIDELY_FLAG_OVERRIDES`,\n * `GUIDELY_FLAG_OVERRIDES_FILE` and `GUIDELY_FLAG_OVERRIDE_BEHAVIOUR` environment variables, and\n * the `__GUIDELY_FLAG_OVERRIDES__` / `__GUIDELY_FLAG_OVERRIDE_BEHAVIOUR__` globals (browser tests).\n * They win over `overrides`, and their behaviour over `overrideBehaviour`.\n */\n devOverrides?: boolean;\n /** Keeps experiment assignments stable (`InMemoryStickyBucketStore`, `LocalStorageStickyBucketStore`). */\n stickyBucketService?: StickyBucketStore;\n /** Persists the last payload. Default: `localStorage` in browsers, none elsewhere. `false` disables. */\n storage?: FlagsStorage | null | false;\n /** Browser only: read `?guidely_flags=key:value,key2:\"text\"` from the URL as overrides (QA). */\n enableUrlOverrides?: boolean;\n /** Called once per (experiment, user, variation) when a user is bucketed into an experiment. */\n trackingCallback?: (experiment: ExperimentAssignment, result: EvaluationResult) => void;\n /** Send experiment exposures to Guidely (`POST /flags/events`). Default: `true` unless `trackingCallback` is set. */\n trackExposures?: boolean;\n /** Send aggregated evaluation counts to Guidely at most every 60 s (default `true`). */\n reportUsage?: boolean;\n onReady?: () => void;\n /** New payload (or remote results) applied. */\n onChange?: (data: FlagsPayload | RemoteEvaluationResponse) => void;\n onFlagEvaluated?: (result: EvaluationResult) => void;\n onError?: (error: Error) => void;\n /** Start without network activity (cached / bootstrapped data keeps serving). */\n offline?: boolean;\n fetch?: typeof fetch;\n /**\n * Defaults to `console`: `error` for failed requests (once per streak), `warn` for misuse and,\n * in development, reads of flags that do not exist. `null` silences the client.\n */\n logger?: FlagsLogger | null;\n /** Evaluation clock (tests). */\n now?: () => Date | number;\n /** Reported with usage (set by Guidely SDK integrations). */\n sdk?: { name: string; version: string };\n}\n\ninterface Resolved {\n mode: PollingMode;\n pollMs: number;\n ttlMs: number;\n debounceMs: number;\n behaviour: OverrideBehaviour;\n trackExposures: boolean;\n reportUsage: boolean;\n}\n\ntype Exposure = NonNullable<FlagEventsBatch[\"exposures\"]>[number];\ninterface HttpResult<T> {\n status: number;\n ok: boolean;\n data: T | null;\n etag: string | null;\n}\n\nconst SECRET_PREFIX = \"gly_sk_\";\nconst MAX_RESULT_CACHE = 20;\nconst MAX_QUEUE = 1000;\nconst USAGE_FLUSH_MS = 60_000;\nconst EXPOSURE_FLUSH_MS = 5_000;\nconst MAX_BACKOFF_MS = 5 * 60_000;\n\nconst isBrowser = () => typeof window !== \"undefined\" && typeof document !== \"undefined\";\n\nfunction defaultStorage(): FlagsStorage | null {\n try {\n if (!isBrowser() || typeof localStorage === \"undefined\") return null;\n const probe = \"__guidely_flags_probe__\";\n localStorage.setItem(probe, \"1\");\n localStorage.removeItem(probe);\n return localStorage;\n } catch {\n return null;\n }\n}\n\nfunction backoff(failures: number, capMs: number): number {\n const base = Math.min(capMs, 1000 * 2 ** Math.max(0, failures - 1));\n return Math.round(base * (0.8 + Math.random() * 0.4));\n}\n\n/**\n * Feature flags for one environment. Create with {@link createFlagsClient}; one per app (browser)\n * or per process (server — use {@link FlagsClient.forUser} per request).\n */\nexport class FlagsClient implements FlagsReader {\n readonly sdkKey: string;\n readonly apiUrl: string;\n readonly environment: string | undefined;\n private readonly o: FlagsClientOptions;\n private readonly cfg: Resolved;\n private readonly secret: boolean;\n private readonly log: FlagsLogger;\n private readonly storage: FlagsStorage | null;\n private readonly storageKey: string;\n private readonly getters: FlagsEvaluator;\n private payload: FlagsPayload | null = null;\n /** The payload as received (possibly encrypted): what `getPayload()` hands to browsers. */\n private raw: FlagsPayload | EncryptedFlagsPayload | null = null;\n private etag: string | null = null;\n private remote: boolean;\n private results: RemoteEvaluationResponse | null = null;\n private resultsKey: string | null = null;\n private readonly resultCache = new Map<string, RemoteEvaluationResponse>();\n private context: Attributes;\n private attributes: Attributes;\n private overrides: Record<string, JsonValue>;\n private readonly devOverrides: Record<string, JsonValue>;\n private readonly urlOverrides: Record<string, JsonValue>;\n private offline: boolean;\n private destroyed = false;\n private ready_ = false;\n private resolveReady!: () => void;\n private readonly readyPromise: Promise<void>;\n private readonly listeners = new Set<() => void>();\n private inflight: Promise<boolean> | null = null;\n private lastFetchAt = 0;\n private failures = 0;\n private retryAt = 0;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private remoteTimer: ReturnType<typeof setTimeout> | null = null;\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private flushDue = Infinity;\n private stream: { close(): void } | null = null;\n private streamConnected = false;\n private streamFailures = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly exposed = new Set<string>();\n private readonly usage = new Map<string, { flag: string; variationId?: string; value: string; count: number }>();\n private exposures: Exposure[] = [];\n private readonly warned = new Set<string>();\n private cleanups: Array<() => void> = [];\n private lastError = \"\";\n\n constructor(options: FlagsClientOptions) {\n const o = options ?? {};\n this.o = o;\n const key = o.sdkKey ?? o.projectId ?? \"\";\n if (!key) throw new Error(\"[Guidely flags] `sdkKey` (your environment public key pk_…, or a gly_sk_… secret key on servers) is required.\");\n this.sdkKey = key;\n this.secret = key.startsWith(SECRET_PREFIX);\n this.environment = o.environment;\n this.apiUrl = (o.apiUrl || DEFAULT_FLAGS_API_URL).replace(/\\/+$/, \"\");\n this.log = o.logger === null ? {} : (o.logger ?? { warn: (...a) => console.warn(\"[Guidely flags]\", ...a), error: (...a) => console.error(\"[Guidely flags]\", ...a) });\n if (this.secret && !o.environment) throw new Error(\"[Guidely flags] `environment` (development | staging | production) is required with a gly_sk_ key.\");\n if (this.secret && isBrowser()) this.log.warn?.(\"A gly_sk_ secret key is being used in a browser. Secret keys belong on servers; use the environment's public pk_ key here.\");\n const dev = o.devOverrides === false ? null : readDevOverrides();\n this.cfg = {\n mode: o.pollingMode ?? \"auto\",\n pollMs: Math.max(1, o.pollIntervalSeconds ?? 60) * 1000,\n ttlMs: Math.max(0, o.cacheTtlSeconds ?? 60) * 1000,\n debounceMs: Math.max(0, o.remoteEvalDebounceMs ?? 50),\n behaviour: dev?.behaviour ?? o.overrideBehaviour ?? \"localOverRemote\",\n trackExposures: o.trackExposures ?? !o.trackingCallback,\n reportUsage: o.reportUsage ?? true,\n };\n this.remote = !!o.remoteEval;\n this.offline = !!o.offline;\n this.context = compact(o.contextAttributes ?? {});\n this.attributes = compact(o.attributes ?? {});\n this.overrides = { ...(o.overrides ?? {}) };\n this.devOverrides = dev?.values ?? {};\n this.urlOverrides = o.enableUrlOverrides && isBrowser() ? parseUrlOverrides(location.search) : {};\n this.storage = o.storage === false || o.storage === null ? null : (o.storage ?? defaultStorage());\n this.storageKey = `guidely:flags:${key}${o.environment && this.secret ? `:${o.environment}` : \"\"}`;\n this.readyPromise = new Promise<void>((r) => (this.resolveReady = r));\n if (dev) {\n dev.errors.forEach((e) => this.reportError(new Error(e)));\n const n = Object.keys(dev.values).length;\n if (dev.sources.length) this.log.warn?.(`Dev overrides from ${dev.sources.join(\", \")}: ${n} flag${n === 1 ? \"\" : \"s\"}, behaviour ${this.cfg.behaviour}.`);\n }\n this.getters = makeEvaluator(\n (k) => this.resolve(k, this.getAttributes(), true),\n () => this.all(this.getAttributes()),\n () => this.getAttributes(),\n );\n\n // Synchronous bootstrap: plain payloads and remote results serve from the first render.\n const boot = o.payload;\n if (isRemoteResults(boot)) this.setResults(boot, stableStringify(this.getAttributes()), false);\n else if (isFlagsPayload(boot) && !isEncryptedPayload(boot)) this.setPayload(boot, boot, null, false);\n if (o.remoteResults) {\n const r = o.remoteResults as RemoteEvaluationResponse;\n this.setResults(isRemoteResults(r) && typeof r.version === \"number\" ? r : { version: 0, flags: o.remoteResults as Record<string, EvaluationResult> }, stableStringify(this.getAttributes()), false);\n }\n if (this.results) this.remote = true;\n this.wireBrowser();\n // Bootstrapped (plain payload or results): ready now, so server and browser render the same.\n if (this.payload || this.results) this.settle();\n void this.start();\n }\n\n // ---------------------------------------------------------------------------\n // Getters (never throw)\n // ---------------------------------------------------------------------------\n\n /** Boolean flag value; `fallback` (default `false`) when missing or not a boolean. */\n isEnabled(key: string, fallback = false): boolean {\n return this.getters.isEnabled(key, fallback);\n }\n /** Flag value; `fallback` when missing, null or of a different JSON type than `fallback`. */\n getValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T> {\n return this.getters.getValue(key, fallback);\n }\n getString(key: string, fallback: string): string {\n return this.getters.getString(key, fallback);\n }\n getNumber(key: string, fallback: number): number {\n return this.getters.getNumber(key, fallback);\n }\n getBoolean(key: string, fallback: boolean): boolean {\n return this.getters.getBoolean(key, fallback);\n }\n getJson<T = JsonValue>(key: string, fallback: T): T {\n return this.getters.getJson(key, fallback);\n }\n /** Value plus reason, rule, variation and experiment assignment. */\n getDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>> {\n return this.getters.getDetails(key, fallback);\n }\n /** Every flag's value for the current attributes (records no exposures or usage). */\n getAll(): Record<string, JsonValue> {\n return this.getters.getAll();\n }\n\n /**\n * Getters bound to other attributes (merged over the client's), without changing the client:\n * the per-request pattern on servers. With remote evaluation they serve results already cached\n * for these attributes, else fallbacks.\n */\n forUser(attributes: Attributes): FlagsEvaluator {\n const attrs = { ...this.getAttributes(), ...compact(attributes ?? {}) };\n if (this.remote) this.warnOnce(\"forUser:remote\", \"forUser() with remote evaluation only serves results cached for those attributes.\");\n return makeEvaluator(\n (k) => this.resolve(k, attrs, true),\n () => this.all(attrs),\n () => ({ ...attrs }),\n );\n }\n\n // ---------------------------------------------------------------------------\n // Attributes and overrides\n // ---------------------------------------------------------------------------\n\n /** Replaces the app attributes. */\n setAttributes(attributes: Attributes): void {\n this.changeAttributes(() => (this.attributes = compact(attributes ?? {})));\n }\n /** Merges into the app attributes. */\n updateAttributes(partial: Attributes): void {\n this.changeAttributes(() => (this.attributes = compact({ ...this.attributes, ...partial })));\n }\n /** Replaces the integration-supplied attributes (see `contextAttributes`). */\n setContextAttributes(attributes: Attributes): void {\n this.changeAttributes(() => (this.context = compact(attributes ?? {})));\n }\n /** Context attributes with app attributes merged over them. */\n getAttributes(): Attributes {\n return { ...this.context, ...this.attributes };\n }\n\n /** Replaces the overrides (`null` clears them). Dev and URL overrides stay. */\n setOverrides(overrides: Record<string, JsonValue> | null): void {\n this.overrides = { ...(overrides ?? {}) };\n this.notify();\n }\n\n private changeAttributes(apply: () => void): void {\n const before = stableStringify(this.getAttributes());\n apply();\n const after = stableStringify(this.getAttributes());\n if (before === after) return;\n if (this.remote) {\n const cached = this.resultCache.get(after);\n if (cached) {\n this.results = cached;\n this.resultsKey = after;\n }\n this.scheduleRemote();\n }\n this.notify();\n }\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n /** Resolves once the first load settled (bootstrap, network success or failure). Never rejects. */\n ready(): Promise<void> {\n return this.readyPromise;\n }\n isReady(): boolean {\n return this.ready_;\n }\n\n /** Calls `listener` whenever values may have changed (payload, attributes, overrides, readiness). */\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => void this.listeners.delete(listener);\n };\n\n /** `true` stops all network activity; cached data keeps serving. `false` resumes and refreshes. */\n setOffline(offline: boolean): void {\n if (this.offline === offline) return;\n this.offline = offline;\n if (offline) return this.stopNetwork();\n void this.refresh().then(() => this.schedule());\n }\n\n /** Fetches now (payload, or remote evaluation). Resolves when done; never rejects. */\n async refresh(): Promise<void> {\n await this.fetchNow();\n }\n\n /**\n * The current data for server-side rendering: pass it as `payload` to the browser client (or\n * framework provider) to render the same values without a flash. Encrypted payloads stay\n * encrypted; with remote evaluation it returns the results. `null` before anything loaded.\n */\n getPayload(): FlagsBootstrap | null {\n if (this.remote) return this.results;\n return this.raw;\n }\n\n /** Sends pending usage counts and exposures now. */\n async flush(): Promise<void> {\n const batch = this.takeBatch();\n if (!batch) return;\n try {\n const res = await this.request<unknown>(\"POST\", \"/flags/events\", { body: batch, keepalive: true });\n if (!res.ok && (res.status >= 500 || res.status === 429)) this.requeue(batch);\n } catch {\n this.requeue(batch);\n }\n }\n\n /** Stops timers and streams and sends pending usage. The client keeps serving cached values. */\n destroy(): void {\n if (this.destroyed) return;\n this.flushOnExit();\n this.destroyed = true;\n this.stopNetwork();\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.cleanups.splice(0).forEach((fn) => fn());\n this.listeners.clear();\n this.settle();\n }\n\n // ---------------------------------------------------------------------------\n // Evaluation\n // ---------------------------------------------------------------------------\n\n private overrideFor(key: string): JsonValue | undefined {\n if (Object.prototype.hasOwnProperty.call(this.urlOverrides, key)) return this.urlOverrides[key];\n if (Object.prototype.hasOwnProperty.call(this.devOverrides, key)) return this.devOverrides[key];\n if (Object.prototype.hasOwnProperty.call(this.overrides, key)) return this.overrides[key];\n return undefined;\n }\n\n private resolve(key: string, attrs: Attributes, track: boolean): EvaluationResult {\n let res: EvaluationResult;\n try {\n if (this.cfg.mode === \"lazy\") this.lazyRefresh();\n const ov = this.overrideFor(key);\n const forced = Object.prototype.hasOwnProperty.call(this.urlOverrides, key);\n const override: EvaluationResult | null = ov === undefined ? null : { key, value: ov, reason: \"OVERRIDE\", inExperiment: false };\n if (override && (forced || this.cfg.behaviour !== \"remoteOverLocal\")) res = override;\n else {\n res = this.evaluateRemoteOrLocal(key, attrs);\n if (override && res.reason === \"FLAG_NOT_FOUND\") res = override;\n }\n } catch (err) {\n res = { key, value: null, reason: \"ERROR\", inExperiment: false, errorMessage: err instanceof Error ? err.message : String(err) };\n }\n if (track) this.record(res, attrs);\n return res;\n }\n\n private evaluateRemoteOrLocal(key: string, attrs: Attributes): EvaluationResult {\n if (this.remote) {\n const k = stableStringify(attrs);\n const results = k === this.resultsKey || k === stableStringify(this.getAttributes()) ? this.results : this.resultCache.get(k);\n return results?.flags[key] ?? notFound(key);\n }\n if (!this.payload) return notFound(key);\n return evaluateFlag(this.payload, key, attrs, { now: this.now(), stickyBuckets: this.o.stickyBucketService });\n }\n\n private all(attrs: Attributes): Record<string, JsonValue> {\n const keys = new Set<string>([...Object.keys(this.overrides), ...Object.keys(this.devOverrides), ...Object.keys(this.urlOverrides)]);\n if (this.remote) for (const k of Object.keys(this.results?.flags ?? {})) keys.add(k);\n else for (const k of Object.keys(this.payload?.flags ?? {})) keys.add(k);\n const out: Record<string, JsonValue> = {};\n for (const k of keys) {\n const v = this.resolve(k, attrs, false).value;\n if (v !== null) out[k] = v;\n }\n return out;\n }\n\n private now(): number {\n const n = this.o.now?.();\n return n === undefined ? Date.now() : typeof n === \"number\" ? n : n.getTime();\n }\n\n private record(res: EvaluationResult, attrs: Attributes): void {\n try {\n this.o.onFlagEvaluated?.(res);\n } catch (err) {\n this.reportError(err);\n }\n if (res.reason === \"FLAG_NOT_FOUND\") {\n if (this.ready_ && (this.payload || this.results) && isDev()) this.warnOnce(`missing:${res.key}`, `Flag \"${res.key}\" does not exist in this environment; serving the fallback.`);\n return;\n }\n if (res.reason === \"OVERRIDE\" || res.reason === \"ERROR\") return;\n if (this.cfg.reportUsage) {\n const value = (JSON.stringify(res.value) ?? \"null\").slice(0, 200);\n const k = `${res.key}\\u0000${res.variationId ?? \"\"}\\u0000${res.variationId ? \"\" : value}`;\n const u = this.usage.get(k);\n if (u) u.count++;\n else if (this.usage.size < MAX_QUEUE) this.usage.set(k, { flag: res.key, ...(res.variationId ? { variationId: res.variationId } : {}), value, count: 1 });\n this.scheduleFlush(USAGE_FLUSH_MS);\n }\n const exp = res.inExperiment ? res.experiment : undefined;\n if (!exp) return;\n const dedupe = `${exp.key}\\u0000${exp.hashValue}\\u0000${exp.variationId}`;\n if (this.exposed.has(dedupe)) return;\n if (this.exposed.size >= 50_000) this.exposed.clear(); // long-running servers evaluating many users\n this.exposed.add(dedupe);\n try {\n this.o.trackingCallback?.(exp, res);\n } catch (err) {\n this.reportError(err);\n }\n if (!this.cfg.trackExposures) return;\n const userId = textOf(attrs.id);\n const anonymousId = textOf(attrs.anonymousId);\n if (this.exposures.length < MAX_QUEUE) {\n this.exposures.push({ experiment: exp.key, flag: res.key, variationId: exp.variationId, hashAttribute: exp.hashAttribute, hashValue: exp.hashValue, ...(userId ? { userId } : {}), ...(anonymousId ? { anonymousId } : {}), timestamp: new Date(this.now()).toISOString() });\n }\n this.scheduleFlush(EXPOSURE_FLUSH_MS);\n }\n\n private warnOnce(id: string, message: string): void {\n if (this.warned.has(id)) return;\n this.warned.add(id);\n this.log.warn?.(message);\n }\n\n // ---------------------------------------------------------------------------\n // Data\n // ---------------------------------------------------------------------------\n\n private setPayload(payload: FlagsPayload, raw: FlagsPayload | EncryptedFlagsPayload, etag: string | null, emit: boolean): void {\n const same = this.payload && this.payload.version === payload.version && this.payload.generatedAt === payload.generatedAt && this.payload.environment === payload.environment;\n this.payload = payload;\n this.raw = raw;\n this.etag = etag;\n if (!emit || same) return;\n try {\n this.o.onChange?.(payload);\n } catch (err) {\n this.reportError(err);\n }\n this.notify();\n }\n\n private setResults(results: RemoteEvaluationResponse, key: string, emit: boolean): void {\n this.resultCache.delete(key);\n this.resultCache.set(key, results);\n while (this.resultCache.size > MAX_RESULT_CACHE) this.resultCache.delete(this.resultCache.keys().next().value!);\n if (key !== stableStringify(this.getAttributes())) return; // attributes moved on meanwhile\n const same = this.results && stableStringify(this.results) === stableStringify(results);\n this.results = results;\n this.resultsKey = key;\n if (!emit || same) return;\n try {\n this.o.onChange?.(results);\n } catch (err) {\n this.reportError(err);\n }\n this.notify();\n }\n\n /** Decrypts when needed and applies. Returns false (keeping the previous payload) when unusable. */\n private async applyRaw(raw: unknown, etag: string | null): Promise<boolean> {\n if (isEncryptedPayload(raw)) {\n if (!this.o.decryptionKey) {\n this.reportError(new Error(\"The payload is encrypted but no `decryptionKey` was given; keeping the previous flags.\"));\n return false;\n }\n try {\n const plain = JSON.parse(await decryptPayloadString(raw.encryptedPayload, this.o.decryptionKey)) as Partial<FlagsPayload>;\n const envelope: Record<string, unknown> = { ...raw };\n delete envelope.encryptedPayload;\n const merged = { ...envelope, ...plain } as FlagsPayload;\n if (!isFlagsPayload(merged)) throw new Error(\"decrypted payload has no flags\");\n this.setPayload(merged, raw, etag, true);\n return true;\n } catch (err) {\n this.reportError(new Error(`Could not decrypt the flags payload (${err instanceof Error ? err.message : String(err)}); keeping the previous flags.`));\n return false;\n }\n }\n if (!isFlagsPayload(raw)) {\n this.reportError(new Error(\"Unexpected flags payload; keeping the previous flags.\"));\n return false;\n }\n this.setPayload(raw, raw, etag, true);\n return true;\n }\n\n private async loadStored(): Promise<void> {\n if (!this.storage) return;\n try {\n const text = await this.storage.getItem(this.storageKey);\n if (!text || this.payload) return;\n const saved = JSON.parse(text) as { payload?: unknown; etag?: string | null };\n if (saved?.payload && !this.payload) await this.applyRaw(saved.payload, saved.etag ?? null);\n } catch (err) {\n this.log.debug?.(\"could not read cached flags\", err);\n }\n }\n\n private persist(): void {\n if (!this.storage || !this.raw) return;\n try {\n void Promise.resolve(this.storage.setItem(this.storageKey, JSON.stringify({ payload: this.raw, etag: this.etag, savedAt: new Date().toISOString() }))).catch(() => {});\n } catch {\n /* quota / private mode */\n }\n }\n\n // ---------------------------------------------------------------------------\n // Network\n // ---------------------------------------------------------------------------\n\n private url(path: string): string {\n const q = this.secret ? `environment=${encodeURIComponent(this.environment ?? \"\")}` : `key=${encodeURIComponent(this.sdkKey)}`;\n return `${this.apiUrl}/api/v1/sdk${path}?${q}`;\n }\n\n private headers(extra: Record<string, string> = {}): Record<string, string> {\n return { ...extra, ...(this.secret ? { Authorization: `Bearer ${this.sdkKey}` } : { \"X-Guidely-Key\": this.sdkKey }) };\n }\n\n private get fetcher(): typeof fetch | null {\n if (this.o.fetch) return this.o.fetch;\n return typeof fetch === \"function\" ? fetch.bind(globalThis) : null;\n }\n\n private async request<T>(method: \"GET\" | \"POST\", path: string, opts: { body?: unknown; etag?: string | null; keepalive?: boolean; timeoutMs?: number } = {}): Promise<HttpResult<T>> {\n const f = this.fetcher;\n if (!f) throw new Error(\"fetch is not available; pass `fetch`\");\n const headers = this.headers({ Accept: \"application/json\" });\n if (opts.body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n if (opts.etag) headers[\"If-None-Match\"] = opts.etag;\n const ac = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const t = ac ? timer(() => ac.abort(), opts.timeoutMs ?? 10_000) : null;\n try {\n // `credentials` / `keepalive` only in browsers: some edge runtimes reject unknown RequestInit fields.\n const browserOnly: RequestInit = isBrowser() ? { credentials: \"omit\", keepalive: opts.keepalive } : {};\n const res = await f(this.url(path), { method, headers, body: opts.body === undefined ? undefined : JSON.stringify(opts.body), signal: ac?.signal, ...browserOnly });\n let data: T | null = null;\n if (res.status !== 304 && res.status !== 204) {\n try {\n data = (await res.json()) as T;\n } catch {\n data = null;\n }\n }\n return { status: res.status, ok: res.ok, data, etag: res.headers?.get?.(\"etag\") ?? null };\n } finally {\n if (t) clearTimeout(t);\n }\n }\n\n private fetchNow(): Promise<boolean> {\n if (this.destroyed || this.offline || this.cfg.behaviour === \"localOnly\") return Promise.resolve(false);\n if (this.inflight) return this.inflight;\n this.inflight = (this.remote ? this.fetchRemote() : this.fetchPayload())\n .catch((err) => {\n this.reportError(err);\n return false;\n })\n .then((ok) => {\n this.inflight = null;\n this.lastFetchAt = Date.now();\n this.failures = ok ? 0 : this.failures + 1;\n this.retryAt = ok ? 0 : Date.now() + backoff(this.failures, Math.max(this.cfg.pollMs, MAX_BACKOFF_MS));\n if (ok) this.lastError = \"\";\n return ok;\n });\n return this.inflight;\n }\n\n private async fetchPayload(): Promise<boolean> {\n let res: HttpResult<unknown>;\n try {\n res = await this.request<unknown>(\"GET\", \"/flags\", { etag: this.payload ? this.etag : null });\n } catch (err) {\n this.reportError(new Error(`Flags request failed: ${err instanceof Error ? err.message : String(err)}`));\n return false;\n }\n if (res.status === 304) return true;\n const code = (res.data as { error?: { code?: string } } | null)?.error?.code;\n if (res.status === 403 && code === \"remote_evaluation_required\" && !this.secret) {\n // The environment hides its rules from public keys: evaluate on the server instead.\n this.log.warn?.(\"This environment requires remote evaluation; switching to `remoteEval: true`.\");\n this.remote = true;\n return this.fetchRemote();\n }\n if (!res.ok || res.data == null) {\n this.reportError(new Error(`Flags request failed (${res.status}${code ? ` ${code}` : \"\"})${this.payload ? \"; serving the last good flags\" : \"\"}`));\n return false;\n }\n const applied = await this.applyRaw(res.data, res.etag);\n if (applied) this.persist();\n return applied;\n }\n\n private async fetchRemote(): Promise<boolean> {\n const attrs = this.getAttributes();\n const key = stableStringify(attrs);\n let res: HttpResult<RemoteEvaluationResponse>;\n try {\n res = await this.request<RemoteEvaluationResponse>(\"POST\", \"/flags/evaluate\", { body: { attributes: attrs } });\n } catch (err) {\n this.reportError(new Error(`Remote evaluation failed: ${err instanceof Error ? err.message : String(err)}`));\n return false;\n }\n if (!res.ok || !res.data || !isRemoteResults(res.data)) {\n this.reportError(new Error(`Remote evaluation failed (${res.status})`));\n return false;\n }\n this.setResults(res.data, key, true);\n return true;\n }\n\n private scheduleRemote(): void {\n if (this.destroyed || this.offline || this.cfg.behaviour === \"localOnly\") return;\n if (this.remoteTimer) clearTimeout(this.remoteTimer);\n this.remoteTimer = timer(() => {\n this.remoteTimer = null;\n void this.fetchRemote().then((ok) => {\n if (!ok) this.failures++;\n });\n }, this.cfg.debounceMs);\n }\n\n private lazyRefresh(): void {\n if (this.inflight || this.offline || this.destroyed || this.cfg.behaviour === \"localOnly\") return;\n const now = Date.now();\n if (now - this.lastFetchAt < this.cfg.ttlMs || now < this.retryAt) return;\n this.lastFetchAt = now; // one fetch per expiry, however many reads\n void this.fetchNow();\n }\n\n private async start(): Promise<void> {\n const boot = this.o.payload;\n if (isEncryptedPayload(boot)) await this.applyRaw(boot, null);\n // localOnly is hermetic: a payload cached by an earlier run must not leak into tests.\n if (!this.payload && !this.results && !this.remote && this.cfg.behaviour !== \"localOnly\") await this.loadStored();\n if (this.destroyed) return;\n const bootstrapped = !!(boot || this.o.remoteResults) && !!(this.payload || this.results);\n if (this.cfg.behaviour === \"localOnly\" || this.offline || this.cfg.mode === \"manual\") return this.settle();\n if (bootstrapped) {\n // Serve the bootstrap from the first render, then revalidate straight away: a server-rendered\n // payload can be a poll interval old, and a kill switch must not wait another one.\n this.settle();\n await this.fetchNow();\n return this.schedule();\n }\n await this.fetchNow();\n this.settle();\n this.schedule();\n }\n\n private settle(): void {\n if (this.ready_) return;\n this.ready_ = true;\n this.resolveReady();\n try {\n this.o.onReady?.();\n } catch (err) {\n this.reportError(err);\n }\n this.notify();\n }\n\n /** Arms the poll timer / stream for the current mode. */\n private schedule(): void {\n if (this.destroyed || this.offline || this.cfg.behaviour === \"localOnly\") return;\n if (this.cfg.mode === \"auto\") this.schedulePoll();\n else if (this.cfg.mode === \"stream\") {\n if (!this.stream) this.openStream();\n if (!this.streamConnected) this.schedulePoll();\n }\n }\n\n private schedulePoll(): void {\n if (this.pollTimer) clearTimeout(this.pollTimer);\n const delay = this.failures ? Math.max(0, this.retryAt - Date.now()) : this.cfg.pollMs;\n this.pollTimer = timer(() => {\n this.pollTimer = null;\n if (this.cfg.mode === \"stream\" && this.streamConnected) return;\n if (isBrowser() && document.visibilityState === \"hidden\") return this.schedulePoll(); // resumes on visibilitychange\n void this.fetchNow().then(() => this.schedule());\n }, delay);\n }\n\n private stopNetwork(): void {\n for (const t of [this.pollTimer, this.remoteTimer, this.reconnectTimer]) if (t) clearTimeout(t);\n this.pollTimer = this.remoteTimer = this.reconnectTimer = null;\n this.stream?.close();\n this.stream = null;\n this.streamConnected = false;\n }\n\n // ---------------------------------------------------------------------------\n // Streaming\n // ---------------------------------------------------------------------------\n\n private onStreamEvent(event: string, data: string): void {\n if (event !== \"flags\") return;\n let version: number | undefined;\n try {\n version = (JSON.parse(data) as { version?: number }).version;\n } catch {\n return;\n }\n const current = this.remote ? (this.results?.version ?? -1) : (this.payload?.version ?? -1);\n if (typeof version === \"number\" && version > current) void this.fetchNow();\n }\n\n private openStream(): void {\n if (this.destroyed || this.offline || this.stream) return;\n const url = this.url(\"/flags/stream\");\n const ES = (globalThis as { EventSource?: typeof EventSource }).EventSource;\n const opened = () => {\n this.streamConnected = true;\n this.streamFailures = 0;\n if (this.pollTimer) clearTimeout(this.pollTimer);\n this.pollTimer = null;\n };\n if (ES && !this.secret) {\n // EventSource cannot send headers: the public key travels in the query string.\n const es = new ES(url);\n es.addEventListener(\"flags\", (e) => this.onStreamEvent(\"flags\", (e as MessageEvent).data as string));\n es.onopen = opened;\n es.onerror = () => {\n es.close();\n if (this.stream === handle) this.streamDown();\n };\n const handle = { close: () => es.close() };\n this.stream = handle;\n return;\n }\n const f = this.fetcher;\n if (!f || typeof TextDecoder === \"undefined\") return this.streamDown(true);\n const ac = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const handle = { close: () => ac?.abort() };\n this.stream = handle;\n void (async () => {\n try {\n const res = await f(url, { headers: this.headers({ Accept: \"text/event-stream\" }), signal: ac?.signal, ...(isBrowser() ? { credentials: \"omit\" as const } : {}) });\n const reader = res.ok ? (res.body as ReadableStream<Uint8Array> | null)?.getReader?.() : undefined;\n if (!reader) {\n if (this.stream === handle) this.streamDown(res.ok); // no streaming body (React Native): poll instead\n return;\n }\n opened();\n const parser = createSseParser((event, data) => this.onStreamEvent(event, data));\n const decoder = new TextDecoder();\n for (;;) {\n const { done, value } = await reader.read();\n if (value) parser.feed(decoder.decode(value, { stream: true }));\n if (done) break;\n }\n parser.end();\n } catch {\n /* aborted or dropped */\n }\n if (this.stream === handle) this.streamDown();\n })();\n }\n\n /** The stream dropped: poll meanwhile and reconnect with backoff (`permanent`: never retry). */\n private streamDown(permanent = false): void {\n this.stream = null;\n this.streamConnected = false;\n if (this.destroyed || this.offline) return;\n this.schedulePoll();\n if (permanent) return;\n this.streamFailures++;\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n this.reconnectTimer = timer(() => {\n this.reconnectTimer = null;\n this.openStream();\n void this.fetchNow(); // catch up on changes missed while disconnected\n }, backoff(this.streamFailures, MAX_BACKOFF_MS));\n }\n\n // ---------------------------------------------------------------------------\n // Usage + exposures\n // ---------------------------------------------------------------------------\n\n private scheduleFlush(ms: number): void {\n if (this.destroyed) return;\n const due = Date.now() + ms;\n if (this.flushTimer && due >= this.flushDue) return;\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushDue = due;\n this.flushTimer = timer(() => {\n this.flushTimer = null;\n this.flushDue = Infinity;\n if (!this.offline) void this.flush();\n else if (this.usage.size || this.exposures.length) this.scheduleFlush(USAGE_FLUSH_MS);\n }, ms);\n }\n\n private takeBatch(): FlagEventsBatch | null {\n if (!this.usage.size && !this.exposures.length) return null;\n if (this.cfg.behaviour === \"localOnly\") {\n // localOnly makes no requests at all: nothing a test evaluates reaches Guidely.\n this.usage.clear();\n this.exposures = [];\n return null;\n }\n const batch: FlagEventsBatch = { sdk: this.o.sdk ?? { name: FLAGS_SDK_NAME, version: FLAGS_SDK_VERSION } };\n if (this.usage.size) batch.usage = Array.from(this.usage.values());\n if (this.exposures.length) batch.exposures = this.exposures;\n this.usage.clear();\n this.exposures = [];\n return batch;\n }\n\n private requeue(batch: FlagEventsBatch): void {\n for (const u of batch.usage ?? []) {\n const k = `${u.flag}\\u0000${u.variationId ?? \"\"}\\u0000${u.variationId ? \"\" : (u.value ?? \"\")}`;\n const cur = this.usage.get(k);\n if (cur) cur.count += u.count;\n else if (this.usage.size < MAX_QUEUE) this.usage.set(k, { flag: u.flag, variationId: u.variationId, value: u.value ?? \"\", count: u.count });\n }\n this.exposures = [...(batch.exposures ?? []), ...this.exposures].slice(0, MAX_QUEUE);\n this.scheduleFlush(USAGE_FLUSH_MS);\n }\n\n /** Page hide / destroy: `sendBeacon` for public keys, else a `keepalive` fetch. */\n private flushOnExit(): void {\n if (this.offline) return;\n const batch = this.takeBatch();\n if (!batch) return;\n try {\n if (!this.secret && typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\" && typeof Blob !== \"undefined\") {\n // text/plain needs no CORS preflight (browsers drop those during unload).\n if (navigator.sendBeacon(this.url(\"/flags/events\"), new Blob([JSON.stringify(batch)], { type: \"text/plain;charset=UTF-8\" }))) return;\n }\n } catch {\n /* fall through */\n }\n void this.request(\"POST\", \"/flags/events\", { body: batch, keepalive: true }).catch(() => {});\n }\n\n private wireBrowser(): void {\n if (!isBrowser()) return;\n const onHide = () => this.flushOnExit();\n const onVisibility = () => {\n if (document.visibilityState === \"hidden\") return this.flushOnExit();\n // Back in view: catch up if a poll was skipped while hidden.\n if ((this.cfg.mode === \"auto\" || (this.cfg.mode === \"stream\" && !this.streamConnected)) && Date.now() - this.lastFetchAt > this.cfg.pollMs) void this.fetchNow().then(() => this.schedule());\n };\n window.addEventListener(\"pagehide\", onHide);\n document.addEventListener(\"visibilitychange\", onVisibility);\n this.cleanups.push(() => {\n window.removeEventListener(\"pagehide\", onHide);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n });\n }\n\n // ---------------------------------------------------------------------------\n\n private notify(): void {\n for (const l of Array.from(this.listeners)) {\n try {\n l();\n } catch (err) {\n this.reportError(err);\n }\n }\n }\n\n private reportError(err: unknown): void {\n const error = err instanceof Error ? err : new Error(String(err));\n // Repeats (a poll failing every minute) are logged once.\n if (error.message !== this.lastError) this.log.error?.(error.message);\n this.lastError = error.message;\n try {\n this.o.onError?.(error);\n } catch {\n /* ignore */\n }\n }\n}\n\n/** Creates a flags client and starts loading (per `pollingMode`). */\nexport function createFlagsClient(options: FlagsClientOptions): FlagsClient {\n return new FlagsClient(options);\n}\n"],"mappings":"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","names":["text","handle"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -209,6 +209,167 @@ async function decryptPayloadString(encrypted, decryptionKey) {
|
|
|
209
209
|
return decodeUtf8(new Uint8Array(plain));
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// src/util.ts
|
|
213
|
+
function stableStringify(value) {
|
|
214
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
215
|
+
if (Array.isArray(value)) return `[${value.map((v) => v === void 0 ? "null" : stableStringify(v)).join(",")}]`;
|
|
216
|
+
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
217
|
+
const obj = value;
|
|
218
|
+
return `{${Object.keys(obj).filter((k) => obj[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
219
|
+
}
|
|
220
|
+
function isEncryptedPayload(x) {
|
|
221
|
+
return !!x && typeof x === "object" && typeof x.encryptedPayload === "string";
|
|
222
|
+
}
|
|
223
|
+
function isRemoteResults(x) {
|
|
224
|
+
return !!x && typeof x === "object" && !("schemaVersion" in x) && !!x.flags && typeof x.flags === "object";
|
|
225
|
+
}
|
|
226
|
+
function isFlagsPayload(x) {
|
|
227
|
+
return !!x && typeof x === "object" && "schemaVersion" in x && !!x.flags && typeof x.flags === "object";
|
|
228
|
+
}
|
|
229
|
+
function kindOf(v) {
|
|
230
|
+
if (v === null || v === void 0) return "null";
|
|
231
|
+
if (Array.isArray(v)) return "array";
|
|
232
|
+
return typeof v;
|
|
233
|
+
}
|
|
234
|
+
function compact(attrs) {
|
|
235
|
+
const out = {};
|
|
236
|
+
for (const [k, v] of Object.entries(attrs)) if (v !== void 0) out[k] = v;
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
239
|
+
function parseUrlOverrides(search, param = "guidely_flags") {
|
|
240
|
+
let raw = null;
|
|
241
|
+
try {
|
|
242
|
+
raw = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search).get(param);
|
|
243
|
+
} catch {
|
|
244
|
+
return {};
|
|
245
|
+
}
|
|
246
|
+
return raw && parseOverrides(raw) || {};
|
|
247
|
+
}
|
|
248
|
+
function parseOverrides(text) {
|
|
249
|
+
const raw = text.trim();
|
|
250
|
+
if (raw.startsWith("{")) {
|
|
251
|
+
try {
|
|
252
|
+
const obj = JSON.parse(raw);
|
|
253
|
+
return obj && typeof obj === "object" && !Array.isArray(obj) ? obj : null;
|
|
254
|
+
} catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const out = {};
|
|
259
|
+
const items = [];
|
|
260
|
+
let depth = 0;
|
|
261
|
+
let quoted = false;
|
|
262
|
+
let start = 0;
|
|
263
|
+
for (let i = 0; i < raw.length; i++) {
|
|
264
|
+
const c = raw[i];
|
|
265
|
+
if (quoted) {
|
|
266
|
+
if (c === "\\") i++;
|
|
267
|
+
else if (c === '"') quoted = false;
|
|
268
|
+
} else if (c === '"') quoted = true;
|
|
269
|
+
else if (c === "[" || c === "{") depth++;
|
|
270
|
+
else if (c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
271
|
+
else if ((c === "," || c === "\n") && depth === 0) {
|
|
272
|
+
items.push(raw.slice(start, i));
|
|
273
|
+
start = i + 1;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
items.push(raw.slice(start));
|
|
277
|
+
for (const item of items) {
|
|
278
|
+
const sep2 = item.indexOf(":");
|
|
279
|
+
if (sep2 <= 0) continue;
|
|
280
|
+
const key = item.slice(0, sep2).trim();
|
|
281
|
+
const text2 = item.slice(sep2 + 1).trim();
|
|
282
|
+
if (!key) continue;
|
|
283
|
+
try {
|
|
284
|
+
out[key] = JSON.parse(text2);
|
|
285
|
+
} catch {
|
|
286
|
+
out[key] = text2;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
function isDev() {
|
|
292
|
+
try {
|
|
293
|
+
const p = globalThis.process;
|
|
294
|
+
return !!p?.env && p.env.NODE_ENV !== "production";
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function timer(fn, ms) {
|
|
300
|
+
const t = setTimeout(fn, ms);
|
|
301
|
+
t.unref?.();
|
|
302
|
+
return t;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/dev-overrides.ts
|
|
306
|
+
var OVERRIDES_ENV = "GUIDELY_FLAG_OVERRIDES";
|
|
307
|
+
var OVERRIDES_FILE_ENV = "GUIDELY_FLAG_OVERRIDES_FILE";
|
|
308
|
+
var OVERRIDE_BEHAVIOUR_ENV = "GUIDELY_FLAG_OVERRIDE_BEHAVIOUR";
|
|
309
|
+
var OVERRIDES_GLOBAL = "__GUIDELY_FLAG_OVERRIDES__";
|
|
310
|
+
var OVERRIDE_BEHAVIOUR_GLOBAL = "__GUIDELY_FLAG_OVERRIDE_BEHAVIOUR__";
|
|
311
|
+
var BEHAVIOURS = ["localOnly", "localOverRemote", "remoteOverLocal"];
|
|
312
|
+
var runtime = () => globalThis;
|
|
313
|
+
function env(name) {
|
|
314
|
+
const g = runtime();
|
|
315
|
+
try {
|
|
316
|
+
const v = g.process?.env?.[name];
|
|
317
|
+
if (v !== void 0) return v;
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
return g.Deno?.env?.get(name);
|
|
322
|
+
} catch {
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function readFile(path) {
|
|
327
|
+
const g = runtime();
|
|
328
|
+
const fs = g.process?.getBuiltinModule?.("node:fs");
|
|
329
|
+
if (fs) return fs.readFileSync(path, "utf8");
|
|
330
|
+
if (g.Deno?.readTextFileSync) return g.Deno.readTextFileSync(path);
|
|
331
|
+
throw new Error("this runtime cannot read files");
|
|
332
|
+
}
|
|
333
|
+
function behaviourOf(raw, source, out) {
|
|
334
|
+
if (typeof raw !== "string" || !raw.trim()) return;
|
|
335
|
+
const b = raw.trim();
|
|
336
|
+
if (BEHAVIOURS.includes(b)) {
|
|
337
|
+
out.behaviour = b;
|
|
338
|
+
out.sources.push(source);
|
|
339
|
+
} else out.errors.push(`${source} must be localOnly, localOverRemote or remoteOverLocal (got "${raw}"); ignoring it.`);
|
|
340
|
+
}
|
|
341
|
+
function merge(text, source, out) {
|
|
342
|
+
if (text === void 0 || !text.trim()) return;
|
|
343
|
+
const values = parseOverrides(text);
|
|
344
|
+
if (!values) return void out.errors.push(`${source} is not a JSON object or a key:value list; ignoring it.`);
|
|
345
|
+
if (!Object.keys(values).length) return;
|
|
346
|
+
Object.assign(out.values, values);
|
|
347
|
+
out.sources.push(source);
|
|
348
|
+
}
|
|
349
|
+
function readDevOverrides() {
|
|
350
|
+
const out = { values: {}, sources: [], errors: [] };
|
|
351
|
+
const file = env(OVERRIDES_FILE_ENV);
|
|
352
|
+
if (file?.trim()) {
|
|
353
|
+
try {
|
|
354
|
+
merge(readFile(file.trim()), `${OVERRIDES_FILE_ENV} (${file.trim()})`, out);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
out.errors.push(`Could not read ${OVERRIDES_FILE_ENV} (${file.trim()}): ${err instanceof Error ? err.message : String(err)}; ignoring it.`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
merge(env(OVERRIDES_ENV), OVERRIDES_ENV, out);
|
|
360
|
+
const g = runtime()[OVERRIDES_GLOBAL];
|
|
361
|
+
if (g && typeof g === "object" && !Array.isArray(g)) {
|
|
362
|
+
if (Object.keys(g).length) {
|
|
363
|
+
Object.assign(out.values, g);
|
|
364
|
+
out.sources.push(OVERRIDES_GLOBAL);
|
|
365
|
+
}
|
|
366
|
+
} else if (typeof g === "string") merge(g, OVERRIDES_GLOBAL, out);
|
|
367
|
+
else if (g !== void 0 && g !== null) out.errors.push(`${OVERRIDES_GLOBAL} must be an object or overrides text; ignoring it.`);
|
|
368
|
+
behaviourOf(env(OVERRIDE_BEHAVIOUR_ENV), OVERRIDE_BEHAVIOUR_ENV, out);
|
|
369
|
+
behaviourOf(runtime()[OVERRIDE_BEHAVIOUR_GLOBAL], OVERRIDE_BEHAVIOUR_GLOBAL, out);
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
|
|
212
373
|
// src/values.ts
|
|
213
374
|
function textOf(v) {
|
|
214
375
|
if (typeof v === "string") return v;
|
|
@@ -789,88 +950,6 @@ function evaluateFlag(payload, key, attributes = {}, options = {}) {
|
|
|
789
950
|
}
|
|
790
951
|
}
|
|
791
952
|
|
|
792
|
-
// src/util.ts
|
|
793
|
-
function stableStringify(value) {
|
|
794
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
795
|
-
if (Array.isArray(value)) return `[${value.map((v) => v === void 0 ? "null" : stableStringify(v)).join(",")}]`;
|
|
796
|
-
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
797
|
-
const obj = value;
|
|
798
|
-
return `{${Object.keys(obj).filter((k) => obj[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
799
|
-
}
|
|
800
|
-
function isEncryptedPayload(x) {
|
|
801
|
-
return !!x && typeof x === "object" && typeof x.encryptedPayload === "string";
|
|
802
|
-
}
|
|
803
|
-
function isRemoteResults(x) {
|
|
804
|
-
return !!x && typeof x === "object" && !("schemaVersion" in x) && !!x.flags && typeof x.flags === "object";
|
|
805
|
-
}
|
|
806
|
-
function isFlagsPayload(x) {
|
|
807
|
-
return !!x && typeof x === "object" && "schemaVersion" in x && !!x.flags && typeof x.flags === "object";
|
|
808
|
-
}
|
|
809
|
-
function kindOf(v) {
|
|
810
|
-
if (v === null || v === void 0) return "null";
|
|
811
|
-
if (Array.isArray(v)) return "array";
|
|
812
|
-
return typeof v;
|
|
813
|
-
}
|
|
814
|
-
function compact(attrs) {
|
|
815
|
-
const out = {};
|
|
816
|
-
for (const [k, v] of Object.entries(attrs)) if (v !== void 0) out[k] = v;
|
|
817
|
-
return out;
|
|
818
|
-
}
|
|
819
|
-
function parseUrlOverrides(search, param = "guidely_flags") {
|
|
820
|
-
const out = {};
|
|
821
|
-
let raw = null;
|
|
822
|
-
try {
|
|
823
|
-
raw = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search).get(param);
|
|
824
|
-
} catch {
|
|
825
|
-
return out;
|
|
826
|
-
}
|
|
827
|
-
if (!raw) return out;
|
|
828
|
-
const items = [];
|
|
829
|
-
let depth = 0;
|
|
830
|
-
let quoted = false;
|
|
831
|
-
let start = 0;
|
|
832
|
-
for (let i = 0; i < raw.length; i++) {
|
|
833
|
-
const c = raw[i];
|
|
834
|
-
if (quoted) {
|
|
835
|
-
if (c === "\\") i++;
|
|
836
|
-
else if (c === '"') quoted = false;
|
|
837
|
-
} else if (c === '"') quoted = true;
|
|
838
|
-
else if (c === "[" || c === "{") depth++;
|
|
839
|
-
else if (c === "]" || c === "}") depth = Math.max(0, depth - 1);
|
|
840
|
-
else if (c === "," && depth === 0) {
|
|
841
|
-
items.push(raw.slice(start, i));
|
|
842
|
-
start = i + 1;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
items.push(raw.slice(start));
|
|
846
|
-
for (const item of items) {
|
|
847
|
-
const sep2 = item.indexOf(":");
|
|
848
|
-
if (sep2 <= 0) continue;
|
|
849
|
-
const key = item.slice(0, sep2).trim();
|
|
850
|
-
const text = item.slice(sep2 + 1).trim();
|
|
851
|
-
if (!key) continue;
|
|
852
|
-
try {
|
|
853
|
-
out[key] = JSON.parse(text);
|
|
854
|
-
} catch {
|
|
855
|
-
out[key] = text;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
return out;
|
|
859
|
-
}
|
|
860
|
-
function isDev() {
|
|
861
|
-
try {
|
|
862
|
-
const p = globalThis.process;
|
|
863
|
-
return !!p?.env && p.env.NODE_ENV !== "production";
|
|
864
|
-
} catch {
|
|
865
|
-
return false;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
function timer(fn, ms) {
|
|
869
|
-
const t = setTimeout(fn, ms);
|
|
870
|
-
t.unref?.();
|
|
871
|
-
return t;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
953
|
// src/evaluator.ts
|
|
875
954
|
function typeMatches(value, fallback) {
|
|
876
955
|
if (value === null || value === void 0) return false;
|
|
@@ -940,7 +1019,7 @@ function createSseParser(onEvent) {
|
|
|
940
1019
|
|
|
941
1020
|
// src/client.ts
|
|
942
1021
|
var FLAGS_SDK_NAME = "@guidelytech/flags";
|
|
943
|
-
var FLAGS_SDK_VERSION = "0.
|
|
1022
|
+
var FLAGS_SDK_VERSION = "0.8.0";
|
|
944
1023
|
var DEFAULT_FLAGS_API_URL = "https://guidelytech.com";
|
|
945
1024
|
var SECRET_PREFIX = "gly_sk_";
|
|
946
1025
|
var MAX_RESULT_CACHE = 20;
|
|
@@ -987,6 +1066,7 @@ var FlagsClient = class {
|
|
|
987
1066
|
__publicField(this, "context");
|
|
988
1067
|
__publicField(this, "attributes");
|
|
989
1068
|
__publicField(this, "overrides");
|
|
1069
|
+
__publicField(this, "devOverrides");
|
|
990
1070
|
__publicField(this, "urlOverrides");
|
|
991
1071
|
__publicField(this, "offline");
|
|
992
1072
|
__publicField(this, "destroyed", false);
|
|
@@ -1028,12 +1108,13 @@ var FlagsClient = class {
|
|
|
1028
1108
|
this.log = o.logger === null ? {} : o.logger ?? { warn: (...a) => console.warn("[Guidely flags]", ...a), error: (...a) => console.error("[Guidely flags]", ...a) };
|
|
1029
1109
|
if (this.secret && !o.environment) throw new Error("[Guidely flags] `environment` (development | staging | production) is required with a gly_sk_ key.");
|
|
1030
1110
|
if (this.secret && isBrowser()) this.log.warn?.("A gly_sk_ secret key is being used in a browser. Secret keys belong on servers; use the environment's public pk_ key here.");
|
|
1111
|
+
const dev = o.devOverrides === false ? null : readDevOverrides();
|
|
1031
1112
|
this.cfg = {
|
|
1032
1113
|
mode: o.pollingMode ?? "auto",
|
|
1033
1114
|
pollMs: Math.max(1, o.pollIntervalSeconds ?? 60) * 1e3,
|
|
1034
1115
|
ttlMs: Math.max(0, o.cacheTtlSeconds ?? 60) * 1e3,
|
|
1035
1116
|
debounceMs: Math.max(0, o.remoteEvalDebounceMs ?? 50),
|
|
1036
|
-
behaviour: o.overrideBehaviour ?? "localOverRemote",
|
|
1117
|
+
behaviour: dev?.behaviour ?? o.overrideBehaviour ?? "localOverRemote",
|
|
1037
1118
|
trackExposures: o.trackExposures ?? !o.trackingCallback,
|
|
1038
1119
|
reportUsage: o.reportUsage ?? true
|
|
1039
1120
|
};
|
|
@@ -1042,10 +1123,16 @@ var FlagsClient = class {
|
|
|
1042
1123
|
this.context = compact(o.contextAttributes ?? {});
|
|
1043
1124
|
this.attributes = compact(o.attributes ?? {});
|
|
1044
1125
|
this.overrides = { ...o.overrides ?? {} };
|
|
1126
|
+
this.devOverrides = dev?.values ?? {};
|
|
1045
1127
|
this.urlOverrides = o.enableUrlOverrides && isBrowser() ? parseUrlOverrides(location.search) : {};
|
|
1046
1128
|
this.storage = o.storage === false || o.storage === null ? null : o.storage ?? defaultStorage();
|
|
1047
1129
|
this.storageKey = `guidely:flags:${key}${o.environment && this.secret ? `:${o.environment}` : ""}`;
|
|
1048
1130
|
this.readyPromise = new Promise((r) => this.resolveReady = r);
|
|
1131
|
+
if (dev) {
|
|
1132
|
+
dev.errors.forEach((e) => this.reportError(new Error(e)));
|
|
1133
|
+
const n = Object.keys(dev.values).length;
|
|
1134
|
+
if (dev.sources.length) this.log.warn?.(`Dev overrides from ${dev.sources.join(", ")}: ${n} flag${n === 1 ? "" : "s"}, behaviour ${this.cfg.behaviour}.`);
|
|
1135
|
+
}
|
|
1049
1136
|
this.getters = makeEvaluator(
|
|
1050
1137
|
(k) => this.resolve(k, this.getAttributes(), true),
|
|
1051
1138
|
() => this.all(this.getAttributes()),
|
|
@@ -1127,7 +1214,7 @@ var FlagsClient = class {
|
|
|
1127
1214
|
getAttributes() {
|
|
1128
1215
|
return { ...this.context, ...this.attributes };
|
|
1129
1216
|
}
|
|
1130
|
-
/** Replaces the overrides (`null` clears them). URL overrides stay. */
|
|
1217
|
+
/** Replaces the overrides (`null` clears them). Dev and URL overrides stay. */
|
|
1131
1218
|
setOverrides(overrides) {
|
|
1132
1219
|
this.overrides = { ...overrides ?? {} };
|
|
1133
1220
|
this.notify();
|
|
@@ -1204,6 +1291,7 @@ var FlagsClient = class {
|
|
|
1204
1291
|
// ---------------------------------------------------------------------------
|
|
1205
1292
|
overrideFor(key) {
|
|
1206
1293
|
if (Object.prototype.hasOwnProperty.call(this.urlOverrides, key)) return this.urlOverrides[key];
|
|
1294
|
+
if (Object.prototype.hasOwnProperty.call(this.devOverrides, key)) return this.devOverrides[key];
|
|
1207
1295
|
if (Object.prototype.hasOwnProperty.call(this.overrides, key)) return this.overrides[key];
|
|
1208
1296
|
return void 0;
|
|
1209
1297
|
}
|
|
@@ -1215,7 +1303,6 @@ var FlagsClient = class {
|
|
|
1215
1303
|
const forced = Object.prototype.hasOwnProperty.call(this.urlOverrides, key);
|
|
1216
1304
|
const override = ov === void 0 ? null : { key, value: ov, reason: "OVERRIDE", inExperiment: false };
|
|
1217
1305
|
if (override && (forced || this.cfg.behaviour !== "remoteOverLocal")) res = override;
|
|
1218
|
-
else if (this.cfg.behaviour === "localOnly") res = notFound(key);
|
|
1219
1306
|
else {
|
|
1220
1307
|
res = this.evaluateRemoteOrLocal(key, attrs);
|
|
1221
1308
|
if (override && res.reason === "FLAG_NOT_FOUND") res = override;
|
|
@@ -1236,11 +1323,9 @@ var FlagsClient = class {
|
|
|
1236
1323
|
return evaluateFlag(this.payload, key, attrs, { now: this.now(), stickyBuckets: this.o.stickyBucketService });
|
|
1237
1324
|
}
|
|
1238
1325
|
all(attrs) {
|
|
1239
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(this.overrides), ...Object.keys(this.urlOverrides)]);
|
|
1240
|
-
if (this.
|
|
1241
|
-
|
|
1242
|
-
else for (const k of Object.keys(this.payload?.flags ?? {})) keys.add(k);
|
|
1243
|
-
}
|
|
1326
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(this.overrides), ...Object.keys(this.devOverrides), ...Object.keys(this.urlOverrides)]);
|
|
1327
|
+
if (this.remote) for (const k of Object.keys(this.results?.flags ?? {})) keys.add(k);
|
|
1328
|
+
else for (const k of Object.keys(this.payload?.flags ?? {})) keys.add(k);
|
|
1244
1329
|
const out = {};
|
|
1245
1330
|
for (const k of keys) {
|
|
1246
1331
|
const v = this.resolve(k, attrs, false).value;
|
|
@@ -1487,7 +1572,7 @@ var FlagsClient = class {
|
|
|
1487
1572
|
async start() {
|
|
1488
1573
|
const boot = this.o.payload;
|
|
1489
1574
|
if (isEncryptedPayload(boot)) await this.applyRaw(boot, null);
|
|
1490
|
-
if (!this.payload && !this.results && !this.remote) await this.loadStored();
|
|
1575
|
+
if (!this.payload && !this.results && !this.remote && this.cfg.behaviour !== "localOnly") await this.loadStored();
|
|
1491
1576
|
if (this.destroyed) return;
|
|
1492
1577
|
const bootstrapped = !!(boot || this.o.remoteResults) && !!(this.payload || this.results);
|
|
1493
1578
|
if (this.cfg.behaviour === "localOnly" || this.offline || this.cfg.mode === "manual") return this.settle();
|
|
@@ -1633,6 +1718,11 @@ var FlagsClient = class {
|
|
|
1633
1718
|
}
|
|
1634
1719
|
takeBatch() {
|
|
1635
1720
|
if (!this.usage.size && !this.exposures.length) return null;
|
|
1721
|
+
if (this.cfg.behaviour === "localOnly") {
|
|
1722
|
+
this.usage.clear();
|
|
1723
|
+
this.exposures = [];
|
|
1724
|
+
return null;
|
|
1725
|
+
}
|
|
1636
1726
|
const batch = { sdk: this.o.sdk ?? { name: FLAGS_SDK_NAME, version: FLAGS_SDK_VERSION } };
|
|
1637
1727
|
if (this.usage.size) batch.usage = Array.from(this.usage.values());
|
|
1638
1728
|
if (this.exposures.length) batch.exposures = this.exposures;
|
|
@@ -1822,7 +1912,7 @@ function parseArgs(argv) {
|
|
|
1822
1912
|
return { command, flags };
|
|
1823
1913
|
}
|
|
1824
1914
|
async function main(argv, io = {}) {
|
|
1825
|
-
const
|
|
1915
|
+
const env2 = io.env ?? process.env;
|
|
1826
1916
|
const out = io.out ?? ((t) => process.stdout.write(t));
|
|
1827
1917
|
const err = io.err ?? ((t) => process.stderr.write(t));
|
|
1828
1918
|
const doFetch = io.fetch ?? fetch;
|
|
@@ -1835,7 +1925,7 @@ async function main(argv, io = {}) {
|
|
|
1835
1925
|
const str = (name, envName) => {
|
|
1836
1926
|
const v = flags[name];
|
|
1837
1927
|
if (v === true) throw new UsageError(`--${name} needs a value`);
|
|
1838
|
-
return v ?? (envName ?
|
|
1928
|
+
return v ?? (envName ? env2[envName] : void 0) ?? void 0;
|
|
1839
1929
|
};
|
|
1840
1930
|
const apiUrl = (str("api-url", "GUIDELY_API_URL") || DEFAULT_FLAGS_API_URL).replace(/\/+$/, "");
|
|
1841
1931
|
const key = str("key", "GUIDELY_API_KEY");
|
|
@@ -1847,9 +1937,9 @@ async function main(argv, io = {}) {
|
|
|
1847
1937
|
const dir = (0, import_node_path2.resolve)(io.cwd ?? process.cwd(), str("dir") ?? ".");
|
|
1848
1938
|
const repo = str("repo", "GITHUB_REPOSITORY");
|
|
1849
1939
|
if (!repo) throw new UsageError("--repo is required");
|
|
1850
|
-
const branch = str("branch") ??
|
|
1940
|
+
const branch = str("branch") ?? env2.GITHUB_HEAD_REF ?? env2.GITHUB_REF_NAME ?? gitValue(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
1851
1941
|
if (!branch) throw new UsageError("--branch is required (not a git checkout)");
|
|
1852
|
-
const commit = str("commit") ??
|
|
1942
|
+
const commit = str("commit") ?? env2.GITHUB_SHA ?? gitValue(dir, ["rev-parse", "HEAD"]);
|
|
1853
1943
|
const auth = { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
1854
1944
|
const res = await doFetch(`${apiUrl}/api/v1/sdk/flags?environment=${encodeURIComponent(environment)}`, { headers: auth });
|
|
1855
1945
|
if (!res.ok) throw new Error(`Downloading flags failed (${res.status}): ${(await res.text()).slice(0, 300)}`);
|