/** * Parameter validators for Tinybird pipe queries * Similar to the column type validators but for query parameters */ // Symbol for brand typing - use Symbol.for() for global registry // This ensures the same symbol is used across module instances const PARAM_BRAND = Symbol.for("tinybird.param"); /** * Base interface for parameter validators */ export interface ParamValidator< TType, TTinybirdType extends string = string, TRequired extends boolean = true > { readonly [PARAM_BRAND]: true; /** The inferred TypeScript type */ readonly _type: TType; /** The Tinybird type string for the parameter */ readonly _tinybirdType: TTinybirdType; /** Whether this parameter is required */ readonly _required: TRequired; /** Default value if optional */ readonly _default?: TType; /** Description for documentation */ readonly _description?: string; /** Whether required/optional was explicitly set by a modifier */ readonly _requiredModifier?: "required" | "optional"; /** Make this parameter optional with an optional default value */ optional( defaultValue?: TDefault ): ParamValidator< TDefault extends undefined ? TType | undefined : TType, TTinybirdType, false >; /** Make this parameter required (default) */ required(): ParamValidator; /** Add a description for this parameter */ describe(description: string): ParamValidator; } // Internal implementation interface ParamValidatorImpl extends ParamValidator { readonly tinybirdType: TTinybirdType; readonly isRequired: TRequired; readonly defaultValue?: TType; readonly description?: string; } function createParamValidator< TType, TTinybirdType extends string, TRequired extends boolean = true >( tinybirdType: TTinybirdType, options: { required?: TRequired; defaultValue?: TType; description?: string; requiredModifier?: "required" | "optional"; } = {} ): ParamValidator { const isRequired = (options.required ?? true) as TRequired; const validator: ParamValidatorImpl = { [PARAM_BRAND]: true, _type: undefined as unknown as TType, _tinybirdType: tinybirdType, _required: isRequired, _default: options.defaultValue, _description: options.description, _requiredModifier: options.requiredModifier, tinybirdType, isRequired, defaultValue: options.defaultValue, description: options.description, optional(defaultValue?: TDefault) { return createParamValidator< TDefault extends undefined ? TType | undefined : TType, TTinybirdType, false >(tinybirdType, { required: false, defaultValue: defaultValue as TDefault extends undefined ? TType | undefined : TType, description: options.description, requiredModifier: "optional", }); }, required() { return createParamValidator(tinybirdType, { required: true, description: options.description, requiredModifier: "required", }); }, describe(description: string) { return createParamValidator(tinybirdType, { required: isRequired, defaultValue: options.defaultValue, description, requiredModifier: options.requiredModifier, }); }, }; return validator; } /** * Parameter validators for Tinybird pipe queries * * @example * ```ts * import { p } from '@tinybirdco/sdk'; * * const params = { * user_id: p.string(), * limit: p.int32().optional(10), * start_date: p.dateTime().describe('Start of date range'), * }; * ``` */ export const p = { // ============ String Types ============ /** String parameter */ string: () => createParamValidator("String"), /** UUID parameter */ uuid: () => createParamValidator("UUID"), // ============ Integer Types ============ /** Int8 parameter */ int8: () => createParamValidator("Int8"), /** Int16 parameter */ int16: () => createParamValidator("Int16"), /** Int32 parameter */ int32: () => createParamValidator("Int32"), /** Int64 parameter */ int64: () => createParamValidator("Int64"), /** UInt8 parameter */ uint8: () => createParamValidator("UInt8"), /** UInt16 parameter */ uint16: () => createParamValidator("UInt16"), /** UInt32 parameter */ uint32: () => createParamValidator("UInt32"), /** UInt64 parameter */ uint64: () => createParamValidator("UInt64"), // ============ Float Types ============ /** Float32 parameter */ float32: () => createParamValidator("Float32"), /** Float64 parameter */ float64: () => createParamValidator("Float64"), // ============ Boolean ============ /** Boolean parameter */ boolean: () => createParamValidator("Boolean"), // ============ Date/Time Types ============ /** Date parameter (YYYY-MM-DD format, e.g. 2024-01-15) */ date: () => createParamValidator("Date"), /** DateTime parameter (YYYY-MM-DD HH:MM:SS format, e.g. 2024-01-15 10:30:00) */ dateTime: () => createParamValidator("DateTime"), /** DateTime64 parameter (YYYY-MM-DD HH:MM:SS[.fraction] format, e.g. 2024-01-15 10:30:00.123) */ dateTime64: () => createParamValidator("DateTime64"), // ============ Array Types ============ /** * Array parameter - values can be passed as comma-separated or repeated params * @param _element - The type of array elements (used for type inference) * @param _separator - Optional custom separator (default: comma) */ array: >( _element: TElement, _separator?: string ): ParamValidator => createParamValidator("Array", { required: true, }), // ============ Special Types ============ /** * Column reference parameter - allows dynamic column selection * Use with caution as it can affect query safety */ column: () => createParamValidator("column"), /** * JSON parameter - for passing complex structured data */ json: () => createParamValidator("JSON"), } as const; /** Type alias for any parameter validator */ export type AnyParamValidator = ParamValidator; /** Extract the TypeScript type from a parameter validator */ export type InferParamType = T["_required"] extends true ? T["_type"] : T["_type"] | undefined; /** Check if a value is a parameter validator */ export function isParamValidator(value: unknown): value is AnyParamValidator { return ( typeof value === "object" && value !== null && PARAM_BRAND in value && (value as Record)[PARAM_BRAND] === true ); } /** Get the Tinybird type string from a parameter validator */ export function getParamTinybirdType(validator: AnyParamValidator): string { return validator._tinybirdType; } /** Check if a parameter is required */ export function isParamRequired(validator: AnyParamValidator): boolean { return validator._required; } /** Get the default value of a parameter */ export function getParamDefault(validator: ParamValidator): T | undefined { return validator._default; } /** Get the description of a parameter */ export function getParamDescription(validator: AnyParamValidator): string | undefined { return validator._description; } /** Check whether required/optional was explicitly set by a modifier */ export function getParamRequiredModifier( validator: AnyParamValidator ): "required" | "optional" | undefined { return validator._requiredModifier; }