@zyacreatives/shared 2.2.87 → 2.2.89

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.
@@ -280,6 +280,15 @@ export declare const GATEWAY_PROVIDER: {
280
280
  readonly PAYSTACK: "PAYSTACK";
281
281
  readonly STRIPE: "STRIPE";
282
282
  };
283
+ export declare const PRICING_MODELS: {
284
+ readonly FREE: "Free";
285
+ readonly FIXED: "Fixed";
286
+ readonly PWYW: "Pay What You Want";
287
+ };
288
+ export declare const DISCOUNT_TYPES: {
289
+ readonly FIXED_AMOUNT: "Fixed Amount";
290
+ readonly PERCENTAGE: "Percentage";
291
+ };
283
292
  export type SignalInterestType = (typeof SIGNAL_INTEREST_TYPES)[keyof typeof SIGNAL_INTEREST_TYPES];
284
293
  export type SignalStatus = (typeof SIGNAL_STATUS)[keyof typeof SIGNAL_STATUS];
285
294
  export type NotificationType = (typeof NOTIFICATION_TYPES)[keyof typeof NOTIFICATION_TYPES];
@@ -316,6 +325,8 @@ export type SellerStatus = (typeof SELLER_STATUS)[keyof typeof SELLER_STATUS];
316
325
  export type CountryOfOperation = (typeof COUNTRY_OF_OPERATION)[keyof typeof COUNTRY_OF_OPERATION];
317
326
  export type GatewayProvider = (typeof GATEWAY_PROVIDER)[keyof typeof GATEWAY_PROVIDER];
318
327
  export type PaymentMethodStatus = (typeof PAYMENT_METHOD_STATUS)[keyof typeof PAYMENT_METHOD_STATUS];
