@wocart/storefront 1.18.0 → 1.20.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 +96 -0
- package/USAGE.md +313 -11
- package/dist/abandoned-checkout.d.ts +45 -0
- package/dist/abandoned-checkout.js +81 -3
- package/dist/answer-readers.d.ts +23 -0
- package/dist/answer-readers.js +155 -0
- package/dist/astro.d.ts +2 -0
- package/dist/astro.js +1 -0
- package/dist/basket-line.d.ts +15 -0
- package/dist/basket-line.js +17 -0
- package/dist/basket.d.ts +12 -2
- package/dist/basket.js +18 -1
- package/dist/catalog.d.ts +18 -0
- package/dist/catalog.js +27 -0
- package/dist/client/cart.js +1 -1
- package/dist/client/categories.js +1 -1
- package/dist/client/collections.js +1 -1
- package/dist/client/config.js +1 -1
- package/dist/client/geo.js +1 -1
- package/dist/client/menus.js +1 -1
- package/dist/client/orders.d.ts +10 -1
- package/dist/client/orders.js +9 -2
- package/dist/client/page-data.js +1 -1
- package/dist/client/pages.js +1 -1
- package/dist/client/products.d.ts +13 -3
- package/dist/client/products.js +36 -13
- package/dist/client/shipping.js +1 -1
- package/dist/coupon.d.ts +16 -1
- package/dist/coupon.js +37 -0
- package/dist/domain.d.ts +0 -70
- package/dist/domain.js +1 -70
- package/dist/endpoints/storefront-routes.d.ts +8 -2
- package/dist/endpoints/storefront-routes.js +14 -3
- package/dist/index.d.ts +18 -6
- package/dist/index.js +12 -3
- package/dist/operations.d.ts +72 -0
- package/dist/operations.js +70 -0
- package/dist/order-number.d.ts +9 -0
- package/dist/order-number.js +11 -0
- package/dist/otp.d.ts +8 -1
- package/dist/otp.js +3 -0
- package/dist/product-view.d.ts +8 -0
- package/dist/product-view.js +26 -4
- package/dist/quote.d.ts +27 -0
- package/dist/quote.js +43 -0
- package/dist/review-capture.d.ts +77 -0
- package/dist/review-capture.js +172 -0
- package/dist/review-media.d.ts +8 -75
- package/dist/review-media.js +7 -170
- package/dist/review-session.d.ts +97 -0
- package/dist/review-session.js +157 -0
- package/dist/reviews.d.ts +33 -0
- package/dist/reviews.js +41 -0
- package/dist/schemas.d.ts +98 -0
- package/dist/schemas.js +31 -0
- package/dist/seo.d.ts +47 -0
- package/dist/seo.js +87 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +1 -0
- package/dist/sitemap.d.ts +22 -0
- package/dist/sitemap.js +51 -0
- package/dist/store-request.d.ts +96 -0
- package/dist/store-request.js +102 -0
- package/dist/types.d.ts +50 -2
- package/dist/utils/image.d.ts +48 -0
- package/dist/utils/image.js +51 -0
- package/dist/wilaya-id.d.ts +1 -0
- package/dist/wilaya-id.js +10 -0
- package/package.json +1 -1
package/dist/client/products.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* half-shaped product into a theme.
|
|
13
13
|
*/
|
|
14
14
|
import { z } from "zod";
|
|
15
|
-
import { ProductListSchema, ProductSchema, ReviewEligibilitySchema, ReviewListEnvelopeSchema, ReviewSubmitResultSchema, ReviewUploadSchema } from "../schemas.js";
|
|
16
|
-
import { STOREFRONT_OPERATIONS } from "../
|
|
15
|
+
import { ProductListSchema, ProductPageEnvelopeSchema, ProductSchema, ReviewEligibilitySchema, ReviewListEnvelopeSchema, ReviewSubmitResultSchema, ReviewUploadSchema } from "../schemas.js";
|
|
16
|
+
import { STOREFRONT_OPERATIONS } from "../operations.js";
|
|
17
17
|
import { localizedPath } from "./paths.js";
|
|
18
18
|
import { isNotFoundAnswer, reportDegradedRead } from "./core.js";
|
|
19
19
|
/** Page size when a caller names none — the storefront's default grid. */
|
|
@@ -24,16 +24,19 @@ export const PRODUCT_PAGE_MAX = 99;
|
|
|
24
24
|
* Create products namespace
|
|
25
25
|
*/
|
|
26
26
|
export function createProductsNamespace(core) {
|
|
27
|
+
const query = (filters, extra = {}) => ({
|
|
28
|
+
...extra,
|
|
29
|
+
categoryId: filters?.categoryId || undefined,
|
|
30
|
+
collection: filters?.collection || undefined,
|
|
31
|
+
onSale: filters?.onSale ? "true" : undefined,
|
|
32
|
+
featured: filters?.featured ? "true" : undefined,
|
|
33
|
+
sort: filters?.sort || undefined,
|
|
34
|
+
limit: filters?.limit === undefined ? undefined : String(filters.limit),
|
|
35
|
+
offset: filters?.offset ? String(filters.offset) : undefined,
|
|
36
|
+
});
|
|
27
37
|
const list = async (filters) => {
|
|
28
38
|
try {
|
|
29
|
-
const products = await core.get(localizedPath(core.config, "/store/products", {
|
|
30
|
-
categoryId: filters?.categoryId || undefined,
|
|
31
|
-
collection: filters?.collection || undefined,
|
|
32
|
-
onSale: filters?.onSale ? "true" : undefined,
|
|
33
|
-
featured: filters?.featured ? "true" : undefined,
|
|
34
|
-
limit: filters?.limit === undefined ? undefined : String(filters.limit),
|
|
35
|
-
offset: filters?.offset ? String(filters.offset) : undefined,
|
|
36
|
-
}), z.array(ProductListSchema), { operation: STOREFRONT_OPERATIONS.productsList });
|
|
39
|
+
const products = await core.get(localizedPath(core.config, "/store/products", query(filters)), z.array(ProductListSchema), { operation: STOREFRONT_OPERATIONS.productsList });
|
|
37
40
|
return products ?? [];
|
|
38
41
|
}
|
|
39
42
|
catch (error) {
|
|
@@ -41,6 +44,22 @@ export function createProductsNamespace(core) {
|
|
|
41
44
|
return [];
|
|
42
45
|
}
|
|
43
46
|
};
|
|
47
|
+
/** One page of `path`, asked for one row more than the page so `hasNext` is exact. */
|
|
48
|
+
const page = async (path, filters, extra, operation) => {
|
|
49
|
+
const size = Math.min(Math.max(1, filters?.limit ?? PRODUCT_PAGE_DEFAULT), PRODUCT_PAGE_MAX);
|
|
50
|
+
try {
|
|
51
|
+
const envelope = await core.get(localizedPath(core.config, path, query({ ...filters, limit: size + 1 }, extra)), ProductPageEnvelopeSchema, {
|
|
52
|
+
envelope: true,
|
|
53
|
+
operation,
|
|
54
|
+
});
|
|
55
|
+
const rows = envelope?.data ?? [];
|
|
56
|
+
return { items: rows.slice(0, size), hasNext: rows.length > size, total: envelope?.total ?? null };
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
reportDegradedRead(core.config, error, operation);
|
|
60
|
+
return { items: [], hasNext: false, total: null };
|
|
61
|
+
}
|
|
62
|
+
};
|
|
44
63
|
return {
|
|
45
64
|
async get(handle) {
|
|
46
65
|
try {
|
|
@@ -55,9 +74,13 @@ export function createProductsNamespace(core) {
|
|
|
55
74
|
},
|
|
56
75
|
list,
|
|
57
76
|
async page(filters) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
return page("/store/products", filters, {}, STOREFRONT_OPERATIONS.productsList);
|
|
78
|
+
},
|
|
79
|
+
async search(text, filters) {
|
|
80
|
+
const q = text.trim().slice(0, 100);
|
|
81
|
+
if (q === "")
|
|
82
|
+
return { items: [], hasNext: false, total: 0 };
|
|
83
|
+
return page("/store/search", filters, { q }, STOREFRONT_OPERATIONS.productsSearch);
|
|
61
84
|
},
|
|
62
85
|
reviews: {
|
|
63
86
|
async list(productId, pagination) {
|
package/dist/client/shipping.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* treated as unsupported.
|
|
14
14
|
*/
|
|
15
15
|
import { ShippingRatesSchema } from "../schemas.js";
|
|
16
|
-
import { STOREFRONT_OPERATIONS } from "../
|
|
16
|
+
import { STOREFRONT_OPERATIONS } from "../operations.js";
|
|
17
17
|
import { reportDegradedRead } from "./core.js";
|
|
18
18
|
/**
|
|
19
19
|
* Create shipping namespace
|
package/dist/coupon.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* code does not apply. A theme shows those answers; it never decides one.
|
|
8
8
|
*/
|
|
9
9
|
import type { ClientResult } from "./domain.js";
|
|
10
|
-
import type { CouponRefusal } from "./types.js";
|
|
10
|
+
import type { CouponRefusal, CouponRefusalReason } from "./types.js";
|
|
11
11
|
/** The error code of an order refused for its code. */
|
|
12
12
|
export declare const COUPON_NOT_APPLICABLE = "COUPON_NOT_APPLICABLE";
|
|
13
13
|
/** The query parameter a shareable coupon link carries: `/products/robe?coupon=RAMADAN10`. */
|
|
@@ -16,3 +16,18 @@ export declare const COUPON_URL_PARAM = "coupon";
|
|
|
16
16
|
export declare function couponFromUrl(url: URL): string | null;
|
|
17
17
|
/** Why an order was refused for its code, or null when it was refused — or placed — for anything else. */
|
|
18
18
|
export declare function couponRefusalOf(result: ClientResult<unknown>): CouponRefusal | null;
|
|
19
|
+
/** Every reason a code is refused for, as the platform names them. A newer server may add one; it then reads as `refused`. */
|
|
20
|
+
export declare const COUPON_REFUSAL_REASONS: readonly ["not_found", "not_started", "expired", "used_up", "identify_first", "not_eligible", "already_used", "no_eligible_products", "offer_applied", "below_minimum_subtotal", "below_minimum_quantity", "delivery_already_free"];
|
|
21
|
+
/**
|
|
22
|
+
* The reasons about the basket, not the code: the code is good, but this
|
|
23
|
+
* basket does not meet it yet — a phone still to type, a product under an
|
|
24
|
+
* offer, a minimum not reached, delivery already free.
|
|
25
|
+
*/
|
|
26
|
+
export declare const COUPON_HINT_REASONS: readonly CouponRefusalReason[];
|
|
27
|
+
/**
|
|
28
|
+
* How a refusal reads: `hint` when the shopper can still meet the code with
|
|
29
|
+
* this basket (see `COUPON_HINT_REASONS`), `refused` when the code itself
|
|
30
|
+
* cannot be used — unknown, outside its dates, used up, not for this shopper —
|
|
31
|
+
* and for a reason this release does not know.
|
|
32
|
+
*/
|
|
33
|
+
export declare function couponRefusalTone(reason: CouponRefusalReason): "refused" | "hint";
|
package/dist/coupon.js
CHANGED
|
@@ -17,3 +17,40 @@ export function couponRefusalOf(result) {
|
|
|
17
17
|
...(typeof result.startsAt === "string" ? { startsAt: result.startsAt } : {}),
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
/** Every reason a code is refused for, as the platform names them. A newer server may add one; it then reads as `refused`. */
|
|
21
|
+
export const COUPON_REFUSAL_REASONS = [
|
|
22
|
+
"not_found",
|
|
23
|
+
"not_started",
|
|
24
|
+
"expired",
|
|
25
|
+
"used_up",
|
|
26
|
+
"identify_first",
|
|
27
|
+
"not_eligible",
|
|
28
|
+
"already_used",
|
|
29
|
+
"no_eligible_products",
|
|
30
|
+
"offer_applied",
|
|
31
|
+
"below_minimum_subtotal",
|
|
32
|
+
"below_minimum_quantity",
|
|
33
|
+
"delivery_already_free",
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* The reasons about the basket, not the code: the code is good, but this
|
|
37
|
+
* basket does not meet it yet — a phone still to type, a product under an
|
|
38
|
+
* offer, a minimum not reached, delivery already free.
|
|
39
|
+
*/
|
|
40
|
+
export const COUPON_HINT_REASONS = [
|
|
41
|
+
"identify_first",
|
|
42
|
+
"no_eligible_products",
|
|
43
|
+
"offer_applied",
|
|
44
|
+
"below_minimum_subtotal",
|
|
45
|
+
"below_minimum_quantity",
|
|
46
|
+
"delivery_already_free",
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* How a refusal reads: `hint` when the shopper can still meet the code with
|
|
50
|
+
* this basket (see `COUPON_HINT_REASONS`), `refused` when the code itself
|
|
51
|
+
* cannot be used — unknown, outside its dates, used up, not for this shopper —
|
|
52
|
+
* and for a reason this release does not know.
|
|
53
|
+
*/
|
|
54
|
+
export function couponRefusalTone(reason) {
|
|
55
|
+
return COUPON_HINT_REASONS.includes(reason) ? "hint" : "refused";
|
|
56
|
+
}
|
package/dist/domain.d.ts
CHANGED
|
@@ -46,9 +46,6 @@ export interface StoreScope {
|
|
|
46
46
|
*/
|
|
47
47
|
readonly readTimeoutMs?: number;
|
|
48
48
|
}
|
|
49
|
-
/** The server's hard limits for one canonical browser basket. */
|
|
50
|
-
export declare const MAX_BASKET_LINES: 20;
|
|
51
|
-
export declare const MAX_BASKET_LINE_QUANTITY: 100;
|
|
52
49
|
/**
|
|
53
50
|
* A wilaya id, bounded by the reference dataset itself rather than a literal.
|
|
54
51
|
*
|
|
@@ -57,11 +54,6 @@ export declare const MAX_BASKET_LINE_QUANTITY: 100;
|
|
|
57
54
|
* range in step.
|
|
58
55
|
*/
|
|
59
56
|
export declare const WilayaIdSchema: z.ZodNumber;
|
|
60
|
-
/**
|
|
61
|
-
* Product + variant is the identity of a basket line. A null variant is a
|
|
62
|
-
* real identity, not the same thing as any selected variant.
|
|
63
|
-
*/
|
|
64
|
-
export declare function basketLineKey(productId: string, variantId: string | null): string;
|
|
65
57
|
/**
|
|
66
58
|
* A browser-owned basket line. `unitPrice` is an optimistic display value;
|
|
67
59
|
* the server re-prices every line during validation and order creation.
|
|
@@ -347,65 +339,3 @@ export type ClientResult<T> = {
|
|
|
347
339
|
readonly domainError: DomainError;
|
|
348
340
|
readonly [key: string]: unknown;
|
|
349
341
|
};
|
|
350
|
-
export type RetryPolicy = "safe-read" | "never";
|
|
351
|
-
export declare const STOREFRONT_OPERATIONS: {
|
|
352
|
-
readonly configGet: "config.get";
|
|
353
|
-
readonly categoriesList: "categories.list";
|
|
354
|
-
readonly productsGet: "products.get";
|
|
355
|
-
readonly productsList: "products.list";
|
|
356
|
-
readonly productsReviewsList: "products.reviews.list";
|
|
357
|
-
readonly productsReviewsSubmit: "products.reviews.submit";
|
|
358
|
-
readonly productsReviewsEligibility: "products.reviews.eligibility";
|
|
359
|
-
readonly productsReviewsUpload: "products.reviews.upload";
|
|
360
|
-
readonly pagesGet: "pages.get";
|
|
361
|
-
readonly pagesLanding: "pages.landing";
|
|
362
|
-
readonly menusGet: "menus.get";
|
|
363
|
-
readonly collectionsGet: "collections.get";
|
|
364
|
-
readonly shippingRates: "shipping.rates";
|
|
365
|
-
readonly shippingRate: "shipping.rate";
|
|
366
|
-
readonly geoCommunes: "geo.communes";
|
|
367
|
-
readonly cartValidate: "cart.validate";
|
|
368
|
-
readonly pageDataGet: "pageData.get";
|
|
369
|
-
readonly ordersCreate: "orders.create";
|
|
370
|
-
readonly ordersTracking: "orders.tracking";
|
|
371
|
-
readonly ordersAbandonedUpsert: "orders.abandoned.upsert";
|
|
372
|
-
readonly ordersAbandonedConvert: "orders.abandoned.convert";
|
|
373
|
-
readonly ordersOtpSend: "orders.otp.send";
|
|
374
|
-
readonly ordersOtpVerify: "orders.otp.verify";
|
|
375
|
-
};
|
|
376
|
-
export type StorefrontOperation = (typeof STOREFRONT_OPERATIONS)[keyof typeof STOREFRONT_OPERATIONS];
|
|
377
|
-
/**
|
|
378
|
-
* The retry policy per operation, as a contract a caller can read.
|
|
379
|
-
*
|
|
380
|
-
* The transport enforces it by kind: every `safe-read` goes through the
|
|
381
|
-
* caching/deduplicating GET path, which repeats a transport or 5xx failure,
|
|
382
|
-
* while every `never` goes through the POST/PATCH path, which returns a result
|
|
383
|
-
* and never retries (a replayed write is how a shopper ends up with two
|
|
384
|
-
* orders). A test asserts this table covers every public operation and agrees
|
|
385
|
-
* with that classification, so a new operation cannot ship unclassified.
|
|
386
|
-
*/
|
|
387
|
-
export declare const STOREFRONT_RETRY_POLICIES: {
|
|
388
|
-
readonly "cart.validate": "safe-read";
|
|
389
|
-
readonly "categories.list": "safe-read";
|
|
390
|
-
readonly "config.get": "safe-read";
|
|
391
|
-
readonly "geo.communes": "safe-read";
|
|
392
|
-
readonly "orders.abandoned.convert": "never";
|
|
393
|
-
readonly "orders.abandoned.upsert": "never";
|
|
394
|
-
readonly "orders.create": "never";
|
|
395
|
-
readonly "orders.otp.send": "never";
|
|
396
|
-
readonly "orders.otp.verify": "never";
|
|
397
|
-
readonly "orders.tracking": "safe-read";
|
|
398
|
-
readonly "pageData.get": "safe-read";
|
|
399
|
-
readonly "pages.get": "safe-read";
|
|
400
|
-
readonly "pages.landing": "safe-read";
|
|
401
|
-
readonly "menus.get": "safe-read";
|
|
402
|
-
readonly "collections.get": "safe-read";
|
|
403
|
-
readonly "products.get": "safe-read";
|
|
404
|
-
readonly "products.list": "safe-read";
|
|
405
|
-
readonly "products.reviews.list": "safe-read";
|
|
406
|
-
readonly "products.reviews.submit": "never";
|
|
407
|
-
readonly "products.reviews.eligibility": "never";
|
|
408
|
-
readonly "products.reviews.upload": "never";
|
|
409
|
-
readonly "shipping.rate": "safe-read";
|
|
410
|
-
readonly "shipping.rates": "safe-read";
|
|
411
|
-
};
|
package/dist/domain.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { WILAYAS } from "./data/wilayas.js";
|
|
3
|
-
|
|
4
|
-
export const MAX_BASKET_LINES = 20;
|
|
5
|
-
export const MAX_BASKET_LINE_QUANTITY = 100;
|
|
3
|
+
import { MAX_BASKET_LINES, MAX_BASKET_LINE_QUANTITY, basketLineKey } from "./basket-line.js";
|
|
6
4
|
/**
|
|
7
5
|
* A wilaya id, bounded by the reference dataset itself rather than a literal.
|
|
8
6
|
*
|
|
@@ -11,13 +9,6 @@ export const MAX_BASKET_LINE_QUANTITY = 100;
|
|
|
11
9
|
* range in step.
|
|
12
10
|
*/
|
|
13
11
|
export const WilayaIdSchema = z.number().int().min(1).max(WILAYAS.length);
|
|
14
|
-
/**
|
|
15
|
-
* Product + variant is the identity of a basket line. A null variant is a
|
|
16
|
-
* real identity, not the same thing as any selected variant.
|
|
17
|
-
*/
|
|
18
|
-
export function basketLineKey(productId, variantId) {
|
|
19
|
-
return `${productId}\u0000${variantId ?? ""}`;
|
|
20
|
-
}
|
|
21
12
|
/**
|
|
22
13
|
* A browser-owned basket line. `unitPrice` is an optimistic display value;
|
|
23
14
|
* the server re-prices every line during validation and order creation.
|
|
@@ -201,63 +192,3 @@ export const TrackingDecisionSchema = z.object({
|
|
|
201
192
|
pixelId: z.string().nullable(),
|
|
202
193
|
event: z.enum(["Purchase", "Lead"]).nullable(),
|
|
203
194
|
});
|
|
204
|
-
export const STOREFRONT_OPERATIONS = {
|
|
205
|
-
configGet: "config.get",
|
|
206
|
-
categoriesList: "categories.list",
|
|
207
|
-
productsGet: "products.get",
|
|
208
|
-
productsList: "products.list",
|
|
209
|
-
productsReviewsList: "products.reviews.list",
|
|
210
|
-
productsReviewsSubmit: "products.reviews.submit",
|
|
211
|
-
productsReviewsEligibility: "products.reviews.eligibility",
|
|
212
|
-
productsReviewsUpload: "products.reviews.upload",
|
|
213
|
-
pagesGet: "pages.get",
|
|
214
|
-
pagesLanding: "pages.landing",
|
|
215
|
-
menusGet: "menus.get",
|
|
216
|
-
collectionsGet: "collections.get",
|
|
217
|
-
shippingRates: "shipping.rates",
|
|
218
|
-
shippingRate: "shipping.rate",
|
|
219
|
-
geoCommunes: "geo.communes",
|
|
220
|
-
cartValidate: "cart.validate",
|
|
221
|
-
pageDataGet: "pageData.get",
|
|
222
|
-
ordersCreate: "orders.create",
|
|
223
|
-
ordersTracking: "orders.tracking",
|
|
224
|
-
ordersAbandonedUpsert: "orders.abandoned.upsert",
|
|
225
|
-
ordersAbandonedConvert: "orders.abandoned.convert",
|
|
226
|
-
ordersOtpSend: "orders.otp.send",
|
|
227
|
-
ordersOtpVerify: "orders.otp.verify",
|
|
228
|
-
};
|
|
229
|
-
/**
|
|
230
|
-
* The retry policy per operation, as a contract a caller can read.
|
|
231
|
-
*
|
|
232
|
-
* The transport enforces it by kind: every `safe-read` goes through the
|
|
233
|
-
* caching/deduplicating GET path, which repeats a transport or 5xx failure,
|
|
234
|
-
* while every `never` goes through the POST/PATCH path, which returns a result
|
|
235
|
-
* and never retries (a replayed write is how a shopper ends up with two
|
|
236
|
-
* orders). A test asserts this table covers every public operation and agrees
|
|
237
|
-
* with that classification, so a new operation cannot ship unclassified.
|
|
238
|
-
*/
|
|
239
|
-
export const STOREFRONT_RETRY_POLICIES = {
|
|
240
|
-
[STOREFRONT_OPERATIONS.cartValidate]: "safe-read",
|
|
241
|
-
[STOREFRONT_OPERATIONS.categoriesList]: "safe-read",
|
|
242
|
-
[STOREFRONT_OPERATIONS.configGet]: "safe-read",
|
|
243
|
-
[STOREFRONT_OPERATIONS.geoCommunes]: "safe-read",
|
|
244
|
-
[STOREFRONT_OPERATIONS.ordersAbandonedConvert]: "never",
|
|
245
|
-
[STOREFRONT_OPERATIONS.ordersAbandonedUpsert]: "never",
|
|
246
|
-
[STOREFRONT_OPERATIONS.ordersCreate]: "never",
|
|
247
|
-
[STOREFRONT_OPERATIONS.ordersOtpSend]: "never",
|
|
248
|
-
[STOREFRONT_OPERATIONS.ordersOtpVerify]: "never",
|
|
249
|
-
[STOREFRONT_OPERATIONS.ordersTracking]: "safe-read",
|
|
250
|
-
[STOREFRONT_OPERATIONS.pageDataGet]: "safe-read",
|
|
251
|
-
[STOREFRONT_OPERATIONS.pagesGet]: "safe-read",
|
|
252
|
-
[STOREFRONT_OPERATIONS.pagesLanding]: "safe-read",
|
|
253
|
-
[STOREFRONT_OPERATIONS.menusGet]: "safe-read",
|
|
254
|
-
[STOREFRONT_OPERATIONS.collectionsGet]: "safe-read",
|
|
255
|
-
[STOREFRONT_OPERATIONS.productsGet]: "safe-read",
|
|
256
|
-
[STOREFRONT_OPERATIONS.productsList]: "safe-read",
|
|
257
|
-
[STOREFRONT_OPERATIONS.productsReviewsList]: "safe-read",
|
|
258
|
-
[STOREFRONT_OPERATIONS.productsReviewsSubmit]: "never",
|
|
259
|
-
[STOREFRONT_OPERATIONS.productsReviewsEligibility]: "never",
|
|
260
|
-
[STOREFRONT_OPERATIONS.productsReviewsUpload]: "never",
|
|
261
|
-
[STOREFRONT_OPERATIONS.shippingRate]: "safe-read",
|
|
262
|
-
[STOREFRONT_OPERATIONS.shippingRates]: "safe-read",
|
|
263
|
-
};
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every theme needs the same routes between the shopper's browser and the
|
|
5
5
|
* platform: OTP send and verify, cart validation, abandoned-checkout tracking
|
|
6
|
-
* and its conversion, communes,
|
|
7
|
-
*
|
|
6
|
+
* and its conversion, communes, reviews (list, find the order, send a photo
|
|
7
|
+
* or voice note, submit), and where a shopper's order is. What
|
|
8
8
|
* each one must get right is decided here once — the cross-origin check on
|
|
9
9
|
* every POST, the shopper's address forwarded wherever the server counts or
|
|
10
10
|
* attributes per shopper, the input bounds, and the response shape the
|
|
@@ -59,6 +59,12 @@ export interface StorefrontRoutes<C extends AstroAPIContext> {
|
|
|
59
59
|
readonly reviewUpload: StorefrontApiRoute<C>;
|
|
60
60
|
/** POST a `ReviewInput` → 201 `{ success: true, data: { id, visibility } }`. */
|
|
61
61
|
readonly reviewSubmit: StorefrontApiRoute<C>;
|
|
62
|
+
/**
|
|
63
|
+
* POST `{ orderNumber, phone }` → `{ data: ShopperOrderStatus }`. A wrong
|
|
64
|
+
* number or phone is refused with `ORDER_NOT_FOUND`; too many tries with
|
|
65
|
+
* `ORDER_LOOKUP_RATE_LIMITED` (429, `windowSeconds`).
|
|
66
|
+
*/
|
|
67
|
+
readonly orderStatus: StorefrontApiRoute<C>;
|
|
62
68
|
}
|
|
63
69
|
export declare const REVIEWS_PAGE_DEFAULT = 20;
|
|
64
70
|
export declare const REVIEWS_PAGE_MAX = 50;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every theme needs the same routes between the shopper's browser and the
|
|
5
5
|
* platform: OTP send and verify, cart validation, abandoned-checkout tracking
|
|
6
|
-
* and its conversion, communes,
|
|
7
|
-
*
|
|
6
|
+
* and its conversion, communes, reviews (list, find the order, send a photo
|
|
7
|
+
* or voice note, submit), and where a shopper's order is. What
|
|
8
8
|
* each one must get right is decided here once — the cross-origin check on
|
|
9
9
|
* every POST, the shopper's address forwarded wherever the server counts or
|
|
10
10
|
* attributes per shopper, the input bounds, and the response shape the
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
* (see `createProxyEndpoint`), and the server's diagnostic fields ride along.
|
|
23
23
|
*/
|
|
24
24
|
import { z } from "zod";
|
|
25
|
-
import { AbandonedCheckoutInputSchema, BasketLineInputSchema, DeliveryDestinationSchema, ProductQuoteInputSchema,
|
|
25
|
+
import { AbandonedCheckoutInputSchema, BasketLineInputSchema, DeliveryDestinationSchema, ProductQuoteInputSchema, WilayaIdSchema, } from "../domain.js";
|
|
26
|
+
import { MAX_BASKET_LINES } from "../basket-line.js";
|
|
26
27
|
import { createProxyEndpoint, } from "./proxy.js";
|
|
27
28
|
export const REVIEWS_PAGE_DEFAULT = 20;
|
|
28
29
|
export const REVIEWS_PAGE_MAX = 50;
|
|
@@ -81,6 +82,10 @@ const reviewSubmitInput = z.object({
|
|
|
81
82
|
grant: z.string().min(1).max(2048).optional(),
|
|
82
83
|
uploads: z.array(z.string().min(1).max(64)).max(8).optional(),
|
|
83
84
|
});
|
|
85
|
+
const orderStatusInput = z.object({
|
|
86
|
+
orderNumber: z.string().min(1).max(64),
|
|
87
|
+
phone: z.string().min(1).max(32),
|
|
88
|
+
});
|
|
84
89
|
const reviewEligibilityInput = z.object({
|
|
85
90
|
orderNumber: z.string().min(1).max(64),
|
|
86
91
|
phone: z.string().min(1).max(32),
|
|
@@ -190,5 +195,11 @@ export function createStorefrontRoutes(options) {
|
|
|
190
195
|
successBody: (data) => ({ success: true, data }),
|
|
191
196
|
call: (client, review) => client.products.reviews.submit(defined(review)),
|
|
192
197
|
}),
|
|
198
|
+
orderStatus: route({
|
|
199
|
+
method: "POST",
|
|
200
|
+
inputSchema: orderStatusInput,
|
|
201
|
+
forwardAttribution: true,
|
|
202
|
+
call: (client, proof, headers) => client.orders.status(proof, headers),
|
|
203
|
+
}),
|
|
193
204
|
};
|
|
194
205
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { formatMoney } from "./utils/money.js";
|
|
13
13
|
export type { MoneyFormatOptions } from "./utils/money.js";
|
|
14
|
-
export { optimizeImage, generateSrcSet, IMAGE_SIZES, getCardImage, getGalleryImage, getThumbnailImage, getHeroImage, getCategoryImage, getLogoImage, createImageHelpers, mediaImageUrl, mediaUrl, } from "./utils/image.js";
|
|
15
|
-
export type { ImageHelpers, ImageResizeOptions, MediaImageParams } from "./utils/image.js";
|
|
14
|
+
export { optimizeImage, generateSrcSet, IMAGE_SIZES, getCardImage, getGalleryImage, getThumbnailImage, getHeroImage, getCategoryImage, getLogoImage, createImageHelpers, mediaImageUrl, mediaImageSrcset, mediaUrl, pictureOf, } from "./utils/image.js";
|
|
15
|
+
export type { ImageHelpers, ImageResizeOptions, MediaImageParams, MediaImageSrcsetOptions, PickedImage, Picture } from "./utils/image.js";
|
|
16
16
|
export { productImageSource } from "./utils/product-image.js";
|
|
17
17
|
export type { ProductImageSourceInput } from "./utils/product-image.js";
|
|
18
18
|
export { MAX_RESOLVED_PREFILL_LENGTH, PREFILL_TOKENS, stripTrackingParams, truncateAtWordBoundary, resolvePrefillText, buildWhatsAppHref, } from "./whatsapp.js";
|
|
@@ -23,24 +23,36 @@ export { alternateLinks, localeRouting, localizeHref, resolveLocalePath } from "
|
|
|
23
23
|
export type { LocalePath, LocaleRouting } from "./locale-routing.js";
|
|
24
24
|
export type { StorefrontPageMatch, StorefrontPageType, StorefrontPathParams, StorefrontRoute } from "./routes.js";
|
|
25
25
|
export { createBasketStore, createMemoryStorageAdapter, createLocalStorageAdapter, DEFAULT_BASKET_STORAGE_KEY, toOrderItems, } from "./basket.js";
|
|
26
|
-
export
|
|
26
|
+
export { MAX_BASKET_LINES, MAX_BASKET_LINE_QUANTITY, basketLineKey } from "./basket-line.js";
|
|
27
|
+
export type { BasketStore, BasketStorageAdapter, BasketStoreOptions, BasketListener, BasketSelectionLine, } from "./basket.js";
|
|
27
28
|
export { createOtpMachine, createOtpTransport, mapOtpResponse, otpTransportFailure, } from "./otp.js";
|
|
28
29
|
export type { OtpMachine, OtpMachineOptions, OtpFailureContext, OtpResponseOptions, OtpState, OtpStatus, OtpTransport, OtpTransportResult, OtpStateListener, } from "./otp.js";
|
|
29
30
|
export { WILAYAS } from "./data/wilayas.js";
|
|
30
31
|
export { ALGERIA_COMMUNES, communesForWilaya } from "./data/communes.js";
|
|
32
|
+
export { isWilayaId } from "./wilaya-id.js";
|
|
33
|
+
export { readCartQuote, readCouponOutcome, readShippingRates } from "./answer-readers.js";
|
|
34
|
+
export { createQuoter } from "./quote.js";
|
|
35
|
+
export type { Quoter, QuoterOptions, QuoteResult } from "./quote.js";
|
|
36
|
+
export { isOrderNumber, ORDER_NUMBER_PATTERN } from "./order-number.js";
|
|
37
|
+
export { DEFAULT_COLLECTION_HANDLES, PRODUCT_SORTS, SHOPPER_ORDER_STAGES } from "./catalog.js";
|
|
38
|
+
export { SEO_TEXT_LIMITS, jsonLd, productMeta, productStructuredData } from "./seo.js";
|
|
39
|
+
export type { PageMeta, ProductStructuredDataOptions } from "./seo.js";
|
|
40
|
+
export { REVIEW_REFUSAL_CODES, REVIEW_TEXT_LIMITS } from "./reviews.js";
|
|
41
|
+
export type { ReviewRefusalCode } from "./reviews.js";
|
|
31
42
|
export type { CommuneTuple } from "./data/communes.js";
|
|
32
43
|
export { normalizeAlgerianPhone, toLocalAlgerianMobile } from "./phone.js";
|
|
33
44
|
export * from "./types.js";
|
|
34
45
|
export * from "./schemas.js";
|
|
35
46
|
export * from "./domain.js";
|
|
47
|
+
export * from "./operations.js";
|
|
36
48
|
export * from "./product-view.js";
|
|
37
49
|
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
38
|
-
export { COUPON_NOT_APPLICABLE, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf } from "./coupon.js";
|
|
50
|
+
export { COUPON_HINT_REASONS, COUPON_NOT_APPLICABLE, COUPON_REFUSAL_REASONS, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf, couponRefusalTone, } from "./coupon.js";
|
|
39
51
|
export { initialVariant, optionStates, pickOption, variantFor } from "./variant-picker.js";
|
|
40
52
|
export type { OptionSelection, OptionState, PickerVariant } from "./variant-picker.js";
|
|
41
53
|
export type { CartQuoteOptions } from "./client/cart.js";
|
|
42
54
|
export type { OfferTierInput, OfferTierView, QuantityTierView } from "./offer-view.js";
|
|
43
|
-
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
44
|
-
export type { AbandonedCheckoutPorts, AbandonedCheckoutSnapshot, AbandonedCheckoutStorage, AbandonedCheckoutTracker, } from "./abandoned-checkout.js";
|
|
55
|
+
export { ABANDONED_CHECKOUT_SESSION_KEY, convertAbandonedCheckout, createAbandonedCheckoutTracker, createBrowserAbandonedCheckoutTracker, } from "./abandoned-checkout.js";
|
|
56
|
+
export type { AbandonedCheckoutConversion, AbandonedCheckoutHost, AbandonedCheckoutPorts, AbandonedCheckoutSnapshot, AbandonedCheckoutStorage, AbandonedCheckoutTracker, BrowserAbandonedCheckoutOptions, } from "./abandoned-checkout.js";
|
|
45
57
|
export { META_CURRENCY, buildMetaAddToCartPayload, buildMetaInitiateCheckoutPayload, buildMetaProductPayload, buildMetaViewContentPayload, createMetaPixelTracker, } from "./meta-tracking.js";
|
|
46
58
|
export type { MetaCartPayload, MetaCatalogPayload, MetaContent, MetaEventName, MetaPixelFunction, MetaPixelOptions, MetaPixelTracker, } from "./meta-tracking.js";
|
package/dist/index.js
CHANGED
|
@@ -10,23 +10,32 @@
|
|
|
10
10
|
* Internal modules stay reachable only through their intentional entry points.
|
|
11
11
|
*/
|
|
12
12
|
export { formatMoney } from "./utils/money.js";
|
|
13
|
-
export { optimizeImage, generateSrcSet, IMAGE_SIZES, getCardImage, getGalleryImage, getThumbnailImage, getHeroImage, getCategoryImage, getLogoImage, createImageHelpers, mediaImageUrl, mediaUrl, } from "./utils/image.js";
|
|
13
|
+
export { optimizeImage, generateSrcSet, IMAGE_SIZES, getCardImage, getGalleryImage, getThumbnailImage, getHeroImage, getCategoryImage, getLogoImage, createImageHelpers, mediaImageUrl, mediaImageSrcset, mediaUrl, pictureOf, } from "./utils/image.js";
|
|
14
14
|
export { productImageSource } from "./utils/product-image.js";
|
|
15
15
|
export { MAX_RESOLVED_PREFILL_LENGTH, PREFILL_TOKENS, stripTrackingParams, truncateAtWordBoundary, resolvePrefillText, buildWhatsAppHref, } from "./whatsapp.js";
|
|
16
16
|
export { STOREFRONT_PAGE_PATHS, STOREFRONT_ROUTES, matchStorefrontPath, showsWidgetOn, storefrontPath } from "./routes.js";
|
|
17
17
|
export { servedLanguage } from "./locales.js";
|
|
18
18
|
export { alternateLinks, localeRouting, localizeHref, resolveLocalePath } from "./locale-routing.js";
|
|
19
19
|
export { createBasketStore, createMemoryStorageAdapter, createLocalStorageAdapter, DEFAULT_BASKET_STORAGE_KEY, toOrderItems, } from "./basket.js";
|
|
20
|
+
export { MAX_BASKET_LINES, MAX_BASKET_LINE_QUANTITY, basketLineKey } from "./basket-line.js";
|
|
20
21
|
export { createOtpMachine, createOtpTransport, mapOtpResponse, otpTransportFailure, } from "./otp.js";
|
|
21
22
|
export { WILAYAS } from "./data/wilayas.js";
|
|
22
23
|
export { ALGERIA_COMMUNES, communesForWilaya } from "./data/communes.js";
|
|
24
|
+
export { isWilayaId } from "./wilaya-id.js";
|
|
25
|
+
export { readCartQuote, readCouponOutcome, readShippingRates } from "./answer-readers.js";
|
|
26
|
+
export { createQuoter } from "./quote.js";
|
|
27
|
+
export { isOrderNumber, ORDER_NUMBER_PATTERN } from "./order-number.js";
|
|
28
|
+
export { DEFAULT_COLLECTION_HANDLES, PRODUCT_SORTS, SHOPPER_ORDER_STAGES } from "./catalog.js";
|
|
29
|
+
export { SEO_TEXT_LIMITS, jsonLd, productMeta, productStructuredData } from "./seo.js";
|
|
30
|
+
export { REVIEW_REFUSAL_CODES, REVIEW_TEXT_LIMITS } from "./reviews.js";
|
|
23
31
|
export { normalizeAlgerianPhone, toLocalAlgerianMobile } from "./phone.js";
|
|
24
32
|
export * from "./types.js";
|
|
25
33
|
export * from "./schemas.js";
|
|
26
34
|
export * from "./domain.js";
|
|
35
|
+
export * from "./operations.js";
|
|
27
36
|
export * from "./product-view.js";
|
|
28
37
|
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
29
|
-
export { COUPON_NOT_APPLICABLE, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf } from "./coupon.js";
|
|
38
|
+
export { COUPON_HINT_REASONS, COUPON_NOT_APPLICABLE, COUPON_REFUSAL_REASONS, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf, couponRefusalTone, } from "./coupon.js";
|
|
30
39
|
export { initialVariant, optionStates, pickOption, variantFor } from "./variant-picker.js";
|
|
31
|
-
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
40
|
+
export { ABANDONED_CHECKOUT_SESSION_KEY, convertAbandonedCheckout, createAbandonedCheckoutTracker, createBrowserAbandonedCheckoutTracker, } from "./abandoned-checkout.js";
|
|
32
41
|
export { META_CURRENCY, buildMetaAddToCartPayload, buildMetaInitiateCheckoutPayload, buildMetaProductPayload, buildMetaViewContentPayload, createMetaPixelTracker, } from "./meta-tracking.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform operations a storefront calls, and the retry policy each one
|
|
3
|
+
* keeps. Plain values and no schema: browser code reads the operation names
|
|
4
|
+
* too — an OTP route's failure names the operation it was — so this module
|
|
5
|
+
* stays free of anything that would bring a validator into a shopper's page.
|
|
6
|
+
*/
|
|
7
|
+
export type RetryPolicy = "safe-read" | "never";
|
|
8
|
+
export declare const STOREFRONT_OPERATIONS: {
|
|
9
|
+
readonly configGet: "config.get";
|
|
10
|
+
readonly categoriesList: "categories.list";
|
|
11
|
+
readonly productsGet: "products.get";
|
|
12
|
+
readonly productsList: "products.list";
|
|
13
|
+
readonly productsSearch: "products.search";
|
|
14
|
+
readonly productsReviewsList: "products.reviews.list";
|
|
15
|
+
readonly productsReviewsSubmit: "products.reviews.submit";
|
|
16
|
+
readonly productsReviewsEligibility: "products.reviews.eligibility";
|
|
17
|
+
readonly productsReviewsUpload: "products.reviews.upload";
|
|
18
|
+
readonly pagesGet: "pages.get";
|
|
19
|
+
readonly pagesLanding: "pages.landing";
|
|
20
|
+
readonly menusGet: "menus.get";
|
|
21
|
+
readonly collectionsGet: "collections.get";
|
|
22
|
+
readonly shippingRates: "shipping.rates";
|
|
23
|
+
readonly shippingRate: "shipping.rate";
|
|
24
|
+
readonly geoCommunes: "geo.communes";
|
|
25
|
+
readonly cartValidate: "cart.validate";
|
|
26
|
+
readonly pageDataGet: "pageData.get";
|
|
27
|
+
readonly ordersCreate: "orders.create";
|
|
28
|
+
readonly ordersTracking: "orders.tracking";
|
|
29
|
+
readonly ordersStatus: "orders.status";
|
|
30
|
+
readonly ordersAbandonedUpsert: "orders.abandoned.upsert";
|
|
31
|
+
readonly ordersAbandonedConvert: "orders.abandoned.convert";
|
|
32
|
+
readonly ordersOtpSend: "orders.otp.send";
|
|
33
|
+
readonly ordersOtpVerify: "orders.otp.verify";
|
|
34
|
+
};
|
|
35
|
+
export type StorefrontOperation = (typeof STOREFRONT_OPERATIONS)[keyof typeof STOREFRONT_OPERATIONS];
|
|
36
|
+
/**
|
|
37
|
+
* The retry policy per operation, as a contract a caller can read.
|
|
38
|
+
*
|
|
39
|
+
* The transport enforces it by kind: every `safe-read` goes through the
|
|
40
|
+
* caching/deduplicating GET path, which repeats a transport or 5xx failure,
|
|
41
|
+
* while every `never` goes through the POST/PATCH path, which returns a result
|
|
42
|
+
* and never retries (a replayed write is how a shopper ends up with two
|
|
43
|
+
* orders). A test asserts this table covers every public operation and agrees
|
|
44
|
+
* with that classification, so a new operation cannot ship unclassified.
|
|
45
|
+
*/
|
|
46
|
+
export declare const STOREFRONT_RETRY_POLICIES: {
|
|
47
|
+
readonly "cart.validate": "safe-read";
|
|
48
|
+
readonly "categories.list": "safe-read";
|
|
49
|
+
readonly "config.get": "safe-read";
|
|
50
|
+
readonly "geo.communes": "safe-read";
|
|
51
|
+
readonly "orders.abandoned.convert": "never";
|
|
52
|
+
readonly "orders.abandoned.upsert": "never";
|
|
53
|
+
readonly "orders.create": "never";
|
|
54
|
+
readonly "orders.otp.send": "never";
|
|
55
|
+
readonly "orders.otp.verify": "never";
|
|
56
|
+
readonly "orders.tracking": "safe-read";
|
|
57
|
+
readonly "orders.status": "never";
|
|
58
|
+
readonly "pageData.get": "safe-read";
|
|
59
|
+
readonly "pages.get": "safe-read";
|
|
60
|
+
readonly "pages.landing": "safe-read";
|
|
61
|
+
readonly "menus.get": "safe-read";
|
|
62
|
+
readonly "collections.get": "safe-read";
|
|
63
|
+
readonly "products.get": "safe-read";
|
|
64
|
+
readonly "products.list": "safe-read";
|
|
65
|
+
readonly "products.search": "safe-read";
|
|
66
|
+
readonly "products.reviews.list": "safe-read";
|
|
67
|
+
readonly "products.reviews.submit": "never";
|
|
68
|
+
readonly "products.reviews.eligibility": "never";
|
|
69
|
+
readonly "products.reviews.upload": "never";
|
|
70
|
+
readonly "shipping.rate": "safe-read";
|
|
71
|
+
readonly "shipping.rates": "safe-read";
|
|
72
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform operations a storefront calls, and the retry policy each one
|
|
3
|
+
* keeps. Plain values and no schema: browser code reads the operation names
|
|
4
|
+
* too — an OTP route's failure names the operation it was — so this module
|
|
5
|
+
* stays free of anything that would bring a validator into a shopper's page.
|
|
6
|
+
*/
|
|
7
|
+
export const STOREFRONT_OPERATIONS = {
|
|
8
|
+
configGet: "config.get",
|
|
9
|
+
categoriesList: "categories.list",
|
|
10
|
+
productsGet: "products.get",
|
|
11
|
+
productsList: "products.list",
|
|
12
|
+
productsSearch: "products.search",
|
|
13
|
+
productsReviewsList: "products.reviews.list",
|
|
14
|
+
productsReviewsSubmit: "products.reviews.submit",
|
|
15
|
+
productsReviewsEligibility: "products.reviews.eligibility",
|
|
16
|
+
productsReviewsUpload: "products.reviews.upload",
|
|
17
|
+
pagesGet: "pages.get",
|
|
18
|
+
pagesLanding: "pages.landing",
|
|
19
|
+
menusGet: "menus.get",
|
|
20
|
+
collectionsGet: "collections.get",
|
|
21
|
+
shippingRates: "shipping.rates",
|
|
22
|
+
shippingRate: "shipping.rate",
|
|
23
|
+
geoCommunes: "geo.communes",
|
|
24
|
+
cartValidate: "cart.validate",
|
|
25
|
+
pageDataGet: "pageData.get",
|
|
26
|
+
ordersCreate: "orders.create",
|
|
27
|
+
ordersTracking: "orders.tracking",
|
|
28
|
+
ordersStatus: "orders.status",
|
|
29
|
+
ordersAbandonedUpsert: "orders.abandoned.upsert",
|
|
30
|
+
ordersAbandonedConvert: "orders.abandoned.convert",
|
|
31
|
+
ordersOtpSend: "orders.otp.send",
|
|
32
|
+
ordersOtpVerify: "orders.otp.verify",
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The retry policy per operation, as a contract a caller can read.
|
|
36
|
+
*
|
|
37
|
+
* The transport enforces it by kind: every `safe-read` goes through the
|
|
38
|
+
* caching/deduplicating GET path, which repeats a transport or 5xx failure,
|
|
39
|
+
* while every `never` goes through the POST/PATCH path, which returns a result
|
|
40
|
+
* and never retries (a replayed write is how a shopper ends up with two
|
|
41
|
+
* orders). A test asserts this table covers every public operation and agrees
|
|
42
|
+
* with that classification, so a new operation cannot ship unclassified.
|
|
43
|
+
*/
|
|
44
|
+
export const STOREFRONT_RETRY_POLICIES = {
|
|
45
|
+
[STOREFRONT_OPERATIONS.cartValidate]: "safe-read",
|
|
46
|
+
[STOREFRONT_OPERATIONS.categoriesList]: "safe-read",
|
|
47
|
+
[STOREFRONT_OPERATIONS.configGet]: "safe-read",
|
|
48
|
+
[STOREFRONT_OPERATIONS.geoCommunes]: "safe-read",
|
|
49
|
+
[STOREFRONT_OPERATIONS.ordersAbandonedConvert]: "never",
|
|
50
|
+
[STOREFRONT_OPERATIONS.ordersAbandonedUpsert]: "never",
|
|
51
|
+
[STOREFRONT_OPERATIONS.ordersCreate]: "never",
|
|
52
|
+
[STOREFRONT_OPERATIONS.ordersOtpSend]: "never",
|
|
53
|
+
[STOREFRONT_OPERATIONS.ordersOtpVerify]: "never",
|
|
54
|
+
[STOREFRONT_OPERATIONS.ordersTracking]: "safe-read",
|
|
55
|
+
[STOREFRONT_OPERATIONS.ordersStatus]: "never",
|
|
56
|
+
[STOREFRONT_OPERATIONS.pageDataGet]: "safe-read",
|
|
57
|
+
[STOREFRONT_OPERATIONS.pagesGet]: "safe-read",
|
|
58
|
+
[STOREFRONT_OPERATIONS.pagesLanding]: "safe-read",
|
|
59
|
+
[STOREFRONT_OPERATIONS.menusGet]: "safe-read",
|
|
60
|
+
[STOREFRONT_OPERATIONS.collectionsGet]: "safe-read",
|
|
61
|
+
[STOREFRONT_OPERATIONS.productsGet]: "safe-read",
|
|
62
|
+
[STOREFRONT_OPERATIONS.productsList]: "safe-read",
|
|
63
|
+
[STOREFRONT_OPERATIONS.productsSearch]: "safe-read",
|
|
64
|
+
[STOREFRONT_OPERATIONS.productsReviewsList]: "safe-read",
|
|
65
|
+
[STOREFRONT_OPERATIONS.productsReviewsSubmit]: "never",
|
|
66
|
+
[STOREFRONT_OPERATIONS.productsReviewsEligibility]: "never",
|
|
67
|
+
[STOREFRONT_OPERATIONS.productsReviewsUpload]: "never",
|
|
68
|
+
[STOREFRONT_OPERATIONS.shippingRate]: "safe-read",
|
|
69
|
+
[STOREFRONT_OPERATIONS.shippingRates]: "safe-read",
|
|
70
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order number a shopper is given (`ORD-20261023-0042`) and types back to
|
|
3
|
+
* review what they bought or follow their order. The server refuses any other
|
|
4
|
+
* shape with `VALIDATION_FAILED`; a form can say so before asking.
|
|
5
|
+
*/
|
|
6
|
+
/** `ORD-`, the day the order was placed (`YYYYMMDD`), a dash and its digits; any case. */
|
|
7
|
+
export declare const ORDER_NUMBER_PATTERN: RegExp;
|
|
8
|
+
/** Whether `value`, without the spaces around it, has the shape of an order number. */
|
|
9
|
+
export declare function isOrderNumber(value: string): boolean;
|