synthex
Version:
Type-safe LLM response simulation with streaming & error injection
508 lines (507 loc) • 18.8 kB
TypeScript
declare class SchemaIO {
/**
* Serialize a schema to JSON string.
*/
static toJSON(schema: SchemaForm): string;
/**
* Deserialize a schema from JSON string.
*/
static fromJSON(json: string): SchemaForm;
/**
* Serialize a schema to YAML string (if js-yaml is available).
*/
static toYAML(schema: SchemaForm): string;
/**
* Deserialize a schema from YAML string (if js-yaml is available).
*/
static fromYAML(yamlStr: string): SchemaForm;
/**
* Save a schema to a file (JSON or YAML by extension).
*/
static saveToFile(schema: SchemaForm, filePath: string): void;
/**
* Load a schema from a file (JSON or YAML by extension).
*/
static loadFromFile(filePath: string): SchemaForm;
}
export { SchemaIO };
/**
* Type for a literal value or a weighted object for enums.
*/
type EnumValue<T> = T | {
value: T;
weight: number;
};
/**
* Defines the structure and constraints for a single field within a schema.
* Represents the internal representation of a field after the builder constructs it.
*/
interface SchemaField<T = any> {
type: "string" | "number" | "boolean" | "array" | "object" | "enum" | "uuid" | "email" | "url" | "date" | "union" | "intersection" | "nullable" | "reference";
required?: boolean;
min?: number;
max?: number;
items?: SchemaField;
properties?: Record<string, SchemaField>;
enum?: Array<EnumValue<any>>;
pattern?: string;
format?: "date" | "date-time";
unionTypes?: SchemaField[];
intersectionTypes?: SchemaField[];
reference?: string;
nullableType?: SchemaField;
probability?: number;
condition?: (currentData: Record<string, any>, globalContext: Record<string, any>) => boolean;
template?: string;
simulateError?: boolean;
errorType?: string;
generateFn?: (context: Record<string, any>, currentData: Record<string, any>, rng: RandomGenerator) => T;
}
/**
* Represents a compiled schema for structured data, typically for an object type.
* This is what m.object().build() returns.
*/
declare class SchemaForm<T = any> implements SchemaField<T> {
type: SchemaField["type"];
_isSchemaForm: true;
name?: string;
version?: string;
fields: Record<string, SchemaField<any>>;
/**
* This property is only for type extraction, not runtime use.
* Use as: type MyType = typeof schema.infer
*/
readonly infer: T;
required?: boolean;
min?: number;
max?: number;
items?: SchemaField;
properties?: Record<string, SchemaField>;
enum?: Array<EnumValue<any>>;
pattern?: string;
format?: "date" | "date-time";
unionTypes?: SchemaField[];
intersectionTypes?: SchemaField[];
reference?: string;
nullableType?: SchemaField;
probability?: number;
condition?: (currentData: Record<string, any>, globalContext: Record<string, any>) => boolean;
template?: string;
simulateError?: boolean;
errorType?: string;
generateFn?: (context: Record<string, any>, currentData: Record<string, any>, rng: RandomGenerator) => T;
constructor(params: {
name?: string;
version?: string;
fields: Record<string, SchemaField<any>>;
required?: boolean;
min?: number;
max?: number;
items?: SchemaField;
properties?: Record<string, SchemaField>;
enum?: Array<EnumValue<any>>;
pattern?: string;
format?: "date" | "date-time";
unionTypes?: SchemaField[];
intersectionTypes?: SchemaField[];
reference?: string;
nullableType?: SchemaField;
probability?: number;
condition?: (currentData: Record<string, any>, globalContext: Record<string, any>) => boolean;
template?: string;
simulateError?: boolean;
errorType?: string;
generateFn?: (context: Record<string, any>, currentData: Record<string, any>, rng: RandomGenerator) => T;
});
}
/**
* Represents a collection of schemas, allowing for structured data definitions
* and references between them.
*/
interface SchemaCollection {
name: string;
version: string;
schemas: SchemaForm[];
}
/**
* Represents a mock response structure, including the schema and the data.
*/
interface MockResponse<T = any> {
schema: SchemaForm;
data: T;
metadata?: {
generatedAt: string;
generator: string;
version: string;
seed?: number;
tokenUsage: number;
modelInfo: string;
latencyMs: number;
roles?: Array<"system" | "user" | "assistant" | "tool">;
log?: {
requestTime: number;
requestId: string;
};
rateLimit?: number;
quota?: number;
quotaUsed?: number;
finishReason?: string;
stopSequence?: string;
[key: string]: any;
};
tokens?: string[];
finishReason?: string;
}
/**
* Configuration options for the mock generator.
*/
interface MockGeneratorOptions {
seed?: number;
locale?: string;
dateRange?: {
start: Date;
end: Date;
};
simulateError?: boolean;
errorProbability?: number;
context?: Record<string, any>;
metadata?: Record<string, any>;
tokenUsage?: number;
modelInfo?: string;
latencyMs?: number;
randomness?: "deterministic" | "fuzz" | "random";
outputFormat?: "json" | "xml" | "markdown";
maxTokens?: number;
roles?: Array<"system" | "user" | "assistant" | "tool">;
logTrace?: boolean;
rateLimit?: number;
rateLimitIntervalMs?: number;
quota?: number;
quotaUsed?: number;
abortSignal?: AbortSignal;
hallucinate?: boolean;
hallucinationProbability?: number;
simulateFunctionCall?: boolean;
streamChunkSize?: number;
streamDelayMs?: number;
}
/**
* Error class for schema validation and generation errors.
*/
declare class SyntexError extends Error {
code: string;
constructor(message: string, code: string);
}
/**
* Utility class for generating random values with constraints.
*
* */
declare class RandomGenerator {
private seed;
private rng;
private randomness;
constructor(seed?: number, randomness?: "deterministic" | "fuzz" | "random");
private createSeededRandom;
random(): number;
randomInt(min: number, max: number): number;
randomChoice<T>(array: T[]): T;
randomWeightedChoice<T>(items: Array<EnumValue<T>>): T;
randomString(length: number, charset?: string): string;
randomEmail(): string;
randomUrl(): string;
randomUuid(): string;
randomDate(start?: Date, end?: Date): Date;
randomBoolean(): boolean;
}
/**
* Main mock generator class that creates structured data based on schemas.
*/
type SynthexPlugin = {
name: string;
onInit?: (generator: MockGenerator) => void;
onGenerateField?: (field: SchemaField, context: Record<string, any>, currentData: Record<string, any>, rng: RandomGenerator) => any | undefined;
};
declare class MockGenerator {
private rng;
private options;
private requestCount;
private lastReset;
private registeredSchemas;
private static _plugins;
private _plugins;
static registerPlugin(plugin: SynthexPlugin): void;
constructor(options?: MockGeneratorOptions);
/**
* Registers a schema to be available for references within other schemas.
* @param schema The SchemaForm to register.
*/
registerSchema(schema: SchemaForm): void;
/**
* Simulate streaming tokens for a given string field.
* Returns an array of tokens and a finish reason.
*/
streamTokens(text: string, maxTokens?: number, stopSequence?: string): {
tokens: string[];
finishReason: string;
};
/**
* Generates mock data based on the provided schema form.
*/
generate<T>(schema: SchemaForm<T>): MockResponse<T>;
/**
* Generates multiple mock responses based on the schema.
*/
generateMultiple<T>(schema: SchemaForm<T>, count: number): MockResponse<T>[];
/**
* Generates mock data from a schema collection by name.
*/
generateFromCollection<T>(collection: SchemaCollection, schemaName: string): MockResponse<T>;
streamGenerate<T>(schema: SchemaForm<T>, chunkSize?: number, delayMs?: number): AsyncGenerator<Record<string, any>, void, unknown>;
/**
* Simulate hallucination for a field (random or nonsense value).
*/
private simulateHallucination;
/**
* Simulate an OpenAI function/tool call response.
*/
simulateFunctionCall(functionName: string, args?: Record<string, any>): any;
formatOutput(response: MockResponse): string;
private toXML;
private toMarkdown;
private validateSchema;
private validateField;
private generateObjectData;
private interpolateTemplate;
private generateFieldValue;
private generateString;
private generatePatternString;
private generateNumber;
private generateArray;
private generateDate;
private handleRateLimiting;
private handleQuota;
private handleGlobalErrorSimulation;
}
/**
* Base class for all schema field builders.
* Handles common modifiers like required, min, max, probability, condition, template, etc.
* @template TInfer The inferred TypeScript type of the field.
*/
declare abstract class SBase<TInfer = any> {
protected _field: Partial<SchemaField>;
protected _type: SchemaField["type"];
constructor(type: SchemaField["type"]);
/** Marks the field as required. */
required(): this;
/** Marks the field as optional (default behavior). */
optional(): this;
/** Sets the minimum value for numbers, minimum length for strings/arrays. */
min(val: number): this;
/** Sets the maximum value for numbers, maximum length for strings/arrays. */
max(val: number): this;
/** Sets a regex pattern for string generation. */
pattern(p: string): this;
/** Sets a format for date generation (e.g., "date-time" or "date"). */
format(f: "date" | "date-time"): this;
/** Sets the probability (0-1) that this field will be included in the output. */
probability(p: number): this;
/**
* Defines a condition function for field inclusion.
* The field will only be generated if the condition returns true.
* @param conditionFn A function that receives the `currentData` (generated fields within the current object) and `globalContext` and returns a boolean.
*/
condition(conditionFn: (currentData: Record<string, any>, globalContext: Record<string, any>) => boolean): this;
/**
* A simplified `when` helper for common conditional logic.
* Includes the field only if `fieldName` in `currentData` equals `value`.
* For more complex logic, use `condition()`.
* @param fieldName The name of another field in the same object.
* @param value The value the `fieldName` should have for this field to be included.
*/
when(fieldName: string, value: any): this;
/** Sets a string template for the field's value. Can use `{{key}}` for context values or other fields. */
template(t: string): this;
/** Enables simulation of an error for this specific field. */
simulateError(enable?: boolean): this;
/** Sets a specific error type for a simulated field error. */
errorType(type: string): this;
/**
* Provides a custom function to generate the field's value.
* This overrides all other generation methods for this field.
* @param fn A function that receives the `globalContext`, `currentData`, and `rng` instance and returns the value.
*/
generate(fn: (context: Record<string, any>, currentData: Record<string, any>, rng: RandomGenerator) => TInfer): this;
/** Returns the compiled SchemaField object. */
build(): SchemaField;
}
declare class SString extends SBase<string> {
constructor();
}
declare class SNumber extends SBase<number> {
constructor();
}
declare class SBoolean extends SBase<boolean> {
constructor();
}
declare class SUUID extends SBase<string> {
constructor();
}
declare class SEmail extends SBase<string> {
constructor();
}
declare class SURL extends SBase<string> {
constructor();
}
declare class SDate extends SBase<string> {
constructor();
}
declare class SEnum<T extends string | number | boolean> extends SBase<T> {
constructor(values: Array<EnumValue<T>>);
}
declare class SArray<TItem extends SBase> extends SBase<Array<InferSBase<TItem>>> {
constructor(item: TItem);
}
/**
* Helper type to extract the inferred type from a `SBase` instance.
* `typeof m.string().infer` would be `string`.
*/
type InferSBase<T extends SBase> = T extends SBase<infer U> ? U : never;
/**
* Maps a record of SBase types to their inferred TypeScript types.
* Used for `SObject` to get the final inferred type.
*/
type MapSBaseToInfer<T extends Record<string, SBase>> = {
[K in keyof T]: InferSBase<T[K]> extends SchemaForm<infer O> ? O : InferSBase<T[K]>;
};
declare class SObject<TFields extends Record<string, SBase>, TInferred = MapSBaseToInfer<TFields>> extends SBase<TInferred> {
private _fields;
constructor(fields: TFields);
/**
* Extends the current object schema with additional fields.
* @param additionalFields A record of new SBase fields to add.
*/
extend<TExtraFields extends Record<string, SBase>>(additionalFields: TExtraFields): SObject<TFields & TExtraFields, TInferred & MapSBaseToInfer<TExtraFields>>;
/**
* Creates a new object schema with only the specified fields.
* @param keys An array of keys to pick from the current schema.
*/
pick<K extends keyof TFields>(keys: K[]): SObject<Pick<TFields, K>, unknown>;
/**
* Creates a new object schema by omitting the specified fields.
* @param keys An array of keys to omit from the current schema.
*/
omit<K extends keyof TFields>(keys: K[]): SObject<Omit<TFields, K>, unknown>;
/** Returns the compiled SchemaField object. */
build(name?: string, version?: string): SchemaForm<TInferred>;
}
declare class SUnion<TTypes extends SBase[]> extends SBase<InferSBase<TTypes[number]>> {
constructor(types: TTypes);
build(): SchemaField<InferSBase<TTypes[number]>> & {
readonly infer: InferSBase<TTypes[number]>;
};
}
declare class SIntersection<TTypes extends SBase[]> extends SBase<InferSBase<TTypes[number]>> {
constructor(types: TTypes);
build(): SchemaField<InferSBase<TTypes[number]>> & {
readonly infer: InferSBase<TTypes[number]>;
};
}
declare class SNullable<TType extends SBase> extends SBase<InferSBase<TType> | null> {
constructor(type: TType);
build(): SchemaField<InferSBase<TType> | null> & {
readonly infer: InferSBase<TType> | null;
};
}
declare class SReference extends SBase<any> {
constructor(ref: string);
}
declare class SnapshotUtils {
static toSnapshot(data: any): string;
static compareSnapshot(data: any, snapshot: string): boolean;
}
declare class DocGenerator {
static toMarkdown(schema: SchemaForm): string;
static fieldType(field: SchemaField): string;
}
/**
* Creates instances of the MockGenerator with various configurations.
*/
declare class SyntexFactory {
static createGenerator(options?: MockGeneratorOptions): MockGenerator;
static createSeededGenerator(seed: number): MockGenerator;
static createLocalizedGenerator(locale: string, options?: MockGeneratorOptions): MockGenerator;
}
declare class SchemaUtils {
/**
* Creates a basic SchemaForm for an object type.
* @deprecated Use `m.object({...}).build('SchemaName', '1.0.0')` directly for better type inference.
*/
static createBasicSchema(name: string, fields: Record<string, SchemaField>): SchemaForm;
static createCollection(name: string, schemas: SchemaForm[]): SchemaCollection;
/**
* Merges two SchemaForms (object types). The fields from `extensionSchema` will override those in `baseSchema`.
* @param baseSchema The base SchemaForm.
* @param extensionSchema The SchemaForm to extend with.
* @returns A new SchemaForm with merged fields.
*/
static mergeSchemas<T1, T2>(baseSchema: SchemaForm<T1>, extensionSchema: SchemaForm<T2>): SchemaForm<T1 & T2>;
static validateData(data: any, schema: SchemaForm): boolean;
static validateType(value: any, field: SchemaField): boolean;
}
/**
* Utility for generating TypeScript interface strings from schemas.
*/
declare class TypeInference {
/**
* Generates a TypeScript interface string from a compiled SchemaForm.
* This is provided for documentation/tooling; for direct type inference,
* use `typeof mySchema.infer`.
* @param schema The SchemaForm object.
* @returns A string representing the TypeScript interface.
*/
static inferType(schema: SchemaForm): string;
static fieldType(field: SchemaField): string;
}
/**
* The main entry point for building Syntex schemas.
*/
export declare const s: {
string: () => SString;
number: () => SNumber;
boolean: () => SBoolean;
uuid: () => SUUID;
email: () => SEmail;
url: () => SURL;
date: () => SDate;
/**
* Defines an enum field. Can accept a simple array of values or an array of objects
* with `value` and `weight` for probabilistic generation.
* @example
* m.enum(['red', 'green', 'blue'])
* m.enum([{ value: 'admin', weight: 0.8 }, { value: 'user', weight: 0.2 }])
*/
enum: <T extends string | number | boolean>(values: Array<EnumValue<T>>) => SEnum<T>;
array: <TItem extends SBase>(item: TItem) => SArray<TItem>;
/**
* Defines an object field with nested properties.
* @example
* m.object({
* id: m.uuid().required(),
* name: m.string(),
* }).build('UserSchema');
*/
object: <TFields extends Record<string, SBase>>(fields: TFields) => SObject<TFields, MapSBaseToInfer<TFields>>;
union: <TTypes extends SBase[]>(types: TTypes) => SUnion<TTypes>;
intersection: <TTypes extends SBase[]>(types: TTypes) => SIntersection<TTypes>;
nullable: <TType extends SBase>(type: TType) => SNullable<TType>;
/**
* Defines a reference to another named schema within a SchemaCollection.
* The referenced schema must be registered with the MockGenerator or included in a collection.
* @example
* m.object({
* organization: m.reference('OrganizationSchema')
* })
*/
reference: (ref: string) => SReference;
};
export { SchemaField, SchemaForm, SchemaCollection, MockResponse, MockGeneratorOptions, SyntexError, MockGenerator, SyntexFactory, TypeInference, SnapshotUtils, DocGenerator, SchemaUtils, SynthexPlugin, };