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;'); 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).