328
+ export type PricingModel = (typeof PRICING_MODELS)[keyof typeof PRICING_MODELS];
329
+ export type DiscountType = (typeof DISCOUNT_TYPES)[keyof typeof DISCOUNT_TYPES];
319
330
  export declare const API_ROUTES: {
320
331
  readonly healthCheck: "/health";
321
332
  readonly username: {
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ANALYTICS_EVENTS = exports.DEFAULT_DISCIPLINES = exports.API_ROUTES = exports.GATEWAY_PROVIDER = exports.COUNTRY_OF_OPERATION = exports.SIGNAL_STATUS = exports.SIGNAL_INTEREST_TYPES = exports.VENTURE_STAGES = exports.NOTIFICATION_TYPES = exports.APPLICATION_STATUS = exports.LINK_TYPES = exports.ACTIVITY_TYPES = exports.POST_BADGE_TYPES = exports.PROJECT_STATUS = exports.MESSAGE_REQUEST_STATUS = exports.JOB_STATUS = exports.WAGES_CURRENCY = exports.GIG_TYPE = exports.MESSAGE_TYPES = exports.JOB_LOCATIONS = exports.JOB_SECTIONS = exports.WAGE_TYPES = exports.JOB_AVAILABILITY_TYPES = exports.EMPLOYMENT_TYPE = exports.WORK_MODE = exports.JOB_TYPE = exports.POST_TYPES = exports.ACTIVITY_PARENT_TYPES = exports.INVESTOR_VERIFICATION_DOCUMENT_TYPES = exports.INVESTOR_VERIFICATION_DOCUMENT_STATUSES = exports.GEOGRAPHIC_FOCUS = exports.INVESTMENT_SIZES = exports.INVESTOR_TYPES = exports.ONBOARDING_PAGES = exports.PAYMENT_METHOD_STATUS = exports.SELLER_STATUS = exports.EXPERIENCE_LEVELS = exports.CLIENT_TYPES = exports.USER_STATUSES = exports.ROLES = void 0;
3
+ exports.ANALYTICS_EVENTS = exports.DEFAULT_DISCIPLINES = exports.API_ROUTES = exports.DISCOUNT_TYPES = exports.PRICING_MODELS = exports.GATEWAY_PROVIDER = exports.COUNTRY_OF_OPERATION = exports.SIGNAL_STATUS = exports.SIGNAL_INTEREST_TYPES = exports.VENTURE_STAGES = exports.NOTIFICATION_TYPES = exports.APPLICATION_STATUS = exports.LINK_TYPES = exports.ACTIVITY_TYPES = exports.POST_BADGE_TYPES = exports.PROJECT_STATUS = exports.MESSAGE_REQUEST_STATUS = exports.JOB_STATUS = exports.WAGES_CURRENCY = exports.GIG_TYPE = exports.MESSAGE_TYPES = exports.JOB_LOCATIONS = exports.JOB_SECTIONS = exports.WAGE_TYPES = exports.JOB_AVAILABILITY_TYPES = exports.EMPLOYMENT_TYPE = exports.WORK_MODE = exports.JOB_TYPE = exports.POST_TYPES = exports.ACTIVITY_PARENT_TYPES = exports.INVESTOR_VERIFICATION_DOCUMENT_TYPES = exports.INVESTOR_VERIFICATION_DOCUMENT_STATUSES = exports.GEOGRAPHIC_FOCUS = exports.INVESTMENT_SIZES = exports.INVESTOR_TYPES = exports.ONBOARDING_PAGES = exports.PAYMENT_METHOD_STATUS = exports.SELLER_STATUS = exports.EXPERIENCE_LEVELS = exports.CLIENT_TYPES = exports.USER_STATUSES = exports.ROLES = void 0;
4
4
  exports.ROLES = {
5
5
  CREATIVE: "CREATIVE",
6
6
  BRAND: "BRAND",
@@ -278,6 +278,15 @@ exports.GATEWAY_PROVIDER = {
278
278
  PAYSTACK: "PAYSTACK",
279
279
  STRIPE: "STRIPE",
280
280
  };
281
+ exports.PRICING_MODELS = {
282
+ FREE: "Free",
283
+ FIXED: "Fixed",
284
+ PWYW: "Pay What You Want",
285
+ };
286
+ exports.DISCOUNT_TYPES = {
287
+ FIXED_AMOUNT: "Fixed Amount",
288
+ PERCENTAGE: "Percentage",
289
+ };
281
290
  exports.API_ROUTES = {
282
291
  healthCheck: "/health",
283
292
  username: {
@@ -24,3 +24,4 @@ export * from "./investor-signal";
24
24
  export * from "./investor-shortlist";
25
25
  export * from "./payout-method";
26
26
  export * from "./seller";
27
+ export * from "./product";
@@ -40,3 +40,4 @@ __exportStar(require("./investor-signal"), exports);
40
40
  __exportStar(require("./investor-shortlist"), exports);
41
41
  __exportStar(require("./payout-method"), exports);
42
42
  __exportStar(require("./seller"), exports);
43
+ __exportStar(require("./product"), exports);
@@ -29,10 +29,7 @@ exports.PostEntitySchema = zod_openapi_1.z.object({
29
29
  .openapi({
30
30
  example: [{ name: "javascript", id: 101 }],
31
31
  }),
32
- badge: zod_openapi_1.z
33
- .enum(constants_1.POST_BADGE_TYPES)
34
- .optional()
35
- .openapi({ example: "FEATURED" }),
32
+ badge: zod_openapi_1.z.enum(constants_1.POST_BADGE_TYPES).optional().openapi({ example: "FEATURED" }),
36
33
  userId: zod_openapi_1.z
37
34
  .cuid2()
38
35
  .openapi({ description: "User id", example: "ckj1a2b3c0000def" }),
@@ -40,10 +37,7 @@ exports.PostEntitySchema = zod_openapi_1.z.object({
40
37
  .string()
41
38
  .optional()
42
39
  .openapi({ description: "Username", example: "dev_guru" }),
43
- creatorFullName: zod_openapi_1.z
44
- .string()
45
- .optional()
46
- .openapi({ example: "Jane Doe" }),
40
+ creatorFullName: zod_openapi_1.z.string().optional().openapi({ example: "Jane Doe" }),
47
41
  creatorImageUrl: zod_openapi_1.z
48
42
  .cuid2()
49
43
  .optional()
@@ -65,8 +59,7 @@ exports.PostEntitySchema = zod_openapi_1.z.object({
65
59
  title: "Post Type",
66
60
  example: "PROJECT",
67
61
  }),
68
- createdAt: zod_openapi_1.z
69
- .coerce
62
+ createdAt: zod_openapi_1.z.coerce
70
63
  .date()
71
64
  .optional()
72
65
  .openapi({ example: "2026-03-11T14:43:09Z" }),
@@ -98,14 +91,17 @@ exports.PostFileEntitySchema = zod_openapi_1.z
98
91
  .object({
99
92
  id: zod_openapi_1.z
100
93
  .cuid2()
101
- .openapi({ description: "CUID2 of the project file record.", example: "cxy1a2b3c0000qwe" }),
94
+ .openapi({
95
+ description: "CUID2 of the project file record.",
96
+ example: "cxy1a2b3c0000qwe",
97
+ }),
102
98
  postId: zod_openapi_1.z.cuid2().openapi({
103
99
  description: "CUID2 of the post this file belongs to.",
104
100
  example: "ckj1a2b3c0000xyz",
105
101
  }),
106
102
  fileId: zod_openapi_1.z.cuid2().openapi({
107
103
  description: "CUID2 of the linked file.",
108
- example: "cvb1a2b3c0000rty"
104
+ example: "cvb1a2b3c0000rty",
109
105
  }),
110
106
  order: zod_openapi_1.z.number().int().openapi({
111
107
  description: "Order index of the file in the project.",
@@ -130,9 +126,9 @@ exports.PostWithFilesEntitySchema = exports.PostEntitySchema.extend({
130
126
  postId: "ckj1a2b3c0000xyz",
131
127
  fileId: "cvb1a2b3c0000rty",
132
128
  order: 1,
133
- url: "https://cdn.example.com/image.png"
134
- }
135
- ]
129
+ url: "https://cdn.example.com/image.png",
130
+ },
131
+ ],
136
132
  }),
137
133
  });
138
134
  exports.CreatePostInputSchema = zod_openapi_1.z.object({
@@ -169,17 +165,22 @@ exports.CreatePostInputSchema = zod_openapi_1.z.object({
169
165
  .max(5, { message: "Cannot attach more than 5 files" })
170
166
  .optional()
171
167
  .openapi({
172
- example: [{ fileId: "cvb1a2b3c0000rty", order: 1 }]
168
+ example: [{ fileId: "cvb1a2b3c0000rty", order: 1 }],
173
169
  }),
174
170
  tags: zod_openapi_1.z
175
- .array(zod_openapi_1.z.string().min(1, { message: "Tag cannot be empty" }).openapi({ example: "react" }))
171
+ .array(zod_openapi_1.z
172
+ .string()
173
+ .min(1, { message: "Tag cannot be empty" })
174
+ .openapi({ example: "react" }))
176
175
  .max(3, { message: "Cannot add more than 3 tags" })
177
176
  .optional()
178
177
  .openapi({ example: ["react", "frontend"] }),
179
178
  badge: zod_openapi_1.z.enum(constants_1.POST_BADGE_TYPES).optional().openapi({ example: "TRENDING" }),
180
179
  linkMeta: zod_openapi_1.z
181
180
  .object({
182
- url: zod_openapi_1.z.url({ message: "Invalid URL format" }).openapi({ example: "https://example.com" }),
181
+ url: zod_openapi_1.z
182
+ .url({ message: "Invalid URL format" })
183
+ .openapi({ example: "https://example.com" }),
183
184
  title: zod_openapi_1.z
184
185
  .string()
185
186
  .max(200, { message: "Title cannot exceed 200 characters" })
@@ -192,7 +193,10 @@ exports.CreatePostInputSchema = zod_openapi_1.z.object({
192
193
  })
193
194
  .optional()
194
195
  .openapi({ example: "This is an example link" }),
195
- image: zod_openapi_1.z.url({ message: "Invalid image URL" }).optional().openapi({ example: "https://example.com/preview.jpg" }),
196
+ image: zod_openapi_1.z
197
+ .url({ message: "Invalid image URL" })
198
+ .optional()
199
+ .openapi({ example: "https://example.com/preview.jpg" }),
196
200
  })
197
201
  .optional()
198
202
  .openapi({
@@ -208,7 +212,7 @@ exports.CreatePostInputSchema = zod_openapi_1.z.object({
208
212
  exports.CreatePostOutputSchema = exports.PostEntitySchema;
209
213
  exports.GetPostOutputSchema = exports.PostWithFilesEntitySchema;
210
214
  exports.PostIdSchema = zod_openapi_1.z.object({
211
- postId: zod_openapi_1.z.cuid2().openapi({ example: "ckj1a2b3c0000xyz" })
215
+ postId: zod_openapi_1.z.cuid2().openapi({ example: "ckj1a2b3c0000xyz" }),
212
216
  });
213
217
  exports.MinimalPostSchema = exports.PostEntitySchema.pick({
214
218
  id: true,
@@ -216,15 +220,21 @@ exports.MinimalPostSchema = exports.PostEntitySchema.pick({
216
220
  content: true,
217
221
  });
218
222
  exports.PostWithLikesEntitySchema = exports.MinimalPostSchema.extend({
219
- likes: zod_openapi_1.z.array(activity_1.ActivitySchema.extend({
223
+ likes: zod_openapi_1.z
224
+ .array(activity_1.ActivitySchema.extend({
220
225
  followsYou: zod_openapi_1.z.boolean().optional().openapi({ example: true }),
221
226
  isFollowing: zod_openapi_1.z.boolean().optional().openapi({ example: false }),
222
- })).openapi({ example: [] }),
227
+ }))
228
+ .openapi({ example: [] }),
223
229
  }).openapi({
224
230
  title: "PostWithPostLikesEntity",
225
231
  });
226
232
  exports.GetPostWithLikesOutputSchema = exports.PostWithLikesEntitySchema.extend({
227
- nextCursor: zod_openapi_1.z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
233
+ nextCursor: zod_openapi_1.z
234
+ .string()
235
+ .optional()
236
+ .nullable()
237
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
228
238
  });
229
239
  exports.PostWithCommentsEntitySchema = exports.MinimalPostSchema.extend({
230
240
  comments: zod_openapi_1.z.array(comment_1.CommentEntitySchema).openapi({ example: [] }),
@@ -232,7 +242,11 @@ exports.PostWithCommentsEntitySchema = exports.MinimalPostSchema.extend({
232
242
  title: "PostWithPostCommentsEntity",
233
243
  });
234
244
  exports.GetPostWithCommentsOutputSchema = exports.PostWithCommentsEntitySchema.extend({
235
- nextCursor: zod_openapi_1.z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
245
+ nextCursor: zod_openapi_1.z
246
+ .string()
247
+ .optional()
248
+ .nullable()
249
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
236
250
  });
237
251
  exports.PostWithBookmarksEntitySchema = exports.MinimalPostSchema.extend({
238
252
  bookmarks: zod_openapi_1.z.array(activity_1.ActivitySchema).openapi({ example: [] }),
@@ -247,9 +261,18 @@ exports.LinkPreviewInputSchema = zod_openapi_1.z.object({
247
261
  });
248
262
  exports.LinkPreviewOutputSchema = zod_openapi_1.z.object({
249
263
  title: zod_openapi_1.z.string().openapi({ example: "Great Article" }),
250
- description: zod_openapi_1.z.string().optional().openapi({ example: "A detailed breakdown of the topic." }),
251
- image: zod_openapi_1.z.string().optional().openapi({ example: "https://example.com/hero.jpg" }),
252
- url: zod_openapi_1.z.string().optional().openapi({ example: "https://example.com/article" }),
264
+ description: zod_openapi_1.z
265
+ .string()
266
+ .optional()
267
+ .openapi({ example: "A detailed breakdown of the topic." }),
268
+ image: zod_openapi_1.z
269
+ .string()
270
+ .optional()
271
+ .openapi({ example: "https://example.com/hero.jpg" }),
272
+ url: zod_openapi_1.z
273
+ .string()
274
+ .optional()
275
+ .openapi({ example: "https://example.com/article" }),
253
276
  });
254
277
  exports.FeedPostEntitySchema = exports.PostWithFilesEntitySchema.extend({
255
278
  stats: entity_stats_1.EntityStatsSchema,
@@ -264,7 +287,11 @@ exports.GetFeedInputSchema = zod_openapi_1.z.object({
264
287
  });
265
288
  exports.GetFeedOutputSchema = zod_openapi_1.z.object({
266
289
  feed: zod_openapi_1.z.array(exports.FeedPostEntitySchema).openapi({ example: [] }),
267
- nextCursor: zod_openapi_1.z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
290
+ nextCursor: zod_openapi_1.z
291
+ .string()
292
+ .optional()
293
+ .nullable()
294
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
268
295
  });
269
296
  exports.SearchPostInputSchema = zod_openapi_1.z.object({
270
297
  queryString: zod_openapi_1.z
@@ -276,7 +303,11 @@ exports.SearchPostInputSchema = zod_openapi_1.z.object({
276
303
  });
277
304
  exports.SearchPostOutputSchema = zod_openapi_1.z.object({
278
305
  posts: zod_openapi_1.z.array(exports.FeedPostEntitySchema).openapi({ example: [] }),
279
- nextCursor: zod_openapi_1.z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
306
+ nextCursor: zod_openapi_1.z
307
+ .string()
308
+ .optional()
309
+ .nullable()
310
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
280
311
  });
281
312
  exports.ReportPostInputSchema = zod_openapi_1.z.object({
282
313
  complaint: zod_openapi_1.z
@@ -301,8 +332,12 @@ exports.PostAnalyticsOutputSchema = zod_openapi_1.z.object({
301
332
  comments: zod_openapi_1.z.number().openapi({ example: 34 }),
302
333
  linkCopied: zod_openapi_1.z.number().openapi({ example: 12 }),
303
334
  bookmarks: zod_openapi_1.z.number().openapi({ example: 56 }),
304
- tagsClicked: zod_openapi_1.z.array(AnalyticsChartItemSchema).openapi({ example: [{ x: "javascript", y: 25 }] }),
305
- platformShares: zod_openapi_1.z.array(AnalyticsChartItemSchema).openapi({ example: [{ x: "Twitter", y: 10 }] }),
335
+ tagsClicked: zod_openapi_1.z
336
+ .array(AnalyticsChartItemSchema)
337
+ .openapi({ example: [{ x: "javascript", y: 25 }] }),
338
+ platformShares: zod_openapi_1.z
339
+ .array(AnalyticsChartItemSchema)
340
+ .openapi({ example: [{ x: "Twitter", y: 10 }] }),
306
341
  }),
307
342
  behavior: zod_openapi_1.z.object({
308
343
  viralityScore: zod_openapi_1.z.number().openapi({ example: 8.5 }),
@@ -318,26 +353,44 @@ exports.PostAnalyticsOutputSchema = zod_openapi_1.z.object({
318
353
  }),
319
354
  }),
320
355
  });
321
- exports.PostSearchDocumentSchema = zod_openapi_1.z.object({
356
+ exports.PostSearchDocumentSchema = zod_openapi_1.z
357
+ .object({
322
358
  id: zod_openapi_1.z.cuid2().openapi({ example: "ckj1a2b3c0000doc" }),
323
359
  userId: zod_openapi_1.z.cuid2().openapi({ example: "ckj1a2b3c0000usr" }),
324
360
  parentId: zod_openapi_1.z.cuid2().nullable().openapi({ example: "ckj1a2b3c0000prt" }),
325
361
  parentType: zod_openapi_1.z.enum(constants_1.ACTIVITY_PARENT_TYPES).openapi({ example: "POST" }),
326
362
  creatorUsername: zod_openapi_1.z.string().nullable().openapi({ example: "tech_lead" }),
327
363
  creatorFullName: zod_openapi_1.z.string().nullable().openapi({ example: "Alex Smith" }),
328
- creatorImageUrl: zod_openapi_1.z.cuid2().nullable().openapi({ example: "clm1a2b3c0000pic" }),
364
+ creatorImageUrl: zod_openapi_1.z
365
+ .cuid2()
366
+ .nullable()
367
+ .openapi({ example: "clm1a2b3c0000pic" }),
329
368
  tagIds: zod_openapi_1.z.array(zod_openapi_1.z.number()).openapi({ example: [101, 102] }),
330
369
  tagNames: zod_openapi_1.z.array(zod_openapi_1.z.string()).openapi({ example: ["react", "typescript"] }),
331
370
  badge: zod_openapi_1.z.enum(constants_1.POST_BADGE_TYPES).nullable().openapi({ example: "TRENDING" }),
332
371
  postType: zod_openapi_1.z.enum(constants_1.POST_TYPES).openapi({ example: "PROJECT" }),
333
- content: zod_openapi_1.z.string().nullable().openapi({ example: "Here is my latest open source tool." }),
372
+ content: zod_openapi_1.z
373
+ .string()
374
+ .nullable()
375
+ .openapi({ example: "Here is my latest open source tool." }),
334
376
  linkTitle: zod_openapi_1.z.string().nullable().openapi({ example: "GitHub Repo" }),
335
- linkDescription: zod_openapi_1.z.string().nullable().openapi({ example: "A fast, modern build system." }),
336
- linkUrl: zod_openapi_1.z.url().nullable().openapi({ example: "https://github.com/project" }),
337
- linkImage: zod_openapi_1.z.url().nullable().openapi({ example: "https://github.com/image.png" }),
377
+ linkDescription: zod_openapi_1.z
378
+ .string()
379
+ .nullable()
380
+ .openapi({ example: "A fast, modern build system." }),
381
+ linkUrl: zod_openapi_1.z
382
+ .url()
383
+ .nullable()
384
+ .openapi({ example: "https://github.com/project" }),
385
+ linkImage: zod_openapi_1.z
386
+ .url()
387
+ .nullable()
388
+ .openapi({ example: "https://github.com/image.png" }),
338
389
  postFiles: zod_openapi_1.z
339
390
  .array(exports.PostFileEntitySchema.extend({
340
- url: zod_openapi_1.z.url().openapi({ example: "https://cdn.example.com/file1.png" }),
391
+ url: zod_openapi_1.z
392
+ .url()
393
+ .openapi({ example: "https://cdn.example.com/file1.png" }),
341
394
  }))
342
395
  .nullable()
343
396
  .openapi({
@@ -351,8 +404,12 @@ exports.PostSearchDocumentSchema = zod_openapi_1.z.object({
351
404
  },
352
405
  ],
353
406
  }),
354
- createdAt: zod_openapi_1.z.string().nullable().openapi({ example: "2026-03-11T14:43:09.000Z" }),
355
- }).openapi({
407
+ createdAt: zod_openapi_1.z
408
+ .string()
409
+ .nullable()
410
+ .openapi({ example: "2026-03-11T14:43:09.000Z" }),
411
+ })
412
+ .openapi({
356
413
  title: "Post Search Document",
357
414
  description: "Flattened schema used for indexing posts in search engines.",
358
415
  });
@@ -0,0 +1,96 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ export declare const ProductCoverImageEntitySchema: z.ZodObject<{
3
+ fileId: z.ZodCUID2;
4
+ isThumbnail: z.ZodDefault<z.ZodBoolean>;
5
+ order: z.ZodDefault<z.ZodNumber>;
6
+ }, z.core.$strip>;
7
+ export declare const ProductDeliveryFileEntitySchema: z.ZodObject<{
8
+ fileId: z.ZodCUID2;
9
+ order: z.ZodDefault<z.ZodNumber>;
10
+ }, z.core.$strip>;
11
+ export declare const ProductDiscountEntitySchema: z.ZodObject<{
12
+ discountType: z.ZodEnum<{
13
+ readonly FIXED_AMOUNT: "Fixed Amount";
14
+ readonly PERCENTAGE: "Percentage";
15
+ }>;
16
+ amount: z.ZodNumber;
17
+ discountCode: z.ZodOptional<z.ZodString>;
18
+ }, z.core.$strip>;
19
+ export declare const CreateProductInputSchema: z.ZodObject<{
20
+ title: z.ZodString;
21
+ description: z.ZodString;
22
+ keyFeatures: z.ZodString;
23
+ category: z.ZodString;
24
+ subcategory: z.ZodOptional<z.ZodString>;
25
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
26
+ coverImages: z.ZodArray<z.ZodObject<{
27
+ fileId: z.ZodCUID2;
28
+ isThumbnail: z.ZodDefault<z.ZodBoolean>;
29
+ order: z.ZodDefault<z.ZodNumber>;
30
+ }, z.core.$strip>>;
31
+ productFiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
32
+ fileId: z.ZodCUID2;
33
+ order: z.ZodDefault<z.ZodNumber>;
34
+ }, z.core.$strip>>>;
35
+ productLinks: z.ZodDefault<z.ZodArray<z.ZodURL>>;
36
+ pricingModel: z.ZodDefault<z.ZodEnum<{
37
+ readonly FREE: "Free";
38
+ readonly FIXED: "Fixed";
39
+ readonly PWYW: "Pay What You Want";
40
+ }>>;
41
+ currency: z.ZodDefault<z.ZodString>;
42
+ price: z.ZodOptional<z.ZodNumber>;
43
+ suggestedPrice: z.ZodOptional<z.ZodNumber>;
44
+ discounts: z.ZodDefault<z.ZodArray<z.ZodObject<{
45
+ discountType: z.ZodEnum<{
46
+ readonly FIXED_AMOUNT: "Fixed Amount";
47
+ readonly PERCENTAGE: "Percentage";
48
+ }>;
49
+ amount: z.ZodNumber;
50
+ discountCode: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strip>>>;
52
+ }, z.core.$strip>;
53
+ export declare const ProductEntitySchema: z.ZodObject<{
54
+ id: z.ZodCUID2;
55
+ userId: z.ZodCUID2;
56
+ title: z.ZodString;
57
+ description: z.ZodString;
58
+ keyFeatures: z.ZodString;
59
+ category: z.ZodString;
60
+ subcategory: z.ZodNullable<z.ZodOptional<z.ZodString>>;
61
+ tags: z.ZodArray<z.ZodString>;
62
+ coverImages: z.ZodDefault<z.ZodArray<z.ZodObject<{
63
+ fileId: z.ZodCUID2;
64
+ isThumbnail: z.ZodDefault<z.ZodBoolean>;
65
+ order: z.ZodDefault<z.ZodNumber>;
66
+ }, z.core.$strip>>>;
67
+ productFiles: z.ZodDefault<z.ZodArray<z.ZodObject<{
68
+ fileId: z.ZodCUID2;
69
+ order: z.ZodDefault<z.ZodNumber>;
70
+ }, z.core.$strip>>>;
71
+ productLinks: z.ZodDefault<z.ZodArray<z.ZodURL>>;
72
+ pricingModel: z.ZodEnum<{
73
+ readonly FREE: "Free";
74
+ readonly FIXED: "Fixed";
75
+ readonly PWYW: "Pay What You Want";
76
+ }>;
77
+ currency: z.ZodString;
78
+ price: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
79
+ suggestedPrice: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
80
+ discounts: z.ZodDefault<z.ZodArray<z.ZodObject<{
81
+ discountType: z.ZodEnum<{
82
+ readonly FIXED_AMOUNT: "Fixed Amount";
83
+ readonly PERCENTAGE: "Percentage";
84
+ }>;
85
+ amount: z.ZodNumber;
86
+ discountCode: z.ZodOptional<z.ZodString>;
87
+ }, z.core.$strip>>>;
88
+ createdAt: z.ZodCoercedDate<unknown>;
89
+ updatedAt: z.ZodCoercedDate<unknown>;
90
+ deletedAt: z.ZodNullable<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
91
+ }, z.core.$strip>;
92
+ export type ProductCoverImageEntity = z.infer<typeof ProductCoverImageEntitySchema>;
93
+ export type ProductDeliveryFileEntity = z.infer<typeof ProductDeliveryFileEntitySchema>;
94
+ export type ProductDiscountEntity = z.infer<typeof ProductDiscountEntitySchema>;
95
+ export type CreateProductInputEntity = z.infer<typeof CreateProductInputSchema>;
96
+ export type ProductEntity = z.infer<typeof ProductEntitySchema>;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProductEntitySchema = exports.CreateProductInputSchema = exports.ProductDiscountEntitySchema = exports.ProductDeliveryFileEntitySchema = exports.ProductCoverImageEntitySchema = void 0;
4
+ const zod_openapi_1 = require("@hono/zod-openapi");
5
+ const constants_1 = require("../constants");
6
+ exports.ProductCoverImageEntitySchema = zod_openapi_1.z.object({
7
+ fileId: zod_openapi_1.z
8
+ .cuid2()
9
+ .openapi({ example: "f123e4567-e89b-12d3-a456-426614174000" }),
10
+ isThumbnail: zod_openapi_1.z.boolean().default(false),
11
+ order: zod_openapi_1.z.number().int().default(0),
12
+ });
13
+ exports.ProductDeliveryFileEntitySchema = zod_openapi_1.z.object({
14
+ fileId: zod_openapi_1.z
15
+ .cuid2()
16
+ .openapi({ example: "f987e6543-e89b-12d3-a456-426614174000" }),
17
+ order: zod_openapi_1.z.number().int().default(0),
18
+ });
19
+ exports.ProductDiscountEntitySchema = zod_openapi_1.z.object({
20
+ discountType: zod_openapi_1.z.enum(constants_1.DISCOUNT_TYPES),
21
+ amount: zod_openapi_1.z
22
+ .number()
23
+ .int("Amount must be a whole number")
24
+ .min(0, "Discount amount cannot be negative"),
25
+ discountCode: zod_openapi_1.z.string().optional(),
26
+ });
27
+ exports.CreateProductInputSchema = zod_openapi_1.z
28
+ .object({
29
+ title: zod_openapi_1.z.string().min(1, "Title is required").max(255),
30
+ description: zod_openapi_1.z.string().min(1, "Description is required"),
31
+ keyFeatures: zod_openapi_1.z.string(),
32
+ category: zod_openapi_1.z.string().min(1, "Category is required"),
33
+ subcategory: zod_openapi_1.z.string().optional(),
34
+ tags: zod_openapi_1.z
35
+ .array(zod_openapi_1.z.string())
36
+ .max(10, "Keep tags to a maximum of 10")
37
+ .default([]),
38
+ coverImages: zod_openapi_1.z
39
+ .array(exports.ProductCoverImageEntitySchema)
40
+ .min(1, "At least one cover image is required")
41
+ .max(3, "Maximum of 3 cover images allowed"),
42
+ productFiles: zod_openapi_1.z.array(exports.ProductDeliveryFileEntitySchema).default([]),
43
+ productLinks: zod_openapi_1.z
44
+ .array(zod_openapi_1.z.url().openapi({ example: "https://figma.com/file/..." }))
45
+ .default([]),
46
+ pricingModel: zod_openapi_1.z.enum(constants_1.PRICING_MODELS).default(constants_1.PRICING_MODELS.FIXED),
47
+ currency: zod_openapi_1.z.string().default("USD").openapi({ example: "USD" }),
48
+ price: zod_openapi_1.z.number().int("Must be in cents").min(0).optional(),
49
+ suggestedPrice: zod_openapi_1.z.number().int("Must be in cents").min(0).optional(),
50
+ discounts: zod_openapi_1.z.array(exports.ProductDiscountEntitySchema).default([]),
51
+ })
52
+ .superRefine((data, ctx) => {
53
+ if (data.pricingModel === constants_1.PRICING_MODELS.FIXED &&
54
+ (!data.price || data.price <= 0)) {
55
+ ctx.addIssue({
56
+ code: "custom",
57
+ message: "Fixed pricing requires a price strictly greater than 0.",
58
+ path: ["price"],
59
+ });
60
+ }
61
+ if (data.pricingModel === constants_1.PRICING_MODELS.PWYW) {
62
+ if (data.suggestedPrice !== undefined && data.price !== undefined) {
63
+ if (data.suggestedPrice < data.price) {
64
+ ctx.addIssue({
65
+ code: "custom",
66
+ message: "Suggested price cannot be lower than the minimum price.",
67
+ path: ["suggestedPrice"],
68
+ });
69
+ }
70
+ }
71
+ }
72
+ if (data.pricingModel === constants_1.PRICING_MODELS.FREE &&
73
+ data.price &&
74
+ data.price > 0) {
75
+ ctx.addIssue({
76
+ code: "custom",
77
+ message: "Free products cannot have a price greater than 0.",
78
+ path: ["price"],
79
+ });
80
+ }
81
+ if (data.productFiles.length === 0 && data.productLinks.length === 0) {
82
+ ctx.addIssue({
83
+ code: "custom",
84
+ message: "You must provide at least one product file or a link for the buyer to receive.",
85
+ path: ["productFiles"],
86
+ });
87
+ }
88
+ const thumbnails = data.coverImages.filter((img) => img.isThumbnail);
89
+ if (thumbnails.length !== 1) {
90
+ ctx.addIssue({
91
+ code: "custom",
92
+ message: "Exactly one cover image must be set as the thumbnail.",
93
+ path: ["coverImages"],
94
+ });
95
+ }
96
+ });
97
+ exports.ProductEntitySchema = zod_openapi_1.z
98
+ .object({
99
+ id: zod_openapi_1.z.cuid2(),
100
+ userId: zod_openapi_1.z.cuid2().openapi({ description: "ID of the creator/seller" }),
101
+ title: zod_openapi_1.z.string(),
102
+ description: zod_openapi_1.z.string(),
103
+ keyFeatures: zod_openapi_1.z.string(),
104
+ category: zod_openapi_1.z.string(),
105
+ subcategory: zod_openapi_1.z.string().optional().nullable(),
106
+ tags: zod_openapi_1.z.array(zod_openapi_1.z.string()),
107
+ coverImages: zod_openapi_1.z.array(exports.ProductCoverImageEntitySchema).default([]),
108
+ productFiles: zod_openapi_1.z.array(exports.ProductDeliveryFileEntitySchema).default([]),
109
+ productLinks: zod_openapi_1.z.array(zod_openapi_1.z.url()).default([]),
110
+ pricingModel: zod_openapi_1.z.enum(constants_1.PRICING_MODELS),
111
+ currency: zod_openapi_1.z.string(),
112
+ price: zod_openapi_1.z.number().int().optional().nullable(),
113
+ suggestedPrice: zod_openapi_1.z.number().int().optional().nullable(),
114
+ discounts: zod_openapi_1.z.array(exports.ProductDiscountEntitySchema).default([]),
115
+ createdAt: zod_openapi_1.z.coerce.date(),
116
+ updatedAt: zod_openapi_1.z.coerce.date(),
117
+ deletedAt: zod_openapi_1.z.coerce.date().optional().nullable(),
118
+ })
119
+ .openapi({ title: "ProductEntity" });
@@ -41,3 +41,42 @@ export declare const UpdateSellerEntitySchema: z.ZodObject<{
41
41
  }>>;
42
42
  }, z.core.$strip>;
43
43
  export type UpdateSellerInput = z.infer<typeof UpdateSellerEntitySchema>;
44
+ export declare const SellerProfileSchema: z.ZodObject<{
45
+ id: z.ZodCUID2;
46
+ businessName: z.ZodString;
47
+ countryOfOperation: z.ZodEnum<{
48
+ readonly NG: "NG";
49
+ readonly GB: "GB";
50
+ }>;
51
+ stripeConnectId: z.ZodNullable<z.ZodString>;
52
+ paystackSubaccountCode: z.ZodNullable<z.ZodString>;
53
+ status: z.ZodDefault<z.ZodEnum<{
54
+ readonly ACTIVE: "ACTIVE";
55
+ readonly PENDING: "PENDING";
56
+ readonly SUSPENDED: "SUSPENDED";
57
+ }>>;
58
+ createdAt: z.ZodCoercedDate<unknown>;
59
+ updatedAt: z.ZodCoercedDate<unknown>;
60
+ payoutMethods: z.ZodArray<z.ZodObject<{
61
+ id: z.ZodCUID2;
62
+ sellerId: z.ZodCUID2;
63
+ provider: z.ZodEnum<{
64
+ readonly PAYSTACK: "PAYSTACK";
65
+ readonly STRIPE: "STRIPE";
66
+ }>;
67
+ currency: z.ZodString;
68
+ bankName: z.ZodString;
69
+ accountLast4: z.ZodString;
70
+ accountName: z.ZodString;
71
+ externalBankId: z.ZodNullable<z.ZodString>;
72
+ isDefault: z.ZodBoolean;
73
+ status: z.ZodDefault<z.ZodEnum<{
74
+ readonly VERIFIED: "VERIFIED";
75
+ readonly PENDING: "PENDING";
76
+ readonly REJECTED: "REJECTED";
77
+ }>>;
78
+ createdAt: z.ZodCoercedDate<unknown>;
79
+ updatedAt: z.ZodCoercedDate<unknown>;
80
+ }, z.core.$strip>>;
81
+ }, z.core.$strip>;
82
+ export type SellerProfile = z.infer<typeof SellerProfileSchema>;
@@ -3,9 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.UpdateSellerEntitySchema = exports.CreateSellerEntityInputSchema = exports.SellerEntitySchema = void 0;
6
+ exports.SellerProfileSchema = exports.UpdateSellerEntitySchema = exports.CreateSellerEntityInputSchema = exports.SellerEntitySchema = void 0;
7
7
  const zod_1 = __importDefault(require("zod"));
8
8
  const constants_1 = require("../constants");
9
+ const payout_method_1 = require("./payout-method");
9
10
  exports.SellerEntitySchema = zod_1.default.object({
10
11
  id: zod_1.default.cuid2(),
11
12
  businessName: zod_1.default.string(), // Kept here for the app to consume!
@@ -31,3 +32,6 @@ exports.UpdateSellerEntitySchema = zod_1.default
31
32
  status: zod_1.default.enum(constants_1.SELLER_STATUS),
32
33
  })
33
34
  .partial();
35
+ exports.SellerProfileSchema = exports.SellerEntitySchema.extend({
36
+ payoutMethods: zod_1.default.array(payout_method_1.PayoutMethodEntitySchema),
37
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyacreatives/shared",
3
- "version": "2.2.87",
3
+ "version": "2.2.89",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/constants.ts CHANGED
@@ -318,6 +318,17 @@ export const GATEWAY_PROVIDER = {
318
318
  STRIPE: "STRIPE",
319
319
  } as const;
320
320
 
321
+ export const PRICING_MODELS = {
322
+ FREE: "Free",
323
+ FIXED: "Fixed",
324
+ PWYW: "Pay What You Want",
325
+ } as const;
326
+
327
+ export const DISCOUNT_TYPES = {
328
+ FIXED_AMOUNT: "Fixed Amount",
329
+ PERCENTAGE: "Percentage",
330
+ } as const;
331
+
321
332
  export type SignalInterestType =
322
333
  (typeof SIGNAL_INTEREST_TYPES)[keyof typeof SIGNAL_INTEREST_TYPES];
323
334
  export type SignalStatus = (typeof SIGNAL_STATUS)[keyof typeof SIGNAL_STATUS];
@@ -373,6 +384,8 @@ export type GatewayProvider =
373
384
  (typeof GATEWAY_PROVIDER)[keyof typeof GATEWAY_PROVIDER];
374
385
  export type PaymentMethodStatus =
375
386
  (typeof PAYMENT_METHOD_STATUS)[keyof typeof PAYMENT_METHOD_STATUS];
387
+ export type PricingModel = (typeof PRICING_MODELS)[keyof typeof PRICING_MODELS];
388
+ export type DiscountType = (typeof DISCOUNT_TYPES)[keyof typeof DISCOUNT_TYPES];
376
389
  export const API_ROUTES = {
377
390
  healthCheck: "/health",
378
391
  username: {
@@ -24,3 +24,4 @@ export * from "./investor-signal";
24
24
  export * from "./investor-shortlist";
25
25
  export * from "./payout-method";
26
26
  export * from "./seller";
27
+ export * from "./product";
@@ -33,10 +33,7 @@ export const PostEntitySchema = z.object({
33
33
  .openapi({
34
34
  example: [{ name: "javascript", id: 101 }],
35
35
  }),
36
- badge: z
37
- .enum(POST_BADGE_TYPES)
38
- .optional()
39
- .openapi({ example: "FEATURED" }),
36
+ badge: z.enum(POST_BADGE_TYPES).optional().openapi({ example: "FEATURED" }),
40
37
  userId: z
41
38
  .cuid2()
42
39
  .openapi({ description: "User id", example: "ckj1a2b3c0000def" }),
@@ -44,10 +41,7 @@ export const PostEntitySchema = z.object({
44
41
  .string()
45
42
  .optional()
46
43
  .openapi({ description: "Username", example: "dev_guru" }),
47
- creatorFullName: z
48
- .string()
49
- .optional()
50
- .openapi({ example: "Jane Doe" }),
44
+ creatorFullName: z.string().optional().openapi({ example: "Jane Doe" }),
51
45
  creatorImageUrl: z
52
46
  .cuid2()
53
47
  .optional()
@@ -72,8 +66,7 @@ export const PostEntitySchema = z.object({
72
66
  example: "PROJECT",
73
67
  }),
74
68
 
75
- createdAt: z
76
- .coerce
69
+ createdAt: z.coerce
77
70
  .date()
78
71
  .optional()
79
72
  .openapi({ example: "2026-03-11T14:43:09Z" }),
@@ -107,14 +100,17 @@ export const PostFileEntitySchema = z
107
100
  .object({
108
101
  id: z
109
102
  .cuid2()
110
- .openapi({ description: "CUID2 of the project file record.", example: "cxy1a2b3c0000qwe" }),
103
+ .openapi({
104
+ description: "CUID2 of the project file record.",
105
+ example: "cxy1a2b3c0000qwe",
106
+ }),
111
107
  postId: z.cuid2().openapi({
112
108
  description: "CUID2 of the post this file belongs to.",
113
109
  example: "ckj1a2b3c0000xyz",
114
110
  }),
115
- fileId: z.cuid2().openapi({
116
- description: "CUID2 of the linked file.",
117
- example: "cvb1a2b3c0000rty"
111
+ fileId: z.cuid2().openapi({
112
+ description: "CUID2 of the linked file.",
113
+ example: "cvb1a2b3c0000rty",
118
114
  }),
119
115
  order: z.number().int().openapi({
120
116
  description: "Order index of the file in the project.",
@@ -134,7 +130,7 @@ export const PostWithFilesEntitySchema = PostEntitySchema.extend({
134
130
  }),
135
131
  )
136
132
  .optional()
137
- .openapi({
133
+ .openapi({
138
134
  description: "Files associated with the project.",
139
135
  example: [
140
136
  {
@@ -142,9 +138,9 @@ export const PostWithFilesEntitySchema = PostEntitySchema.extend({
142
138
  postId: "ckj1a2b3c0000xyz",
143
139
  fileId: "cvb1a2b3c0000rty",
144
140
  order: 1,
145
- url: "https://cdn.example.com/image.png"
146
- }
147
- ]
141
+ url: "https://cdn.example.com/image.png",
142
+ },
143
+ ],
148
144
  }),
149
145
  });
150
146
 
@@ -185,19 +181,26 @@ export const CreatePostInputSchema = z.object({
185
181
  )
186
182
  .max(5, { message: "Cannot attach more than 5 files" })
187
183
  .optional()
188
- .openapi({
189
- example: [{ fileId: "cvb1a2b3c0000rty", order: 1 }]
184
+ .openapi({
185
+ example: [{ fileId: "cvb1a2b3c0000rty", order: 1 }],
190
186
  }),
191
187
 
192
188
  tags: z
193
- .array(z.string().min(1, { message: "Tag cannot be empty" }).openapi({ example: "react" }))
189
+ .array(
190
+ z
191
+ .string()
192
+ .min(1, { message: "Tag cannot be empty" })
193
+ .openapi({ example: "react" }),
194
+ )
194
195
  .max(3, { message: "Cannot add more than 3 tags" })
195
196
  .optional()
196
197
  .openapi({ example: ["react", "frontend"] }),
197
198
  badge: z.enum(POST_BADGE_TYPES).optional().openapi({ example: "TRENDING" }),
198
199
  linkMeta: z
199
200
  .object({
200
- url: z.url({ message: "Invalid URL format" }).openapi({ example: "https://example.com" }),
201
+ url: z
202
+ .url({ message: "Invalid URL format" })
203
+ .openapi({ example: "https://example.com" }),
201
204
  title: z
202
205
  .string()
203
206
  .max(200, { message: "Title cannot exceed 200 characters" })
@@ -210,7 +213,10 @@ export const CreatePostInputSchema = z.object({
210
213
  })
211
214
  .optional()
212
215
  .openapi({ example: "This is an example link" }),
213
- image: z.url({ message: "Invalid image URL" }).optional().openapi({ example: "https://example.com/preview.jpg" }),
216
+ image: z
217
+ .url({ message: "Invalid image URL" })
218
+ .optional()
219
+ .openapi({ example: "https://example.com/preview.jpg" }),
214
220
  })
215
221
  .optional()
216
222
  .openapi({
@@ -226,8 +232,8 @@ export const CreatePostInputSchema = z.object({
226
232
 
227
233
  export const CreatePostOutputSchema = PostEntitySchema;
228
234
  export const GetPostOutputSchema = PostWithFilesEntitySchema;
229
- export const PostIdSchema = z.object({
230
- postId: z.cuid2().openapi({ example: "ckj1a2b3c0000xyz" })
235
+ export const PostIdSchema = z.object({
236
+ postId: z.cuid2().openapi({ example: "ckj1a2b3c0000xyz" }),
231
237
  });
232
238
 
233
239
  export const MinimalPostSchema = PostEntitySchema.pick({
@@ -237,18 +243,24 @@ export const MinimalPostSchema = PostEntitySchema.pick({
237
243
  });
238
244
 
239
245
  export const PostWithLikesEntitySchema = MinimalPostSchema.extend({
240
- likes: z.array(
241
- ActivitySchema.extend({
242
- followsYou: z.boolean().optional().openapi({ example: true }),
243
- isFollowing: z.boolean().optional().openapi({ example: false }),
244
- }),
245
- ).openapi({ example: [] }),
246
+ likes: z
247
+ .array(
248
+ ActivitySchema.extend({
249
+ followsYou: z.boolean().optional().openapi({ example: true }),
250
+ isFollowing: z.boolean().optional().openapi({ example: false }),
251
+ }),
252
+ )
253
+ .openapi({ example: [] }),
246
254
  }).openapi({
247
255
  title: "PostWithPostLikesEntity",
248
256
  });
249
257
 
250
258
  export const GetPostWithLikesOutputSchema = PostWithLikesEntitySchema.extend({
251
- nextCursor: z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
259
+ nextCursor: z
260
+ .string()
261
+ .optional()
262
+ .nullable()
263
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
252
264
  });
253
265
 
254
266
  export const PostWithCommentsEntitySchema = MinimalPostSchema.extend({
@@ -259,7 +271,11 @@ export const PostWithCommentsEntitySchema = MinimalPostSchema.extend({
259
271
 
260
272
  export const GetPostWithCommentsOutputSchema =
261
273
  PostWithCommentsEntitySchema.extend({
262
- nextCursor: z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
274
+ nextCursor: z
275
+ .string()
276
+ .optional()
277
+ .nullable()
278
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
263
279
  });
264
280
 
265
281
  export const PostWithBookmarksEntitySchema = MinimalPostSchema.extend({
@@ -274,14 +290,23 @@ export const GetPostWithBookmarksOutputSchema =
274
290
  });
275
291
 
276
292
  export const LinkPreviewInputSchema = z.object({
277
- url: z.url().openapi({ example: "https://example.com/article" }),
293
+ url: z.url().openapi({ example: "https://example.com/article" }),
278
294
  });
279
295
 
280
296
  export const LinkPreviewOutputSchema = z.object({
281
297
  title: z.string().openapi({ example: "Great Article" }),
282
- description: z.string().optional().openapi({ example: "A detailed breakdown of the topic." }),
283
- image: z.string().optional().openapi({ example: "https://example.com/hero.jpg" }),
284
- url: z.string().optional().openapi({ example: "https://example.com/article" }),
298
+ description: z
299
+ .string()
300
+ .optional()
301
+ .openapi({ example: "A detailed breakdown of the topic." }),
302
+ image: z
303
+ .string()
304
+ .optional()
305
+ .openapi({ example: "https://example.com/hero.jpg" }),
306
+ url: z
307
+ .string()
308
+ .optional()
309
+ .openapi({ example: "https://example.com/article" }),
285
310
  });
286
311
 
287
312
  export const FeedPostEntitySchema = PostWithFilesEntitySchema.extend({
@@ -299,7 +324,11 @@ export const GetFeedInputSchema = z.object({
299
324
 
300
325
  export const GetFeedOutputSchema = z.object({
301
326
  feed: z.array(FeedPostEntitySchema).openapi({ example: [] }),
302
- nextCursor: z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
327
+ nextCursor: z
328
+ .string()
329
+ .optional()
330
+ .nullable()
331
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
303
332
  });
304
333
 
305
334
  export const SearchPostInputSchema = z.object({
@@ -313,7 +342,11 @@ export const SearchPostInputSchema = z.object({
313
342
 
314
343
  export const SearchPostOutputSchema = z.object({
315
344
  posts: z.array(FeedPostEntitySchema).openapi({ example: [] }),
316
- nextCursor: z.string().optional().nullable().openapi({ example: "ckj1a2b3c0000nxt" }),
345
+ nextCursor: z
346
+ .string()
347
+ .optional()
348
+ .nullable()
349
+ .openapi({ example: "ckj1a2b3c0000nxt" }),
317
350
  });
318
351
 
319
352
  export const ReportPostInputSchema = z.object({
@@ -341,8 +374,12 @@ export const PostAnalyticsOutputSchema = z.object({
341
374
  comments: z.number().openapi({ example: 34 }),
342
375
  linkCopied: z.number().openapi({ example: 12 }),
343
376
  bookmarks: z.number().openapi({ example: 56 }),
344
- tagsClicked: z.array(AnalyticsChartItemSchema).openapi({ example: [{ x: "javascript", y: 25 }] }),
345
- platformShares: z.array(AnalyticsChartItemSchema).openapi({ example: [{ x: "Twitter", y: 10 }] }),
377
+ tagsClicked: z
378
+ .array(AnalyticsChartItemSchema)
379
+ .openapi({ example: [{ x: "javascript", y: 25 }] }),
380
+ platformShares: z
381
+ .array(AnalyticsChartItemSchema)
382
+ .openapi({ example: [{ x: "Twitter", y: 10 }] }),
346
383
  }),
347
384
  behavior: z.object({
348
385
  viralityScore: z.number().openapi({ example: 8.5 }),
@@ -359,44 +396,65 @@ export const PostAnalyticsOutputSchema = z.object({
359
396
  }),
360
397
  });
361
398
 
362
-
363
- export const PostSearchDocumentSchema = z.object({
364
- id: z.cuid2().openapi({ example: "ckj1a2b3c0000doc" }),
365
- userId: z.cuid2().openapi({ example: "ckj1a2b3c0000usr" }),
366
- parentId: z.cuid2().nullable().openapi({ example: "ckj1a2b3c0000prt" }),
367
- parentType: z.enum(ACTIVITY_PARENT_TYPES).openapi({ example: "POST" }),
368
- creatorUsername: z.string().nullable().openapi({ example: "tech_lead" }),
369
- creatorFullName: z.string().nullable().openapi({ example: "Alex Smith" }),
370
- creatorImageUrl: z.cuid2().nullable().openapi({ example: "clm1a2b3c0000pic" }),
371
- tagIds: z.array(z.number()).openapi({ example: [101, 102] }),
372
- tagNames: z.array(z.string()).openapi({ example: ["react", "typescript"] }),
373
- badge: z.enum(POST_BADGE_TYPES).nullable().openapi({ example: "TRENDING" }),
374
- postType: z.enum(POST_TYPES).openapi({ example: "PROJECT" }),
375
- content: z.string().nullable().openapi({ example: "Here is my latest open source tool." }),
376
- linkTitle: z.string().nullable().openapi({ example: "GitHub Repo" }),
377
- linkDescription: z.string().nullable().openapi({ example: "A fast, modern build system." }),
378
- linkUrl: z.url().nullable().openapi({ example: "https://github.com/project" }),
379
- linkImage: z.url().nullable().openapi({ example: "https://github.com/image.png" }),
380
- postFiles: z
381
- .array(
382
- PostFileEntitySchema.extend({
383
- url: z.url().openapi({ example: "https://cdn.example.com/file1.png" }),
399
+ export const PostSearchDocumentSchema = z
400
+ .object({
401
+ id: z.cuid2().openapi({ example: "ckj1a2b3c0000doc" }),
402
+ userId: z.cuid2().openapi({ example: "ckj1a2b3c0000usr" }),
403
+ parentId: z.cuid2().nullable().openapi({ example: "ckj1a2b3c0000prt" }),
404
+ parentType: z.enum(ACTIVITY_PARENT_TYPES).openapi({ example: "POST" }),
405
+ creatorUsername: z.string().nullable().openapi({ example: "tech_lead" }),
406
+ creatorFullName: z.string().nullable().openapi({ example: "Alex Smith" }),
407
+ creatorImageUrl: z
408
+ .cuid2()
409
+ .nullable()
410
+ .openapi({ example: "clm1a2b3c0000pic" }),
411
+ tagIds: z.array(z.number()).openapi({ example: [101, 102] }),
412
+ tagNames: z.array(z.string()).openapi({ example: ["react", "typescript"] }),
413
+ badge: z.enum(POST_BADGE_TYPES).nullable().openapi({ example: "TRENDING" }),
414
+ postType: z.enum(POST_TYPES).openapi({ example: "PROJECT" }),
415
+ content: z
416
+ .string()
417
+ .nullable()
418
+ .openapi({ example: "Here is my latest open source tool." }),
419
+ linkTitle: z.string().nullable().openapi({ example: "GitHub Repo" }),
420
+ linkDescription: z
421
+ .string()
422
+ .nullable()
423
+ .openapi({ example: "A fast, modern build system." }),
424
+ linkUrl: z
425
+ .url()
426
+ .nullable()
427
+ .openapi({ example: "https://github.com/project" }),
428
+ linkImage: z
429
+ .url()
430
+ .nullable()
431
+ .openapi({ example: "https://github.com/image.png" }),
432
+ postFiles: z
433
+ .array(
434
+ PostFileEntitySchema.extend({
435
+ url: z
436
+ .url()
437
+ .openapi({ example: "https://cdn.example.com/file1.png" }),
438
+ }),
439
+ )
440
+ .nullable()
441
+ .openapi({
442
+ example: [
443
+ {
444
+ id: "cxy1a2b3c0000qwe",
445
+ postId: "ckj1a2b3c0000doc",
446
+ fileId: "cvb1a2b3c0000rty",
447
+ order: 1,
448
+ url: "https://cdn.example.com/file1.png",
449
+ },
450
+ ],
384
451
  }),
385
- )
386
- .nullable()
387
- .openapi({
388
- example: [
389
- {
390
- id: "cxy1a2b3c0000qwe",
391
- postId: "ckj1a2b3c0000doc",
392
- fileId: "cvb1a2b3c0000rty",
393
- order: 1,
394
- url: "https://cdn.example.com/file1.png",
395
- },
396
- ],
397
- }),
398
- createdAt: z.string().nullable().openapi({ example: "2026-03-11T14:43:09.000Z" }),
399
- }).openapi({
400
- title: "Post Search Document",
401
- description: "Flattened schema used for indexing posts in search engines.",
402
- });
452
+ createdAt: z
453
+ .string()
454
+ .nullable()
455
+ .openapi({ example: "2026-03-11T14:43:09.000Z" }),
456
+ })
457
+ .openapi({
458
+ title: "Post Search Document",
459
+ description: "Flattened schema used for indexing posts in search engines.",
460
+ });
@@ -0,0 +1,153 @@
1
+ import { z } from "@hono/zod-openapi";
2
+ import { DISCOUNT_TYPES, PRICING_MODELS } from "../constants";
3
+
4
+ export const ProductCoverImageEntitySchema = z.object({
5
+ fileId: z
6
+ .cuid2()
7
+ .openapi({ example: "f123e4567-e89b-12d3-a456-426614174000" }),
8
+ isThumbnail: z.boolean().default(false),
9
+ order: z.number().int().default(0),
10
+ });
11
+
12
+ export const ProductDeliveryFileEntitySchema = z.object({
13
+ fileId: z
14
+ .cuid2()
15
+ .openapi({ example: "f987e6543-e89b-12d3-a456-426614174000" }),
16
+ order: z.number().int().default(0),
17
+ });
18
+
19
+ export const ProductDiscountEntitySchema = z.object({
20
+ discountType: z.enum(DISCOUNT_TYPES),
21
+
22
+ amount: z
23
+ .number()
24
+ .int("Amount must be a whole number")
25
+ .min(0, "Discount amount cannot be negative"),
26
+ discountCode: z.string().optional(),
27
+ });
28
+
29
+ export const CreateProductInputSchema = z
30
+ .object({
31
+ title: z.string().min(1, "Title is required").max(255),
32
+ description: z.string().min(1, "Description is required"),
33
+ keyFeatures: z.string(),
34
+
35
+ category: z.string().min(1, "Category is required"),
36
+ subcategory: z.string().optional(),
37
+ tags: z
38
+ .array(z.string())
39
+ .max(10, "Keep tags to a maximum of 10")
40
+ .default([]),
41
+
42
+ coverImages: z
43
+ .array(ProductCoverImageEntitySchema)
44
+ .min(1, "At least one cover image is required")
45
+ .max(3, "Maximum of 3 cover images allowed"),
46
+
47
+ productFiles: z.array(ProductDeliveryFileEntitySchema).default([]),
48
+ productLinks: z
49
+ .array(z.url().openapi({ example: "https://figma.com/file/..." }))
50
+ .default([]),
51
+
52
+ pricingModel: z.enum(PRICING_MODELS).default(PRICING_MODELS.FIXED),
53
+ currency: z.string().default("USD").openapi({ example: "USD" }),
54
+
55
+ price: z.number().int("Must be in cents").min(0).optional(),
56
+ suggestedPrice: z.number().int("Must be in cents").min(0).optional(),
57
+
58
+ discounts: z.array(ProductDiscountEntitySchema).default([]),
59
+ })
60
+ .superRefine((data, ctx) => {
61
+ if (
62
+ data.pricingModel === PRICING_MODELS.FIXED &&
63
+ (!data.price || data.price <= 0)
64
+ ) {
65
+ ctx.addIssue({
66
+ code: "custom",
67
+ message: "Fixed pricing requires a price strictly greater than 0.",
68
+ path: ["price"],
69
+ });
70
+ }
71
+
72
+ if (data.pricingModel === PRICING_MODELS.PWYW) {
73
+ if (data.suggestedPrice !== undefined && data.price !== undefined) {
74
+ if (data.suggestedPrice < data.price) {
75
+ ctx.addIssue({
76
+ code: "custom",
77
+ message: "Suggested price cannot be lower than the minimum price.",
78
+ path: ["suggestedPrice"],
79
+ });
80
+ }
81
+ }
82
+ }
83
+
84
+ if (
85
+ data.pricingModel === PRICING_MODELS.FREE &&
86
+ data.price &&
87
+ data.price > 0
88
+ ) {
89
+ ctx.addIssue({
90
+ code: "custom",
91
+ message: "Free products cannot have a price greater than 0.",
92
+ path: ["price"],
93
+ });
94
+ }
95
+
96
+ if (data.productFiles.length === 0 && data.productLinks.length === 0) {
97
+ ctx.addIssue({
98
+ code: "custom",
99
+ message:
100
+ "You must provide at least one product file or a link for the buyer to receive.",
101
+ path: ["productFiles"],
102
+ });
103
+ }
104
+
105
+ const thumbnails = data.coverImages.filter((img) => img.isThumbnail);
106
+ if (thumbnails.length !== 1) {
107
+ ctx.addIssue({
108
+ code: "custom",
109
+ message: "Exactly one cover image must be set as the thumbnail.",
110
+ path: ["coverImages"],
111
+ });
112
+ }
113
+ });
114
+
115
+ export const ProductEntitySchema = z
116
+ .object({
117
+ id: z.cuid2(),
118
+ userId: z.cuid2().openapi({ description: "ID of the creator/seller" }),
119
+
120
+ title: z.string(),
121
+ description: z.string(),
122
+ keyFeatures: z.string(),
123
+
124
+ category: z.string(),
125
+ subcategory: z.string().optional().nullable(),
126
+ tags: z.array(z.string()),
127
+
128
+ coverImages: z.array(ProductCoverImageEntitySchema).default([]),
129
+ productFiles: z.array(ProductDeliveryFileEntitySchema).default([]),
130
+ productLinks: z.array(z.url()).default([]),
131
+
132
+ pricingModel: z.enum(PRICING_MODELS),
133
+ currency: z.string(),
134
+ price: z.number().int().optional().nullable(),
135
+ suggestedPrice: z.number().int().optional().nullable(),
136
+
137
+ discounts: z.array(ProductDiscountEntitySchema).default([]),
138
+
139
+ createdAt: z.coerce.date(),
140
+ updatedAt: z.coerce.date(),
141
+ deletedAt: z.coerce.date().optional().nullable(),
142
+ })
143
+ .openapi({ title: "ProductEntity" });
144
+
145
+ export type ProductCoverImageEntity = z.infer<
146
+ typeof ProductCoverImageEntitySchema
147
+ >;
148
+ export type ProductDeliveryFileEntity = z.infer<
149
+ typeof ProductDeliveryFileEntitySchema
150
+ >;
151
+ export type ProductDiscountEntity = z.infer<typeof ProductDiscountEntitySchema>;
152
+ export type CreateProductInputEntity = z.infer<typeof CreateProductInputSchema>;
153
+ export type ProductEntity = z.infer<typeof ProductEntitySchema>;
@@ -1,5 +1,6 @@
1
1
  import z from "zod";
2
2
  import { COUNTRY_OF_OPERATION, SELLER_STATUS } from "../constants";
3
+ import { PayoutMethodEntitySchema } from "./payout-method";
3
4
 
4
5
  export const SellerEntitySchema = z.object({
5
6
  id: z.cuid2(),
@@ -31,3 +32,9 @@ export const UpdateSellerEntitySchema = z
31
32
  })
32
33
  .partial();
33
34
  export type UpdateSellerInput = z.infer<typeof UpdateSellerEntitySchema>;
35
+
36
+ export const SellerProfileSchema = SellerEntitySchema.extend({
37
+ payoutMethods: z.array(PayoutMethodEntitySchema),
38
+ });
39
+
40
+ export type SellerProfile = z.infer<typeof SellerProfileSchema>;