{"version":3,"file":"key-value-record.mjs","sources":["../../src/record/key-value-record.mts"],"sourcesContent":["import { Arr, isRecord, Result, tp } from 'ts-data-forge';\nimport { type ReadonlyRecord } from 'ts-type-forge';\nimport { type AnyType, type Type, type TypeOf } from '../type.mjs';\nimport {\n  createAssertFn,\n  createCastFn,\n  createIsFn,\n  createPrimitiveValidationError,\n  prependPathToValidationErrors,\n  type ValidationError,\n} from '../utils/index.mjs';\n\ntype RecordResultType<\n  K extends Type<string>,\n  V extends AnyType,\n> = ReadonlyRecord<TypeOf<K>, TypeOf<V>>;\n\nexport const keyValueRecord = <K extends Type<string>, V extends AnyType>(\n  keyType: K,\n  valueType: V,\n  options?: Partial<\n    Readonly<{\n      typeName: string;\n    }>\n  >,\n): Type<RecordResultType<K, V>> => {\n  type T = RecordResultType<K, V>;\n\n  const typeName = options?.typeName ?? 'key-value-record';\n\n  // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n  const defaultValue = {} as T;\n\n  const validate: Type<T>['validate'] = (a) => {\n    if (!isRecord(a)) {\n      return Result.err([\n        createPrimitiveValidationError({\n          actualValue: a,\n          expectedType: 'record',\n          typeName,\n          details: undefined,\n        }),\n      ]);\n    }\n\n    const errors: readonly ValidationError[] = Arr.generate(function* () {\n      for (const [k, v] of Object.entries(a)) {\n        {\n          const res = keyType.validate(k);\n\n          if (Result.isErr(res)) {\n            yield {\n              path: [],\n              actualValue: k,\n              expectedType: typeName,\n              typeName,\n              details: {\n                kind: 'record-entry',\n                entry: 'key',\n                expectedType: keyType.typeName,\n              },\n            } satisfies ValidationError;\n\n            yield* res.value;\n          }\n        }\n\n        {\n          const res = valueType.validate(v);\n\n          if (Result.isErr(res)) {\n            yield {\n              path: [],\n              actualValue: v,\n              expectedType: typeName,\n              typeName,\n              details: {\n                kind: 'record-entry',\n                entry: 'value',\n                expectedType: valueType.typeName,\n              },\n            } satisfies ValidationError;\n\n            yield* prependPathToValidationErrors(res.value, k);\n          }\n        }\n      }\n    });\n\n    if (Arr.isNonEmpty(errors)) {\n      return Result.err(errors);\n    }\n\n    // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n    return Result.ok(a as T);\n  };\n\n  const fill: Type<T>['fill'] = (a) =>\n    isRecord(a)\n      ? // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n        (Object.fromEntries(\n          Object.entries(a).filter(\n            ([k, v]) => keyType.is(k) && valueType.is(v),\n          ),\n        ) as T)\n      : defaultValue;\n\n  // Keys not matching keyType are excess paths; values are pruned recursively.\n  const prune = (a: T): T =>\n    // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n    Object.fromEntries(\n      Object.entries(a)\n        .filter(([k]) => keyType.is(k))\n        .map(([k, v]) => tp(k, valueType.prune(v))),\n    ) as T;\n\n  return {\n    typeName,\n    defaultValue,\n    fill,\n    prune,\n    validate,\n    is: createIsFn(validate),\n    assertIs: createAssertFn(validate),\n    cast: createCastFn(validate),\n  };\n};\n"],"names":[],"mappings":"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"}