rotorise
Version:
Supercharge your DynamoDB with Rotorise!
162 lines (148 loc) • 9.53 kB
text/typescript
type KeysOfUnion<ObjectType> = ObjectType extends unknown
? keyof ObjectType
: never
type IsEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends <
G,
>() => G extends U ? 1 : 2
? true
: false
type ArrayElement<T> = T extends readonly unknown[] ? T[0] : never
type ExactObject<ParameterType, InputType> = {
[Key in keyof ParameterType]: Exact<
ParameterType[Key],
Key extends keyof InputType ? InputType[Key] : never
>
} & Record<Exclude<keyof InputType, KeysOfUnion<ParameterType>>, never>
type Exact<ParameterType, InputType> = IsEqual<
ParameterType,
InputType
> extends true
? ParameterType
: // Convert union of array to array of union: A[] & B[] => (A & B)[]
ParameterType extends unknown[]
? Array<Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>>
: // In TypeScript, Array is a subtype of ReadonlyArray, so always test Array before ReadonlyArray.
ParameterType extends readonly unknown[]
? ReadonlyArray<
Exact<ArrayElement<ParameterType>, ArrayElement<InputType>>
>
: ParameterType extends object
? ExactObject<ParameterType, InputType>
: ParameterType
type ValueOf<
ObjectType,
ValueType extends keyof ObjectType = keyof ObjectType,
> = ObjectType[ValueType]
type evaluate<T> = { [K in keyof T]: T[K] } & unknown
type DistributivePick<T, K> = T extends unknown
? K extends keyof T
? Pick<T, K>
: never
: never
type DistributiveOmit<T, K extends keyof T> = T extends unknown
? Omit<T, K>
: never
type SliceFromStart<
T,
End extends number,
Acc extends unknown[] = [],
> = T extends unknown[]
? Acc['length'] extends End
? Acc
: T extends [infer Head, ...infer Tail]
? SliceFromStart<Tail, End, [...Acc, Head]>
: Acc
: never
type MergeIntersectionObject<T, Keys = keyof T> = {
[K in Keys]: T[K]
}
// type t = MergeIntersectionObject<
// { a: 'a1'; b: 1n; extra: 'extra' } | { a: 'a2'; b: 2 }
// >
type NonEmptyArray<T> = [T, ...T[]]
type Replace<T, U, V> = T extends U ? V : T
type CompositeKeyParamsImpl<Entity, InputSpec extends InputSpecShape, skip extends number = 1> = Entity extends unknown ? evaluate<Pick<Entity, extractHeadOrPass<SliceFromStart<InputSpec, number extends skip ? 1 : skip>[number]> & keyof Entity> & Partial<Pick<Entity, extractHeadOrPass<InputSpec[number]> & keyof Entity>>> : never;
type CompositeKeyParams<Entity extends Record<string, unknown>, FullSpec extends InputSpec<MergeIntersectionObject<Entity>>[], skip extends number = 1> = CompositeKeyParamsImpl<Entity, FullSpec, skip>;
type CompositeKeyBuilderImpl<Entity, Spec, Separator extends string = '#', Deep extends number = number, isPartial extends boolean = false> = Entity extends unknown ? Join<CompositeKeyRec<Entity, number extends Deep ? Spec : SliceFromStart<Spec, Deep>>, Separator, (boolean extends isPartial ? false : isPartial) extends false ? false : true> : never;
type CompositeKeyBuilder<Entity extends Record<string, unknown>, Spec extends InputSpec<MergeIntersectionObject<Entity>>[], Separator extends string = '#', Deep extends number = number, isPartial extends boolean = false> = CompositeKeyBuilderImpl<Entity, Spec, Separator, Deep, isPartial>;
type joinable = string | number | bigint | boolean | null | undefined;
type joinablePair = [joinable, joinable];
type Join<Pairs, Separator extends string, KeepIntermediate extends boolean = false, Acc extends string = '', AllAcc extends string = never> = Pairs extends [infer Head extends joinablePair, ...infer Tail] ? Join<Tail, Separator, KeepIntermediate, Acc extends '' ? `${Head[0]}${Separator}${Head[1]}` : `${Acc}${Separator}${Head[0]}${Separator}${Head[1]}`, KeepIntermediate extends true ? AllAcc | (Acc extends '' ? never : Acc) : never> : AllAcc | Acc;
type ExtractPair<Entity, Spec> = Spec extends [
infer Key extends string,
(...key: any[]) => infer Value extends joinable
] ? [Uppercase<Key>, Value] : Spec extends keyof Entity & string ? [Uppercase<Spec>, Entity[Spec] & joinable] : never;
type CompositeKeyRec<Entity, Spec, Acc extends joinablePair[] = [], KeysCache extends string = keyof Entity & string> = Spec extends [infer Head, ...infer Tail] ? CompositeKeyRec<Entity, Tail, [
...Acc,
ExtractPair<Entity, Head>
], KeysCache> : Acc;
type DiscriminatedSchemaShape = {
discriminator: PropertyKey;
spec: {
[k in PropertyKey]: unknown;
};
};
type InputSpecShape = ([PropertyKey, (key: any) => unknown] | PropertyKey)[];
type TableEntryImpl<Entity, Schema, Separator extends string = '#'> = Entity extends unknown ? {
[Key in keyof Schema]: Schema[Key] extends DiscriminatedSchemaShape ? ValueOf<{
[K in Schema[Key]['discriminator']]: {
[V in keyof Schema[Key]['spec']]: ProcessKey<Entity, Schema[Key]['spec'][V], Separator>;
}[Entity[K & keyof Entity] & keyof Schema[Key]['spec']];
}> : ProcessKey<Entity, Schema[Key], Separator>;
} & Entity : never;
type TableEntry<Entity extends Record<string, unknown>, Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = '#'> = TableEntryImpl<Entity, Schema, Separator>;
type InputSpec<E> = {
[key in keyof E]: [key, (key: E[key]) => unknown] | (undefined extends E[key] ? never : null extends E[key] ? never : key);
}[keyof E];
type extractHeadOrPass<T> = T extends unknown[] ? T[0] : T;
type FullKeySpecSimple<Entity> = NonEmptyArray<InputSpec<MergeIntersectionObject<Entity>>> | (keyof Entity & string) | null;
type DiscriminatedSchema<Entity, E> = {
[key in keyof E]: E[key] extends PropertyKey ? {
discriminator: key;
spec: {
[val in E[key]]: FullKeySpecSimple<Extract<Entity, {
[k in key]: val;
}>>;
};
} : never;
}[keyof E];
type FullKeySpec<Entity> = FullKeySpecSimple<Entity> | DiscriminatedSchema<Entity, MergeIntersectionObject<Entity>>;
type ProcessSpecType<Entity, Spec, Config extends SpecConfigShape> = Spec extends string ? DistributivePick<Entity, Spec> : Spec extends InputSpecShape ? CompositeKeyParamsImpl<Entity, Spec, Config['allowPartial'] extends true ? 1 : Extract<Config['depth'], number>> : never;
type SpecConfig<Spec> = Spec extends string ? never : SpecConfigShape;
type SpecConfigShape = {
depth?: number;
allowPartial?: boolean;
};
type VariantType<Entity, K extends PropertyKey, V extends PropertyKey> = [
Entity
] extends [never] ? {
[k in K]: V;
} : Entity & {
[k in K]: V;
};
type ProcessVariant<Entity, K extends PropertyKey, V extends PropertyKey, Spec extends DiscriminatedSchemaShape, Config extends SpecConfigShape> = VariantType<ProcessSpecType<VariantType<Entity, K, V>, Spec['spec'][V & keyof Spec['spec']], Config>, K, V>;
type OptimizedAttributes<Entity, Spec, Config extends SpecConfigShape> = Spec extends DiscriminatedSchemaShape ? {
[K in Spec['discriminator']]: {
[V in keyof Spec['spec']]: ProcessVariant<Entity, K, V, Spec, Config>;
}[keyof Spec['spec']];
}[Spec['discriminator']] : ProcessSpecType<Entity, Spec, Config>;
type ProcessKey<Entity, Spec, Separator extends string, NullAs extends never | undefined = never, Config extends SpecConfigShape = SpecConfigShape, Attributes = Pick<Entity, Spec & keyof Entity>> = [Entity] extends [never] ? never : Spec extends keyof Entity ? Replace<ValueOf<Attributes>, null, undefined> : Spec extends InputSpecShape ? CompositeKeyBuilderImpl<Entity, Spec, Separator, Exclude<Config['depth'], undefined>, Exclude<Config['allowPartial'], undefined>> : Spec extends null ? NullAs : never;
type OptimizedBuildedKey<NarrowEntity, Spec, Separator extends string, Config extends SpecConfigShape, Attributes> = Spec extends DiscriminatedSchemaShape ? {
[K in Spec['discriminator']]: {
[V in keyof Spec['spec']]: ProcessKey<NarrowEntity extends {
[k in K]: V;
} ? NarrowEntity : never, Spec['spec'][V], Separator, undefined, Config, Attributes>;
}[keyof Spec['spec']];
}[Spec['discriminator']] : ProcessKey<NarrowEntity, Spec, Separator, undefined, Config, Attributes>;
type TableEntryDefinition<Entity, Schema, Separator extends string> = {
toEntry: <const ExactEntity extends Exact<Entity, ExactEntity>>(item: ExactEntity) => ExactEntity extends infer E extends Entity ? TableEntryImpl<E, Schema, Separator> : never;
fromEntry: <const Entry extends TableEntryImpl<Entity, Schema, Separator>>(entry: Entry) => DistributiveOmit<Entry, keyof Schema>;
key: <const Key extends keyof Schema, const Config extends SpecConfig<Spec>, const Attributes extends OptimizedAttributes<Entity, Spec, Config_>, Spec = Schema[Key], Config_ extends SpecConfigShape = [SpecConfigShape] extends [Config] ? {
depth?: undefined;
allowPartial?: undefined;
} : Config>(key: Key, attributes: Attributes, config?: Config) => OptimizedBuildedKey<Entity & Attributes, Spec, Separator, Config_, Attributes>;
infer: TableEntryImpl<Entity, Schema, Separator>;
path: () => TableEntryImpl<Entity, Schema, Separator>;
};
declare const tableEntry: <const Entity extends Record<string, unknown>>() => <const Schema extends Record<string, FullKeySpec<Entity>>, Separator extends string = "#">(schema: Schema, separator?: Separator) => TableEntryDefinition<Entity, Schema, Separator>;
export { type CompositeKeyBuilder, type CompositeKeyParams, type CompositeKeyParamsImpl, type TableEntry, tableEntry };