@aglyn/plugins-commerce 1.0.0-beta.244 → 1.0.0-beta.247

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 (61) hide show
  1. package/README.md +4 -1
  2. package/package.json +13 -13
  3. package/src/lib/components/commerce-console-page.js +10 -0
  4. package/src/lib/components/commerce-console-page.js.map +1 -1
  5. package/src/lib/components/console/local-delivery-settings-card.component.js +10 -13
  6. package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
  7. package/src/lib/components/console/locations-card.component.js +5 -9
  8. package/src/lib/components/console/locations-card.component.js.map +1 -1
  9. package/src/lib/components/console/product-zones.d.ts +7 -1
  10. package/src/lib/components/console/product-zones.js.map +1 -1
  11. package/src/lib/components/console/products-hub-zone.component.d.ts +3 -2
  12. package/src/lib/components/console/products-hub-zone.component.js +9 -1
  13. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  14. package/src/lib/components/console/review-settings-card.component.d.ts +14 -0
  15. package/src/lib/components/console/review-settings-card.component.js +168 -0
  16. package/src/lib/components/console/review-settings-card.component.js.map +1 -0
  17. package/src/lib/components/console/reviews-moderation-card.component.d.ts +14 -0
  18. package/src/lib/components/console/reviews-moderation-card.component.js +85 -11
  19. package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -1
  20. package/src/lib/components/product-reviews.d.ts +29 -1
  21. package/src/lib/components/product-reviews.js +97 -12
  22. package/src/lib/components/product-reviews.js.map +1 -1
  23. package/src/lib/model/commerce.d.ts +11 -2
  24. package/src/lib/model/commerce.js +14 -2
  25. package/src/lib/model/commerce.js.map +1 -1
  26. package/src/lib/model/index.d.ts +1 -0
  27. package/src/lib/model/index.js +1 -0
  28. package/src/lib/model/index.js.map +1 -1
  29. package/src/lib/model/product-reviews.d.ts +124 -0
  30. package/src/lib/model/product-reviews.js +133 -0
  31. package/src/lib/model/product-reviews.js.map +1 -0
  32. package/src/lib/model/store-setup-content.d.ts +145 -0
  33. package/src/lib/model/store-setup-content.js +813 -0
  34. package/src/lib/model/store-setup-content.js.map +1 -0
  35. package/src/lib/server/membership-reset.js +8 -2
  36. package/src/lib/server/membership-reset.js.map +1 -1
  37. package/src/lib/server/order-event-triggers.d.ts +2 -0
  38. package/src/lib/server/order-event-triggers.js +18 -1
  39. package/src/lib/server/order-event-triggers.js.map +1 -1
  40. package/src/lib/server/review-requests.d.ts +102 -0
  41. package/src/lib/server/review-requests.js +346 -0
  42. package/src/lib/server/review-requests.js.map +1 -0
  43. package/src/lib/server/review-tokens.d.ts +82 -0
  44. package/src/lib/server/review-tokens.js +119 -0
  45. package/src/lib/server/review-tokens.js.map +1 -0
  46. package/src/lib/server/reviews.d.ts +36 -3
  47. package/src/lib/server/reviews.js +310 -52
  48. package/src/lib/server/reviews.js.map +1 -1
  49. package/src/lib/server/store-setup-ai-capability.d.ts +61 -0
  50. package/src/lib/server/store-setup-ai-capability.js +343 -0
  51. package/src/lib/server/store-setup-ai-capability.js.map +1 -0
  52. package/src/lib/server/store-setup-ai-writer.console.d.ts +27 -0
  53. package/src/lib/server/store-setup-ai-writer.console.js +44 -0
  54. package/src/lib/server/store-setup-ai-writer.console.js.map +1 -0
  55. package/src/lib/server/store-setup-ai-writer.d.ts +37 -0
  56. package/src/lib/server/store-setup-ai-writer.js +174 -0
  57. package/src/lib/server/store-setup-ai-writer.js.map +1 -0
  58. package/src/lib/server.js +27 -0
  59. package/src/lib/server.js.map +1 -1
  60. package/src/lib/tenant-emails.js +58 -0
  61. package/src/lib/tenant-emails.js.map +1 -1
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { orderEntitlesProduct } from "./commerce-orders.js";
18
+ export const REVIEW_STATUSES = [
19
+ 'pending',
20
+ 'approved',
21
+ 'hidden',
22
+ 'rejected'
23
+ ];
24
+ /**
25
+ * Why a merchant hid or rejected a review. Content reasons only: the FTC's
26
+ * 2024 rule on fake reviews bars suppressing an honest review for being
27
+ * negative, so "I don't like it" is not on the list.
28
+ */ export const REVIEW_MODERATION_REASONS = [
29
+ 'spam',
30
+ 'abusive',
31
+ 'off_topic',
32
+ 'private_information'
33
+ ];
34
+ export const REVIEW_MODERATION_REASON_LABELS = {
35
+ spam: 'Spam or advertising',
36
+ abusive: 'Abusive or hateful',
37
+ off_topic: 'Not about the product',
38
+ private_information: 'Shares private information'
39
+ };
40
+ export function isReviewModerationReason(value) {
41
+ return REVIEW_MODERATION_REASONS.includes(String(value));
42
+ }
43
+ /** The review-request email's delay, in days after fulfilment. */ export const REVIEW_REQUEST_DELAY_DEFAULT_DAYS = 7;
44
+ export const REVIEW_REQUEST_DELAY_MIN_DAYS = 3;
45
+ export const REVIEW_REQUEST_DELAY_MAX_DAYS = 30;
46
+ /** How long a review link from the email works. */ export const REVIEW_TOKEN_TTL_MS = 60 * 24 * 60 * 60 * 1000;
47
+ const DAY_MS = 24 * 60 * 60 * 1000;
48
+ /**
49
+ * Reads `hosts/{hostId}/settings/store.reviews`. Defensive in the safe
50
+ * direction: anything but an explicit `'anyone'` is `verified`, so every
51
+ * existing store (no map at all) is verified-only, and only an explicit
52
+ * `false` turns the email off — the transaction-email convention (AGL-3600).
53
+ */ export function reviewSettingsFrom(storeSettings) {
54
+ const raw = storeSettings && typeof storeSettings === 'object' ? storeSettings['reviews'] : undefined;
55
+ const days = Math.round(Number(raw == null ? void 0 : raw['requestDelayDays']));
56
+ return {
57
+ policy: (raw == null ? void 0 : raw['policy']) === 'anyone' ? 'anyone' : 'verified',
58
+ requestEmail: (raw == null ? void 0 : raw['requestEmail']) !== false,
59
+ requestDelayDays: Number.isFinite(days) ? Math.min(REVIEW_REQUEST_DELAY_MAX_DAYS, Math.max(REVIEW_REQUEST_DELAY_MIN_DAYS, days)) : REVIEW_REQUEST_DELAY_DEFAULT_DAYS
60
+ };
61
+ }
62
+ /**
63
+ * When an order fulfilled at `fulfilledAtMs` is owed its review request.
64
+ * The scheduler stamps the EARLIEST possible moment (the 3-day minimum) and
65
+ * the sweep re-reads the store's current delay, so a setting changed after
66
+ * fulfilment still applies.
67
+ */ export function reviewRequestDueAtMs(fulfilledAtMs, delayDays) {
68
+ return fulfilledAtMs + delayDays * DAY_MS;
69
+ }
70
+ export function reviewRequestEarliestDueAtMs(fulfilledAtMs) {
71
+ return reviewRequestDueAtMs(fulfilledAtMs, REVIEW_REQUEST_DELAY_MIN_DAYS);
72
+ }
73
+ /**
74
+ * Whether an order's buyer is asked for a review at all. A register sale is
75
+ * not (the address was given for a receipt at the counter), nor is an order
76
+ * sold on an outside marketplace (its reviews belong there).
77
+ */ export function orderTakesReviewRequest(order) {
78
+ if (order.channel === 'pos' || order.channel === 'marketplace') return false;
79
+ return ![
80
+ 'pending',
81
+ 'cancelled',
82
+ 'refunded'
83
+ ].includes(String(order.status));
84
+ }
85
+ /**
86
+ * The products a review request lists: each distinct product on the order
87
+ * that the order still entitles — a line refunded by name is never asked
88
+ * about, and neither is a fully refunded or cancelled order.
89
+ */ export function reviewableProductIds(order) {
90
+ var _order_lineItems;
91
+ const ids = [];
92
+ for (const line of (_order_lineItems = order.lineItems) != null ? _order_lineItems : []){
93
+ var _ref;
94
+ const productId = String((_ref = line == null ? void 0 : line.productId) != null ? _ref : '');
95
+ if (!productId || ids.includes(productId)) continue;
96
+ if (orderEntitlesProduct(order, productId)) ids.push(productId);
97
+ }
98
+ return ids;
99
+ }
100
+ /** Error codes the reviews route answers with, for the storefront to word. */ export const REVIEW_ERROR_CODES = {
101
+ verifiedPurchaseRequired: 'verified_purchase_required',
102
+ tokenInvalid: 'review_token_invalid',
103
+ tokenUsed: 'review_token_used',
104
+ tokenExpired: 'review_token_expired',
105
+ rateLimited: 'review_rate_limited',
106
+ notEnabled: 'reviews_not_enabled',
107
+ memberSuspended: 'member_suspended',
108
+ alreadyReviewed: 'already_reviewed'
109
+ };
110
+ /**
111
+ * The eligibility answer from what the server established. Pure, so the
112
+ * storefront's three states are specified in one place.
113
+ */ export function reviewEligibility(input) {
114
+ const verifiedVia = input.tokenVerified ? 'token' : input.memberVerified ? 'member' : null;
115
+ const canReview = verifiedVia !== null || input.policy === 'anyone';
116
+ return _extends({
117
+ policy: input.policy,
118
+ signedIn: input.signedIn,
119
+ verifiedVia,
120
+ canReview,
121
+ // A signed-in member is never asked for an email: the account has one.
122
+ needsEmail: canReview && verifiedVia === null && !input.signedIn
123
+ }, input.tokenProblem ? {
124
+ tokenProblem: input.tokenProblem
125
+ } : {});
126
+ }
127
+ /** The status a new review is saved with. */ export function initialReviewStatus(verified) {
128
+ // Verified reviews publish at once (the FTC's 2024 rule); unverified ones,
129
+ // possible only under 'anyone', wait for the merchant.
130
+ return verified ? 'approved' : 'pending';
131
+ }
132
+
133
+ //# sourceMappingURL=product-reviews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/product-reviews.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { HostOrder } from './commerce-orders'\nimport { orderEntitlesProduct } from './commerce-orders'\n\n/**\n * Storefront product reviews, verified buyers by default (AGL-3736).\n *\n * The industry standard (the Shopify review apps, Squarespace, Etsy): a review\n * carries \"Verified buyer\" only when the platform KNOWS the reviewer bought\n * the product — a signed-in customer whose orders hold it, or a one-time link\n * from the review-request email. A typed email proves nothing (anybody can\n * type anybody's address), so it no longer verifies anything.\n *\n * Pure: read by the storefront block, the console and the server.\n */\n\n/** Who may review a product on this store. */\nexport type ReviewPolicy = 'verified' | 'anyone'\n\n/** Every status a review document can hold. */\nexport type ReviewStatus = 'pending' | 'approved' | 'hidden' | 'rejected'\n\nexport const REVIEW_STATUSES: readonly ReviewStatus[] = [\n 'pending',\n 'approved',\n 'hidden',\n 'rejected',\n]\n\n/**\n * Why a merchant hid or rejected a review. Content reasons only: the FTC's\n * 2024 rule on fake reviews bars suppressing an honest review for being\n * negative, so \"I don't like it\" is not on the list.\n */\nexport const REVIEW_MODERATION_REASONS = [\n 'spam',\n 'abusive',\n 'off_topic',\n 'private_information',\n] as const\n\nexport type ReviewModerationReason = (typeof REVIEW_MODERATION_REASONS)[number]\n\nexport const REVIEW_MODERATION_REASON_LABELS: Readonly<\n Record<ReviewModerationReason, string>\n> = {\n spam: 'Spam or advertising',\n abusive: 'Abusive or hateful',\n off_topic: 'Not about the product',\n private_information: 'Shares private information',\n}\n\nexport function isReviewModerationReason(value: unknown): value is ReviewModerationReason {\n return (REVIEW_MODERATION_REASONS as readonly string[]).includes(String(value))\n}\n\n/** The review-request email's delay, in days after fulfilment. */\nexport const REVIEW_REQUEST_DELAY_DEFAULT_DAYS = 7\nexport const REVIEW_REQUEST_DELAY_MIN_DAYS = 3\nexport const REVIEW_REQUEST_DELAY_MAX_DAYS = 30\n\n/** How long a review link from the email works. */\nexport const REVIEW_TOKEN_TTL_MS = 60 * 24 * 60 * 60 * 1000\n\nconst DAY_MS = 24 * 60 * 60 * 1000\n\n/** A store's review settings, typed and bounded. */\nexport interface ReviewSettings {\n policy: ReviewPolicy\n /** Whether the review-request email goes out. On unless switched off. */\n requestEmail: boolean\n /** Days after fulfilment, 3–30. */\n requestDelayDays: number\n}\n\n/**\n * Reads `hosts/{hostId}/settings/store.reviews`. Defensive in the safe\n * direction: anything but an explicit `'anyone'` is `verified`, so every\n * existing store (no map at all) is verified-only, and only an explicit\n * `false` turns the email off — the transaction-email convention (AGL-3600).\n */\nexport function reviewSettingsFrom(storeSettings: unknown): ReviewSettings {\n const raw =\n storeSettings && typeof storeSettings === 'object'\n ? ((storeSettings as Record<string, unknown>)['reviews'] as\n | Record<string, unknown>\n | undefined)\n : undefined\n const days = Math.round(Number(raw?.['requestDelayDays']))\n return {\n policy: raw?.['policy'] === 'anyone' ? 'anyone' : 'verified',\n requestEmail: raw?.['requestEmail'] !== false,\n requestDelayDays: Number.isFinite(days)\n ? Math.min(\n REVIEW_REQUEST_DELAY_MAX_DAYS,\n Math.max(REVIEW_REQUEST_DELAY_MIN_DAYS, days),\n )\n : REVIEW_REQUEST_DELAY_DEFAULT_DAYS,\n }\n}\n\n/**\n * When an order fulfilled at `fulfilledAtMs` is owed its review request.\n * The scheduler stamps the EARLIEST possible moment (the 3-day minimum) and\n * the sweep re-reads the store's current delay, so a setting changed after\n * fulfilment still applies.\n */\nexport function reviewRequestDueAtMs(fulfilledAtMs: number, delayDays: number): number {\n return fulfilledAtMs + delayDays * DAY_MS\n}\n\nexport function reviewRequestEarliestDueAtMs(fulfilledAtMs: number): number {\n return reviewRequestDueAtMs(fulfilledAtMs, REVIEW_REQUEST_DELAY_MIN_DAYS)\n}\n\n/**\n * Whether an order's buyer is asked for a review at all. A register sale is\n * not (the address was given for a receipt at the counter), nor is an order\n * sold on an outside marketplace (its reviews belong there).\n */\nexport function orderTakesReviewRequest(\n order: Pick<HostOrder, 'channel' | 'status'>,\n): boolean {\n if (order.channel === 'pos' || order.channel === 'marketplace') return false\n return !['pending', 'cancelled', 'refunded'].includes(String(order.status))\n}\n\n/**\n * The products a review request lists: each distinct product on the order\n * that the order still entitles — a line refunded by name is never asked\n * about, and neither is a fully refunded or cancelled order.\n */\nexport function reviewableProductIds(order: Partial<HostOrder>): string[] {\n const ids: string[] = []\n for (const line of order.lineItems ?? []) {\n const productId = String(line?.productId ?? '')\n if (!productId || ids.includes(productId)) continue\n if (orderEntitlesProduct(order, productId)) ids.push(productId)\n }\n return ids\n}\n\n/** Error codes the reviews route answers with, for the storefront to word. */\nexport const REVIEW_ERROR_CODES = {\n verifiedPurchaseRequired: 'verified_purchase_required',\n tokenInvalid: 'review_token_invalid',\n tokenUsed: 'review_token_used',\n tokenExpired: 'review_token_expired',\n rateLimited: 'review_rate_limited',\n notEnabled: 'reviews_not_enabled',\n memberSuspended: 'member_suspended',\n alreadyReviewed: 'already_reviewed',\n} as const\n\nexport type ReviewErrorCode =\n (typeof REVIEW_ERROR_CODES)[keyof typeof REVIEW_ERROR_CODES]\n\n/** What the storefront form is told before it draws itself. */\nexport interface ReviewEligibility {\n /** The store's plan includes reviews. */\n enabled?: boolean\n policy: ReviewPolicy\n /** A site member is signed in on this store. */\n signedIn: boolean\n /** The review would carry \"Verified buyer\", and through what. */\n verifiedVia: 'member' | 'token' | null\n /** The form may be submitted at all. */\n canReview: boolean\n /** The form must ask for a name and an email (an unverified visitor). */\n needsEmail: boolean\n /** Why a presented review link does not work, when it does not. */\n tokenProblem?: ReviewErrorCode\n}\n\n/**\n * The eligibility answer from what the server established. Pure, so the\n * storefront's three states are specified in one place.\n */\nexport function reviewEligibility(input: {\n policy: ReviewPolicy\n signedIn: boolean\n memberVerified: boolean\n tokenVerified: boolean\n tokenProblem?: ReviewErrorCode\n}): ReviewEligibility {\n const verifiedVia = input.tokenVerified\n ? 'token'\n : input.memberVerified\n ? 'member'\n : null\n const canReview = verifiedVia !== null || input.policy === 'anyone'\n return {\n policy: input.policy,\n signedIn: input.signedIn,\n verifiedVia,\n canReview,\n // A signed-in member is never asked for an email: the account has one.\n needsEmail: canReview && verifiedVia === null && !input.signedIn,\n ...(input.tokenProblem ? { tokenProblem: input.tokenProblem } : {}),\n }\n}\n\n/** The status a new review is saved with. */\nexport function initialReviewStatus(verified: boolean): ReviewStatus {\n // Verified reviews publish at once (the FTC's 2024 rule); unverified ones,\n // possible only under 'anyone', wait for the merchant.\n return verified ? 'approved' : 'pending'\n}\n"],"names":["orderEntitlesProduct","REVIEW_STATUSES","REVIEW_MODERATION_REASONS","REVIEW_MODERATION_REASON_LABELS","spam","abusive","off_topic","private_information","isReviewModerationReason","value","includes","String","REVIEW_REQUEST_DELAY_DEFAULT_DAYS","REVIEW_REQUEST_DELAY_MIN_DAYS","REVIEW_REQUEST_DELAY_MAX_DAYS","REVIEW_TOKEN_TTL_MS","DAY_MS","reviewSettingsFrom","storeSettings","raw","undefined","days","Math","round","Number","policy","requestEmail","requestDelayDays","isFinite","min","max","reviewRequestDueAtMs","fulfilledAtMs","delayDays","reviewRequestEarliestDueAtMs","orderTakesReviewRequest","order","channel","status","reviewableProductIds","ids","line","lineItems","productId","push","REVIEW_ERROR_CODES","verifiedPurchaseRequired","tokenInvalid","tokenUsed","tokenExpired","rateLimited","notEnabled","memberSuspended","alreadyReviewed","reviewEligibility","input","verifiedVia","tokenVerified","memberVerified","canReview","signedIn","needsEmail","tokenProblem","initialReviewStatus","verified"],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type ShippingTier } from './commerce-shipping';
18
+ import type { PickupLocationSettings } from './order-local-fulfillment';
19
+ /** The resource the writer is registered under; the commerce record route `store-settings` links it. */
20
+ export declare const STORE_SETUP_RESOURCE = "store-settings";
21
+ export declare const STORE_SETUP_SECTIONS: readonly ["shipping", "pickup", "local-delivery", "tax"];
22
+ export type StoreSetupSection = (typeof STORE_SETUP_SECTIONS)[number];
23
+ /** The rate kinds an AI change may set: every kind of the Shipping card except carrier rates. */
24
+ export declare const STORE_SHIPPING_AI_KINDS: readonly ["flat", "free_over", "price_tiers", "weight_tiers"];
25
+ export type StoreShippingAiKind = (typeof STORE_SHIPPING_AI_KINDS)[number];
26
+ export declare const STORE_SHIPPING_ACTIONS: readonly ["set", "remove"];
27
+ export declare const STORE_TAX_MODES: readonly ["manual", "stripe", "none"];
28
+ export declare const STORE_SETUP_NAME_MAX = 80;
29
+ export declare const STORE_SETUP_COUNTRIES_MAX = 60;
30
+ export declare const STORE_SHIPPING_TIERS_MAX = 10;
31
+ export declare const STORE_SETUP_LOCATION_MAX = 80;
32
+ export declare const STORE_TAX_LABEL_MAX = 60;
33
+ /** Grams; the heaviest tier a weight table may name (1,000 kg). */
34
+ export declare const STORE_SHIPPING_MAX_GRAMS = 1000000;
35
+ /** Two weeks, the longest preparation or lead time the cards keep. */
36
+ export declare const STORE_SETUP_MAX_MINUTES: number;
37
+ export interface StoreShippingChange {
38
+ section: 'shipping';
39
+ action: 'set' | 'remove';
40
+ zoneName: string;
41
+ countries?: string[];
42
+ rateName?: string;
43
+ kind?: StoreShippingAiKind;
44
+ amountCents?: number;
45
+ freeOverCents?: number;
46
+ tiers?: ShippingTier[];
47
+ }
48
+ export interface StorePickupChange {
49
+ section: 'pickup';
50
+ /** An inventory location's name, city or address; absent = the store-wide switch. */
51
+ location?: string;
52
+ enabled: boolean;
53
+ hours?: string;
54
+ instructions?: string;
55
+ readyWithinMinutes?: number;
56
+ }
57
+ export interface StoreLocalDeliveryChange {
58
+ section: 'local-delivery';
59
+ enabled: boolean;
60
+ country?: string;
61
+ location?: string;
62
+ zoneName?: string;
63
+ postcodes?: string[];
64
+ feeCents?: number;
65
+ minimumCents?: number;
66
+ freeOverCents?: number;
67
+ removeZone?: boolean;
68
+ windows?: string;
69
+ leadTimeMinutes?: number;
70
+ daysAhead?: number;
71
+ instructions?: string;
72
+ }
73
+ export interface StoreTaxChange {
74
+ section: 'tax';
75
+ mode?: 'manual' | 'stripe' | 'none';
76
+ pricesIncludeTax?: boolean;
77
+ originCountry?: string;
78
+ originState?: string;
79
+ rateCountry?: string;
80
+ rateState?: string;
81
+ ratePct?: number;
82
+ rateLabel?: string;
83
+ removeRate?: boolean;
84
+ }
85
+ export type StoreSetupChange = StoreShippingChange | StorePickupChange | StoreLocalDeliveryChange | StoreTaxChange;
86
+ export type StoreSetupRead = {
87
+ ok: true;
88
+ value: StoreSetupChange;
89
+ } | {
90
+ ok: false;
91
+ problems: string[];
92
+ };
93
+ type Raw = Readonly<Record<string, unknown>>;
94
+ /** Reads a change from content; the problems name what is wrong, in words a person reads. */
95
+ export declare function readStoreSetupChange(content: Raw): StoreSetupRead;
96
+ /** An inventory location as the writer reads it. */
97
+ export interface StoreSetupLocation {
98
+ id: string;
99
+ name?: string;
100
+ address?: string;
101
+ city?: string;
102
+ pickup?: unknown;
103
+ }
104
+ /** What the change writes: one map of the settings document, or one location's pickup. */
105
+ export type StoreSetupWrite = {
106
+ target: 'settings';
107
+ field: 'shipping' | 'localDelivery' | 'tax';
108
+ value: Record<string, unknown>;
109
+ } | {
110
+ target: 'location';
111
+ locationId: string;
112
+ value: PickupLocationSettings;
113
+ };
114
+ export type StoreSetupApplied = {
115
+ ok: true;
116
+ /** `null` when the settings already say what the change asks: nothing to write. */
117
+ write: StoreSetupWrite | null;
118
+ /** One sentence on what the store now does. */
119
+ summary: string;
120
+ /** What the person should know, in the console's words. */
121
+ warnings: string[];
122
+ } | {
123
+ ok: false;
124
+ status: 400 | 404 | 409;
125
+ error: string;
126
+ };
127
+ export interface StoreSetupState {
128
+ /** `hosts/{hostId}/settings/store`, or `{}`. */
129
+ settings: Readonly<Record<string, unknown>>;
130
+ locations: readonly StoreSetupLocation[];
131
+ }
132
+ /** Ids for what a change creates, derived from the request so a retry makes the same ones. */
133
+ export interface StoreSetupIds {
134
+ zoneId: string;
135
+ rateId: string;
136
+ }
137
+ /** Key-order-blind equality over plain JSON values. */
138
+ export declare function sameSetting(a: unknown, b: unknown): boolean;
139
+ /** The store's locations a phrase names: by exact name first, then by city or address. */
140
+ export declare function matchStoreLocations(locations: readonly StoreSetupLocation[], phrase: string): StoreSetupLocation[];
141
+ /** The settings after a change, what to write, and what the person is told. */
142
+ export declare function applyStoreSetupChange(state: StoreSetupState, change: StoreSetupChange, ids: StoreSetupIds): StoreSetupApplied;
143
+ /** What a reader is told about a store's setup, for the writer's `read`. */
144
+ export declare function storeSetupFacts(settings: Readonly<Record<string, unknown>>): Record<string, unknown>;
145
+ export {};