@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +143 -2
  5. package/USAGE.md +1465 -0
  6. package/dist/abandoned-checkout.d.ts +22 -0
  7. package/dist/abandoned-checkout.js +89 -0
  8. package/dist/actions.d.ts +117 -0
  9. package/dist/actions.js +196 -0
  10. package/dist/astro.d.ts +6 -0
  11. package/dist/astro.js +3 -0
  12. package/dist/basket.d.ts +87 -0
  13. package/dist/basket.js +258 -0
  14. package/dist/client/cart.d.ts +30 -0
  15. package/dist/client/cart.js +26 -0
  16. package/dist/client/categories.d.ts +25 -0
  17. package/dist/client/categories.js +37 -0
  18. package/dist/client/config.d.ts +29 -0
  19. package/dist/client/config.js +41 -0
  20. package/dist/client/core.d.ts +86 -0
  21. package/dist/client/core.js +582 -0
  22. package/dist/client/errors.d.ts +19 -0
  23. package/dist/client/errors.js +31 -0
  24. package/dist/client/geo.d.ts +31 -0
  25. package/dist/client/geo.js +39 -0
  26. package/dist/client/index.d.ts +39 -0
  27. package/dist/client/index.js +80 -0
  28. package/dist/client/orders.d.ts +68 -0
  29. package/dist/client/orders.js +76 -0
  30. package/dist/client/page-data.d.ts +31 -0
  31. package/dist/client/page-data.js +37 -0
  32. package/dist/client/pages.d.ts +33 -0
  33. package/dist/client/pages.js +44 -0
  34. package/dist/client/paths.d.ts +8 -0
  35. package/dist/client/paths.js +10 -0
  36. package/dist/client/products.d.ts +56 -0
  37. package/dist/client/products.js +78 -0
  38. package/dist/client/shipping.d.ts +37 -0
  39. package/dist/client/shipping.js +48 -0
  40. package/dist/data/communes.d.ts +25 -0
  41. package/dist/data/communes.js +1690 -0
  42. package/dist/data/wilayas.d.ts +6 -0
  43. package/dist/data/wilayas.js +64 -0
  44. package/dist/domain.d.ts +332 -0
  45. package/dist/domain.js +214 -0
  46. package/dist/endpoints/proxy.d.ts +111 -0
  47. package/dist/endpoints/proxy.js +161 -0
  48. package/dist/index.d.ts +42 -0
  49. package/dist/index.js +30 -0
  50. package/dist/locale-routing.d.ts +59 -0
  51. package/dist/locale-routing.js +62 -0
  52. package/dist/locales.d.ts +15 -0
  53. package/dist/locales.js +8 -0
  54. package/dist/meta-tracking.d.ts +34 -0
  55. package/dist/meta-tracking.js +72 -0
  56. package/dist/middleware.d.ts +100 -0
  57. package/dist/middleware.js +72 -0
  58. package/dist/offer-view.d.ts +10 -0
  59. package/dist/offer-view.js +13 -0
  60. package/dist/otp.d.ts +80 -0
  61. package/dist/otp.js +295 -0
  62. package/dist/phone.d.ts +2 -0
  63. package/dist/phone.js +34 -0
  64. package/dist/product-view.d.ts +12 -0
  65. package/dist/product-view.js +42 -0
  66. package/dist/routes.d.ts +45 -0
  67. package/dist/routes.js +59 -0
  68. package/dist/schemas.d.ts +753 -0
  69. package/dist/schemas.js +377 -0
  70. package/dist/server.d.ts +4 -0
  71. package/dist/server.js +2 -0
  72. package/dist/types.d.ts +418 -0
  73. package/dist/types.js +1 -0
  74. package/dist/utils/image.d.ts +140 -0
  75. package/dist/utils/image.js +131 -0
  76. package/dist/utils/money.d.ts +39 -0
  77. package/dist/utils/money.js +43 -0
  78. package/dist/utils/product-image.d.ts +37 -0
  79. package/dist/utils/product-image.js +38 -0
  80. package/dist/whatsapp.d.ts +55 -0
  81. package/dist/whatsapp.js +89 -0
  82. package/package.json +76 -4
@@ -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
+ });
@@ -0,0 +1,4 @@
1
+ export { createStoreClient } from "./client/index.js";
2
+ export type { StoreClient } from "./client/index.js";
3
+ export type { ClientResult } from "./client/core.js";
4
+ export { StorefrontError, isStorefrontError } from "./client/errors.js";
package/dist/server.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createStoreClient } from "./client/index.js";
2
+ export { StorefrontError, isStorefrontError } from "./client/errors.js";