{{#each instances}}
{{#eq type "entity"}}
export declare namespace {{pascal name}} {
    namespace Where {
        {{#each attributes}}
        const {{pascal name}}Symbol: unique symbol;

        {{/each}}
        {{#each attributes}}
        interface {{pascal name}} {
            [{{pascal name}}Symbol]: void;
        }

        {{/each}}
        type AttributeName = {{{union (pascal attributeNames)}}}

        type AttributeType<T extends AttributeName> =
            {{#each attributes}}
            T extends {{pascal name}} ? {{type}} :
            {{/each}}
            never;
        
        type Operations = {
            eq: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            gt: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            lt: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            gte: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            lte: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            between: <T extends AttributeName>(attr: T, value: AttributeType<T>, value2: AttributeType<T>) => string;
            begins: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            exists: <T extends AttributeName>(attr: T) => string;
            notExists: <T extends AttributeName>(attr: T) => string;
            contains: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            notContains: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            value: <T extends AttributeName>(attr: T, value: AttributeType<T>) => string;
            name: <T extends AttributeName>(attr: T) => string;
        };

        type Attributes = {
            {{#each attributes}}
            {{name}}: {{pascal name}};
            {{/each}}
        }

        type Callback = (attr: Attributes, op: Operations) => string;
    }

    namespace Enums {
    {{#each enums}}
        export type {{name}} = {{{stringUnion values}}};
    {{/each}}
    }

    {{#each enums}}
    export type {{name}} = {{{stringUnion values}}};

    {{/each}}
    type TableIndexNames = {{{stringUnion tableIndexFields}}};
    
    export type Item = {
    {{#each attributes}}
        {{name}}{{#if optional}}?{{/if}}: {{type}};
    {{/each}}
    }
    
    export type Attributes = {
    {{#each attributes}}
        {{name}}: {{type}};
    {{/each}}
    }
    
    export type RawItem = {
    {{#each keyNames}}
        {{this}}: string;
    {{/each}}
    {{#each attributes}}
        {{name}}{{#if optional}}?{{/if}}: {{type}};
    {{/each}}
    }
    
    export type config = {
        raw?: boolean;
        params?: object;
        includeKeys?: boolean;
        originalErr?: boolean;
    }
    
    export type NonReadOnlyProperties = {
    {{#each attributes}}
        {{#if readonly}}
        {{else}}
        {{name}}{{#if optional}}?{{/if}}: {{type}};
        {{/if}}
    {{/each}}
    }
    
    {{#each indexes}}
    export type {{pascal typeName}}Facets = {
    {{#each facets}}
        {{this.name}}: {{this.type}};
    {{/each}}
    }

    export type {{pascal typeName}} = {{{ buildIndexType facets }}};
    
    {{#each facets}}
    export type {{pascal ../typeName}}{{pascal this.name}}Remainders = {{{ buildIndexType ../facets this.name}}};
    {{/each}}

    type {{pascal typeName}}RemainingFacets<T extends {{pascal typeName}}> = 
    {{#each facets}}
        Omit<{{pascal ../typeName}}Facets, keyof T> extends {{pascal ../typeName}}{{pascal this.name}}Remainders ? Required<{{pascal ../typeName}}{{pascal this.name}}Remainders> :
    {{/each}}
        never;

    {{#if index}}
    export type {{pascal typeName}}PK = {{{ buildIndexType (filterIndexType facets "pk") }}};

    export type {{pascal typeName}}SK = {{{ buildIndexType (filterIndexType facets "sk") }}};

    {{/if}}
    {{/each}}
    
    export type FilterOperations<T> = {
        gte: (value: T) => string;
        gt: (value: T) => string;
        lte: (value: T) => string;
        lt: (value: T) => string;
        eq: (value: T) => string;
        begins: (value: T) => string;
        exists: () => T;
        notExists: () => T;
        contains: (value: T) => string;
        notContains: (value: T) => string;
        between: (start: T, end: T) => string;
        name: () => T;
        value: (value: T) => string;
    };
    
    export type FilterAttributes<T extends Attributes> = {
        [K in keyof T]: FilterOperations<T[K]>;
    }

    export type GoOptions = {
        params?: object;
        raw?: boolean;
        includeKeys?: boolean;
        originalErr?: boolean;
        lastEvaluatedKeyRaw?: boolean;
        table?: string;
    };

    export type ParamOptions = {
        params?: object;
        table?: string;
    }

    export type BatchGoOptions = GoOptions & {concurrent?: number};
    
    export type GoRecord<T, O extends GoOptions = GoOptions> = (options?: O) => Promise<T>;

    export type PageRecord<T> = (page?: {{pascal tableIndexName}} | null, options?: GoOptions) => Promise<[{{pascal tableIndexName}}Facets | null, T]>;

    export type ParamRecord<T = object> = (options?: ParamOptions) => T;

    export type FilterRecords<T> = (filter: <A extends Attributes>(record: FilterAttributes<A>) => string) => RecordsActionOptions<T>;

    export type WhereRecords<T> = (where: Where.Callback) => RecordsActionOptions<T>;

    export type RecordsActionOptions<T> = {
        go: GoRecord<T>;
        params: ParamRecord;
        page: PageRecord<T>;
        filter: FilterRecords<T>;
        where: WhereRecords<T>;
    }

    export type BatchRecordOptions<T> = {
        go: GoRecord<T, BatchGoOptions>;
        params: ParamRecord<object[]>;
    }

    export type SingleRecordOperationOptions<T, P = object> = {
        go: GoRecord<T>;
        params: ParamRecord<P>;
        where: WhereRecords<T>;
    };
    
    export type SetRecordActionOptions<T> = {
        go: GoRecord<T>;
        params: ParamRecord;
        filter: FilterRecords<T>;
        page: PageRecord<T>;
        set: SetRecord<T>;
        where: WhereRecords<T>;
    }
    
    export type SetRecord<T> = (properties: Partial<NonReadOnlyProperties>) => SetRecordActionOptions<T>;
    
    export type QueryOperations<T> = {
        between: (skFacetsStart: T, skFacetsEnd: T) => RecordsActionOptions<Item[]>;
        gt: (skFacets: T) => RecordsActionOptions<Item[]>;
        gte: (skFacets: T) => RecordsActionOptions<Item[]>;
        lt: (skFacets: T) => RecordsActionOptions<Item[]>;
        lte: (skFacets: T) => RecordsActionOptions<Item[]>;
        begins: (skFacets: T) => RecordsActionOptions<Item[]>;
        go: GoRecord<Item[]>;
        params: ParamRecord;
        page: PageRecord<Item[]>;
        filter: FilterRecords<Item[]>;
        where: WhereRecords<Item[]>;
    }
    
    export class {{pascal name}} {
        get(key: {{pascal tableIndexName}}Facets): SingleRecordOperationOptions<Item>;
        get(key: {{pascal tableIndexName}}Facets[]): BatchRecordOptions<[Item[], {{pascal tableIndexName}}Facets[]]>;
        delete(key: {{pascal tableIndexName}}Facets): SingleRecordOperationOptions<Item>;
        delete(key: {{pascal tableIndexName}}Facets[]): BatchRecordOptions<{{pascal tableIndexName}}Facets[]>;
        update(key: {{pascal tableIndexName}}Facets): {set: SetRecord<Item>};
        patch(key: {{pascal tableIndexName}}Facets): {set: SetRecord<Item>};
        put(record: Item): SingleRecordOperationOptions<Item>;
        put(record: Item[]): BatchRecordOptions<Item[]>;
        create(record: Item): SingleRecordOperationOptions<Item>;
        find(record: Partial<Item>): RecordsActionOptions<Item[]>;
        setIdentifier(type: "model" | "table", value: string): void;
        scan: RecordsActionOptions<Item[]>
        query: {
            {{#each indexes}}
            {{#if hasSK}}
            {{name}}<T extends {{pascal typeName}}>(key: T): QueryOperations<{{pascal typeName}}RemainingFacets<T>>;
            {{else}}
            {{name}}(key: {{pascal typeName}}): RecordsActionOptions<Item[]>;
            {{/if}}
            {{/each}}
        };
        {{#each staticProperties}}
        {{name}}: {{{value}}};
        {{/each}}
    }
}
{{/eq}}
{{#eq type "collection"}}
export declare namespace {{pascal name}}Collection {
    namespace Where {
        {{#each attributes}}
        const {{pascal name}}Symbol: unique symbol;

        {{/each}}
        {{#each attributes}}
        interface {{pascal name}} {
            [{{pascal name}}Symbol]: void;
        }

        {{/each}}
        type AttributesName = {{{union (pascal attributeNames)}}};

        type AttributeType<T extends AttributesName> =
        {{#each attributes}}
            T extends {{pascal name}} ? {{type}} :
        {{/each}}
            never;
        
        type Operations = {
            eq: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            gt: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            lt: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            gte: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            lte: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            between: <T extends AttributesName>(attr: T, value: AttributeType<T>, value2: AttributeType<T>) => string;
            begins: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            exists: <T extends AttributesName>(attr: T) => string;
            notExists: <T extends AttributesName>(attr: T) => string;
            contains: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            notContains: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            value: <T extends AttributesName>(attr: T, value: AttributeType<T>) => string;
            name: <T extends AttributesName>(attr: T) => string;
        };

        type Attributes = {
            {{#each attributes}}
            {{name}}: {{pascal name}};
            {{/each}}
        }

        type Callback = (attr: Attributes, op: Operations) => string;
    }

    {{#each enums}}
    type {{name}} = {{{stringUnion values}}};

    {{/each}}
    type IndexFacets = {{{ buildIndexType facets }}};
    
    type Attributes = {
    {{#each attributes}}
        {{name}}{{#if optional}}?{{/if}}: {{type}};
    {{/each}}
    }
    
    {{#each item}}
    type {{pascal @key}}Item = {
    {{#each this}}
        {{name}}{{#if optional}}?{{/if}}: {{type}};
    {{/each}}
    }
    
    {{/each}}
    export type Item = {
    {{#each item}}
        {{@key}}: {{pascal @key}}Item[];
    {{/each}}
    }
    
    type FilterOperations<T> = {
        gte: (value: T) => string;
        gt: (value: T) => string;
        lte: (value: T) => string;
        lt: (value: T) => string;
        eq: (value: T) => string;
        begins: (value: T) => string;
        exists: () => T;
        notExists: () => T;
        contains: (value: T) => string;
        notContains: (value: T) => string;
        between: (start: T, end: T) => string;
        name: () => T;
        value: (value: T) => string;
    };
    
    type FilterAttributes<T extends Attributes> = {
        [K in keyof T]: FilterOperations<T[K]>;
    }

    export type GoOptions = {
        params?: object;
        raw?: boolean;
        includeKeys?: boolean;
        originalErr?: boolean;
        lastEvaluatedKeyRaw?: boolean;
        table?: string;
    };

    export type ParamOptions = {
        params?: object;
        table?: string;
    }
    
    type GoRecord<T, O extends GoOptions = GoOptions> = (options?: O) => Promise<T>;

    type ParamRecord = (options?: ParamOptions) => object;

    type FilterRecords<T> = (filter: <A extends Attributes>(record: FilterAttributes<A>) => string) => RecordsActionOptions<T>;

    type WhereRecords<T> = (where: Where.Callback) => RecordsActionOptions<T>;

    type RecordsActionOptions<T> = {
        go: GoRecord<T>;
        params: ParamRecord;
        filter: FilterRecords<T>;
        where: WhereRecords<T>;
    }
    
    type {{pascal name}} = (key: IndexFacets) => RecordsActionOptions<Item>;
}
{{/eq}}

{{/each}}
{{#if isService}}
export declare class Instance {
    service: {
        name: string;
        table: string;
    };
    entities: {
        {{#each instances}}
        {{#eq type "entity"}}
        {{name}}: {{pascal name}}.{{pascal name}}, 
        {{/eq}}
        {{/each}}
    };
    collections: {
        {{#each instances}}
        {{#eq type "collection"}}
        {{name}}: {{pascal name}}Collection.{{pascal name}}
        {{/eq}}
        {{/each}}
    };
}

declare const _default: Instance;

{{else}}
export type Instance = {{pascal export}}.{{pascal export}}

declare const _default: Instance;

{{/if}}
{{#ne type "model"}}
export default _default;
{{/ne}}