From 6dab3a84463e261801715b2152dc18f18dd016aa Mon Sep 17 00:00:00 2001 From: Ola Adebayo Date: Sat, 14 Mar 2026 23:16:26 +0000 Subject: [PATCH 1/2] fix(cli): add defaultValue overloads and typed key unions to typegen output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated .d.ts was missing the defaultValue overload signatures for get() in both the REP interface and standalone exports, and standalone get()/getSecure() used key: string instead of the typed key union. Now fully mirrors the SDK's actual overload signatures per §5.2. --- cli/src/commands/typegen.ts | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/cli/src/commands/typegen.ts b/cli/src/commands/typegen.ts index 021be6b..b0fab25 100644 --- a/cli/src/commands/typegen.ts +++ b/cli/src/commands/typegen.ts @@ -77,19 +77,23 @@ function generateTypeDefinition(publicVars: string[], sensitiveVars: string[]): lines.push(' export interface REP {'); lines.push(' /**'); lines.push(' * Get a PUBLIC tier variable value synchronously.'); + lines.push(' * When called with a defaultValue the return type narrows to string.'); lines.push(' */'); - + if (publicVars.length > 0) { - lines.push(' get(key: ' + publicVars.map(v => `"${v}"`).join(' | ') + '): string | undefined;'); + const keyUnion = publicVars.map(v => `"${v}"`).join(' | '); + lines.push(' get(key: ' + keyUnion + ', defaultValue: string): string;'); + lines.push(' get(key: ' + keyUnion + ', defaultValue?: undefined): string | undefined;'); } else { - lines.push(' get(key: string): string | undefined;'); + lines.push(' get(key: string, defaultValue: string): string;'); + lines.push(' get(key: string, defaultValue?: undefined): string | undefined;'); } - + lines.push(''); lines.push(' /**'); lines.push(' * Get a SENSITIVE tier variable value asynchronously.'); lines.push(' */'); - + if (sensitiveVars.length > 0) { lines.push(' getSecure(key: ' + sensitiveVars.map(v => `"${v}"`).join(' | ') + '): Promise;'); } else { @@ -131,8 +135,21 @@ function generateTypeDefinition(publicVars: string[], sensitiveVars: string[]): lines.push(' }'); lines.push(''); lines.push(' export const rep: REP;'); - lines.push(' export function get(key: string): string | undefined;'); - lines.push(' export function getSecure(key: string): Promise;'); + + if (publicVars.length > 0) { + const keyUnion = publicVars.map(v => `"${v}"`).join(' | '); + lines.push(' export function get(key: ' + keyUnion + ', defaultValue: string): string;'); + lines.push(' export function get(key: ' + keyUnion + ', defaultValue?: undefined): string | undefined;'); + } else { + lines.push(' export function get(key: string, defaultValue: string): string;'); + lines.push(' export function get(key: string, defaultValue?: undefined): string | undefined;'); + } + + if (sensitiveVars.length > 0) { + lines.push(' export function getSecure(key: ' + sensitiveVars.map(v => `"${v}"`).join(' | ') + '): Promise;'); + } else { + lines.push(' export function getSecure(key: string): Promise;'); + } lines.push(' export function getAll(): Readonly>;'); lines.push(' export function onChange(key: string, callback: (newValue: string, oldValue: string | undefined) => void): () => void;'); lines.push(' export function onAnyChange(callback: (key: string, newValue: string, oldValue: string | undefined) => void): () => void;'); From cf2c39c3cb6e018fa78dc3a3282d30f32df21ff4 Mon Sep 17 00:00:00 2001 From: Ola Adebayo Date: Sat, 14 Mar 2026 23:33:34 +0000 Subject: [PATCH 2/2] docs(cli): update typegen section with defaultValue overloads and strict key enforcement note --- docs/src/content/docs/reference/cli.mdx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/src/content/docs/reference/cli.mdx b/docs/src/content/docs/reference/cli.mdx index 8d7fa98..7da7871 100644 --- a/docs/src/content/docs/reference/cli.mdx +++ b/docs/src/content/docs/reference/cli.mdx @@ -50,13 +50,23 @@ rep typegen [--manifest ] [--output ] ```typescript declare module "@rep-protocol/sdk" { export interface REP { - get(key: "API_URL" | "FEATURE_FLAGS"): string | undefined; + // Overloaded: passing a defaultValue narrows the return type to string. + get(key: "API_URL" | "FEATURE_FLAGS", defaultValue: string): string; + get(key: "API_URL" | "FEATURE_FLAGS", defaultValue?: undefined): string | undefined; getSecure(key: "ANALYTICS_KEY"): Promise; // ... other methods } + export function get(key: "API_URL" | "FEATURE_FLAGS", defaultValue: string): string; + export function get(key: "API_URL" | "FEATURE_FLAGS", defaultValue?: undefined): string | undefined; + export function getSecure(key: "ANALYTICS_KEY"): Promise; + // ... } ``` +**Strict by design** — the key unions are closed. Calling `rep.get('UNDECLARED_KEY')` is a TypeScript error if that variable is not declared in `.rep.yaml`. This is intentional: it forces every variable the app reads to be explicitly declared in the manifest, keeping the manifest the single source of truth. The fix is always to add the missing variable to `.rep.yaml` and re-run `rep typegen`, not to widen the types. + +If you don't want strict type enforcement, skip `rep typegen` and rely on the base SDK types (`key: string`). + ## `rep lint` Scan built JavaScript bundles for accidentally leaked secrets. Uses the same guardrail detection as the gateway (Shannon entropy, known secret formats).