@zyacreatives/shared 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export declare const slugify: ({ value }: {
2
+ value: string;
3
+ }) => string;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.slugify = void 0;
4
+ const slugify = ({ value }) => {
5
+ return value
6
+ .normalize("NFKD")
7
+ .replace(/[\u0300-\u036f]/g, "") // Remove diacritics
8
+ .toLowerCase()
9
+ .trim()
10
+ .replace(/[^a-z0-9]+/g, "_") // Replace non-alphanum with underscore
11
+ .replace(/^_+|_+$/g, "") // Trim leading/trailing underscores
12
+ .replace(/__+/g, "_"); // Collapse multiple underscores
13
+ };
14
+ exports.slugify = slugify;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyacreatives/shared",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,35 +1,77 @@
1
1
  import { z } from "@hono/zod-openapi";
2
2
  import { CuidSchema, ProfileIdentifierSchema } from "./common";
3
3
  import { MinimalUserSchema } from "./user";
4
+ import { EXPERIENCE_LEVELS, ExperienceLevel } from "../constants";
4
5
 
5
6
  export const BrandEntitySchema = z
6
7
  .object({
7
- id: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
8
- userId: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
9
- brandName: z.string().min(1).openapi({ example: "Acme Creative Studio" }),
10
- searchVector: z.string().optional(),
11
- bio: z.string().optional().openapi({
12
- example: "A creative studio specializing in product design.",
13
- }),
14
- disciplines: z
15
- .array(z.string())
8
+ id: z.cuid2().openapi({ example: "brd_cksd0v6q0000s9a5y8z7p3x9" }),
9
+ userId: z.cuid2().openapi({ example: "user_owner_123" }),
10
+ brandName: z.string().openapi({ example: "TechInnovate Inc." }),
11
+ bio: z
12
+ .string()
16
13
  .optional()
17
- .openapi({
18
- example: ["graphic-design", "branding"],
19
- }),
14
+ .openapi({ example: "Leading software development firm focused on AI." }),
20
15
  tags: z
21
16
  .array(z.string())
22
17
  .optional()
23
- .openapi({ example: ["branding", "design", "marketing"] }),
24
- createdAt: z.iso
18
+ .openapi({ example: ["technology", "saas", "startup"] }),
19
+ createdAt: z
20
+ .string()
25
21
  .datetime()
26
22
  .openapi({ example: "2025-10-13T09:00:00.000Z" }),
27
- updatedAt: z.iso
23
+ updatedAt: z
24
+ .string()
28
25
  .datetime()
29
26
  .openapi({ example: "2025-10-13T09:00:00.000Z" }),
30
27
  })
31
28
  .openapi("BrandEntity");
32
29
 
30
+ export const BrandWithUserEntitySchema = BrandEntitySchema.extend({
31
+ user: MinimalUserSchema,
32
+ disciplines: z
33
+ .array(z.string())
34
+ .openapi({ example: ["Marketing", "Product Development"] }),
35
+ }).openapi("BrandWithUserEntity");
36
+
37
+ export const ListBrandsInputSchema = z
38
+ .object({
39
+ query: z.string().optional().openapi({ example: "AI software brand" }),
40
+ disciplines: z
41
+ .array(z.string())
42
+ .optional()
43
+ .openapi({ example: ["design", "marketing"] }),
44
+ experienceLevels: z
45
+ .array(
46
+ z.enum(
47
+ Object.values(EXPERIENCE_LEVELS) as [
48
+ ExperienceLevel,
49
+ ...ExperienceLevel[]
50
+ ]
51
+ )
52
+ )
53
+ .optional()
54
+ .openapi({
55
+ description:
56
+ "Filter based on the required experience level of partners.",
57
+ }),
58
+ location: z.string().optional().openapi({ example: "San Francisco" }),
59
+ tags: z
60
+ .array(z.string())
61
+ .optional()
62
+ .openapi({ example: ["fintech", "remote"] }),
63
+ page: z.number().int().min(1).default(1).optional().openapi({ example: 1 }),
64
+ perPage: z
65
+ .number()
66
+ .int()
67
+ .min(1)
68
+ .max(100)
69
+ .default(20)
70
+ .optional()
71
+ .openapi({ example: 20 }),
72
+ })
73
+ .openapi("ListBrandsInput");
74
+
33
75
  export const CreateBrandProfileSchema = z
