Merge branch '548-issue-generic-support-of-multiple-dto-per-service' into 540-issue-nlp-module-strictnullchecks-issues

This commit is contained in:
yassinedorbozgithub
2025-01-13 14:14:40 +01:00
88 changed files with 608 additions and 263 deletions

View File

@@ -10,6 +10,7 @@ import { NotFoundException } from '@nestjs/common';
import { TFilterQuery } from '@/utils/types/filter.types';
import { DtoConfig } from '../types/dto.types';
import { TValidateProps } from '../types/filter.types';
import { BaseSchema } from './base-schema';
@@ -20,8 +21,9 @@ export abstract class BaseController<
TStub = never,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
> {
constructor(protected readonly service: BaseService<T, P, TFull>) {}
constructor(protected readonly service: BaseService<T, P, TFull, Dto>) {}
/**
* Checks if the given populate fields are allowed based on the allowed fields list.

View File

@@ -30,6 +30,7 @@ import {
import { TFilterQuery } from '@/utils/types/filter.types';
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseSchema } from './base-schema';
import { LifecycleHookManager } from './lifecycle-hook-manager';
@@ -70,7 +71,8 @@ export abstract class BaseRepository<
T extends FlattenMaps<unknown>,
P extends string = never,
TFull extends Omit<T, P> = never,
U = Omit<T, keyof BaseSchema>,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
D = Document<T>,
> {
private readonly transformOpts = { excludePrefixes: ['_', 'password'] };
@@ -454,7 +456,7 @@ export abstract class BaseRepository<
return await this.model.countDocuments(criteria).exec();
}
async create(dto: U): Promise<T> {
async create(dto: DtoInfer<DtoAction.Create, Dto, U>): Promise<T> {
const doc = await this.model.create(dto);
return plainToClass(
@@ -464,7 +466,9 @@ export abstract class BaseRepository<
);
}
async createMany(dtoArray: U[]): Promise<T[]> {
async createMany(
dtoArray: DtoInfer<DtoAction.Create, Dto, U>[],
): Promise<T[]> {
const docs = await this.model.create(dtoArray);
return docs.map((doc) =>

View File

@@ -8,6 +8,8 @@
import { FlattenMaps } from 'mongoose';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseRepository } from './base-repository';
import { BaseSchema } from './base-schema';
@@ -15,8 +17,12 @@ export abstract class BaseSeeder<
T extends FlattenMaps<unknown>,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
> {
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
constructor(
protected readonly repository: BaseRepository<T, P, TFull, Dto>,
) {}
async findAll(): Promise<T[]> {
return await this.repository.findAll();
@@ -27,7 +33,7 @@ export abstract class BaseSeeder<
return count === 0;
}
async seed(models: Omit<T, keyof BaseSchema>[]): Promise<boolean> {
async seed(models: DtoInfer<DtoAction.Create, Dto, U>[]): Promise<boolean> {
if (await this.isEmpty()) {
await this.repository.createMany(models);
return true;

View File

@@ -14,6 +14,7 @@ import { ProjectionType, QueryOptions } from 'mongoose';
import { TFilterQuery } from '@/utils/types/filter.types';
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseRepository } from './base-repository';
import { BaseSchema } from './base-schema';
@@ -22,8 +23,12 @@ export abstract class BaseService<
T extends BaseSchema,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
> {
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
constructor(
protected readonly repository: BaseRepository<T, P, TFull, Dto>,
) {}
getRepository() {
return this.repository;
@@ -140,7 +145,7 @@ export abstract class BaseService<
return await this.repository.count(criteria);
}
async create<D extends Omit<T, keyof BaseSchema>>(dto: D): Promise<T> {
async create(dto: DtoInfer<DtoAction.Create, Dto, U>): Promise<T> {
try {
return await this.repository.create(dto);
} catch (error) {
@@ -153,9 +158,9 @@ export abstract class BaseService<
}
}
async findOneOrCreate<D extends Omit<T, keyof BaseSchema>>(
async findOneOrCreate(
criteria: string | TFilterQuery<T>,
dto: D,
dto: DtoInfer<DtoAction.Create, Dto, U>,
): Promise<T> {
const result = await this.findOne(criteria);
if (!result) {
@@ -164,24 +169,21 @@ export abstract class BaseService<
return result;
}
async createMany<D extends Omit<T, keyof BaseSchema>>(
dtoArray: D[],
async createMany(
dtoArray: DtoInfer<DtoAction.Create, Dto, U>[],
): Promise<T[]> {
return await this.repository.createMany(dtoArray);
}
async updateOne<D extends Partial<Omit<T, keyof BaseSchema>>>(
async updateOne(
criteria: string | TFilterQuery<T>,
dto: D,
options?: QueryOptions<D> | null,
dto: Partial<U>,
options?: QueryOptions<Partial<U>> | null,
): Promise<T | null> {
return await this.repository.updateOne(criteria, dto, options);
}
async updateMany<D extends Partial<Omit<T, keyof BaseSchema>>>(
filter: TFilterQuery<T>,
dto: D,
) {
async updateMany(filter: TFilterQuery<T>, dto: Partial<U>) {
return await this.repository.updateMany(filter, dto);
}

View File

@@ -9,20 +9,34 @@
import mongoose from 'mongoose';
import { BlockCreateDto } from '@/chat/dto/block.dto';
import { BlockModel, Block } from '@/chat/schemas/block.schema';
import { Block, BlockModel } from '@/chat/schemas/block.schema';
import { CategoryModel } from '@/chat/schemas/category.schema';
import { FileType } from '@/chat/schemas/types/attachment';
import { ButtonType } from '@/chat/schemas/types/button';
import { QuickReplyType } from '@/chat/schemas/types/quick-reply';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const blocks: BlockCreateDto[] = [
type TBlockFixtures = FixturesTypeBuilder<Block, BlockCreateDto>;
export const blockDefaultValues: TBlockFixtures['defaultValues'] = {
options: {},
nextBlocks: [],
capture_vars: [],
assign_labels: [],
trigger_labels: [],
trigger_channels: [],
builtin: false,
starts_conversation: false,
attachedBlock: null,
attachedToBlock: null,
};
export const blocks: TBlockFixtures['values'][] = [
{
name: 'hasNextBlocks',
patterns: ['Hi'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -41,7 +55,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'hasPreviousBlocks',
patterns: ['colors'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -79,7 +92,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'buttons',
patterns: ['about'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -117,7 +129,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'attachment',
patterns: ['image'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -144,7 +155,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'test',
patterns: ['yes'],
trigger_channels: [],
category: null,
//to be verified
options: {
@@ -163,18 +173,9 @@ export const blocks: BlockCreateDto[] = [
},
];
export const blockDefaultValues: TFixturesDefaultValues<Block> = {
options: {},
builtin: false,
nextBlocks: [],
capture_vars: [],
assign_labels: [],
trigger_labels: [],
starts_conversation: false,
attachedToBlock: null,
};
export const blockFixtures = getFixturesWithDefaultValues<Block>({
export const blockFixtures = getFixturesWithDefaultValues<
TBlockFixtures['values']
>({
fixtures: blocks,
defaultValues: blockDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -9,12 +9,23 @@
import mongoose from 'mongoose';
import { CategoryCreateDto } from '@/chat/dto/category.dto';
import { CategoryModel, Category } from '@/chat/schemas/category.schema';
import { Category, CategoryModel } from '@/chat/schemas/category.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const categories: CategoryCreateDto[] = [
export type TCategoryFixtures = FixturesTypeBuilder<
Category,
CategoryCreateDto
>;
export const categoryDefaultValues: TCategoryFixtures['defaultValues'] = {
builtin: false,
zoom: 100,
offset: [0, 0],
};
export const categories: TCategoryFixtures['values'][] = [
{
label: 'test category 1',
},
@@ -23,13 +34,9 @@ export const categories: CategoryCreateDto[] = [
},
];
export const categoryDefaultValues: TFixturesDefaultValues<Category> = {
builtin: false,
zoom: 100,
offset: [0, 0],
};
export const categoryFixtures = getFixturesWithDefaultValues<Category>({
export const categoryFixtures = getFixturesWithDefaultValues<
TCategoryFixtures['values']
>({
fixtures: categories,
defaultValues: categoryDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,12 +12,18 @@ import { ContentCreateDto } from '@/cms/dto/content.dto';
import { Content, ContentModel } from '@/cms/schemas/content.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installAttachmentFixtures } from './attachment';
import { installContentTypeFixtures } from './contenttype';
const contents: ContentCreateDto[] = [
type TContentFixtures = FixturesTypeBuilder<Content, ContentCreateDto>;
export const contentDefaultValues: TContentFixtures['defaultValues'] = {
status: true,
};
const contents: TContentFixtures['values'][] = [
{
title: 'Jean',
dynamicFields: {
@@ -131,14 +137,11 @@ const contents: ContentCreateDto[] = [
},
];
export const categoryDefaultValues: TFixturesDefaultValues<Content> = {
status: true,
createdAt: undefined,
};
export const contentFixtures = getFixturesWithDefaultValues<Content>({
export const contentFixtures = getFixturesWithDefaultValues<
TContentFixtures['values']
>({
fixtures: contents,
defaultValues: categoryDefaultValues,
defaultValues: contentDefaultValues,
});
export const installContentFixtures = async () => {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -15,9 +15,29 @@ import {
} from '@/cms/schemas/content-type.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
const contentTypes: ContentTypeCreateDto[] = [
type TContentTypeFixtures = FixturesTypeBuilder<
ContentType,
ContentTypeCreateDto
>;
export const contentTypeDefaultValues: TContentTypeFixtures['defaultValues'] = {
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
},
{
name: 'status',
label: 'Status',
type: 'checkbox',
},
],
};
const contentTypes: TContentTypeFixtures['values'][] = [
{
name: 'Product',
fields: [
@@ -100,22 +120,9 @@ const contentTypes: ContentTypeCreateDto[] = [
},
];
export const contentTypeDefaultValues: TFixturesDefaultValues<ContentType> = {
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
},
{
name: 'status',
label: 'Status',
type: 'checkbox',
},
],
};
export const contentTypeFixtures = getFixturesWithDefaultValues<ContentType>({
export const contentTypeFixtures = getFixturesWithDefaultValues<
TContentTypeFixtures['values']
>({
fixtures: contentTypes,
defaultValues: contentTypeDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -9,25 +9,33 @@
import mongoose from 'mongoose';
import { ContextVarCreateDto } from '@/chat/dto/context-var.dto';
import { ContextVarModel, ContextVar } from '@/chat/schemas/context-var.schema';
import { ContextVar, ContextVarModel } from '@/chat/schemas/context-var.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { FixturesTypeBuilder } from '../types';
const contextVars: ContextVarCreateDto[] = [
type TContentVarFixtures = FixturesTypeBuilder<ContextVar, ContextVarCreateDto>;
export const contentVarDefaultValues: TContentVarFixtures['defaultValues'] = {
permanent: false,
};
const contextVars: TContentVarFixtures['values'][] = [
{
label: 'test context var 1',
name: 'test1',
permanent: false,
},
{
label: 'test context var 2',
name: 'test2',
permanent: false,
},
];
export const contextVarFixtures = getFixturesWithDefaultValues<ContextVar>({
export const contextVarFixtures = getFixturesWithDefaultValues<
TContentVarFixtures['values']
>({
fixtures: contextVars,
defaultValues: contentVarDefaultValues,
});
export const installContextVarFixtures = async () => {

View File

@@ -9,10 +9,7 @@
import mongoose from 'mongoose';
import { ConversationCreateDto } from '@/chat/dto/conversation.dto';
import {
Conversation,
ConversationModel,
} from '@/chat/schemas/conversation.schema';
import { ConversationModel } from '@/chat/schemas/conversation.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
@@ -116,14 +113,16 @@ const conversations: ConversationCreateDto[] = [
},
];
export const conversationDefaultValues: TFixturesDefaultValues<Conversation> = {
active: false,
};
export const conversationDefaultValues: TFixturesDefaultValues<ConversationCreateDto> =
{
active: false,
};
export const conversationFixtures = getFixturesWithDefaultValues<Conversation>({
fixtures: conversations,
defaultValues: conversationDefaultValues,
});
export const conversationFixtures =
getFixturesWithDefaultValues<ConversationCreateDto>({
fixtures: conversations,
defaultValues: conversationDefaultValues,
});
export const installConversationTypeFixtures = async () => {
const subscribers = await installSubscriberFixtures();

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,9 +12,15 @@ import { LabelCreateDto } from '@/chat/dto/label.dto';
import { Label, LabelModel } from '@/chat/schemas/label.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const labels: LabelCreateDto[] = [
type TLabelFixtures = FixturesTypeBuilder<Label, LabelCreateDto>;
export const contentLabelDefaultValues: TLabelFixtures['defaultValues'] = {
builtin: false,
};
export const labels: TLabelFixtures['values'][] = [
{
description: 'test description 1',
label_id: {
@@ -39,13 +45,11 @@ export const labels: LabelCreateDto[] = [
},
];
export const labelDefaultValues: TFixturesDefaultValues<Label> = {
builtin: false,
};
export const labelFixtures = getFixturesWithDefaultValues<Label>({
export const labelFixtures = getFixturesWithDefaultValues<
TLabelFixtures['values']
>({
fixtures: labels,
defaultValues: labelDefaultValues,
defaultValues: contentLabelDefaultValues,
});
export const installLabelFixtures = async () => {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -13,11 +13,18 @@ import { NlpSample, NlpSampleModel } from '@/nlp/schemas/nlp-sample.schema';
import { NlpSampleState } from '@/nlp/schemas/types';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installLanguageFixtures } from './language';
const nlpSamples: NlpSampleCreateDto[] = [
type TNlpSampleFixtures = FixturesTypeBuilder<NlpSample, NlpSampleCreateDto>;
export const nlpSampleDefaultValues: TNlpSampleFixtures['defaultValues'] = {
type: NlpSampleState.train,
trained: false,
};
const nlpSamples: TNlpSampleFixtures['values'][] = [
{
text: 'yess',
language: '0',
@@ -38,12 +45,9 @@ const nlpSamples: NlpSampleCreateDto[] = [
},
];
export const nlpSampleDefaultValues: TFixturesDefaultValues<NlpSample> = {
type: NlpSampleState.train,
trained: false,
};
export const nlpSampleFixtures = getFixturesWithDefaultValues<NlpSample>({
export const nlpSampleFixtures = getFixturesWithDefaultValues<
TNlpSampleFixtures['values']
>({
fixtures: nlpSamples,
defaultValues: nlpSampleDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,12 +12,23 @@ import { SubscriberCreateDto } from '@/chat/dto/subscriber.dto';
import { Subscriber, SubscriberModel } from '@/chat/schemas/subscriber.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installLabelFixtures } from './label';
import { installUserFixtures } from './user';
const subscribers: SubscriberCreateDto[] = [
type TSubscriberFixtures = FixturesTypeBuilder<Subscriber, SubscriberCreateDto>;
export const subscriberDefaultValues: TSubscriberFixtures['defaultValues'] = {
timezone: 0,
assignedTo: null,
assignedAt: null,
lastvisit: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
retainedFrom: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
avatar: null,
};
const subscribers: TSubscriberFixtures['values'][] = [
{
foreign_id: 'foreign-id-messenger',
first_name: 'Jhon',
@@ -30,7 +41,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'messenger-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2020-01-01T20:40:03.249Z'),
retainedFrom: new Date('2020-01-01T20:40:03.249Z'),
},
@@ -46,7 +56,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2021-01-01T20:40:03.249Z'),
retainedFrom: new Date('2021-01-02T20:40:03.249Z'),
},
@@ -62,7 +71,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2022-01-01T20:40:03.249Z'),
retainedFrom: new Date('2022-01-02T20:40:03.249Z'),
},
@@ -78,22 +86,14 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2024-01-01T20:40:03.249Z'),
retainedFrom: new Date('2024-01-02T20:40:03.249Z'),
},
];
export const subscriberDefaultValues: TFixturesDefaultValues<Subscriber> = {
timezone: 0,
assignedTo: null,
assignedAt: null,
lastvisit: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
retainedFrom: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
avatar: null,
};
export const subscriberFixtures = getFixturesWithDefaultValues<Subscriber>({
export const subscriberFixtures = getFixturesWithDefaultValues<
TSubscriberFixtures['values']
>({
fixtures: subscribers,
defaultValues: subscriberDefaultValues,
});

View File

@@ -95,6 +95,7 @@ export const baseBlockInstance = {
category: undefined,
previousBlocks: [],
trigger_channels: [],
nextBlocks: [],
...modelInstance,
};
@@ -103,6 +104,7 @@ export const blockEmpty: BlockFull = {
name: 'Empty',
patterns: [],
message: [''],
nextBlocks: [],
};
// Translation Data

View File

@@ -8,7 +8,7 @@
import { BaseSchema } from '../generics/base-schema';
type TOptionalPropertyOf<T> = Exclude<
export type TOptionalPropertyOf<T> = Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? never : K;
}[keyof T],
@@ -23,3 +23,25 @@ export type TFixtures<T> = Omit<T, keyof BaseSchema> & {
export type TFixturesDefaultValues<T, S = TFixtures<T>> = {
[key in TOptionalPropertyOf<S>]?: S[key];
} & { createdAt?: BaseSchema['createdAt'] };
export type TOptionalPropertyFrom<O extends object, O1 extends object> = Pick<
O1,
Exclude<keyof O1, keyof O>
> &
Pick<O, Exclude<keyof O, keyof O1>>;
export type OptionalProperties<T, K extends keyof T> = Omit<
T,
K | keyof BaseSchema
> &
Partial<Pick<T, K>>;
export type FixturesTypeBuilder<
S extends object,
D extends object,
DO = TFixturesDefaultValues<D>,
U = Partial<TFixtures<TOptionalPropertyFrom<D, S>>>,
> = {
defaultValues: DO & U;
values: OptionalProperties<S, keyof S & keyof (DO & U)>;
};

View File

@@ -0,0 +1,24 @@
/*
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
export enum DtoAction {
Create = 'create',
Read = 'read',
Update = 'update',
Delete = 'delete',
}
export type DtoConfig<
C extends Partial<Record<DtoAction, object>> = Partial<
Record<DtoAction, object>
>,
> = C;
export type DtoInfer<K extends keyof Dto, Dto, I> = Dto[K] extends object
? Dto[K]
: I;