cwait/packages/lib/src/shared/utils/claimKeys.ts

107 lines
3.7 KiB
TypeScript

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<TFirstPart extends unknown[], TSecondPart extends unknown[] | null> = {
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<TCreateFirstPart>) => [string, ...ReturnType<TCreateFirstPart>]
: () => [string];
secondPart: TCreateSecondPart;
} & (TCreateFirstPart extends ClaimKeySecondPartConstructor
? (
...args: [...Parameters<TCreateFirstPart>, ...Parameters<TCreateSecondPart>]
) => ClaimKey<[string, ...ReturnType<TCreateFirstPart>], ReturnType<TCreateSecondPart>>
: (...args: Parameters<TCreateSecondPart>) => ClaimKey<[string], ReturnType<TCreateSecondPart>>);
type ConstructKeys<T extends ClaimKeyDefinitions> = {
[Key in keyof T]: T[Key] extends ClaimKeyDefinitions
? ConstructKeys<T[Key]>
: T[Key] extends ClaimKeyConstructors
? KeyConstructor<T[Key]['secondPart'], T[Key]['firstPart']>
: never;
};
const domainPropSymbol = Symbol('domain');
export const constructClaimKeys = <T extends ClaimKeyDefinitions>(domain: string, keys: T): ConstructKeys<T> =>
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<typeof firstPartDefinedConstructor>) => [
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<T>;