@guidelytech/flags 0.16.0 → 0.17.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/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  decryptPayloadString,
6
6
  isEncryptedPayload,
7
7
  isFlagsPayload
8
- } from "./chunk-V2JSFLDN.js";
8
+ } from "./chunk-LHORTYIA.js";
9
9
  import "./chunk-OY4QS3WL.js";
10
10
 
11
11
  // src/cli-main.ts
@@ -98,7 +98,7 @@ code-refs: finds flag keys used as quoted string literals in your code and uploa
98
98
  references, so the dashboard shows where each flag is used (run it in CI on your main branch).
99
99
  --api-url <url> Guidely URL (default ${DEFAULT_FLAGS_API_URL}; env GUIDELY_API_URL)
100
100
  --key <gly_sk_\u2026> Secret API key with flags:read and flags:code-refs (env GUIDELY_API_KEY)
101
- --environment <env> development | staging | production (env GUIDELY_ENVIRONMENT)
101
+ --environment <env> environment key, e.g. development, staging, production, qa (env GUIDELY_ENVIRONMENT)
102
102
  --repo <name> Repository name, e.g. acme/web (env GITHUB_REPOSITORY)
103
103
  --branch <branch> Default: the current git branch
104
104
  --commit <sha> Default: the current git commit
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli-main.ts","../src/code-refs.ts","../src/cli.ts"],"sourcesContent":["/** `guidely-flags` command line (Node only). Kept separate from `cli.ts` so tests can call {@link main}. */\nimport { resolve } from \"node:path\";\nimport { createFlagsClient, DEFAULT_FLAGS_API_URL } from \"./client\";\nimport { gitValue, listFiles, scanFiles, MAX_REFS, type CodeRef } from \"./code-refs\";\nimport { decryptPayloadString } from \"./crypto\";\nimport type { FlagsPayload } from \"./types\";\nimport { isEncryptedPayload, isFlagsPayload } from \"./util\";\n\nexport const HELP = `guidely-flags — Guidely feature flag tools\n\nUsage:\n guidely-flags code-refs --key gly_sk_… --environment production --repo <name> [options]\n guidely-flags evaluate --key <pk_…|gly_sk_…> [--environment <env>] [--flag <key>] [--attributes '{\"id\":\"u1\"}']\n\ncode-refs: finds flag keys used as quoted string literals in your code and uploads the\nreferences, so the dashboard shows where each flag is used (run it in CI on your main branch).\n --api-url <url> Guidely URL (default ${DEFAULT_FLAGS_API_URL}; env GUIDELY_API_URL)\n --key <gly_sk_…> Secret API key with flags:read and flags:code-refs (env GUIDELY_API_KEY)\n --environment <env> development | staging | production (env GUIDELY_ENVIRONMENT)\n --repo <name> Repository name, e.g. acme/web (env GITHUB_REPOSITORY)\n --branch <branch> Default: the current git branch\n --commit <sha> Default: the current git commit\n --dir <path> Directory to scan (default .)\n --decryption-key <key> For environments with encrypted payloads\n --dry-run Print the references instead of uploading them\n\nevaluate: prints how a flag (or every flag) evaluates for some attributes.\n --flag <key> Flag key (default: all flags)\n --attributes <json> Attributes object (default {})\n\nExit codes: 0 success, 1 failure, 2 usage error.\n`;\n\nexport interface CliIo {\n fetch?: typeof fetch;\n env?: Record<string, string | undefined>;\n out?: (text: string) => void;\n err?: (text: string) => void;\n cwd?: string;\n}\n\nclass UsageError extends Error {}\n\nfunction parseArgs(argv: string[]): { command?: string; flags: Record<string, string | true> } {\n const flags: Record<string, string | true> = {};\n let command: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a.startsWith(\"--\")) {\n const eq = a.indexOf(\"=\");\n if (eq > 0) flags[a.slice(2, eq)] = a.slice(eq + 1);\n else if (argv[i + 1] !== undefined && !argv[i + 1]!.startsWith(\"--\")) flags[a.slice(2)] = argv[++i]!;\n else flags[a.slice(2)] = true;\n } else if (a === \"-h\") flags.help = true;\n else if (!command) command = a;\n else throw new UsageError(`Unexpected argument: ${a}`);\n }\n return { command, flags };\n}\n\nexport async function main(argv: string[], io: CliIo = {}): Promise<number> {\n const env = io.env ?? process.env;\n const out = io.out ?? ((t: string) => process.stdout.write(t));\n const err = io.err ?? ((t: string) => process.stderr.write(t));\n const doFetch = io.fetch ?? fetch;\n try {\n const { command, flags } = parseArgs(argv);\n if (!command || flags.help || command === \"help\") {\n out(HELP);\n return command || flags.help ? 0 : 2;\n }\n const str = (name: string, envName?: string): string | undefined => {\n const v = flags[name];\n if (v === true) throw new UsageError(`--${name} needs a value`);\n return v ?? (envName ? env[envName] : undefined) ?? undefined;\n };\n const apiUrl = (str(\"api-url\", \"GUIDELY_API_URL\") || DEFAULT_FLAGS_API_URL).replace(/\\/+$/, \"\");\n const key = str(\"key\", \"GUIDELY_API_KEY\");\n const environment = str(\"environment\", \"GUIDELY_ENVIRONMENT\");\n if (!key) throw new UsageError(\"--key is required\");\n\n if (command === \"code-refs\") {\n if (!key.startsWith(\"gly_sk_\")) throw new UsageError(\"code-refs needs a secret gly_sk_ API key\");\n if (!environment) throw new UsageError(\"--environment is required\");\n const dir = resolve(io.cwd ?? process.cwd(), str(\"dir\") ?? \".\");\n const repo = str(\"repo\", \"GITHUB_REPOSITORY\");\n if (!repo) throw new UsageError(\"--repo is required\");\n const branch = str(\"branch\") ?? env.GITHUB_HEAD_REF ?? env.GITHUB_REF_NAME ?? gitValue(dir, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n if (!branch) throw new UsageError(\"--branch is required (not a git checkout)\");\n const commit = str(\"commit\") ?? env.GITHUB_SHA ?? gitValue(dir, [\"rev-parse\", \"HEAD\"]);\n\n const auth = { Authorization: `Bearer ${key}`, Accept: \"application/json\" };\n const res = await doFetch(`${apiUrl}/api/v1/sdk/flags?environment=${encodeURIComponent(environment)}`, { headers: auth });\n if (!res.ok) throw new Error(`Downloading flags failed (${res.status}): ${(await res.text()).slice(0, 300)}`);\n let payload = (await res.json()) as unknown;\n if (isEncryptedPayload(payload)) {\n const dk = str(\"decryption-key\", \"GUIDELY_DECRYPTION_KEY\");\n if (!dk) throw new Error(\"The payload is encrypted: pass --decryption-key\");\n payload = { ...payload, ...JSON.parse(await decryptPayloadString(payload.encryptedPayload, dk)) };\n }\n if (!isFlagsPayload(payload)) throw new Error(\"Unexpected flags payload\");\n const keys = Object.keys((payload as FlagsPayload).flags);\n const files = listFiles(dir);\n const refs: CodeRef[] = scanFiles(dir, files, keys);\n if (refs.length >= MAX_REFS) err(`Stopped after ${MAX_REFS} references.\\n`);\n if (flags[\"dry-run\"]) {\n for (const r of refs) out(`${r.flag}\\t${r.path}:${r.line}\\t${r.snippet}\\n`);\n out(`${refs.length} reference(s) to ${new Set(refs.map((r) => r.flag)).size} of ${keys.length} flag(s) in ${files.length} file(s) (dry run, nothing uploaded)\\n`);\n return 0;\n }\n const up = await doFetch(`${apiUrl}/api/v1/flags/code-refs`, { method: \"POST\", headers: { ...auth, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ repo, branch, ...(commit ? { commit } : {}), refs }) });\n if (!up.ok) throw new Error(`Uploading code references failed (${up.status}): ${(await up.text()).slice(0, 300)}`);\n out(`Uploaded ${refs.length} reference(s) to ${new Set(refs.map((r) => r.flag)).size} flag(s) for ${repo}@${branch}\\n`);\n return 0;\n }\n\n if (command === \"evaluate\") {\n let attributes: Record<string, unknown> = {};\n const raw = str(\"attributes\");\n if (raw) {\n try {\n attributes = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new UsageError(\"--attributes must be a JSON object\");\n }\n if (!attributes || typeof attributes !== \"object\" || Array.isArray(attributes)) throw new UsageError(\"--attributes must be a JSON object\");\n }\n let failure: Error | null = null;\n const client = createFlagsClient({ sdkKey: key, apiUrl, environment, pollingMode: \"manual\", reportUsage: false, trackExposures: false, storage: false, logger: null, fetch: doFetch, decryptionKey: str(\"decryption-key\", \"GUIDELY_DECRYPTION_KEY\"), onError: (e) => (failure = e) });\n await client.refresh();\n client.destroy();\n if (!client.getPayload()) throw failure ?? new Error(\"Could not load flags\");\n const user = client.forUser(attributes);\n const flag = str(\"flag\");\n const result = flag ? user.getDetails(flag, null) : user.getAll();\n out(`${JSON.stringify(result, null, 2)}\\n`);\n return 0;\n }\n throw new UsageError(`Unknown command: ${command}`);\n } catch (e) {\n if (e instanceof UsageError) {\n err(`${e.message}\\n\\n${HELP}`);\n return 2;\n }\n err(`guidely-flags: ${e instanceof Error ? e.message : String(e)}\\n`);\n return 1;\n }\n}\n","/** Node-only: finds flag keys used as string literals in a source tree (`guidely-flags code-refs`). */\nimport { execFileSync } from \"node:child_process\";\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface CodeRef {\n flag: string;\n path: string;\n line: number;\n snippet: string;\n}\n\nconst SKIP_DIRS = new Set([\"node_modules\", \".git\", \"dist\", \"build\", \"vendor\", \".next\", \".svelte-kit\", \".turbo\", \"coverage\", \"target\", \"out\"]);\nconst SKIP_FILES = /(^|\\/)(package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock|bun\\.lockb?|composer\\.lock|Cargo\\.lock|Gemfile\\.lock|poetry\\.lock)$|\\.(min\\.js|map|png|jpe?g|gif|webp|ico|svg|pdf|zip|gz|woff2?|ttf|eot|mp4|mov|mp3|jar|class|so|dylib|dll|exe|bin)$/i;\nexport const MAX_FILE_BYTES = 1024 * 1024;\nexport const MAX_REFS = 10_000;\n\n/** Files to scan, relative to `dir` with `/` separators: `git ls-files` (tracked + untracked, not ignored), else a directory walk. */\nexport function listFiles(dir: string): string[] {\n try {\n const out = execFileSync(\"git\", [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"], { cwd: dir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], maxBuffer: 256 * 1024 * 1024 });\n const files = out.split(\"\\0\").filter(Boolean);\n if (files.length) return files.filter((f) => !f.split(\"/\").some((p) => SKIP_DIRS.has(p)));\n } catch {\n /* not a git checkout, or git missing */\n }\n const files: string[] = [];\n const walk = (abs: string) => {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = readdirSync(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (e.isDirectory()) {\n if (!SKIP_DIRS.has(e.name)) walk(join(abs, e.name));\n } else if (e.isFile()) files.push(relative(dir, join(abs, e.name)).split(sep).join(\"/\"));\n }\n };\n walk(dir);\n return files.sort();\n}\n\nconst escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n/** Every line where a flag key appears as a quoted literal: `'key'`, `\"key\"` or `` `key` ``. */\nexport function scanFiles(dir: string, files: string[], flagKeys: string[]): CodeRef[] {\n const keys = Array.from(new Set(flagKeys.filter(Boolean))).sort((a, b) => b.length - a.length);\n if (!keys.length) return [];\n const re = new RegExp(`(['\"\\`])(${keys.map(escapeRe).join(\"|\")})\\\\1`, \"g\");\n const refs: CodeRef[] = [];\n for (const path of files) {\n if (SKIP_FILES.test(path)) continue;\n const abs = join(dir, path);\n let text: string;\n try {\n const st = statSync(abs);\n if (!st.isFile() || st.size > MAX_FILE_BYTES) continue;\n const buf = readFileSync(abs);\n if (buf.subarray(0, 8000).includes(0)) continue; // binary\n text = buf.toString(\"utf8\");\n } catch {\n continue;\n }\n if (!keys.some((k) => text.includes(k))) continue;\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n re.lastIndex = 0;\n const seen = new Set<string>();\n for (let m = re.exec(line); m; m = re.exec(line)) {\n const flag = m[2]!;\n if (seen.has(flag)) continue;\n seen.add(flag);\n refs.push({ flag, path, line: i + 1, snippet: line.trim().slice(0, 200) });\n if (refs.length >= MAX_REFS) return refs;\n }\n }\n }\n return refs;\n}\n\nexport function gitValue(dir: string, args: string[]): string | undefined {\n try {\n return execFileSync(\"git\", args, { cwd: dir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] }).trim() || undefined;\n } catch {\n return undefined;\n }\n}\n","#!/usr/bin/env node\nimport { main } from \"./cli-main\";\n\nmain(process.argv.slice(2)).then((code) => process.exit(code));\n"],"mappings":"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","names":["files"]}
1
+ {"version":3,"sources":["../src/cli-main.ts","../src/code-refs.ts","../src/cli.ts"],"sourcesContent":["/** `guidely-flags` command line (Node only). Kept separate from `cli.ts` so tests can call {@link main}. */\nimport { resolve } from \"node:path\";\nimport { createFlagsClient, DEFAULT_FLAGS_API_URL } from \"./client\";\nimport { gitValue, listFiles, scanFiles, MAX_REFS, type CodeRef } from \"./code-refs\";\nimport { decryptPayloadString } from \"./crypto\";\nimport type { FlagsPayload } from \"./types\";\nimport { isEncryptedPayload, isFlagsPayload } from \"./util\";\n\nexport const HELP = `guidely-flags — Guidely feature flag tools\n\nUsage:\n guidely-flags code-refs --key gly_sk_… --environment production --repo <name> [options]\n guidely-flags evaluate --key <pk_…|gly_sk_…> [--environment <env>] [--flag <key>] [--attributes '{\"id\":\"u1\"}']\n\ncode-refs: finds flag keys used as quoted string literals in your code and uploads the\nreferences, so the dashboard shows where each flag is used (run it in CI on your main branch).\n --api-url <url> Guidely URL (default ${DEFAULT_FLAGS_API_URL}; env GUIDELY_API_URL)\n --key <gly_sk_…> Secret API key with flags:read and flags:code-refs (env GUIDELY_API_KEY)\n --environment <env> environment key, e.g. development, staging, production, qa (env GUIDELY_ENVIRONMENT)\n --repo <name> Repository name, e.g. acme/web (env GITHUB_REPOSITORY)\n --branch <branch> Default: the current git branch\n --commit <sha> Default: the current git commit\n --dir <path> Directory to scan (default .)\n --decryption-key <key> For environments with encrypted payloads\n --dry-run Print the references instead of uploading them\n\nevaluate: prints how a flag (or every flag) evaluates for some attributes.\n --flag <key> Flag key (default: all flags)\n --attributes <json> Attributes object (default {})\n\nExit codes: 0 success, 1 failure, 2 usage error.\n`;\n\nexport interface CliIo {\n fetch?: typeof fetch;\n env?: Record<string, string | undefined>;\n out?: (text: string) => void;\n err?: (text: string) => void;\n cwd?: string;\n}\n\nclass UsageError extends Error {}\n\nfunction parseArgs(argv: string[]): { command?: string; flags: Record<string, string | true> } {\n const flags: Record<string, string | true> = {};\n let command: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (a.startsWith(\"--\")) {\n const eq = a.indexOf(\"=\");\n if (eq > 0) flags[a.slice(2, eq)] = a.slice(eq + 1);\n else if (argv[i + 1] !== undefined && !argv[i + 1]!.startsWith(\"--\")) flags[a.slice(2)] = argv[++i]!;\n else flags[a.slice(2)] = true;\n } else if (a === \"-h\") flags.help = true;\n else if (!command) command = a;\n else throw new UsageError(`Unexpected argument: ${a}`);\n }\n return { command, flags };\n}\n\nexport async function main(argv: string[], io: CliIo = {}): Promise<number> {\n const env = io.env ?? process.env;\n const out = io.out ?? ((t: string) => process.stdout.write(t));\n const err = io.err ?? ((t: string) => process.stderr.write(t));\n const doFetch = io.fetch ?? fetch;\n try {\n const { command, flags } = parseArgs(argv);\n if (!command || flags.help || command === \"help\") {\n out(HELP);\n return command || flags.help ? 0 : 2;\n }\n const str = (name: string, envName?: string): string | undefined => {\n const v = flags[name];\n if (v === true) throw new UsageError(`--${name} needs a value`);\n return v ?? (envName ? env[envName] : undefined) ?? undefined;\n };\n const apiUrl = (str(\"api-url\", \"GUIDELY_API_URL\") || DEFAULT_FLAGS_API_URL).replace(/\\/+$/, \"\");\n const key = str(\"key\", \"GUIDELY_API_KEY\");\n const environment = str(\"environment\", \"GUIDELY_ENVIRONMENT\");\n if (!key) throw new UsageError(\"--key is required\");\n\n if (command === \"code-refs\") {\n if (!key.startsWith(\"gly_sk_\")) throw new UsageError(\"code-refs needs a secret gly_sk_ API key\");\n if (!environment) throw new UsageError(\"--environment is required\");\n const dir = resolve(io.cwd ?? process.cwd(), str(\"dir\") ?? \".\");\n const repo = str(\"repo\", \"GITHUB_REPOSITORY\");\n if (!repo) throw new UsageError(\"--repo is required\");\n const branch = str(\"branch\") ?? env.GITHUB_HEAD_REF ?? env.GITHUB_REF_NAME ?? gitValue(dir, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n if (!branch) throw new UsageError(\"--branch is required (not a git checkout)\");\n const commit = str(\"commit\") ?? env.GITHUB_SHA ?? gitValue(dir, [\"rev-parse\", \"HEAD\"]);\n\n const auth = { Authorization: `Bearer ${key}`, Accept: \"application/json\" };\n const res = await doFetch(`${apiUrl}/api/v1/sdk/flags?environment=${encodeURIComponent(environment)}`, { headers: auth });\n if (!res.ok) throw new Error(`Downloading flags failed (${res.status}): ${(await res.text()).slice(0, 300)}`);\n let payload = (await res.json()) as unknown;\n if (isEncryptedPayload(payload)) {\n const dk = str(\"decryption-key\", \"GUIDELY_DECRYPTION_KEY\");\n if (!dk) throw new Error(\"The payload is encrypted: pass --decryption-key\");\n payload = { ...payload, ...JSON.parse(await decryptPayloadString(payload.encryptedPayload, dk)) };\n }\n if (!isFlagsPayload(payload)) throw new Error(\"Unexpected flags payload\");\n const keys = Object.keys((payload as FlagsPayload).flags);\n const files = listFiles(dir);\n const refs: CodeRef[] = scanFiles(dir, files, keys);\n if (refs.length >= MAX_REFS) err(`Stopped after ${MAX_REFS} references.\\n`);\n if (flags[\"dry-run\"]) {\n for (const r of refs) out(`${r.flag}\\t${r.path}:${r.line}\\t${r.snippet}\\n`);\n out(`${refs.length} reference(s) to ${new Set(refs.map((r) => r.flag)).size} of ${keys.length} flag(s) in ${files.length} file(s) (dry run, nothing uploaded)\\n`);\n return 0;\n }\n const up = await doFetch(`${apiUrl}/api/v1/flags/code-refs`, { method: \"POST\", headers: { ...auth, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ repo, branch, ...(commit ? { commit } : {}), refs }) });\n if (!up.ok) throw new Error(`Uploading code references failed (${up.status}): ${(await up.text()).slice(0, 300)}`);\n out(`Uploaded ${refs.length} reference(s) to ${new Set(refs.map((r) => r.flag)).size} flag(s) for ${repo}@${branch}\\n`);\n return 0;\n }\n\n if (command === \"evaluate\") {\n let attributes: Record<string, unknown> = {};\n const raw = str(\"attributes\");\n if (raw) {\n try {\n attributes = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new UsageError(\"--attributes must be a JSON object\");\n }\n if (!attributes || typeof attributes !== \"object\" || Array.isArray(attributes)) throw new UsageError(\"--attributes must be a JSON object\");\n }\n let failure: Error | null = null;\n const client = createFlagsClient({ sdkKey: key, apiUrl, environment, pollingMode: \"manual\", reportUsage: false, trackExposures: false, storage: false, logger: null, fetch: doFetch, decryptionKey: str(\"decryption-key\", \"GUIDELY_DECRYPTION_KEY\"), onError: (e) => (failure = e) });\n await client.refresh();\n client.destroy();\n if (!client.getPayload()) throw failure ?? new Error(\"Could not load flags\");\n const user = client.forUser(attributes);\n const flag = str(\"flag\");\n const result = flag ? user.getDetails(flag, null) : user.getAll();\n out(`${JSON.stringify(result, null, 2)}\\n`);\n return 0;\n }\n throw new UsageError(`Unknown command: ${command}`);\n } catch (e) {\n if (e instanceof UsageError) {\n err(`${e.message}\\n\\n${HELP}`);\n return 2;\n }\n err(`guidely-flags: ${e instanceof Error ? e.message : String(e)}\\n`);\n return 1;\n }\n}\n","/** Node-only: finds flag keys used as string literals in a source tree (`guidely-flags code-refs`). */\nimport { execFileSync } from \"node:child_process\";\nimport { readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface CodeRef {\n flag: string;\n path: string;\n line: number;\n snippet: string;\n}\n\nconst SKIP_DIRS = new Set([\"node_modules\", \".git\", \"dist\", \"build\", \"vendor\", \".next\", \".svelte-kit\", \".turbo\", \"coverage\", \"target\", \"out\"]);\nconst SKIP_FILES = /(^|\\/)(package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock|bun\\.lockb?|composer\\.lock|Cargo\\.lock|Gemfile\\.lock|poetry\\.lock)$|\\.(min\\.js|map|png|jpe?g|gif|webp|ico|svg|pdf|zip|gz|woff2?|ttf|eot|mp4|mov|mp3|jar|class|so|dylib|dll|exe|bin)$/i;\nexport const MAX_FILE_BYTES = 1024 * 1024;\nexport const MAX_REFS = 10_000;\n\n/** Files to scan, relative to `dir` with `/` separators: `git ls-files` (tracked + untracked, not ignored), else a directory walk. */\nexport function listFiles(dir: string): string[] {\n try {\n const out = execFileSync(\"git\", [\"ls-files\", \"-z\", \"--cached\", \"--others\", \"--exclude-standard\"], { cwd: dir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], maxBuffer: 256 * 1024 * 1024 });\n const files = out.split(\"\\0\").filter(Boolean);\n if (files.length) return files.filter((f) => !f.split(\"/\").some((p) => SKIP_DIRS.has(p)));\n } catch {\n /* not a git checkout, or git missing */\n }\n const files: string[] = [];\n const walk = (abs: string) => {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = readdirSync(abs, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (e.isDirectory()) {\n if (!SKIP_DIRS.has(e.name)) walk(join(abs, e.name));\n } else if (e.isFile()) files.push(relative(dir, join(abs, e.name)).split(sep).join(\"/\"));\n }\n };\n walk(dir);\n return files.sort();\n}\n\nconst escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n/** Every line where a flag key appears as a quoted literal: `'key'`, `\"key\"` or `` `key` ``. */\nexport function scanFiles(dir: string, files: string[], flagKeys: string[]): CodeRef[] {\n const keys = Array.from(new Set(flagKeys.filter(Boolean))).sort((a, b) => b.length - a.length);\n if (!keys.length) return [];\n const re = new RegExp(`(['\"\\`])(${keys.map(escapeRe).join(\"|\")})\\\\1`, \"g\");\n const refs: CodeRef[] = [];\n for (const path of files) {\n if (SKIP_FILES.test(path)) continue;\n const abs = join(dir, path);\n let text: string;\n try {\n const st = statSync(abs);\n if (!st.isFile() || st.size > MAX_FILE_BYTES) continue;\n const buf = readFileSync(abs);\n if (buf.subarray(0, 8000).includes(0)) continue; // binary\n text = buf.toString(\"utf8\");\n } catch {\n continue;\n }\n if (!keys.some((k) => text.includes(k))) continue;\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n re.lastIndex = 0;\n const seen = new Set<string>();\n for (let m = re.exec(line); m; m = re.exec(line)) {\n const flag = m[2]!;\n if (seen.has(flag)) continue;\n seen.add(flag);\n refs.push({ flag, path, line: i + 1, snippet: line.trim().slice(0, 200) });\n if (refs.length >= MAX_REFS) return refs;\n }\n }\n }\n return refs;\n}\n\nexport function gitValue(dir: string, args: string[]): string | undefined {\n try {\n return execFileSync(\"git\", args, { cwd: dir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] }).trim() || undefined;\n } catch {\n return undefined;\n }\n}\n","#!/usr/bin/env node\nimport { main } from \"./cli-main\";\n\nmain(process.argv.slice(2)).then((code) => process.exit(code));\n"],"mappings":"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","names":["files"]}
package/dist/index.cjs CHANGED
@@ -1123,7 +1123,7 @@ function createSseParser(onEvent) {
1123
1123
 
1124
1124
  // src/client.ts
1125
1125
  var FLAGS_SDK_NAME = "@guidelytech/flags";
1126
- var FLAGS_SDK_VERSION = "0.16.0";
1126
+ var FLAGS_SDK_VERSION = "0.17.0";
1127
1127
  var DEFAULT_FLAGS_API_URL = "https://guidelytech.com";
1128
1128
  var SECRET_PREFIX = "gly_sk_";
1129
1129
  var MAX_RESULT_CACHE = 20;
@@ -1210,7 +1210,7 @@ var FlagsClient = class {
1210
1210
  this.environment = o.environment;
1211
1211
  this.apiUrl = (o.apiUrl || DEFAULT_FLAGS_API_URL).replace(/\/+$/, "");
1212
1212
  this.log = o.logger === null ? {} : o.logger ?? { warn: (...a) => console.warn("[Guidely flags]", ...a), error: (...a) => console.error("[Guidely flags]", ...a) };
1213
- if (this.secret && !o.environment) throw new Error("[Guidely flags] `environment` (development | staging | production) is required with a gly_sk_ key.");
1213
+ if (this.secret && !o.environment) throw new Error("[Guidely flags] `environment` (an environment key: development, staging, production or one you added) is required with a gly_sk_ key.");
1214
1214
  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.");
1215
1215
  const dev = o.devOverrides === false ? null : readDevOverrides();
1216
1216
  this.cfg = {