{"version":3,"file":"validation.mjs","names":[],"sources":["../../../src/batteries/vector/validation.ts"],"sourcesContent":["/**\n * Runtime validation schemas and throwing wrappers for the vector battery.\n *\n * @module @nhtio/adk/batteries/vector/validation\n */\n\nimport { vectorFilterSchema } from './filters'\nimport { validator, ValidationError } from '@nhtio/validation'\nimport { implementsVectorStoreConstructor } from './vector_store_constructor'\nimport { E_INVALID_VECTOR_STORE_CONFIG, E_INVALID_VECTOR_RECORD } from './exceptions'\nimport type { VectorRecord } from './types'\n\nconst detail = (e: ValidationError): string => e.details.map((d) => d.message).join('; ')\n\n/** Validator schema for {@link @nhtio/adk/batteries/vector!BaseVectorStoreOptions}; allows adapter-specific extra keys. */\nexport const baseVectorStoreOptionsSchema = validator\n  .object({\n    metric: validator.string().valid('cosine', 'dot', 'euclidean').optional(),\n    encoder: validator\n      .custom((value, helpers) => {\n        if (value === undefined) return value\n        if (typeof value === 'function') return value\n        return helpers.error('any.invalid')\n      })\n      .optional(),\n    dimensions: validator.number().integer().min(1).optional(),\n    defaultCollection: validator.string().optional(),\n    consistency: validator.string().valid('strong', 'best-effort', 'eventual').optional(),\n  })\n  .unknown(true)\n\n/** Validator schema for a single {@link @nhtio/adk/batteries/vector!VectorRecord}; rejects unknown keys. */\nexport const vectorRecordSchema = validator\n  .object({\n    id: validator.string().min(1).required(),\n    vector: validator.array().items(validator.number()).optional(),\n    document: validator.string().optional(),\n    metadata: validator.object().unknown(true).optional(),\n  })\n  .unknown(false)\n\nexport { vectorFilterSchema }\n\n/**\n * `client` accepts three forms, validated as alternatives:\n *   1. the adapter class itself           — a BaseVectorStore subclass constructor\n *   2. a sync resolver `() => Class`       — returns the class\n *   3. an async resolver `() => import(…)` — resolves to the class (e.g. a dynamic import)\n * Forms 2 and 3 are both plain functions that are NOT store constructors; the factory awaits\n * them and re-validates the resolved value (see resolveClientCtor).\n */\nconst clientSchema = validator.alternatives(\n  // form 1: an actual adapter class\n  validator.custom((v, h) => (implementsVectorStoreConstructor(v) ? v : h.error('any.invalid'))),\n  // forms 2 & 3: a (sync or async) resolver function that is not itself a store class\n  validator.custom((v, h) =>\n    typeof v === 'function' && !implementsVectorStoreConstructor(v) ? v : h.error('any.invalid')\n  )\n)\n\n/**\n * Validate a {@link @nhtio/adk/batteries/vector!CreateVectorStoreConfig} shape (client + options).\n *\n * @param input - The raw config to validate.\n * @throws {@link @nhtio/adk/batteries/vector!E_INVALID_VECTOR_STORE_CONFIG} when the shape is invalid.\n */\nexport const validateCreateConfig = (input: unknown): void => {\n  const schema = validator\n    .object({\n      client: clientSchema.required(),\n      options: validator.object().unknown(true).required(),\n    })\n    .unknown(true)\n  const { error } = schema.validate(input, { abortEarly: false, convert: false })\n  if (error) throw new E_INVALID_VECTOR_STORE_CONFIG([detail(error)])\n}\n\n/**\n * Resolve the `client` (class | sync resolver | async resolver) down to the adapter constructor,\n * then validate the resolved value is a BaseVectorStore subclass — throwing the same\n * E_INVALID_VECTOR_STORE_CONFIG if a resolver hands back something that isn't one.\n */\nexport const resolveClientCtor = async (client: unknown): Promise<new (options: any) => any> => {\n  let resolved: unknown = client\n  // A resolver is a plain function that is not itself a store class; call it (it may be async).\n  // A bare non-store class is also a function — invoking it throws \"cannot invoke without new\";\n  // we catch that and fall through to the not-a-store rejection below rather than leaking it.\n  if (typeof client === 'function' && !implementsVectorStoreConstructor(client)) {\n    try {\n      resolved = await (client as () => unknown)()\n    } catch {\n      resolved = undefined\n    }\n    // A resolver may return a module namespace; unwrap a `.default` export if present.\n    if (resolved && typeof resolved === 'object' && 'default' in (resolved as object)) {\n      const def = (resolved as { default?: unknown }).default\n      if (implementsVectorStoreConstructor(def)) resolved = def\n    }\n  }\n  if (!implementsVectorStoreConstructor(resolved)) {\n    throw new E_INVALID_VECTOR_STORE_CONFIG([\n      'client must be, or resolve to, a vector store constructor (a BaseVectorStore subclass)',\n    ])\n  }\n  return resolved as new (options: any) => any\n}\n\n/**\n * Validate a batch of {@link @nhtio/adk/batteries/vector!VectorRecord}s, including that vectors contain only finite numbers.\n *\n * @param records - The records to validate.\n * @throws {@link @nhtio/adk/batteries/vector!E_INVALID_VECTOR_RECORD} (carrying the offending index) on the first invalid record.\n */\nexport const validateRecords = (records: VectorRecord[]): void => {\n  for (const [i, record] of records.entries()) {\n    const { error } = vectorRecordSchema.validate(record, { abortEarly: false, convert: false })\n    if (error) throw new E_INVALID_VECTOR_RECORD([i, detail(error)])\n    const vec = record.vector\n    if (vec && !vec.every((n) => Number.isFinite(n))) {\n      throw new E_INVALID_VECTOR_RECORD([i, 'vector contains a non-finite number'])\n    }\n  }\n}\n"],"mappings":";;;;;;;;;;AAYA,IAAM,UAAU,MAA+B,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI;;AAGxF,IAAa,+BAA+B,UACzC,OAAO;CACN,QAAQ,UAAU,OAAO,EAAE,MAAM,UAAU,OAAO,WAAW,EAAE,SAAS;CACxE,SAAS,UACN,QAAQ,OAAO,YAAY;EAC1B,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,IAAI,OAAO,UAAU,YAAY,OAAO;EACxC,OAAO,QAAQ,MAAM,aAAa;CACpC,CAAC,EACA,SAAS;CACZ,YAAY,UAAU,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,SAAS;CACzD,mBAAmB,UAAU,OAAO,EAAE,SAAS;CAC/C,aAAa,UAAU,OAAO,EAAE,MAAM,UAAU,eAAe,UAAU,EAAE,SAAS;AACtF,CAAC,EACA,QAAQ,IAAI;;AAGf,IAAa,qBAAqB,UAC/B,OAAO;CACN,IAAI,UAAU,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;CACvC,QAAQ,UAAU,MAAM,EAAE,MAAM,UAAU,OAAO,CAAC,EAAE,SAAS;CAC7D,UAAU,UAAU,OAAO,EAAE,SAAS;CACtC,UAAU,UAAU,OAAO,EAAE,QAAQ,IAAI,EAAE,SAAS;AACtD,CAAC,EACA,QAAQ,KAAK;;;;;;;;;AAYhB,IAAM,eAAe,UAAU,aAE7B,UAAU,QAAQ,GAAG,MAAO,iCAAiC,CAAC,IAAI,IAAI,EAAE,MAAM,aAAa,CAAE,GAE7F,UAAU,QAAQ,GAAG,MACnB,OAAO,MAAM,cAAc,CAAC,iCAAiC,CAAC,IAAI,IAAI,EAAE,MAAM,aAAa,CAC7F,CACF;;;;;;;AAQA,IAAa,wBAAwB,UAAyB;CAO5D,MAAM,EAAE,UANO,UACZ,OAAO;EACN,QAAQ,aAAa,SAAS;EAC9B,SAAS,UAAU,OAAO,EAAE,QAAQ,IAAI,EAAE,SAAS;CACrD,CAAC,EACA,QAAQ,IACO,EAAO,SAAS,OAAO;EAAE,YAAY;EAAO,SAAS;CAAM,CAAC;CAC9E,IAAI,OAAO,MAAM,IAAI,8BAA8B,CAAC,OAAO,KAAK,CAAC,CAAC;AACpE;;;;;;AAOA,IAAa,oBAAoB,OAAO,WAAwD;CAC9F,IAAI,WAAoB;CAIxB,IAAI,OAAO,WAAW,cAAc,CAAC,iCAAiC,MAAM,GAAG;EAC7E,IAAI;GACF,WAAW,MAAO,OAAyB;EAC7C,QAAQ;GACN,WAAW,KAAA;EACb;EAEA,IAAI,YAAY,OAAO,aAAa,YAAY,aAAc,UAAqB;GACjF,MAAM,MAAO,SAAmC;GAChD,IAAI,iCAAiC,GAAG,GAAG,WAAW;EACxD;CACF;CACA,IAAI,CAAC,iCAAiC,QAAQ,GAC5C,MAAM,IAAI,8BAA8B,CACtC,wFACF,CAAC;CAEH,OAAO;AACT;;;;;;;AAQA,IAAa,mBAAmB,YAAkC;CAChE,KAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,QAAQ,GAAG;EAC3C,MAAM,EAAE,UAAU,mBAAmB,SAAS,QAAQ;GAAE,YAAY;GAAO,SAAS;EAAM,CAAC;EAC3F,IAAI,OAAO,MAAM,IAAI,wBAAwB,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC;EAC/D,MAAM,MAAM,OAAO;EACnB,IAAI,OAAO,CAAC,IAAI,OAAO,MAAM,OAAO,SAAS,CAAC,CAAC,GAC7C,MAAM,IAAI,wBAAwB,CAAC,GAAG,qCAAqC,CAAC;CAEhF;AACF"}