@zyacreatives/shared 2.2.89 → 2.2.90
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.
- package/dist/schemas/product.d.ts +80 -0
- package/dist/schemas/product.js +37 -9
- package/package.json +1 -1
- package/src/schemas/product.ts +84 -27
|
@@ -16,6 +16,10 @@ export declare const ProductDiscountEntitySchema: z.ZodObject<{
|
|
|
16
16
|
amount: z.ZodNumber;
|
|
17
17
|
discountCode: z.ZodOptional<z.ZodString>;
|
|
18
18
|
}, z.core.$strip>;
|
|
19
|
+
export declare const ProductAdditionalServiceSchema: z.ZodObject<{
|
|
20
|
+
title: z.ZodString;
|
|
21
|
+
price: z.ZodNumber;
|
|
22
|
+
}, z.core.$strip>;
|
|
19
23
|
export declare const CreateProductInputSchema: z.ZodObject<{
|
|
20
24
|
title: z.ZodString;
|
|
21
25
|
description: z.ZodString;
|
|
@@ -50,6 +54,70 @@ export declare const CreateProductInputSchema: z.ZodObject<{
|
|
|
50
54
|
discountCode: z.ZodOptional<z.ZodString>;
|
|
51
55
|
}, z.core.$strip>>>;
|
|
52
56
|
}, z.core.$strip>;
|
|
57
|
+
export declare const ProductServiceAndComplianceInputSchema: z.ZodObject<{
|
|
58
|
+
id: z.ZodCUID2;
|
|
59
|
+
supportEmail: z.ZodEmail;
|
|
60
|
+
supportPhone: z.ZodOptional<z.ZodString>;
|
|
61
|
+
additionalServices: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
62
|
+
title: z.ZodString;
|
|
63
|
+
price: z.ZodNumber;
|
|
64
|
+
}, z.core.$strip>>>;
|
|
65
|
+
ownsRights: z.ZodLiteral<true>;
|
|
66
|
+
noHarmfulContent: z.ZodLiteral<true>;
|
|
67
|
+
providesSupport: z.ZodBoolean;
|
|
68
|
+
agreesToTerms: z.ZodLiteral<true>;
|
|
69
|
+
}, z.core.$strip>;
|
|
70
|
+
export declare const UpdateProductInputSchema: z.ZodObject<{
|
|
71
|
+
title: z.ZodOptional<z.ZodString>;
|
|
72
|
+
description: z.ZodOptional<z.ZodString>;
|
|
73
|
+
keyFeatures: z.ZodOptional<z.ZodString>;
|
|
74
|
+
category: z.ZodOptional<z.ZodString>;
|
|
75
|
+
subcategory: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
76
|
+
tags: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
|
|
77
|
+
coverImages: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
78
|
+
fileId: z.ZodCUID2;
|
|
79
|
+
isThumbnail: z.ZodDefault<z.ZodBoolean>;
|
|
80
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
81
|
+
}, z.core.$strip>>>;
|
|
82
|
+
productFiles: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
83
|
+
fileId: z.ZodCUID2;
|
|
84
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
85
|
+
}, z.core.$strip>>>>;
|
|
86
|
+
productLinks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodURL>>>;
|
|
87
|
+
pricingModel: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
|
|
88
|
+
readonly FREE: "Free";
|
|
89
|
+
readonly FIXED: "Fixed";
|
|
90
|
+
readonly PWYW: "Pay What You Want";
|
|
91
|
+
}>>>;
|
|
92
|
+
currency: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
93
|
+
price: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
94
|
+
suggestedPrice: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
95
|
+
discounts: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
96
|
+
discountType: z.ZodEnum<{
|
|
97
|
+
readonly FIXED_AMOUNT: "Fixed Amount";
|
|
98
|
+
readonly PERCENTAGE: "Percentage";
|
|
99
|
+
}>;
|
|
100
|
+
amount: z.ZodNumber;
|
|
101
|
+
discountCode: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, z.core.$strip>>>>;
|
|
103
|
+
supportEmail: z.ZodOptional<z.ZodEmail>;
|
|
104
|
+
supportPhone: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
105
|
+
additionalServices: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
106
|
+
title: z.ZodString;
|
|
107
|
+
price: z.ZodNumber;
|
|
108
|
+
}, z.core.$strip>>>>;
|
|
109
|
+
ownsRights: z.ZodOptional<z.ZodLiteral<true>>;
|
|
110
|
+
noHarmfulContent: z.ZodOptional<z.ZodLiteral<true>>;
|
|
111
|
+
providesSupport: z.ZodOptional<z.ZodBoolean>;
|
|
112
|
+
agreesToTerms: z.ZodOptional<z.ZodLiteral<true>>;
|
|
113
|
+
id: z.ZodCUID2;
|
|
114
|
+
}, z.core.$strip>;
|
|
115
|
+
export declare const ProductAdditionalServiceEntitySchema: z.ZodObject<{
|
|
116
|
+
title: z.ZodString;
|
|
117
|
+
price: z.ZodNumber;
|
|
118
|
+
id: z.ZodCUID2;
|
|
119
|
+
productId: z.ZodCUID2;
|
|
120
|
+
}, z.core.$strip>;
|
|
53
121
|
export declare const ProductEntitySchema: z.ZodObject<{
|
|
54
122
|
id: z.ZodCUID2;
|
|
55
123
|
userId: z.ZodCUID2;
|
|
@@ -85,6 +153,14 @@ export declare const ProductEntitySchema: z.ZodObject<{
|
|
|
85
153
|
amount: z.ZodNumber;
|
|
86
154
|
discountCode: z.ZodOptional<z.ZodString>;
|
|
87
155
|
}, z.core.$strip>>>;
|
|
156
|
+
supportEmail: z.ZodNullable<z.ZodOptional<z.ZodEmail>>;
|
|
157
|
+
supportPhone: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
158
|
+
additionalServices: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
159
|
+
title: z.ZodString;
|
|
160
|
+
price: z.ZodNumber;
|
|
161
|
+
id: z.ZodCUID2;
|
|
162
|
+
productId: z.ZodCUID2;
|
|
163
|
+
}, z.core.$strip>>>;
|
|
88
164
|
createdAt: z.ZodCoercedDate<unknown>;
|
|
89
165
|
updatedAt: z.ZodCoercedDate<unknown>;
|
|
90
166
|
deletedAt: z.ZodNullable<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
|
|
@@ -92,5 +168,9 @@ export declare const ProductEntitySchema: z.ZodObject<{
|
|
|
92
168
|
export type ProductCoverImageEntity = z.infer<typeof ProductCoverImageEntitySchema>;
|
|
93
169
|
export type ProductDeliveryFileEntity = z.infer<typeof ProductDeliveryFileEntitySchema>;
|
|
94
170
|
export type ProductDiscountEntity = z.infer<typeof ProductDiscountEntitySchema>;
|
|
171
|
+
export type ProductAdditionalService = z.infer<typeof ProductAdditionalServiceSchema>;
|
|
172
|
+
export type ProductAdditionalServiceEntity = z.infer<typeof ProductAdditionalServiceEntitySchema>;
|
|
95
173
|
export type CreateProductInputEntity = z.infer<typeof CreateProductInputSchema>;
|
|
174
|
+
export type ProductServiceAndComplianceInputEntity = z.infer<typeof ProductServiceAndComplianceInputSchema>;
|
|
175
|
+
export type UpdateProductInputEntity = z.infer<typeof UpdateProductInputSchema>;
|
|
96
176
|
export type ProductEntity = z.infer<typeof ProductEntitySchema>;
|
package/dist/schemas/product.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ProductEntitySchema = exports.CreateProductInputSchema = exports.ProductDiscountEntitySchema = exports.ProductDeliveryFileEntitySchema = exports.ProductCoverImageEntitySchema = void 0;
|
|
3
|
+
exports.ProductEntitySchema = exports.ProductAdditionalServiceEntitySchema = exports.UpdateProductInputSchema = exports.ProductServiceAndComplianceInputSchema = exports.CreateProductInputSchema = exports.ProductAdditionalServiceSchema = exports.ProductDiscountEntitySchema = exports.ProductDeliveryFileEntitySchema = exports.ProductCoverImageEntitySchema = void 0;
|
|
4
4
|
const zod_openapi_1 = require("@hono/zod-openapi");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
|
+
// --- Sub-Entity Schemas ---
|
|
6
7
|
exports.ProductCoverImageEntitySchema = zod_openapi_1.z.object({
|
|
7
8
|
fileId: zod_openapi_1.z
|
|
8
9
|
.cuid2()
|
|
@@ -24,17 +25,18 @@ exports.ProductDiscountEntitySchema = zod_openapi_1.z.object({
|
|
|
24
25
|
.min(0, "Discount amount cannot be negative"),
|
|
25
26
|
discountCode: zod_openapi_1.z.string().optional(),
|
|
26
27
|
});
|
|
27
|
-
exports.
|
|
28
|
-
.
|
|
28
|
+
exports.ProductAdditionalServiceSchema = zod_openapi_1.z.object({
|
|
29
|
+
title: zod_openapi_1.z.string().min(1, "Service title is required"),
|
|
30
|
+
price: zod_openapi_1.z.number().int("Price must be in cents").min(0),
|
|
31
|
+
});
|
|
32
|
+
// --- Base Core Schema (Reusable shape) ---
|
|
33
|
+
const ProductCoreInputSchema = zod_openapi_1.z.object({
|
|
29
34
|
title: zod_openapi_1.z.string().min(1, "Title is required").max(255),
|
|
30
35
|
description: zod_openapi_1.z.string().min(1, "Description is required"),
|
|
31
36
|
keyFeatures: zod_openapi_1.z.string(),
|
|
32
37
|
category: zod_openapi_1.z.string().min(1, "Category is required"),
|
|
33
38
|
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([]),
|
|
39
|
+
tags: zod_openapi_1.z.array(zod_openapi_1.z.string()).max(10, "Keep tags to a maximum of 10").default([]),
|
|
38
40
|
coverImages: zod_openapi_1.z
|
|
39
41
|
.array(exports.ProductCoverImageEntitySchema)
|
|
40
42
|
.min(1, "At least one cover image is required")
|
|
@@ -48,8 +50,9 @@ exports.CreateProductInputSchema = zod_openapi_1.z
|
|
|
48
50
|
price: zod_openapi_1.z.number().int("Must be in cents").min(0).optional(),
|
|
49
51
|
suggestedPrice: zod_openapi_1.z.number().int("Must be in cents").min(0).optional(),
|
|
50
52
|
discounts: zod_openapi_1.z.array(exports.ProductDiscountEntitySchema).default([]),
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
+
});
|
|
54
|
+
// --- 1. Create Product Input (Step 1 - Strict) ---
|
|
55
|
+
exports.CreateProductInputSchema = ProductCoreInputSchema.superRefine((data, ctx) => {
|
|
53
56
|
if (data.pricingModel === constants_1.PRICING_MODELS.FIXED &&
|
|
54
57
|
(!data.price || data.price <= 0)) {
|
|
55
58
|
ctx.addIssue({
|
|
@@ -94,6 +97,26 @@ exports.CreateProductInputSchema = zod_openapi_1.z
|
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
99
|
});
|
|
100
|
+
// --- 2. Service & Compliance Input (Step 2 - Strict) ---
|
|
101
|
+
exports.ProductServiceAndComplianceInputSchema = zod_openapi_1.z.object({
|
|
102
|
+
id: zod_openapi_1.z.cuid2().openapi({ description: "ID of the product" }),
|
|
103
|
+
supportEmail: zod_openapi_1.z.email("A valid support email is required"),
|
|
104
|
+
supportPhone: zod_openapi_1.z.string().optional(),
|
|
105
|
+
additionalServices: zod_openapi_1.z.array(exports.ProductAdditionalServiceSchema).default([]),
|
|
106
|
+
ownsRights: zod_openapi_1.z.literal(true, "You must confirm you own the rights to this product."),
|
|
107
|
+
noHarmfulContent: zod_openapi_1.z.literal(true, "You must confirm no harmful content."),
|
|
108
|
+
providesSupport: zod_openapi_1.z.boolean(),
|
|
109
|
+
agreesToTerms: zod_openapi_1.z.literal(true, "You must agree to the Terms."),
|
|
110
|
+
});
|
|
111
|
+
exports.UpdateProductInputSchema = ProductCoreInputSchema.extend(exports.ProductServiceAndComplianceInputSchema.omit({ id: true }).shape)
|
|
112
|
+
.partial()
|
|
113
|
+
.extend({
|
|
114
|
+
id: zod_openapi_1.z.cuid2().openapi({ description: "ID of the product being updated" }),
|
|
115
|
+
});
|
|
116
|
+
exports.ProductAdditionalServiceEntitySchema = exports.ProductAdditionalServiceSchema.extend({
|
|
117
|
+
id: zod_openapi_1.z.cuid2(),
|
|
118
|
+
productId: zod_openapi_1.z.cuid2(),
|
|
119
|
+
});
|
|
97
120
|
exports.ProductEntitySchema = zod_openapi_1.z
|
|
98
121
|
.object({
|
|
99
122
|
id: zod_openapi_1.z.cuid2(),
|
|
@@ -112,6 +135,11 @@ exports.ProductEntitySchema = zod_openapi_1.z
|
|
|
112
135
|
price: zod_openapi_1.z.number().int().optional().nullable(),
|
|
113
136
|
suggestedPrice: zod_openapi_1.z.number().int().optional().nullable(),
|
|
114
137
|
discounts: zod_openapi_1.z.array(exports.ProductDiscountEntitySchema).default([]),
|
|
138
|
+
supportEmail: zod_openapi_1.z.email().optional().nullable(),
|
|
139
|
+
supportPhone: zod_openapi_1.z.string().optional().nullable(),
|
|
140
|
+
additionalServices: zod_openapi_1.z
|
|
141
|
+
.array(exports.ProductAdditionalServiceEntitySchema)
|
|
142
|
+
.default([]),
|
|
115
143
|
createdAt: zod_openapi_1.z.coerce.date(),
|
|
116
144
|
updatedAt: zod_openapi_1.z.coerce.date(),
|
|
117
145
|
deletedAt: zod_openapi_1.z.coerce.date().optional().nullable(),
|
package/package.json
CHANGED
package/src/schemas/product.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "@hono/zod-openapi";
|
|
2
2
|
import { DISCOUNT_TYPES, PRICING_MODELS } from "../constants";
|
|
3
3
|
|
|
4
|
+
// --- Sub-Entity Schemas ---
|
|
5
|
+
|
|
4
6
|
export const ProductCoverImageEntitySchema = z.object({
|
|
5
7
|
fileId: z
|
|
6
8
|
.cuid2()
|
|
@@ -18,7 +20,6 @@ export const ProductDeliveryFileEntitySchema = z.object({
|
|
|
18
20
|
|
|
19
21
|
export const ProductDiscountEntitySchema = z.object({
|
|
20
22
|
discountType: z.enum(DISCOUNT_TYPES),
|
|
21
|
-
|
|
22
23
|
amount: z
|
|
23
24
|
.number()
|
|
24
25
|
.int("Amount must be a whole number")
|
|
@@ -26,38 +27,43 @@ export const ProductDiscountEntitySchema = z.object({
|
|
|
26
27
|
discountCode: z.string().optional(),
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
export const
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
keyFeatures: z.string(),
|
|
30
|
+
export const ProductAdditionalServiceSchema = z.object({
|
|
31
|
+
title: z.string().min(1, "Service title is required"),
|
|
32
|
+
price: z.number().int("Price must be in cents").min(0),
|
|
33
|
+
});
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.default([]),
|
|
35
|
+
// --- Base Core Schema (Reusable shape) ---
|
|
36
|
+
const ProductCoreInputSchema = z.object({
|
|
37
|
+
title: z.string().min(1, "Title is required").max(255),
|
|
38
|
+
description: z.string().min(1, "Description is required"),
|
|
39
|
+
keyFeatures: z.string(),
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.max(3, "Maximum of 3 cover images allowed"),
|
|
41
|
+
category: z.string().min(1, "Category is required"),
|
|
42
|
+
subcategory: z.string().optional(),
|
|
43
|
+
tags: z.array(z.string()).max(10, "Keep tags to a maximum of 10").default([]),
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
coverImages: z
|
|
46
|
+
.array(ProductCoverImageEntitySchema)
|
|
47
|
+
.min(1, "At least one cover image is required")
|
|
48
|
+
.max(3, "Maximum of 3 cover images allowed"),
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
productFiles: z.array(ProductDeliveryFileEntitySchema).default([]),
|
|
51
|
+
productLinks: z
|
|
52
|
+
.array(z.url().openapi({ example: "https://figma.com/file/..." }))
|
|
53
|
+
.default([]),
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
pricingModel: z.enum(PRICING_MODELS).default(PRICING_MODELS.FIXED),
|
|
56
|
+
currency: z.string().default("USD").openapi({ example: "USD" }),
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
price: z.number().int("Must be in cents").min(0).optional(),
|
|
59
|
+
suggestedPrice: z.number().int("Must be in cents").min(0).optional(),
|
|
60
|
+
|
|
61
|
+
discounts: z.array(ProductDiscountEntitySchema).default([]),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// --- 1. Create Product Input (Step 1 - Strict) ---
|
|
65
|
+
export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
|
|
66
|
+
(data, ctx) => {
|
|
61
67
|
if (
|
|
62
68
|
data.pricingModel === PRICING_MODELS.FIXED &&
|
|
63
69
|
(!data.price || data.price <= 0)
|
|
@@ -110,6 +116,40 @@ export const CreateProductInputSchema = z
|
|
|
110
116
|
path: ["coverImages"],
|
|
111
117
|
});
|
|
112
118
|
}
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// --- 2. Service & Compliance Input (Step 2 - Strict) ---
|
|
123
|
+
export const ProductServiceAndComplianceInputSchema = z.object({
|
|
124
|
+
id: z.cuid2().openapi({ description: "ID of the product" }),
|
|
125
|
+
|
|
126
|
+
supportEmail: z.email("A valid support email is required"),
|
|
127
|
+
supportPhone: z.string().optional(),
|
|
128
|
+
|
|
129
|
+
additionalServices: z.array(ProductAdditionalServiceSchema).default([]),
|
|
130
|
+
|
|
131
|
+
ownsRights: z.literal(
|
|
132
|
+
true,
|
|
133
|
+
"You must confirm you own the rights to this product.",
|
|
134
|
+
),
|
|
135
|
+
noHarmfulContent: z.literal(true, "You must confirm no harmful content."),
|
|
136
|
+
|
|
137
|
+
providesSupport: z.boolean(),
|
|
138
|
+
agreesToTerms: z.literal(true, "You must agree to the Terms."),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export const UpdateProductInputSchema = ProductCoreInputSchema.extend(
|
|
142
|
+
ProductServiceAndComplianceInputSchema.omit({ id: true }).shape,
|
|
143
|
+
)
|
|
144
|
+
.partial()
|
|
145
|
+
.extend({
|
|
146
|
+
id: z.cuid2().openapi({ description: "ID of the product being updated" }),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const ProductAdditionalServiceEntitySchema =
|
|
150
|
+
ProductAdditionalServiceSchema.extend({
|
|
151
|
+
id: z.cuid2(),
|
|
152
|
+
productId: z.cuid2(),
|
|
113
153
|
});
|
|
114
154
|
|
|
115
155
|
export const ProductEntitySchema = z
|
|
@@ -136,6 +176,12 @@ export const ProductEntitySchema = z
|
|
|
136
176
|
|
|
137
177
|
discounts: z.array(ProductDiscountEntitySchema).default([]),
|
|
138
178
|
|
|
179
|
+
supportEmail: z.email().optional().nullable(),
|
|
180
|
+
supportPhone: z.string().optional().nullable(),
|
|
181
|
+
additionalServices: z
|
|
182
|
+
.array(ProductAdditionalServiceEntitySchema)
|
|
183
|
+
.default([]),
|
|
184
|
+
|
|
139
185
|
createdAt: z.coerce.date(),
|
|
140
186
|
updatedAt: z.coerce.date(),
|
|
141
187
|
deletedAt: z.coerce.date().optional().nullable(),
|
|
@@ -149,5 +195,16 @@ export type ProductDeliveryFileEntity = z.infer<
|
|
|
149
195
|
typeof ProductDeliveryFileEntitySchema
|
|
150
196
|
>;
|
|
151
197
|
export type ProductDiscountEntity = z.infer<typeof ProductDiscountEntitySchema>;
|
|
198
|
+
export type ProductAdditionalService = z.infer<
|
|
199
|
+
typeof ProductAdditionalServiceSchema
|
|
200
|
+
>;
|
|
201
|
+
export type ProductAdditionalServiceEntity = z.infer<
|
|
202
|
+
typeof ProductAdditionalServiceEntitySchema
|
|
203
|
+
>;
|
|
204
|
+
|
|
152
205
|
export type CreateProductInputEntity = z.infer<typeof CreateProductInputSchema>;
|
|
206
|
+
export type ProductServiceAndComplianceInputEntity = z.infer<
|
|
207
|
+
typeof ProductServiceAndComplianceInputSchema
|
|
208
|
+
>;
|
|
209
|
+
export type UpdateProductInputEntity = z.infer<typeof UpdateProductInputSchema>;
|
|
153
210
|
export type ProductEntity = z.infer<typeof ProductEntitySchema>;
|