import type { AnyEntityESchema, AnyUnkeyedESchema, ESchemaEncoded, ESchemaType, } from '../../../eschema/index.js'; import type { LogicalTableSnapshot } from '../snapshot/index.js'; import { makeTableBuilder } from './table-definition.js'; export type { LogicalEntitySnapshot, LogicalTableSnapshot, } from '../snapshot/index.js'; export interface PrimaryIndex< Pk extends string = string, Sk extends string = string, > { readonly pk: Pk; readonly sk: Sk; } export interface LocalSecondaryIndex< Name extends string = string, Pk extends string = string, Sk extends string = string, > extends PrimaryIndex { readonly name: Name; readonly kind: 'lsi'; } export interface GlobalSecondaryIndex< Name extends string = string, Pk extends string = string, Sk extends string = string, > extends PrimaryIndex { readonly name: Name; readonly kind: 'gsi'; } export type LocalSecondaryIndexMap = Readonly< Record >; export type GlobalSecondaryIndexMap = Readonly< Record >; type EncodedStringKey = { [Key in keyof ESchemaEncoded & string]: NonNullable< ESchemaEncoded[Key] > extends string ? Key : never; }[keyof ESchemaEncoded & string]; export type IndexComponent = | Exclude, '_v'> | '_u'; export interface AccessPatternDefinition< Slot extends string | undefined = string | undefined, Kind extends 'primary' | 'lsi' | 'gsi' = 'primary' | 'lsi' | 'gsi', Pk extends readonly string[] = readonly string[], Sk extends readonly string[] = readonly string[], > { readonly index?: Slot; readonly kind: Kind; readonly pk: Pk; readonly sk: Sk; } export type AccessPatternMap = Readonly< Record >; export interface KeyedEntityDefinition< TableName extends string = string, TSchema extends AnyEntityESchema = AnyEntityESchema, PrimaryPk extends readonly string[] = readonly string[], PrimarySk extends readonly string[] = readonly string[], Patterns extends AccessPatternMap = AccessPatternMap, > { readonly kind: 'keyed'; readonly name: TSchema['name']; readonly table: TableDefinition; readonly schema: TSchema; readonly primary: { readonly pk: PrimaryPk; readonly sk: PrimarySk }; readonly accessPatterns: Patterns; } export interface SingleEntityDefinition< TableName extends string = string, TSchema extends AnyUnkeyedESchema = AnyUnkeyedESchema, > { readonly kind: 'single'; readonly name: TSchema['name']; readonly table: TableDefinition; readonly schema: TSchema; readonly defaultValue: ESchemaType; } export interface KeyedEntityBuilderStart< TableName extends string, TSchema extends AnyEntityESchema, Lsis extends LocalSecondaryIndexMap, Gsis extends GlobalSecondaryIndexMap, > { primary< const Pk extends readonly Exclude, '_u'>[] = [], >(derivation?: { readonly pk: Pk; }): KeyedEntityBuilder< TableName, TSchema, Lsis, Gsis, Pk, readonly [TSchema['idField']], Record< 'primary', AccessPatternDefinition< undefined, 'primary', Pk, readonly [TSchema['idField']] > > >; } type LsiDerivation = { readonly sk: readonly IndexComponent[]; readonly pk?: never; }; type GsiDerivation = { readonly pk: readonly IndexComponent[]; readonly sk?: readonly IndexComponent[]; }; export type PatternFor< Slot extends string, TSchema extends AnyEntityESchema, Lsis extends LocalSecondaryIndexMap, Pk extends readonly string[], Derivation extends LsiDerivation | GsiDerivation, > = Slot extends keyof Lsis ? AccessPatternDefinition< Slot, 'lsi', Pk, Derivation extends { readonly sk: infer Sk extends readonly string[] } ? Sk : never > : AccessPatternDefinition< Slot, 'gsi', Derivation extends { readonly pk: infer GsiPk extends readonly string[] } ? GsiPk : never, Derivation extends { readonly sk: infer GsiSk extends readonly string[] } ? GsiSk : readonly ['_u'] >; export interface KeyedEntityBuilder< TableName extends string, TSchema extends AnyEntityESchema, Lsis extends LocalSecondaryIndexMap, Gsis extends GlobalSecondaryIndexMap, PrimaryPk extends readonly string[], PrimarySk extends readonly string[], Patterns extends AccessPatternMap, > { index< const Slot extends keyof (Lsis & Gsis) & string, const Name extends string, const Derivation extends (Slot extends keyof Lsis ? LsiDerivation : GsiDerivation), >( slot: Slot, name: Name, derivation: Derivation, ): KeyedEntityBuilder< TableName, TSchema, Lsis, Gsis, PrimaryPk, PrimarySk, Patterns & Record> >; build(): KeyedEntityDefinition< TableName, TSchema, PrimaryPk, PrimarySk, Patterns >; } export interface SingleEntityBuilder< TableName extends string, TSchema extends AnyUnkeyedESchema, > { default( defaultValue: ESchemaType, ): SingleEntityDefinition; } export interface TableDefinition< Name extends string = string, Primary extends PrimaryIndex = PrimaryIndex, Lsis extends LocalSecondaryIndexMap = LocalSecondaryIndexMap, Gsis extends GlobalSecondaryIndexMap = GlobalSecondaryIndexMap, > { readonly logicalName: Name; readonly primary: Primary; readonly localSecondaryIndexes: Lsis; readonly globalSecondaryIndexes: Gsis; entity( schema: TSchema, ): KeyedEntityBuilderStart; singleEntity( schema: TSchema, ): SingleEntityBuilder; snapshot(): LogicalTableSnapshot; readonly registeredEntities: readonly ( | KeyedEntityDefinition | SingleEntityDefinition )[]; } export interface TableTopologyBuilder< Name extends string, Primary extends PrimaryIndex, Lsis extends LocalSecondaryIndexMap, Gsis extends GlobalSecondaryIndexMap, > { lsi( name: Slot, sk: Sk, ): TableTopologyBuilder< Name, Primary, Lsis & Record>, Gsis >; gsi< const Slot extends string, const Pk extends string, const Sk extends string, >( name: Slot, pk: Pk, sk: Sk, ): TableTopologyBuilder< Name, Primary, Lsis, Gsis & Record> >; build(): TableDefinition; } export interface TableBuilder { readonly logicalName: Name; primary( pk: Pk, sk: Sk, ): TableTopologyBuilder, {}, {}>; } type NonEmpty = Name extends '' ? never : Name; export const Table = { make( logicalName: NonEmpty, ): TableBuilder { return makeTableBuilder(logicalName); }, assertUniqueNames( definitions: Definitions, ): Definitions { const names = new Set(); for (const definition of definitions) { if (names.has(definition.logicalName)) { throw new Error( `Logical Table "${definition.logicalName}" is already defined`, ); } names.add(definition.logicalName); } return definitions; }, } as const;