34
76
  .object({
35
77
  brandName: z
@@ -76,22 +118,3 @@ export const GetBrandEndpointResponseSchema = BrandEntitySchema.extend({
76
118
  });
77
119
  export const UpdateBrandEndpointResponseSchema = BrandEntitySchema;
78
120
 
79
- export type CreateBrandDto = z.infer<typeof CreateBrandProfileSchema> & {
80
- userId: string;
81
- };
82
- export type UpdateBrandDto = z.infer<typeof UpdateBrandProfileSchema> & {
83
- userId: string;
84
- };
85
-
86
- export type GetBrandParams = z.infer<typeof GetBrandParamsSchema>;
87
- export type GetBrandQuery = z.infer<typeof GetBrandQuerySchema>;
88
-
89
- export type CreateBrandEndpointResponse = z.infer<
90
- typeof CreateBrandEndpointResponseSchema
91
- >;
92
- export type GetBrandEndpointResponse = z.infer<
93
- typeof GetBrandEndpointResponseSchema
94
- >;
95
- export type UpdateBrandEndpointResponse = z.infer<
96
- typeof UpdateBrandEndpointResponseSchema
97
- >;
@@ -1,30 +1,34 @@
1
1
  import { z } from "@hono/zod-openapi";
2
- import { EXPERIENCE_LEVELS } from "../constants";
2
+ import { EXPERIENCE_LEVELS, ExperienceLevel } from "../constants";
3
3
  import { CuidSchema, ProfileIdentifierSchema } from "./common";
4
4
  import { MinimalUserSchema } from "./user";
5
5
 
6
- export const CreativeEntitySchema = z
6
+ export const BaseCreativeEntitySchema = z
7
7
  .object({
8
- id: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
9
- userId: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
10
- bio: z
11
- .string()
12
- .optional()
13
- .openapi({ example: "Passionate visual artist." }),
14
- location: z.string().optional().openapi({ example: "Lagos, Nigeria" }),
15
- searchVector: z.string().optional(),
8
+ id: z.cuid2().openapi({ example: "cre_cksd0v6q0000s9a5y8z7p3x9" }),
9
+ userId: z.cuid2().openapi({ example: "user_abc123" }),
10
+ bio: z.string().optional().openapi({
11
+ example: "A multi-disciplinary designer specializing in brand identity.",
12
+ }),
13
+ location: z.string().optional().openapi({ example: "London, UK" }),
16
14
  experienceLevel: z
17
- .enum(Object.values(EXPERIENCE_LEVELS) as [string, ...string[]])
15
+ .enum(
16
+ Object.values(EXPERIENCE_LEVELS) as [
17
+ ExperienceLevel,
18
+ ...ExperienceLevel[]
19
+ ]
20
+ )
18
21
  .optional()
19
- .openapi({ example: "1-3 years" }),
22
+ .openapi({ example: "SENIOR" }),
20
23
  tags: z
21
24
  .array(z.string())
22
25
  .optional()
23
- .openapi({ example: ["digital-art", "illustration"] }),
26
+ .openapi({ example: ["branding", "typography", "UX"] }),
24
27
  disciplines: z
25
28
  .array(z.string())
26
29
  .optional()
27
- .openapi({ example: ["painting", "animation"] }),
30
+ .openapi({ example: ["Design", "Art Direction"] }),
31
+ user: z.any().optional(),
28
32
  createdAt: z.iso
29
33
  .datetime()
30
34
  .openapi({ example: "2025-10-13T09:00:00.000Z" }),
@@ -32,7 +36,49 @@ export const CreativeEntitySchema = z
32
36
  .datetime()
33
37
  .openapi({ example: "2025-10-13T09:00:00.000Z" }),
34
38
  })
35
- .openapi("CreativeEntity");
39
+ .openapi("BaseCreativeEntity");
40
+
41
+ export const CreativeEntitySchema =
42
+ BaseCreativeEntitySchema.openapi("CreativeEntity");
43
+
44
+ export const CreativeWithUserEntitySchema = CreativeEntitySchema.extend({
45
+ user: MinimalUserSchema,
46
+ }).openapi("CreativeWithUserEntity");
47
+
48
+ export const ListCreativesInputSchema = z
49
+ .object({
50
+ query: z.string().optional().openapi({ example: "logo designer" }),
51
+ disciplines: z
52
+ .array(z.string())
53
+ .optional()
54
+ .openapi({ example: ["branding", "web design"] }),
55
+ experienceLevels: z
56
+ .array(
57
+ z.enum(
58
+ Object.values(EXPERIENCE_LEVELS) as [
59
+ ExperienceLevel,
60
+ ...ExperienceLevel[]
61
+ ]
62
+ )
63
+ )
64
+ .optional()
65
+ .openapi({ example: ["SENIOR", "EXPERT"] }),
66
+ location: z.string().optional().openapi({ example: "Los Angeles" }),
67
+ tags: z
68
+ .array(z.string())
69
+ .optional()
70
+ .openapi({ example: ["Figma", "AI"] }),
71
+ page: z.number().int().min(1).default(1).optional().openapi({ example: 1 }),
72
+ perPage: z
73
+ .number()
74
+ .int()
75
+ .min(1)
76
+ .max(100)
77
+ .default(20)
78
+ .optional()
79
+ .openapi({ example: 20 }),
80
+ })
81
+ .openapi("ListCreativesInput");
36
82
 
37
83
  export const CreateCreativeProfileSchema = z
38
84
  .object({
@@ -101,25 +147,6 @@ export const GetCreativeQuerySchema = ProfileIdentifierSchema;
101
147
  export const CreateCreativeEndpointResponseSchema = CreativeEntitySchema;
102
148
  export const GetCreativeEndpointResponseSchema = CreativeEntitySchema.extend({
103
149
  user: MinimalUserSchema,
104
- })
150
+ });
105
151
  export const UpdateCreativeEndpointResponseSchema = CreativeEntitySchema;
106
152
 
107
- export type CreateCreativeDto = z.infer<typeof CreateCreativeProfileSchema> & {
108
- userId: string;
109
- };
110
- export type UpdateCreativeDto = z.infer<typeof UpdateCreativeProfileSchema> & {
111
- userId: string;
112
- };
113
-
114
- export type GetCreativeParams = z.infer<typeof GetCreativeParamsSchema>;
115
- export type GetCreativeQuery = z.infer<typeof GetCreativeQuerySchema>;
116
-
117
- export type CreateCreativeEndpointResponse = z.infer<
118
- typeof CreateCreativeEndpointResponseSchema
119
- >;
120
- export type GetCreativeEndpointResponse = z.infer<
121
- typeof GetCreativeEndpointResponseSchema
122
- >;
123
- export type UpdateCreativeEndpointResponse = z.infer<
124
- typeof UpdateCreativeEndpointResponseSchema
125
- >;
@@ -0,0 +1,212 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ import {
3
+ EXPERIENCE_LEVELS,
4
+ ExperienceLevel,
5
+ GEOGRAPHIC_FOCUS,
6
+ GeographicFocus,
7
+ INVESTMENT_SIZES,
8
+ InvestmentSize,
9
+ INVESTOR_TYPES,
10
+ InvestorType,
11
+ } from "../constants";
12
+ import { MinimalUserSchema } from "./user";
13
+ import { CuidSchema, ProfileIdentifierSchema } from "./common";
14
+
15
+ export const InvestorEntitySchema = z
16
+ .object({
17
+ id: z.cuid2().openapi({ example: "inv_cksd0v6q0000s9a5y8z7p3x9" }),
18
+ userId: z.cuid2().openapi({ example: "user_owner_123" }),
19
+ bio: z
20
+ .string()
21
+ .optional()
22
+ .openapi({ example: "Early stage VC focusing on creative technology." }),
23
+ location: z.string().optional().openapi({ example: "New York, USA" }),
24
+ experienceLevel: z
25
+ .enum(
26
+ Object.values(EXPERIENCE_LEVELS) as [
27
+ ExperienceLevel,
28
+ ...ExperienceLevel[]
29
+ ]
30
+ )
31
+ .optional()
32
+ .openapi({ example: "EXPERT" }),
33
+ geographicFocus: z
34
+ .enum(
35
+ Object.values(GEOGRAPHIC_FOCUS) as [
36
+ GeographicFocus,
37
+ ...GeographicFocus[]
38
+ ]
39
+ )
40
+ .optional()
41
+ .openapi({ example: "NORTH_AMERICA" }),
42
+ investmentSize: z
43
+ .enum(
44
+ Object.values(INVESTMENT_SIZES) as [InvestmentSize, ...InvestmentSize[]]
45
+ )
46
+ .optional()
47
+ .openapi({ example: "SEED" }),
48
+ investorType: z
49
+ .enum(Object.values(INVESTOR_TYPES) as [InvestorType, ...InvestorType[]])
50
+ .optional()
51
+ .openapi({ example: "VC" }),
52
+ websiteURL: z
53
+ .string()
54
+ .url()
55
+ .optional()
56
+ .openapi({ example: "https://investorpartners.com" }),
57
+ disciplines: z
58
+ .array(z.string())
59
+ .optional()
60
+ .openapi({ example: ["Product Design", "AI Strategy"] }),
61
+ createdAt: z
62
+ .string()
63
+ .datetime()
64
+ .openapi({ example: "2025-10-13T09:00:00.000Z" }),
65
+ updatedAt: z
66
+ .string()
67
+ .datetime()
68
+ .openapi({ example: "2025-10-13T09:00:00.000Z" }),
69
+ })
70
+ .openapi("InvestorEntity");
71
+
72
+ export const InvestorWithUserEntitySchema = InvestorEntitySchema.extend({
73
+ user: MinimalUserSchema,
74
+ disciplines: z
75
+ .array(z.string())
76
+ .openapi({ example: ["Product Design", "AI Strategy"] }),
77
+ }).openapi("InvestorWithUserEntity");
78
+
79
+ export const CreateInvestorProfileSchema = z
80
+ .object({
81
+ bio: z.string().max(210).optional().default("").openapi({
82
+ example: "Angel investor backing early-stage African startups.",
83
+ }),
84
+ websiteURL: z.string().url("Invalid url").optional().openapi({
85
+ example: "https://www.investorportfolio.com",
86
+ }),
87
+ experienceLevel: z
88
+ .enum(
89
+ Object.values(EXPERIENCE_LEVELS) as [
90
+ ExperienceLevel,
91
+ ...ExperienceLevel[]
92
+ ]
93
+ )
94
+ .default(EXPERIENCE_LEVELS.YEAR_0_1)
95
+ .openapi({
96
+ example: "0-1 year",
97
+ }),
98
+ location: z.string().openapi({
99
+ example: "UK",
100
+ }),
101
+ })
102
+ .openapi({
103
+ title: "Create Investor Profile",
104
+ });
105
+
106
+ export const UpdateInvestorProfileSchema = z
107
+ .object({
108
+ bio: z.string().max(210).optional().openapi({
109
+ example: "Seasoned venture capitalist with a focus on healthtech.",
110
+ }),
111
+ websiteURL: z.string().url("Invalid url").optional().openapi({
112
+ example: "https://www.vcgroup.com",
113
+ }),
114
+ experienceLevel: z
115
+ .enum(
116
+ Object.values(EXPERIENCE_LEVELS) as [
117
+ ExperienceLevel,
118
+ ...ExperienceLevel[]
119
+ ]
120
+ )
121
+ .optional()
122
+ .openapi({
123
+ example: "SENIOR",
124
+ }),
125
+ investorType: z
126
+ .enum(Object.values(INVESTOR_TYPES) as [InvestorType, ...InvestorType[]])
127
+ .optional()
128
+ .openapi({
129
+ example: "VC",
130
+ }),
131
+ disciplineSlugs: z
132
+ .array(z.string())
133
+ .min(1, "At least one discipline is required")
134
+ .optional()
135
+ .openapi({
136
+ example: ["fintech", "edtech"],
137
+ }),
138
+ investmentSize: z
139
+ .enum(
140
+ Object.values(INVESTMENT_SIZES) as [InvestmentSize, ...InvestmentSize[]]
141
+ )
142
+ .optional()
143
+ .openapi({
144
+ example: "SEED",
145
+ }),
146
+ geographicFocus: z
147
+ .enum(
148
+ Object.values(GEOGRAPHIC_FOCUS) as [
149
+ GeographicFocus,
150
+ ...GeographicFocus[]
151
+ ]
152
+ )
153
+ .optional()
154
+ .openapi({
155
+ example: "GLOBAL",
156
+ }),
157
+ location: z.string().optional().openapi({
158
+ example: "UK",
159
+ }),
160
+ })
161
+ .openapi({
162
+ title: "Update Investor Profile",
163
+ });
164
+
165
+ export const ListInvestorsInputSchema = z
166
+ .object({
167
+ query: z.string().optional().openapi({ example: "creative tech investor" }),
168
+ disciplines: z
169
+ .array(z.string())
170
+ .optional()
171
+ .openapi({ example: ["branding", "UX"] }),
172
+ experienceLevels: z
173
+ .array(
174
+ z.enum(
175
+ Object.values(EXPERIENCE_LEVELS) as [
176
+ ExperienceLevel,
177
+ ...ExperienceLevel[]
178
+ ]
179
+ )
180
+ )
181
+ .optional()
182
+ .openapi({
183
+ description: "Filter based on the required experience level.",
184
+ }),
185
+ location: z.string().optional().openapi({ example: "San Francisco" }),
186
+ tags: z
187
+ .array(z.string())
188
+ .optional()
189
+ .openapi({ example: ["design", "future"] }),
190
+ page: z.number().int().min(1).default(1).optional().openapi({ example: 1 }),
191
+ perPage: z
192
+ .number()
193
+ .int()
194
+ .min(1)
195
+ .max(100)
196
+ .default(20)
197
+ .optional()
198
+ .openapi({ example: 20 }),
199
+ })
200
+ .openapi("ListInvestorsInput");
201
+
202
+ export const GetInvestorParamsSchema = z.object({
203
+ value: CuidSchema,
204
+ });
205
+
206
+ export const GetInvestorQuerySchema = ProfileIdentifierSchema;
207
+
208
+ export const CreateInvestorEndpointResponseSchema = InvestorEntitySchema;
209
+ export const GetInvestorEndpointResponseSchema = InvestorEntitySchema.extend({
210
+ user: MinimalUserSchema,
211
+ });
212
+ export const UpdateInvestorEndpointResponseSchema = InvestorEntitySchema;
@@ -0,0 +1,161 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ import { CLIENT_TYPES, ClientType, Role, ROLES } from "../constants";
3
+
4
+ export const ProjectEntitySchema = z
5
+ .object({
6
+ id: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
7
+ createdAt: z.iso
8
+ .datetime()
9
+ .openapi({ example: "2025-10-13T09:00:00.000Z" }),
10
+ updatedAt: z.iso
11
+ .datetime()
12
+ .openapi({ example: "2025-10-13T09:00:00.000Z" }),
13
+ userId: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
14
+ title: z.string().openapi({ example: "Brand Identity Design" }),
15
+ description: z
16
+ .string()
17
+ .optional()
18
+ .openapi({ example: "A full rebrand for a fashion label." }),
19
+ overview: z
20
+ .string()
21
+ .optional()
22
+ .openapi({ example: "Detailed project story and outcomes." }),
23
+ url: z
24
+ .string()
25
+ .url()
26
+ .optional()
27
+ .openapi({ example: "https://example.com/project" }),
28
+ clientId: z.string().optional().openapi({ example: "client_abc123" }),
29
+ clientType: z
30
+ .enum(Object.values(CLIENT_TYPES) as [ClientType, ...ClientType[]])
31
+ .optional()
32
+ .openapi({ example: "BRAND" }),
33
+ clientName: z.string().optional().openapi({ example: "Nike" }),
34
+ projectCreatorType: z
35
+ .enum(Object.values(ROLES) as [Role, ...Role[]])
36
+ .openapi({ example: "CREATIVE" }),
37
+ tags: z
38
+ .array(z.string())
39
+ .optional()
40
+ .openapi({ example: ["branding", "logo"] }),
41
+ isFeatured: z.boolean().optional().openapi({ example: false }),
42
+ startDate: z.iso
43
+ .datetime()
44
+ .optional()
45
+ .openapi({ example: "2025-06-01T00:00:00.000Z" }),
46
+ endDate: z.iso
47
+ .datetime()
48
+ .optional()
49
+ .openapi({ example: "2025-07-15T00:00:00.000Z" }),
50
+ imagePlaceholderUrl: z
51
+ .string()
52
+ .url()
53
+ .optional()
54
+ .openapi({ example: "https://example.com/project-image.png" }),
55
+ })
56
+ .openapi("ProjectEntity");
57
+
58
+ export const ProjectFileEntitySchema = z
59
+ .object({
60
+ id: z.cuid2().openapi({ example: "pfe_cksd0v6q0000s9a5y8z7p3x9" }),
61
+ projectId: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
62
+ url: z
63
+ .string()
64
+ .url()
65
+ .openapi({ example: "https://cdn.example.com/project/image.jpg" }),
66
+ mimeType: z.string().openapi({ example: "image/jpeg" }),
67
+ fileSize: z.number().int().positive().openapi({ example: 1500000 }), // size in bytes
68
+ })
69
+ .openapi("ProjectFileEntity");
70
+
71
+ export const ProjectSocialGraphEntitySchema = z
72
+ .object({
73
+ noOfLikes: z.number().int().optional().openapi({ example: 150 }),
74
+ noOfComments: z.number().int().optional().openapi({ example: 45 }),
75
+ noOfBookmarks: z.number().int().optional().openapi({ example: 22 }),
76
+ noOfViews: z.number().int().optional().openapi({ example: 1200 }),
77
+ })
78
+ .openapi("ProjectSocialGraphEntity");
79
+
80
+ export const UserSocialGraphEntitySchema = z
81
+ .object({
82
+ followerCount: z.number().int().optional().openapi({ example: 5000 }),
83
+ followingCount: z.number().int().optional().openapi({ example: 150 }),
84
+ })
85
+ .openapi("UserSocialGraphEntity");
86
+
87
+ export const ProjectWithFilesEntitySchema = ProjectEntitySchema.merge(
88
+ ProjectSocialGraphEntitySchema
89
+ )
90
+ .extend({
91
+ projectFiles: z
92
+ .array(ProjectFileEntitySchema)
93
+ .optional()
94
+ .openapi({ description: "Files associated with the project" }),
95
+ })
96
+ .openapi("ProjectWithFilesEntity");
97
+
98
+ export const ProjectViewEntitySchema = z
99
+ .object({
100
+ id: z.cuid2().openapi({ example: "view_cksd0v6q0000s9a5y8z7p3x9" }),
101
+ userId: z.cuid2().optional().openapi({ example: "user_view_xyz" }),
102
+ ipAddress: z.ipv4().optional().openapi({ example: "192.168.1.1" }),
103
+ userAgent: z
104
+ .string()
105
+ .optional()
106
+ .openapi({ example: "Mozilla/5.0 (Windows NT 10.0; Win64)" }),
107
+ projectId: z.cuid2().openapi({ example: "proj_abc456" }),
108
+ sessionId: z.string().optional().openapi({ example: "sess_xyz789" }),
109
+ viewedAt: z.iso.datetime().openapi({ example: "2025-10-14T10:30:00.000Z" }),
110
+ viewDate: z.iso.datetime().openapi({ example: "2025-10-14T00:00:00.000Z" }),
111
+ })
112
+ .openapi("ProjectViewEntity");
113
+
114
+ export const ProjectLikeEntitySchema = z
115
+ .object({
116
+ createdAt: z.iso
117
+ .datetime()
118
+ .openapi({ example: "2025-10-13T11:00:00.000Z" }),
119
+ userId: z.cuid2().openapi({ example: "user_liker_123" }),
120
+ projectId: z.cuid2().openapi({ example: "proj_abc456" }),
121
+ })
122
+ .openapi("ProjectLikeEntity");
123
+
124
+ export const ProjectCommentEntitySchema = z
125
+ .object({
126
+ id: z.cuid2().openapi({ example: "comment_id_1" }),
127
+ createdAt: z.iso
128
+ .datetime()
129
+ .openapi({ example: "2025-10-13T12:00:00.000Z" }),
130
+ userId: z.cuid2().openapi({ example: "user_commenter_456" }),
131
+ projectId: z.cuid2().openapi({ example: "proj_abc456" }),
132
+ parentCommentId: z
133
+ .cuid2()
134
+ .optional()
135
+ .openapi({ example: "comment_id_parent_1" }),
136
+ content: z
137
+ .string()
138
+ .min(1)
139
+ .openapi({ example: "Amazing work on the color palette!" }),
140
+ })
141
+ .openapi("ProjectCommentEntity");
142
+
143
+ export const ProjectBookmarkEntitySchema = z
144
+ .object({
145
+ createdAt: z.iso
146
+ .datetime()
147
+ .openapi({ example: "2025-10-13T13:00:00.000Z" }),
148
+ userId: z.cuid2().openapi({ example: "user_bookmark_789" }),
149
+ projectId: z.cuid2().openapi({ example: "proj_abc456" }),
150
+ })
151
+ .openapi("ProjectBookmarkEntity");
152
+
153
+ // =================================================================
154
+ // 5. Output Schemas
155
+ // =================================================================
156
+
157
+ export const ProjectUpdateOutputEntitySchema = z
158
+ .object({
159
+ id: z.cuid2().openapi({ example: "cksd0v6q0000s9a5y8z7p3x9" }),
160
+ })
161
+ .openapi("ProjectUpdateOutputEntity");