Skip to content

Commit a7e243d

Browse files
committed
feat: replace joi by zod
1 parent e642ea0 commit a7e243d

File tree

22 files changed

+321
-366
lines changed

22 files changed

+321
-366
lines changed

api/package-lock.json

Lines changed: 10 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171
"dotenv": "^16.3.1",
7272
"ejs": "^3.1.9",
7373
"express-session": "^1.17.3",
74-
"joi": "^17.11.0",
7574
"module-alias": "^2.2.3",
7675
"mongoose": "^8.0.0",
7776
"mongoose-lean-defaults": "^2.2.1",
@@ -92,7 +91,8 @@
9291
"sanitize-filename": "^1.6.3",
9392
"slug": "^8.2.2",
9493
"ts-migrate-mongoose": "^3.8.4",
95-
"uuid": "^9.0.1"
94+
"uuid": "^9.0.1",
95+
"zod": "^3.23.8"
9696
},
9797
"devDependencies": {
9898
"@compodoc/compodoc": "^1.1.24",

api/src/attachment/schemas/attachment.schema.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,14 @@ export class Attachment extends BaseSchema {
102102
* @returns The attachment type ('image', 'audio', 'video' or 'file')
103103
*/
104104
static getTypeByMime(mimeType: string): FileType {
105-
if (mimeType.startsWith(FileType.image)) {
106-
return FileType.image;
107-
} else if (mimeType.startsWith(FileType.audio)) {
108-
return FileType.audio;
109-
} else if (mimeType.startsWith(FileType.video)) {
110-
return FileType.video;
105+
if (mimeType.startsWith('image')) {
106+
return 'image';
107+
} else if (mimeType.startsWith('audio')) {
108+
return 'audio';
109+
} else if (mimeType.startsWith('video')) {
110+
return 'video';
111111
} else {
112-
return FileType.file;
112+
return 'file';
113113
}
114114
}
115115
}

api/src/channel/lib/__test__/common.mock.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { Attachment } from '@/attachment/schemas/attachment.schema';
1010
import { WithUrl } from '@/chat/schemas/types/attachment';
1111
import { ButtonType } from '@/chat/schemas/types/button';
1212
import {
13-
FileType,
1413
OutgoingMessageFormat,
1514
StdOutgoingAttachmentMessage,
1615
StdOutgoingButtonsMessage,
@@ -160,7 +159,7 @@ export const attachmentMessage: StdOutgoingAttachmentMessage<
160159
WithUrl<Attachment>
161160
> = {
162161
attachment: {
163-
type: FileType.image,
162+
type: 'image',
164163
payload: attachmentWithUrl,
165164
},
166165
quickReplies: [

api/src/chat/schemas/types/attachment.ts

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,47 @@
66
* 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).
77
*/
88

9+
import { z } from 'zod';
10+
911
import { Attachment } from '@/attachment/schemas/attachment.schema';
1012

11-
export enum FileType {
12-
image = 'image',
13-
video = 'video',
14-
audio = 'audio',
15-
file = 'file',
16-
unknown = 'unknown',
17-
}
13+
// Enum for FileType
14+
export const fileTypeSchema = z.enum([
15+
'image',
16+
'video',
17+
'audio',
18+
'file',
19+
'unknown',
20+
]);
21+
22+
export type FileType = z.infer<typeof fileTypeSchema>;
23+
24+
// AttachmentForeignKey type schema
25+
export const attachmentForeignKeySchema = z.object({
26+
url: z.string().url().optional(),
27+
attachment_id: z.string().nullable(),
28+
});
1829

19-
export type AttachmentForeignKey = {
20-
url?: string;
21-
attachment_id: string;
22-
};
30+
export type AttachmentForeignKey = z.infer<typeof attachmentForeignKeySchema>;
2331

32+
// WithUrl helper type
2433
export type WithUrl<A> = A & { url?: string };
2534

26-
export interface AttachmentPayload<
35+
// Generic AttachmentPayload type schema
36+
export const attachmentPayloadSchema = <
2737
A extends WithUrl<Attachment> | AttachmentForeignKey,
28-
> {
29-
type: FileType;
30-
payload: A;
31-
}
32-
33-
export interface IncomingAttachmentPayload {
34-
type: FileType;
35-
payload: {
36-
url: string;
37-
};
38-
}
38+
>(
39+
payloadSchema: z.ZodType<A>,
40+
) =>
41+
z.object({
42+
type: fileTypeSchema,
43+
payload: payloadSchema,
44+
});
45+
46+
export type AttachmentPayload<
47+
A extends WithUrl<Attachment> | AttachmentForeignKey,
48+
> = z.infer<ReturnType<typeof attachmentPayloadSchema<A>>>;
49+
50+
export type IncomingAttachmentPayload = z.infer<
51+
ReturnType<typeof attachmentPayloadSchema<AttachmentForeignKey>>
52+
>;

api/src/chat/schemas/types/button.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,50 @@
66
* 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).
77
*/
88

9+
import { z } from 'zod';
10+
11+
// Enum for ButtonType
912
export enum ButtonType {
1013
postback = 'postback',
1114
web_url = 'web_url',
1215
}
1316

14-
export type PostBackButton = {
15-
type: ButtonType.postback;
16-
title: string;
17-
payload: string;
18-
};
19-
20-
export type WebUrlButton = {
21-
type: ButtonType.web_url;
22-
title: string;
23-
url: string;
24-
messenger_extensions?: boolean;
25-
webview_height_ratio?: 'compact' | 'tall' | 'full';
26-
};
27-
28-
export type Button = PostBackButton | WebUrlButton;
17+
// Zod schema for ButtonType
18+
export const buttonTypeSchema = z.enum([
19+
ButtonType.postback,
20+
ButtonType.web_url,
21+
]);
22+
23+
// Base schema for shared fields
24+
export const baseButtonSchema = z.object({
25+
type: buttonTypeSchema,
26+
title: z.string().max(20),
27+
});
28+
29+
// Conditional schemas
30+
export const postBackButtonSchema = baseButtonSchema.extend({
31+
type: z.literal(ButtonType.postback),
32+
payload: z.string().max(1000),
33+
// No `url`, `messenger_extensions`, or `webview_height_ratio` fields here
34+
});
35+
36+
export const webUrlButtonSchema = baseButtonSchema.extend({
37+
type: z.literal(ButtonType.web_url),
38+
url: z.string().url(),
39+
messenger_extensions: z.boolean().optional(),
40+
webview_height_ratio: z.enum(['compact', 'tall', 'full']).optional(),
41+
// No `payload` field here
42+
});
43+
44+
// Union schema for Button
45+
export const buttonSchema = z.union([postBackButtonSchema, webUrlButtonSchema]);
46+
47+
// Array schema for buttons
48+
export const buttonsSchema = z.array(buttonSchema).max(3);
49+
50+
// Infer types
51+
export type PostBackButton = z.infer<typeof postBackButtonSchema>;
52+
53+
export type WebUrlButton = z.infer<typeof webUrlButtonSchema>;
54+
55+
export type Button = z.infer<typeof buttonSchema>;

api/src/chat/schemas/types/capture-var.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@
66
* 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).
77
*/
88

9-
export interface CaptureVar {
10-
// entity=`-1` to match text message
11-
// entity=`-2` for postback payload
12-
// entity is `String` for NLP entities
13-
entity: number | string;
14-
context_var: string;
15-
}
9+
import { z } from 'zod';
10+
11+
// Zod schema for CaptureVar
12+
const captureVarSchema = z.object({
13+
entity: z.union([
14+
// entity=`-1` to match text message
15+
// entity=`-2` for postback payload
16+
// entity is `String` for NLP entities
17+
z
18+
.number()
19+
.int()
20+
.refine((val) => val === -1 || val === -2, {
21+
message: "entity must be -1 or -2 when it's a number",
22+
}),
23+
z.string(), // entity is a string for NLP entities
24+
]),
25+
context_var: z.string(),
26+
});
27+
28+
// Infer the TypeScript type
29+
type CaptureVar = z.infer<typeof captureVarSchema>;
30+
31+
// Export the schema and type
32+
export { CaptureVar, captureVarSchema };

api/src/chat/schemas/types/message.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,6 @@ export enum OutgoingMessageFormat {
5656
carousel = 'carousel',
5757
}
5858

59-
/**
60-
* FileType enum is declared, and currently not used
61-
**/
62-
export enum FileType {
63-
image = 'image',
64-
video = 'video',
65-
audio = 'audio',
66-
file = 'file',
67-
unknown = 'unknown',
68-
}
69-
70-
export enum PayloadType {
71-
location = 'location',
72-
attachments = 'attachments',
73-
}
74-
7559
export type StdOutgoingTextMessage = { text: string };
7660

7761
export type StdOutgoingQuickRepliesMessage = {
@@ -130,15 +114,15 @@ export type StdIncomingPostBackMessage = StdIncomingTextMessage & {
130114
};
131115

132116
export type StdIncomingLocationMessage = {
133-
type: PayloadType.location;
117+
type: 'location';
134118
coordinates: {
135119
lat: number;
136120
lon: number;
137121
};
138122
};
139123

140124
export type StdIncomingAttachmentMessage = {
141-
type: PayloadType.attachments;
125+
type: 'attachments';
142126
serialized_text: string;
143127
attachment: IncomingAttachmentPayload | IncomingAttachmentPayload[];
144128
};

0 commit comments

Comments
 (0)