axiom
Version:
Axiom AI SDK provides - an API to wrap your AI calls with observability instrumentation. - offline evals - online evals
128 lines (125 loc) • 5.79 kB
TypeScript
import { ZodObject, ZodDefault, z } from 'zod';
type DefaultMaxDepth = 8;
type HasDefaults<S> = S extends {
_zod: {
def: {
defaultValue: unknown;
};
};
} ? true : S extends ZodObject<infer Shape> ? {
[K in keyof Shape]: HasDefaults<Shape[K]>;
} extends Record<keyof Shape, true> ? true : false : false;
type UnwrapSchema<T> = T extends ZodDefault<infer U> ? U : T;
type AllFieldsHaveDefaults<Schema> = Schema extends {
_zod: {
def: {
defaultValue: unknown;
};
};
} ? true : HasDefaults<UnwrapSchema<Schema>>;
interface AppScopeConfig<FlagSchema extends ZodObject<any> | undefined = undefined, FactSchema extends ZodObject<any> | undefined = undefined> {
flagSchema: FlagSchema;
factSchema?: FactSchema;
}
/**
* Recursive type to extract all possible paths from an object type.
* Uses stack-based depth limiting for better performance.
*
* @template T - The object type to extract paths from
* @template Stack - Internal stack counter (do not set manually)
* @template MaxDepth - Maximum recursion depth (default: 8 for good balance)
*/
type ObjectPaths<T, Stack extends unknown[] = [], MaxDepth extends number = DefaultMaxDepth> = Stack['length'] extends MaxDepth ? never : T extends object ? {
[K in keyof T]-?: K extends string | number ? `${K}` | `${K}.${ObjectPaths<T[K], [1, ...Stack], MaxDepth>}` : never;
}[keyof T] : never;
type ObjectPathValue<T, P extends string> = P extends keyof T ? T[P] : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? ObjectPathValue<T[K], Rest> : never : never;
/**
* Generate deep nested paths from flag schema.
*
* @template T - ZodObject to extract paths from
* @template MaxDepth - Maximum recursion depth (default: 8, override for deeper nesting)
* @example
* // Default 8-level depth
* type Paths = DotPaths<MySchema>
*
* // Custom depth for deeper nesting (impacts performance)
* type DeepPaths = DotPaths<MySchema, 12>
*/
type DotPaths<T extends ZodObject<any>, MaxDepth extends number = DefaultMaxDepth> = {
[NS in keyof T['shape']]: (string & NS) | {
[P in ObjectPaths<z.output<UnwrapSchema<T['shape'][NS]>>, [
], MaxDepth>]: `${string & NS}.${P}`;
}[ObjectPaths<z.output<UnwrapSchema<T['shape'][NS]>>, [], MaxDepth>];
}[keyof T['shape']];
type PathValue<T extends ZodObject<any>, P extends string> = P extends `${infer NS}.${infer Rest}` ? NS extends keyof T['shape'] ? ObjectPathValue<z.output<UnwrapSchema<T['shape'][NS]>>, Rest> : never : P extends keyof T['shape'] ? z.output<UnwrapSchema<T['shape'][P]>> : never;
type DotNotationFlagFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? <P extends DotPaths<FS>>(path: P) => PathValue<FS, P> : never;
type FactFunction<SC extends ZodObject<any> | undefined> = SC extends ZodObject<any> ? <P extends DotPaths<SC> & string>(name: P, value: PathValue<SC, P>) => void : never;
type OverrideFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? (partial: {
[K in DotPaths<FS>]?: PathValue<FS, K>;
}) => void : (partial: Record<string, any>) => void;
type WithFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? <T>(overrides: {
[K in DotPaths<FS>]?: PathValue<FS, K>;
}, fn: () => T) => T : <T>(overrides: Record<string, any>, fn: () => T) => T;
type PickFlagsFunction<FS extends ZodObject<any> | undefined> = FS extends ZodObject<any> ? {
<K extends ReadonlyArray<DotPaths<FS> & string>>(...paths: K): K;
<K extends ReadonlyArray<DotPaths<FS> & string>>(paths: K): K;
} : never;
interface AppScope<FS extends ZodObject<any> | undefined, SC extends ZodObject<any> | undefined> {
flag: DotNotationFlagFunction<FS>;
fact: FactFunction<SC>;
overrideFlags: OverrideFlagsFunction<FS>;
withFlags: WithFlagsFunction<FS>;
pickFlags: PickFlagsFunction<FS>;
getAllDefaultFlags: () => Record<string, any>;
}
/**
* Create a new application-level evaluation scope.
*
* @param config.flagSchema A zod object describing the schema for flags **(required)**
* @param config.factSchema A zod object describing the schema for facts (optional)
*
* @example
* import { z } from 'zod';
*
* const { flag, fact, withFlags, pickFlags, overrideFlags } = createAppScope({
* flagSchema: z.object({
* ui: z.object({
* darkMode: z.boolean().default(false),
* theme: z.object({
* primary: z.string().default('#00f'),
* }),
* }),
* api: z.object({
endpoint: z.string().default('/api')
}),
* }),
* factSchema: z.object({
* userAction: z.string(),
* timing: z.number(),
* }),
* });
*
* // Typed flag access
* const dark = flag('ui.darkMode'); // inferred boolean
* const theme = flag('ui.theme'); // entire object
* const primary = flag('ui.theme.primary'); // '#00f'
* const endpoint = flag('api.endpoint'); // uses schema default
*
* // Typed fact recording
* fact('userAction', 'clicked_button');
* fact('timing', 1250);
*
* // Temporarily override flags for a block of code
* withFlags({ 'ui.darkMode': true }, () => {
* // code here, `ui.darkMode` will be true in this block and reset after
* });
*
* // Override flags globally for the current evaluation run
* overrideFlags({ 'api.endpoint': '/custom' });
*/
declare function createAppScope<FlagSchema extends ZodObject<any>, FactSchema extends ZodObject<any> | undefined = undefined>(config: AllFieldsHaveDefaults<FlagSchema> extends true ? AppScopeConfig<FlagSchema, FactSchema> : {
flagSchema: FlagSchema;
factSchema?: FactSchema;
__error__: 'createAppScope: flagSchema must have .default() for all leaf fields';
}): AppScope<FlagSchema, FactSchema>;
export { createAppScope as c };