import { constructClaimKey } from '@coinweb/contract-kit'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ClaimKeyFirstPartConstructor = (...args: any[]) => unknown[]; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ClaimKeySecondPartConstructor = (...args: any[]) => unknown[] | null; type ClaimKeyConstructors = { firstPart?: ClaimKeyFirstPartConstructor; secondPart: ClaimKeySecondPartConstructor; }; type ClaimKey = { first_part: TFirstPart; second_part: TSecondPart; }; export type ClaimKeyDefinitions = { [Key: string]: ClaimKeyConstructors | ClaimKeyDefinitions; }; type KeyConstructor< TCreateSecondPart extends (...args: unknown[]) => unknown[] | null, TCreateFirstPart extends ((...args: unknown[]) => unknown[]) | undefined = undefined, > = { firstPart: TCreateFirstPart extends ClaimKeyFirstPartConstructor ? (...args: Parameters) => [string, ...ReturnType] : () => [string]; secondPart: TCreateSecondPart; } & (TCreateFirstPart extends ClaimKeySecondPartConstructor ? ( ...args: [...Parameters, ...Parameters] ) => ClaimKey<[string, ...ReturnType], ReturnType> : (...args: Parameters) => ClaimKey<[string], ReturnType>); type ConstructKeys = { [Key in keyof T]: T[Key] extends ClaimKeyDefinitions ? ConstructKeys : T[Key] extends ClaimKeyConstructors ? KeyConstructor : never; }; const domainPropSymbol = Symbol('domain'); export const constructClaimKeys = (domain: string, keys: T): ConstructKeys => new Proxy(keys as ClaimKeyDefinitions & { [domainPropSymbol]?: string }, { get: (target, prop) => { if (typeof prop !== 'string') { throw new Error('Property is not a string'); } const value = target[prop]; const currentDomain = target[domainPropSymbol] ? `${target[domainPropSymbol]}_${prop}` : domain; if ('secondPart' in value) { const secondPart = value.secondPart; const firstPartDefinedConstructor = value.firstPart; if (typeof firstPartDefinedConstructor === 'function') { const firstPart = (...args: Parameters) => [ currentDomain, ...firstPartDefinedConstructor(...args), ]; return { firstPart, secondPart, }; } return { firstPart: () => [currentDomain], secondPart, }; } (value as ClaimKeyDefinitions & { [domainPropSymbol]: string })[domainPropSymbol] = currentDomain; return value; }, apply(target, prop, argArray) { const value = target[prop]; const currentDomain = target[domainPropSymbol] ? `${target[domainPropSymbol]}_${prop}` : domain; const secondPartConstructor = value.secondPart; if (typeof secondPartConstructor !== 'function') { throw new Error('Property is not callable'); } const secondPartArgs = argArray.slice(argArray.length - secondPartConstructor.length); const secondPart = secondPartConstructor(...secondPartArgs); const firstPartDefinedConstructor = value.firstPart; const firstPart: unknown[] = [currentDomain]; if (typeof firstPartDefinedConstructor === 'function') { firstPart.push(...firstPartDefinedConstructor(...argArray)); } return constructClaimKey(firstPart, secondPart); }, }) as ConstructKeys;