{"version":3,"file":"tuple-validator.mjs","names":[],"sources":["../../../../../../../@warlock.js/seal/src/validators/tuple-validator.ts"],"sourcesContent":["import { setKeyPath } from \"../helpers\";\r\nimport { arrayRule } from \"../rules\";\r\nimport type { SchemaContext, ValidationResult } from \"../types\";\r\nimport { BaseValidator } from \"./base-validator\";\r\nimport { applyNullable } from \"../standard-schema/json-schema\";\r\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\r\n\r\n/**\r\n * Tuple validator class - validates fixed-length arrays with position-specific types\r\n *\r\n * @example\r\n * ```ts\r\n * // RGB color tuple\r\n * v.tuple([v.number(), v.number(), v.number()])\r\n * // Valid: [255, 128, 0]\r\n * // Invalid: [255, 128] (too short)\r\n *\r\n * // Mixed types\r\n * v.tuple([v.string(), v.int(), v.boolean()])\r\n * // Valid: [\"John\", 25, true]\r\n * ```\r\n */\r\nexport class TupleValidator extends BaseValidator {\r\n  public constructor(\r\n    public validators: BaseValidator[],\r\n    errorMessage?: string,\r\n  ) {\r\n    super();\r\n    this.addMutableRule(arrayRule, errorMessage);\r\n  }\r\n\r\n  /**\r\n   * Check if value is an array type\r\n   */\r\n  public matchesType(value: any): boolean {\r\n    return Array.isArray(value);\r\n  }\r\n\r\n  /**\r\n   * Clone the validator\r\n   */\r\n  public override clone(): this {\r\n    const cloned = super.clone();\r\n    cloned.validators = this.validators.map((v) => v.clone());\r\n    return cloned;\r\n  }\r\n\r\n  /**\r\n   * Validate tuple - check length then validate each position\r\n   *\r\n   * Absent input (and absent without `.default()`) propagates as `data: undefined`\r\n   * so the parent ObjectValidator can omit the key. Without this, optional tuple\r\n   * fields would silently materialise as `[]` in the validated output and then\r\n   * fail the length check.\r\n   */\r\n  public async validate(data: any, context: SchemaContext): Promise<ValidationResult> {\r\n    // Apply default when absent, then mutate. Mirrors BaseValidator's\r\n    // `valueForRules = data ?? this.getDefaultValue()` so `.default([...])`\r\n    // works on tuples too.\r\n    const valueForRules = data ?? this.getDefaultValue();\r\n    const mutatedData = await this.mutate(valueForRules, context);\r\n\r\n    const result = await super.validate(data, context);\r\n\r\n    if (result.isValid === false) return result;\r\n\r\n    // Nothing to iterate for absent (no default) or null (nullable) inputs —\r\n    // propagate so the parent ObjectValidator can omit the key.\r\n    if (mutatedData === undefined || mutatedData === null) return result;\r\n\r\n    // Defensive: type rule (arrayRule) should have failed for non-arrays.\r\n    if (!Array.isArray(mutatedData)) return result;\r\n\r\n    const errors: ValidationResult[\"errors\"] = [];\r\n\r\n    // Tuple-specific: length validation\r\n    if (mutatedData.length !== this.validators.length) {\r\n      errors.push({\r\n        type: \"tuple\",\r\n        input: context.key || \"value\",\r\n        error: `Expected exactly ${this.validators.length} items, but got ${mutatedData.length}`,\r\n      });\r\n      return { isValid: false, errors, data: mutatedData };\r\n    }\r\n\r\n    // Validate each position with its specific validator in parallel\r\n    const validationPromises = this.validators.map(async (validator, index) => {\r\n      const childContext: SchemaContext = {\r\n        ...context,\r\n        parent: mutatedData,\r\n        value: mutatedData[index],\r\n        key: index.toString(),\r\n        path: setKeyPath(context.path, index.toString()),\r\n      };\r\n\r\n      const childResult = await validator.validate(mutatedData[index], childContext);\r\n\r\n      // Update mutated data with validated result\r\n      mutatedData[index] = childResult.data;\r\n\r\n      // Collect errors from this element\r\n      if (childResult.isValid === false) {\r\n        errors.push(...childResult.errors);\r\n      }\r\n    });\r\n\r\n    await Promise.all(validationPromises);\r\n\r\n    return {\r\n      isValid: errors.length === 0,\r\n      errors,\r\n      data: await this.startTransformationPipeline(mutatedData, context),\r\n    };\r\n  }\r\n\r\n  /**\r\n   * @inheritdoc\r\n   *\r\n   * Tuple keyword diverges between targets:\r\n   * - `draft-2020-12` → `prefixItems` + `items: false` (exact length enforced)\r\n   * - `draft-07`      → `items` as array + `additionalItems: false`\r\n   * - `openapi-3.0`   → same as draft-07 (OpenAPI 3.0 is based on draft-07)\r\n   *\r\n   * @example\r\n   * ```ts\r\n   * v.tuple([v.string(), v.int(), v.boolean()]).toJsonSchema(\"draft-2020-12\")\r\n   * // → {\r\n   * //   type: \"array\",\r\n   * //   prefixItems: [{ type: \"string\" }, { type: \"integer\" }, { type: \"boolean\" }],\r\n   * //   items: false,\r\n   * //   minItems: 3,\r\n   * //   maxItems: 3\r\n   * // }\r\n   * ```\r\n   */\r\n  public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\r\n    const itemSchemas = this.validators.map(v => v.toJsonSchema(target));\r\n    const length = this.validators.length;\r\n\r\n    const schema: JsonSchemaResult = {\r\n      type: \"array\",\r\n      minItems: length,\r\n      maxItems: length,\r\n    };\r\n\r\n    if (target === \"draft-2020-12\") {\r\n      // prefixItems is the draft-2020-12 keyword for positional tuple items\r\n      schema.prefixItems = itemSchemas;\r\n      schema.items = false; // no additional items beyond the tuple\r\n    } else {\r\n      // draft-07 and openapi-3.0: items as array + additionalItems: false\r\n      schema.items = itemSchemas;\r\n      schema.additionalItems = false;\r\n    }\r\n\r\n    if (this.isNullable) applyNullable(schema, target);\r\n\r\n    return schema;\r\n  }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,iBAAb,cAAoC,cAAc;CAChD,AAAO,YACL,AAAO,YACP,cACA;EACA,MAAM;EAHC;EAIP,KAAK,eAAe,WAAW,YAAY;CAC7C;;;;CAKA,AAAO,YAAY,OAAqB;EACtC,OAAO,MAAM,QAAQ,KAAK;CAC5B;;;;CAKA,AAAgB,QAAc;EAC5B,MAAM,SAAS,MAAM,MAAM;EAC3B,OAAO,aAAa,KAAK,WAAW,KAAK,MAAM,EAAE,MAAM,CAAC;EACxD,OAAO;CACT;;;;;;;;;CAUA,MAAa,SAAS,MAAW,SAAmD;EAIlF,MAAM,gBAAgB,QAAQ,KAAK,gBAAgB;EACnD,MAAM,cAAc,MAAM,KAAK,OAAO,eAAe,OAAO;EAE5D,MAAM,SAAS,MAAM,MAAM,SAAS,MAAM,OAAO;EAEjD,IAAI,OAAO,YAAY,OAAO,OAAO;EAIrC,IAAI,gBAAgB,UAAa,gBAAgB,MAAM,OAAO;EAG9D,IAAI,CAAC,MAAM,QAAQ,WAAW,GAAG,OAAO;EAExC,MAAM,SAAqC,CAAC;EAG5C,IAAI,YAAY,WAAW,KAAK,WAAW,QAAQ;GACjD,OAAO,KAAK;IACV,MAAM;IACN,OAAO,QAAQ,OAAO;IACtB,OAAO,oBAAoB,KAAK,WAAW,OAAO,kBAAkB,YAAY;GAClF,CAAC;GACD,OAAO;IAAE,SAAS;IAAO;IAAQ,MAAM;GAAY;EACrD;EAGA,MAAM,qBAAqB,KAAK,WAAW,IAAI,OAAO,WAAW,UAAU;GACzE,MAAM,eAA8B;IAClC,GAAG;IACH,QAAQ;IACR,OAAO,YAAY;IACnB,KAAK,MAAM,SAAS;IACpB,MAAM,WAAW,QAAQ,MAAM,MAAM,SAAS,CAAC;GACjD;GAEA,MAAM,cAAc,MAAM,UAAU,SAAS,YAAY,QAAQ,YAAY;GAG7E,YAAY,SAAS,YAAY;GAGjC,IAAI,YAAY,YAAY,OAC1B,OAAO,KAAK,GAAG,YAAY,MAAM;EAErC,CAAC;EAED,MAAM,QAAQ,IAAI,kBAAkB;EAEpC,OAAO;GACL,SAAS,OAAO,WAAW;GAC3B;GACA,MAAM,MAAM,KAAK,4BAA4B,aAAa,OAAO;EACnE;CACF;;;;;;;;;;;;;;;;;;;;;CAsBA,AAAgB,aAAa,SAA2B,iBAAmC;EACzF,MAAM,cAAc,KAAK,WAAW,KAAI,MAAK,EAAE,aAAa,MAAM,CAAC;EACnE,MAAM,SAAS,KAAK,WAAW;EAE/B,MAAM,SAA2B;GAC/B,MAAM;GACN,UAAU;GACV,UAAU;EACZ;EAEA,IAAI,WAAW,iBAAiB;GAE9B,OAAO,cAAc;GACrB,OAAO,QAAQ;EACjB,OAAO;GAEL,OAAO,QAAQ;GACf,OAAO,kBAAkB;EAC3B;EAEA,IAAI,KAAK,YAAY,cAAc,QAAQ,MAAM;EAEjD,OAAO;CACT;AACF"}