@wocart/storefront 0.0.0-stage → 1.0.0
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/CHANGELOG.md +66 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +143 -2
- package/USAGE.md +1465 -0
- package/dist/abandoned-checkout.d.ts +22 -0
- package/dist/abandoned-checkout.js +89 -0
- package/dist/actions.d.ts +117 -0
- package/dist/actions.js +196 -0
- package/dist/astro.d.ts +6 -0
- package/dist/astro.js +3 -0
- package/dist/basket.d.ts +87 -0
- package/dist/basket.js +258 -0
- package/dist/client/cart.d.ts +30 -0
- package/dist/client/cart.js +26 -0
- package/dist/client/categories.d.ts +25 -0
- package/dist/client/categories.js +37 -0
- package/dist/client/config.d.ts +29 -0
- package/dist/client/config.js +41 -0
- package/dist/client/core.d.ts +86 -0
- package/dist/client/core.js +582 -0
- package/dist/client/errors.d.ts +19 -0
- package/dist/client/errors.js +31 -0
- package/dist/client/geo.d.ts +31 -0
- package/dist/client/geo.js +39 -0
- package/dist/client/index.d.ts +39 -0
- package/dist/client/index.js +80 -0
- package/dist/client/orders.d.ts +68 -0
- package/dist/client/orders.js +76 -0
- package/dist/client/page-data.d.ts +31 -0
- package/dist/client/page-data.js +37 -0
- package/dist/client/pages.d.ts +33 -0
- package/dist/client/pages.js +44 -0
- package/dist/client/paths.d.ts +8 -0
- package/dist/client/paths.js +10 -0
- package/dist/client/products.d.ts +56 -0
- package/dist/client/products.js +78 -0
- package/dist/client/shipping.d.ts +37 -0
- package/dist/client/shipping.js +48 -0
- package/dist/data/communes.d.ts +25 -0
- package/dist/data/communes.js +1690 -0
- package/dist/data/wilayas.d.ts +6 -0
- package/dist/data/wilayas.js +64 -0
- package/dist/domain.d.ts +332 -0
- package/dist/domain.js +214 -0
- package/dist/endpoints/proxy.d.ts +111 -0
- package/dist/endpoints/proxy.js +161 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +30 -0
- package/dist/locale-routing.d.ts +59 -0
- package/dist/locale-routing.js +62 -0
- package/dist/locales.d.ts +15 -0
- package/dist/locales.js +8 -0
- package/dist/meta-tracking.d.ts +34 -0
- package/dist/meta-tracking.js +72 -0
- package/dist/middleware.d.ts +100 -0
- package/dist/middleware.js +72 -0
- package/dist/offer-view.d.ts +10 -0
- package/dist/offer-view.js +13 -0
- package/dist/otp.d.ts +80 -0
- package/dist/otp.js +295 -0
- package/dist/phone.d.ts +2 -0
- package/dist/phone.js +34 -0
- package/dist/product-view.d.ts +12 -0
- package/dist/product-view.js +42 -0
- package/dist/routes.d.ts +45 -0
- package/dist/routes.js +59 -0
- package/dist/schemas.d.ts +753 -0
- package/dist/schemas.js +377 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +2 -0
- package/dist/types.d.ts +418 -0
- package/dist/types.js +1 -0
- package/dist/utils/image.d.ts +140 -0
- package/dist/utils/image.js +131 -0
- package/dist/utils/money.d.ts +39 -0
- package/dist/utils/money.js +43 -0
- package/dist/utils/product-image.d.ts +37 -0
- package/dist/utils/product-image.js +38 -0
- package/dist/whatsapp.d.ts +55 -0
- package/dist/whatsapp.js +89 -0
- package/package.json +76 -4
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ProductImageSchema = z.object({
|
|
3
|
+
id: z.string(),
|
|
4
|
+
src: z.string().url(),
|
|
5
|
+
srcSm: z.string().url().nullable(),
|
|
6
|
+
srcMd: z.string().url().nullable(),
|
|
7
|
+
srcLg: z.string().url().nullable(),
|
|
8
|
+
altText: z.string().nullable(),
|
|
9
|
+
position: z.number().int().min(0),
|
|
10
|
+
});
|
|
11
|
+
export const ProductVariantSchema = z.object({
|
|
12
|
+
id: z.string(),
|
|
13
|
+
productId: z.string(),
|
|
14
|
+
variations: z.record(z.string(), z.string()),
|
|
15
|
+
currency: z.string(),
|
|
16
|
+
price: z.number().int().min(0),
|
|
17
|
+
compareAtPrice: z.number().int().min(0).nullable(),
|
|
18
|
+
sku: z.string().nullable(),
|
|
19
|
+
barcode: z.string().nullable(),
|
|
20
|
+
inventory: z.number().int().min(0),
|
|
21
|
+
lowStockThreshold: z.number().int().min(0),
|
|
22
|
+
weightKg: z.number().min(0).nullable(),
|
|
23
|
+
imageId: z.string().nullable(),
|
|
24
|
+
isDefault: z.boolean(),
|
|
25
|
+
active: z.boolean(),
|
|
26
|
+
position: z.number().int().min(0),
|
|
27
|
+
createdAt: z.string().datetime(),
|
|
28
|
+
updatedAt: z.string().datetime(),
|
|
29
|
+
});
|
|
30
|
+
export const OfferSchema = z.object({
|
|
31
|
+
id: z.string(),
|
|
32
|
+
name: z.string(),
|
|
33
|
+
discountType: z.enum(["free", "free_shipping"]),
|
|
34
|
+
triggerQuantity: z.number().int(),
|
|
35
|
+
triggerVariantId: z.string().nullable(),
|
|
36
|
+
rewardQuantity: z.number().int(),
|
|
37
|
+
rewardProductId: z.string().nullable(),
|
|
38
|
+
rewardProductName: z.string(),
|
|
39
|
+
rewardVariantId: z.string().nullable(),
|
|
40
|
+
rewardVariantLabel: z.string().nullable(),
|
|
41
|
+
});
|
|
42
|
+
const productCategorySchema = z.object({
|
|
43
|
+
id: z.string(),
|
|
44
|
+
name: z.string(),
|
|
45
|
+
slug: z.string(),
|
|
46
|
+
description: z.string().nullable(),
|
|
47
|
+
parentId: z.string().nullable(),
|
|
48
|
+
imageUrl: z.string().url().nullable(),
|
|
49
|
+
metaTitle: z.string().nullable(),
|
|
50
|
+
metaDescription: z.string().nullable(),
|
|
51
|
+
metaKeywords: z.string().nullable(),
|
|
52
|
+
position: z.number().int().min(0),
|
|
53
|
+
createdAt: z.string().datetime(),
|
|
54
|
+
updatedAt: z.string().datetime(),
|
|
55
|
+
});
|
|
56
|
+
export const CategorySchema = productCategorySchema;
|
|
57
|
+
const reviewStatsSchema = z.object({
|
|
58
|
+
avgRating: z.number(),
|
|
59
|
+
reviewCount: z.number().int(),
|
|
60
|
+
});
|
|
61
|
+
const productBaseFields = {
|
|
62
|
+
id: z.string(),
|
|
63
|
+
name: z.string(),
|
|
64
|
+
description: z.string().nullable(),
|
|
65
|
+
descriptionFormat: z.enum(["text", "html"]),
|
|
66
|
+
descriptionPlain: z.string().nullable(),
|
|
67
|
+
handle: z.string(),
|
|
68
|
+
currency: z.string(),
|
|
69
|
+
price: z.number(),
|
|
70
|
+
compareAtPrice: z.number().nullable(),
|
|
71
|
+
costPrice: z.number().nullable(),
|
|
72
|
+
type: z.enum(["PHYSICAL", "DIGITAL"]),
|
|
73
|
+
hasVariants: z.boolean(),
|
|
74
|
+
sku: z.string().nullable(),
|
|
75
|
+
inventory: z.number().int(),
|
|
76
|
+
trackInventory: z.boolean(),
|
|
77
|
+
lowStockThreshold: z.number().int(),
|
|
78
|
+
categoryId: z.string().nullable(),
|
|
79
|
+
visibility: z.boolean(),
|
|
80
|
+
status: z.enum(["DRAFT", "ACTIVE", "ARCHIVED"]),
|
|
81
|
+
showInStore: z.boolean(),
|
|
82
|
+
storeFeatured: z.boolean(),
|
|
83
|
+
deletedAt: z.string().datetime().nullable(),
|
|
84
|
+
publishedAt: z.string().datetime().nullable(),
|
|
85
|
+
createdAt: z.string().datetime(),
|
|
86
|
+
updatedAt: z.string().datetime(),
|
|
87
|
+
shippingProfileId: z.string().nullable().optional(),
|
|
88
|
+
};
|
|
89
|
+
const variantOptionSchema = z.object({
|
|
90
|
+
name: z.string(),
|
|
91
|
+
values: z.array(z.object({
|
|
92
|
+
value: z.string(),
|
|
93
|
+
// The key may be ABSENT, not just null: rows written before the colour
|
|
94
|
+
// picker existed never carry it, and a strict `.nullable()` would reject
|
|
95
|
+
// the whole product, since a failed read degrades to null. Normalising to
|
|
96
|
+
// null keeps the public shape `string | null` for every consumer.
|
|
97
|
+
hexColor: z.string().nullable().default(null),
|
|
98
|
+
})),
|
|
99
|
+
});
|
|
100
|
+
const rawProductFields = {
|
|
101
|
+
...productBaseFields,
|
|
102
|
+
variantOptions: z.string().nullable(),
|
|
103
|
+
tags: z.string().nullable(),
|
|
104
|
+
coverImage: ProductImageSchema.nullable(),
|
|
105
|
+
reviewStats: reviewStatsSchema.nullable(),
|
|
106
|
+
};
|
|
107
|
+
export const ProductListSchema = z.object(rawProductFields);
|
|
108
|
+
export const ProductSchema = z.object({
|
|
109
|
+
...productBaseFields,
|
|
110
|
+
variantOptions: z.array(variantOptionSchema).nullable(),
|
|
111
|
+
tags: z.array(z.string()),
|
|
112
|
+
category: productCategorySchema.nullable(),
|
|
113
|
+
variants: z.array(ProductVariantSchema),
|
|
114
|
+
images: z.array(ProductImageSchema),
|
|
115
|
+
reviewStats: reviewStatsSchema.nullable(),
|
|
116
|
+
offers: z.array(OfferSchema),
|
|
117
|
+
});
|
|
118
|
+
export const StorePagePublicSchema = z.object({
|
|
119
|
+
id: z.string(),
|
|
120
|
+
kind: z.enum(["terms", "privacy", "refund", "shipping", "custom"]),
|
|
121
|
+
slug: z.string(),
|
|
122
|
+
locale: z.string(),
|
|
123
|
+
title: z.string(),
|
|
124
|
+
bodyHtml: z.string(),
|
|
125
|
+
metaTitle: z.string().nullable(),
|
|
126
|
+
metaDescription: z.string().nullable(),
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* The persisted theme page document (PageData): the
|
|
130
|
+
* TRANSPORT shape only. `sections` stays `unknown[]` so instances of a newer
|
|
131
|
+
* document reach the theme verbatim; the theme SDK's `parsePageData` owns the
|
|
132
|
+
* semantics (version, component types, props).
|
|
133
|
+
*
|
|
134
|
+
* **Loose on purpose**: unknown top-level keys must
|
|
135
|
+
* survive the transport, or a document's metadata would be destroyed before the
|
|
136
|
+
* theme SDK ever sees it.
|
|
137
|
+
*/
|
|
138
|
+
export const PageDocumentPayloadSchema = z.looseObject({
|
|
139
|
+
version: z.number().int(),
|
|
140
|
+
surface: z.string(),
|
|
141
|
+
sections: z.array(z.unknown()),
|
|
142
|
+
});
|
|
143
|
+
export const StorePageLinkSchema = z.object({
|
|
144
|
+
id: z.string(),
|
|
145
|
+
kind: z.enum(["terms", "privacy", "refund", "shipping", "custom"]),
|
|
146
|
+
slug: z.string(),
|
|
147
|
+
title: z.string(),
|
|
148
|
+
position: z.number().int(),
|
|
149
|
+
});
|
|
150
|
+
export const StoreLegalContactSchema = z.object({
|
|
151
|
+
contactEmail: z.string().nullable(),
|
|
152
|
+
contactPhone: z.string().nullable(),
|
|
153
|
+
deliveryMinDays: z.number().int(),
|
|
154
|
+
deliveryMaxDays: z.number().int(),
|
|
155
|
+
});
|
|
156
|
+
export const StoreWhatsAppWidgetSchema = z.object({
|
|
157
|
+
href: z.string(),
|
|
158
|
+
agentName: z.string().nullable(),
|
|
159
|
+
caption: z.string().nullable(),
|
|
160
|
+
avatarUrl: z.string().nullable(),
|
|
161
|
+
welcomeMessage: z.string().nullable(),
|
|
162
|
+
launcherLabel: z.string().nullable(),
|
|
163
|
+
ctaLabel: z.string().nullable(),
|
|
164
|
+
prefillGeneral: z.string().nullable(),
|
|
165
|
+
prefillProduct: z.string().nullable(),
|
|
166
|
+
thankYouButtonLabel: z.string().nullable().default(null),
|
|
167
|
+
prefillThankYou: z.string().nullable().default(null),
|
|
168
|
+
accent: z.enum(["whatsapp", "primary"]),
|
|
169
|
+
position: z.enum(["right", "left"]),
|
|
170
|
+
attention: z.boolean(),
|
|
171
|
+
surfaces: z.object({
|
|
172
|
+
home: z.boolean(),
|
|
173
|
+
catalog: z.boolean(),
|
|
174
|
+
product: z.boolean(),
|
|
175
|
+
pages: z.boolean(),
|
|
176
|
+
thankYou: z.boolean(),
|
|
177
|
+
checkout: z.boolean(),
|
|
178
|
+
landing: z.boolean(),
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
export const CheckoutFormCustomFieldSchema = z.object({
|
|
182
|
+
id: z.string(),
|
|
183
|
+
label: z.string(),
|
|
184
|
+
type: z.enum(["text", "textarea", "number", "select"]),
|
|
185
|
+
required: z.boolean(),
|
|
186
|
+
options: z.array(z.string()).optional(),
|
|
187
|
+
});
|
|
188
|
+
export const CheckoutFormPolicySchema = z.object({
|
|
189
|
+
address: z.enum(["required", "optional", "hidden"]),
|
|
190
|
+
notes: z.enum(["optional", "hidden"]),
|
|
191
|
+
email: z.enum(["required", "optional", "hidden"]),
|
|
192
|
+
deliveryOptions: z.object({ home: z.boolean(), stopDesk: z.boolean() }),
|
|
193
|
+
customFields: z.array(CheckoutFormCustomFieldSchema),
|
|
194
|
+
});
|
|
195
|
+
export const StorefrontLanguageSchema = z.object({
|
|
196
|
+
code: z.string().min(1),
|
|
197
|
+
nativeName: z.string(),
|
|
198
|
+
direction: z.enum(["rtl", "ltr"]),
|
|
199
|
+
});
|
|
200
|
+
export const StoreConfigSchema = z.object({
|
|
201
|
+
id: z.string(),
|
|
202
|
+
name: z.string(),
|
|
203
|
+
domain: z.string().nullable(),
|
|
204
|
+
logoUrl: z.string().nullable(),
|
|
205
|
+
themeId: z.string(),
|
|
206
|
+
primaryColor: z.string(),
|
|
207
|
+
accentColor: z.string(),
|
|
208
|
+
bgColor: z.string(),
|
|
209
|
+
fontFamily: z.string(),
|
|
210
|
+
fontUrl: z.string().nullable(),
|
|
211
|
+
lang: z.string().min(1),
|
|
212
|
+
locale: z.string().min(1),
|
|
213
|
+
/**
|
|
214
|
+
* The published languages, primary first. Codes are open strings: the
|
|
215
|
+
* server owns which languages exist, so a new one never fails this parse.
|
|
216
|
+
*/
|
|
217
|
+
locales: z.array(StorefrontLanguageSchema),
|
|
218
|
+
/** The requesting theme's text edits, one map per published language. */
|
|
219
|
+
contentOverrides: z.record(z.string(), z.record(z.string(), z.string())),
|
|
220
|
+
currency: z.string(),
|
|
221
|
+
currencySymbol: z.string(),
|
|
222
|
+
metaTitle: z.string().nullable(),
|
|
223
|
+
metaDescription: z.string().nullable(),
|
|
224
|
+
ogImage: z.string().nullable(),
|
|
225
|
+
announcementBar: z.string().nullable(),
|
|
226
|
+
reviewsEnabled: z.boolean(),
|
|
227
|
+
cartEnabled: z.boolean(),
|
|
228
|
+
freeShippingThreshold: z.number().int().nullable(),
|
|
229
|
+
cartShippingMode: z.enum(["highest", "default_profile"]).default("highest"),
|
|
230
|
+
otpEnabled: z.boolean(),
|
|
231
|
+
turnstileEnabled: z.boolean(),
|
|
232
|
+
turnstileSiteKey: z.string().nullable(),
|
|
233
|
+
status: z.enum(["active", "inactive"]),
|
|
234
|
+
createdAt: z.string().optional().default(() => new Date().toISOString()),
|
|
235
|
+
updatedAt: z.string().optional().default(() => new Date().toISOString()),
|
|
236
|
+
checkoutForm: CheckoutFormPolicySchema.default({
|
|
237
|
+
address: "optional",
|
|
238
|
+
notes: "optional",
|
|
239
|
+
email: "optional",
|
|
240
|
+
deliveryOptions: { home: true, stopDesk: true },
|
|
241
|
+
customFields: [],
|
|
242
|
+
}),
|
|
243
|
+
pixelId: z.string().nullable().default(null),
|
|
244
|
+
conversionEvent: z
|
|
245
|
+
.enum(["Purchase", "Purchase_Confirmed", "Purchase_Delivered", "Lead"])
|
|
246
|
+
.default("Purchase"),
|
|
247
|
+
pages: z.array(StorePageLinkSchema).default([]),
|
|
248
|
+
legalContact: StoreLegalContactSchema.nullable().default(null),
|
|
249
|
+
whatsapp: StoreWhatsAppWidgetSchema.nullable().default(null),
|
|
250
|
+
});
|
|
251
|
+
export const LandingPageImageSchema = z.object({
|
|
252
|
+
id: z.string(),
|
|
253
|
+
src: z.string(),
|
|
254
|
+
altText: z.string().nullable(),
|
|
255
|
+
position: z.number().int(),
|
|
256
|
+
width: z.number().int().nullable().default(null),
|
|
257
|
+
height: z.number().int().nullable().default(null),
|
|
258
|
+
});
|
|
259
|
+
export const LandingPageSchema = z.object({
|
|
260
|
+
id: z.string(),
|
|
261
|
+
slug: z.string(),
|
|
262
|
+
name: z.string(),
|
|
263
|
+
status: z.enum(["draft", "published", "archived"]),
|
|
264
|
+
imageGap: z.number().int(),
|
|
265
|
+
metaTitle: z.string().nullable(),
|
|
266
|
+
metaDescription: z.string().nullable(),
|
|
267
|
+
publishedAt: z.string().nullable(),
|
|
268
|
+
images: z.array(LandingPageImageSchema),
|
|
269
|
+
product: ProductSchema.nullable(),
|
|
270
|
+
tracking: z.object({
|
|
271
|
+
pixelId: z.string().nullable(),
|
|
272
|
+
conversionEvent: z.enum([
|
|
273
|
+
"Lead",
|
|
274
|
+
"Purchase",
|
|
275
|
+
"Purchase_Confirmed",
|
|
276
|
+
"Purchase_Delivered",
|
|
277
|
+
]),
|
|
278
|
+
}),
|
|
279
|
+
});
|
|
280
|
+
export const WilayaRatesSchema = z.object({
|
|
281
|
+
home: z.number(),
|
|
282
|
+
stopDesk: z.number(),
|
|
283
|
+
});
|
|
284
|
+
export const ShippingRatesSchema = z.record(z.string(), WilayaRatesSchema);
|
|
285
|
+
export const CommuneSchema = z.object({
|
|
286
|
+
id: z.string(),
|
|
287
|
+
name: z.string(),
|
|
288
|
+
nameAr: z.string(),
|
|
289
|
+
});
|
|
290
|
+
export const CartValidationLineSchema = z.object({
|
|
291
|
+
productId: z.string(),
|
|
292
|
+
variantId: z.string().nullable(),
|
|
293
|
+
variantLabel: z.string().nullable(),
|
|
294
|
+
productName: z.string(),
|
|
295
|
+
quantity: z.number().int(),
|
|
296
|
+
unitPrice: z.number(),
|
|
297
|
+
lineTotal: z.number(),
|
|
298
|
+
maxQuantity: z.number().nullable(),
|
|
299
|
+
blocker: z.enum(["missing", "unlisted", "out_of_stock"]).nullable(),
|
|
300
|
+
});
|
|
301
|
+
export const CartRewardSchema = z.object({
|
|
302
|
+
offerId: z.string(),
|
|
303
|
+
productId: z.string().nullable(),
|
|
304
|
+
productName: z.string().nullable(),
|
|
305
|
+
quantity: z.number().int(),
|
|
306
|
+
});
|
|
307
|
+
export const CartValidationSchema = z.object({
|
|
308
|
+
lines: z.array(CartValidationLineSchema),
|
|
309
|
+
subtotal: z.number(),
|
|
310
|
+
rewards: z.array(CartRewardSchema),
|
|
311
|
+
freeDelivery: z.object({
|
|
312
|
+
fromOffer: z.boolean(),
|
|
313
|
+
threshold: z.number().nullable(),
|
|
314
|
+
qualified: z.boolean(),
|
|
315
|
+
remaining: z.number(),
|
|
316
|
+
}),
|
|
317
|
+
});
|
|
318
|
+
export const CreatedOrderSchema = z.object({
|
|
319
|
+
orderId: z.string(),
|
|
320
|
+
orderNumber: z.string(),
|
|
321
|
+
price: z.number(),
|
|
322
|
+
deliveryFee: z.number(),
|
|
323
|
+
total: z.number(),
|
|
324
|
+
});
|
|
325
|
+
export const OrderTrackingSchema = z.object({
|
|
326
|
+
pixelId: z.string().nullable(),
|
|
327
|
+
event: z.enum(["Purchase", "Lead"]).nullable(),
|
|
328
|
+
});
|
|
329
|
+
/**
|
|
330
|
+
* The `{ id }` success payload of the write endpoints that only mint a row:
|
|
331
|
+
* POST /store/reviews and POST /store/abandoned.
|
|
332
|
+
*/
|
|
333
|
+
export const IdResponseSchema = z.object({
|
|
334
|
+
id: z.string(),
|
|
335
|
+
});
|
|
336
|
+
export const OtpSendResultSchema = z.discriminatedUnion("status", [
|
|
337
|
+
z.object({
|
|
338
|
+
status: z.literal("sent"),
|
|
339
|
+
requestId: z.string(),
|
|
340
|
+
expiresAt: z.number(),
|
|
341
|
+
maxAttempts: z.number(),
|
|
342
|
+
}),
|
|
343
|
+
z.object({
|
|
344
|
+
status: z.literal("unavailable"),
|
|
345
|
+
reason: z.string(),
|
|
346
|
+
bypassToken: z.string(),
|
|
347
|
+
}),
|
|
348
|
+
]);
|
|
349
|
+
export const OtpVerifyResultSchema = z.object({
|
|
350
|
+
status: z.literal("verified"),
|
|
351
|
+
otpToken: z.string(),
|
|
352
|
+
});
|
|
353
|
+
export const ReviewSchema = z.preprocess((raw) => {
|
|
354
|
+
if (typeof raw === "object" && raw !== null) {
|
|
355
|
+
const obj = raw;
|
|
356
|
+
return {
|
|
357
|
+
...obj,
|
|
358
|
+
customerName: obj.customerName ?? obj.authorName ?? "Customer",
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
return raw;
|
|
362
|
+
}, z.object({
|
|
363
|
+
id: z.string(),
|
|
364
|
+
customerName: z.string(),
|
|
365
|
+
rating: z.number(),
|
|
366
|
+
title: z.string().nullable().optional().default(null),
|
|
367
|
+
body: z.string(),
|
|
368
|
+
createdAt: z.string(),
|
|
369
|
+
}));
|
|
370
|
+
/**
|
|
371
|
+
* GET /store/reviews answers `{ success, data: rows, count, total }` — the
|
|
372
|
+
* total lives OUTSIDE `data`, so a caller that reads only `data` loses it.
|
|
373
|
+
*/
|
|
374
|
+
export const ReviewListEnvelopeSchema = z.object({
|
|
375
|
+
data: z.array(ReviewSchema),
|
|
376
|
+
total: z.number().int(),
|
|
377
|
+
});
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED