@asteby/storefront 0.17.0 → 0.18.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/README.md +24 -0
- package/dist/{account-settings-DNcJKOQl.d.ts → account-settings-Cw3PamUA.d.ts} +98 -1
- package/dist/{account-settings-H6zuI7iG.d.cts → account-settings-DAzmXHIn.d.cts} +98 -1
- package/dist/{chunk-IPUAZ3LQ.js → chunk-5BJRZIS3.js} +196 -3
- package/dist/chunk-5BJRZIS3.js.map +1 -0
- package/dist/cli.js +25 -2
- package/dist/fiscal-document.d.cts +2 -2
- package/dist/fiscal-document.d.ts +2 -2
- package/dist/index.cjs +232 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -3
- package/dist/index.d.ts +104 -3
- package/dist/index.js +26 -3
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +247 -1
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +18 -2
- package/dist/mock.d.ts +18 -2
- package/dist/mock.js +143 -3
- package/dist/mock.js.map +1 -1
- package/dist/mockup.d.cts +1 -1
- package/dist/mockup.d.ts +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-IPUAZ3LQ.js.map +0 -1
package/dist/mock.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a6 as IdentityRulesV1, bg as RegionFiscalId, a4 as IdentityDocumentSpec, be as Region, bx as Shop, a$ as Product, y as Collection,
|
|
1
|
+
import { bM as SubjectKind, a6 as IdentityRulesV1, bg as RegionFiscalId, a4 as IdentityDocumentSpec, be as Region, bx as Shop, a$ as Product, y as Collection, bP as Theme, aG as Page, ap as Menu, c as Article, C as Cart, a as Address, af as InvoiceRequest, B as Customer, $ as FiscalProfile, bG as Subject, aw as Order, aJ as PaymentAttemptResult, aY as PendingPayment, ah as InvoiceStage, D as CustomerCapabilities, b6 as PromotionAnnouncement, bw as ShippingRate, aQ as PaymentMethod } from './account-settings-DAzmXHIn.cjs';
|
|
2
2
|
import './geo.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,6 +16,8 @@ declare const MOCK_POSTAL_CENTROIDS: Record<string, {
|
|
|
16
16
|
lng: number;
|
|
17
17
|
}>;
|
|
18
18
|
|
|
19
|
+
declare const MOCK_SUBJECT_KINDS: SubjectKind[];
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Region fixture of the mock: Mexico exactly as `GET /storefront/region` publishes it (see `region-mx.seed.ts`),
|
|
21
23
|
* identity document `MX:csf` included, so a storefront can be built and tested without a server. It lives here,
|
|
@@ -89,6 +91,11 @@ type MockStorefrontOptions = {
|
|
|
89
91
|
* carriers, ETAs and quote deadlines). `expires_at` missing: 24 h from the request. Free shipping still applies.
|
|
90
92
|
*/
|
|
91
93
|
shippingRates?: ShippingRate[];
|
|
94
|
+
/**
|
|
95
|
+
* `fixed_when_free`: past `policies.free_shipping_min` `delivery-options` serves ONLY the shop's free rate and flags
|
|
96
|
+
* `free` / `fixed` (what the `storefront_cart` addon does with the setting of the same name). Default: the usual list.
|
|
97
|
+
*/
|
|
98
|
+
shippingQuoteMode?: 'carriers' | 'fixed_when_free';
|
|
92
99
|
/** Methods served by `payment-methods` (default one builtin `mock`). */
|
|
93
100
|
paymentMethods?: PaymentMethod[];
|
|
94
101
|
/**
|
|
@@ -96,6 +103,11 @@ type MockStorefrontOptions = {
|
|
|
96
103
|
* organization has no region, the route answers `404` and invoice requests are not validated against a region.
|
|
97
104
|
*/
|
|
98
105
|
region?: Region | null;
|
|
106
|
+
/**
|
|
107
|
+
* The kinds of buyer item («My items») the store declares, served by `GET /subject-kinds` and enforced by
|
|
108
|
+
* `customer.subjects`. Default none: no such section, as in a store that declares nothing (`MOCK_SUBJECT_KINDS` has two).
|
|
109
|
+
*/
|
|
110
|
+
subjectKinds?: SubjectKind[];
|
|
99
111
|
};
|
|
100
112
|
type CartRecord = {
|
|
101
113
|
id: string;
|
|
@@ -128,6 +140,10 @@ type MockState = {
|
|
|
128
140
|
addresses: Map<string, Address[]>;
|
|
129
141
|
/** Saved fiscal profiles by customer id (contract 1.9). */
|
|
130
142
|
fiscalProfiles: Map<string, FiscalProfile[]>;
|
|
143
|
+
/** Saved items by customer id (contract 1.9); `private` holds the values the API only returns masked. */
|
|
144
|
+
subjects: Map<string, (Subject & {
|
|
145
|
+
private: Record<string, string>;
|
|
146
|
+
})[]>;
|
|
131
147
|
orders: (Order & {
|
|
132
148
|
customer_id: string | null;
|
|
133
149
|
})[];
|
|
@@ -191,4 +207,4 @@ declare function createMockStorefront(options?: MockStorefrontOptions): {
|
|
|
191
207
|
};
|
|
192
208
|
type MockStorefront = ReturnType<typeof createMockStorefront>;
|
|
193
209
|
|
|
194
|
-
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, type MockImageUrl, type MockState, type MockStorefront, type MockStorefrontOptions, createMockStorefront };
|
|
210
|
+
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, MOCK_SUBJECT_KINDS, type MockImageUrl, type MockState, type MockStorefront, type MockStorefrontOptions, createMockStorefront };
|
package/dist/mock.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a6 as IdentityRulesV1, bg as RegionFiscalId, a4 as IdentityDocumentSpec, be as Region, bx as Shop, a$ as Product, y as Collection,
|
|
1
|
+
import { bM as SubjectKind, a6 as IdentityRulesV1, bg as RegionFiscalId, a4 as IdentityDocumentSpec, be as Region, bx as Shop, a$ as Product, y as Collection, bP as Theme, aG as Page, ap as Menu, c as Article, C as Cart, a as Address, af as InvoiceRequest, B as Customer, $ as FiscalProfile, bG as Subject, aw as Order, aJ as PaymentAttemptResult, aY as PendingPayment, ah as InvoiceStage, D as CustomerCapabilities, b6 as PromotionAnnouncement, bw as ShippingRate, aQ as PaymentMethod } from './account-settings-Cw3PamUA.js';
|
|
2
2
|
import './geo.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,6 +16,8 @@ declare const MOCK_POSTAL_CENTROIDS: Record<string, {
|
|
|
16
16
|
lng: number;
|
|
17
17
|
}>;
|
|
18
18
|
|
|
19
|
+
declare const MOCK_SUBJECT_KINDS: SubjectKind[];
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Region fixture of the mock: Mexico exactly as `GET /storefront/region` publishes it (see `region-mx.seed.ts`),
|
|
21
23
|
* identity document `MX:csf` included, so a storefront can be built and tested without a server. It lives here,
|
|
@@ -89,6 +91,11 @@ type MockStorefrontOptions = {
|
|
|
89
91
|
* carriers, ETAs and quote deadlines). `expires_at` missing: 24 h from the request. Free shipping still applies.
|
|
90
92
|
*/
|
|
91
93
|
shippingRates?: ShippingRate[];
|
|
94
|
+
/**
|
|
95
|
+
* `fixed_when_free`: past `policies.free_shipping_min` `delivery-options` serves ONLY the shop's free rate and flags
|
|
96
|
+
* `free` / `fixed` (what the `storefront_cart` addon does with the setting of the same name). Default: the usual list.
|
|
97
|
+
*/
|
|
98
|
+
shippingQuoteMode?: 'carriers' | 'fixed_when_free';
|
|
92
99
|
/** Methods served by `payment-methods` (default one builtin `mock`). */
|
|
93
100
|
paymentMethods?: PaymentMethod[];
|
|
94
101
|
/**
|
|
@@ -96,6 +103,11 @@ type MockStorefrontOptions = {
|
|
|
96
103
|
* organization has no region, the route answers `404` and invoice requests are not validated against a region.
|
|
97
104
|
*/
|
|
98
105
|
region?: Region | null;
|
|
106
|
+
/**
|
|
107
|
+
* The kinds of buyer item («My items») the store declares, served by `GET /subject-kinds` and enforced by
|
|
108
|
+
* `customer.subjects`. Default none: no such section, as in a store that declares nothing (`MOCK_SUBJECT_KINDS` has two).
|
|
109
|
+
*/
|
|
110
|
+
subjectKinds?: SubjectKind[];
|
|
99
111
|
};
|
|
100
112
|
type CartRecord = {
|
|
101
113
|
id: string;
|
|
@@ -128,6 +140,10 @@ type MockState = {
|
|
|
128
140
|
addresses: Map<string, Address[]>;
|
|
129
141
|
/** Saved fiscal profiles by customer id (contract 1.9). */
|
|
130
142
|
fiscalProfiles: Map<string, FiscalProfile[]>;
|
|
143
|
+
/** Saved items by customer id (contract 1.9); `private` holds the values the API only returns masked. */
|
|
144
|
+
subjects: Map<string, (Subject & {
|
|
145
|
+
private: Record<string, string>;
|
|
146
|
+
})[]>;
|
|
131
147
|
orders: (Order & {
|
|
132
148
|
customer_id: string | null;
|
|
133
149
|
})[];
|
|
@@ -191,4 +207,4 @@ declare function createMockStorefront(options?: MockStorefrontOptions): {
|
|
|
191
207
|
};
|
|
192
208
|
type MockStorefront = ReturnType<typeof createMockStorefront>;
|
|
193
209
|
|
|
194
|
-
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, type MockImageUrl, type MockState, type MockStorefront, type MockStorefrontOptions, createMockStorefront };
|
|
210
|
+
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, MOCK_SUBJECT_KINDS, type MockImageUrl, type MockState, type MockStorefront, type MockStorefrontOptions, createMockStorefront };
|
package/dist/mock.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveAvailability, isTestShipment, sortTrackingEvents, orderProgress, nextAction, orderShipments, shipmentsStatus } from './chunk-
|
|
1
|
+
import { resolveAvailability, isMasked, isTestShipment, sortTrackingEvents, orderProgress, nextAction, orderShipments, shipmentsStatus, PRIVATE_MASK, resolveSubject } from './chunk-5BJRZIS3.js';
|
|
2
2
|
import { isValidPoint, rankByDistance } from './chunk-45R47HYO.js';
|
|
3
3
|
|
|
4
4
|
// src/mock/account-seed.ts
|
|
@@ -1511,6 +1511,55 @@ function buildCustomers() {
|
|
|
1511
1511
|
];
|
|
1512
1512
|
}
|
|
1513
1513
|
|
|
1514
|
+
// src/mock/subjects.ts
|
|
1515
|
+
var MOCK_SUBJECT_KINDS = [
|
|
1516
|
+
{
|
|
1517
|
+
key: "vehicle",
|
|
1518
|
+
label: "Veh\xEDculo",
|
|
1519
|
+
label_plural: "Mi garage",
|
|
1520
|
+
max_per_account: 3,
|
|
1521
|
+
attach_to_order: true,
|
|
1522
|
+
fit_key_template: "{make}/{model}/{year}/{engine}",
|
|
1523
|
+
fields: [
|
|
1524
|
+
{ key: "make", label: "Marca", type: "select", source: "catalog", required: true },
|
|
1525
|
+
{ key: "model", label: "Modelo", type: "select", source: "catalog", depends_on: "make", required: true },
|
|
1526
|
+
{ key: "year", label: "A\xF1o", type: "year", source: "catalog", depends_on: "model", required: true },
|
|
1527
|
+
{ key: "engine", label: "Versi\xF3n / motor", type: "select", source: "catalog", depends_on: "year" },
|
|
1528
|
+
{ key: "plate", label: "Placas (opcional)", type: "text", max: 12, private: true }
|
|
1529
|
+
],
|
|
1530
|
+
catalog: {
|
|
1531
|
+
version: "mock-1",
|
|
1532
|
+
tree: {
|
|
1533
|
+
nissan: {
|
|
1534
|
+
label: "Nissan",
|
|
1535
|
+
children: {
|
|
1536
|
+
np300: {
|
|
1537
|
+
label: "NP300 Frontier",
|
|
1538
|
+
from: 2008,
|
|
1539
|
+
to: 2022,
|
|
1540
|
+
children: { "2.5": { label: "2.5 L", ranges: [[2010, 2015]] }, "2.4": { label: "2.4 L" } }
|
|
1541
|
+
},
|
|
1542
|
+
versa: { label: "Versa", from: 2012, to: 2024, children: { "1.6": { label: "1.6 L" } } }
|
|
1543
|
+
}
|
|
1544
|
+
},
|
|
1545
|
+
ford: { label: "Ford", children: { f150: { label: "F-150", from: 2e3, to: 2020, children: { "5.0": { label: "5.0 L" } } } } }
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
key: "size",
|
|
1551
|
+
label: "Talla",
|
|
1552
|
+
label_plural: "Mis tallas",
|
|
1553
|
+
max_per_account: 5,
|
|
1554
|
+
fit_key_template: "{garment}/{value}",
|
|
1555
|
+
fields: [
|
|
1556
|
+
{ key: "garment", label: "Prenda", type: "text", required: true },
|
|
1557
|
+
{ key: "value", label: "Talla", type: "text", required: true, max: 8 }
|
|
1558
|
+
],
|
|
1559
|
+
catalog: null
|
|
1560
|
+
}
|
|
1561
|
+
];
|
|
1562
|
+
|
|
1514
1563
|
// src/mock/index.ts
|
|
1515
1564
|
var HttpError = class extends Error {
|
|
1516
1565
|
constructor(status, code, message, field, details, requiredScopes) {
|
|
@@ -1588,6 +1637,7 @@ function createMockStorefront(options = {}) {
|
|
|
1588
1637
|
notifications: { channels: ["email"] },
|
|
1589
1638
|
data_export: true,
|
|
1590
1639
|
account_delete: true,
|
|
1640
|
+
...(options.subjectKinds?.length ?? 0) > 0 ? { subjects: { kinds: options.subjectKinds.map((k) => k.key), share_with_workshop: false } } : {},
|
|
1591
1641
|
...options.customerCapabilities
|
|
1592
1642
|
};
|
|
1593
1643
|
const missingScopes = options.missingScopes ?? [];
|
|
@@ -1619,6 +1669,7 @@ function createMockStorefront(options = {}) {
|
|
|
1619
1669
|
customers: buildCustomers(),
|
|
1620
1670
|
addresses: /* @__PURE__ */ new Map(),
|
|
1621
1671
|
fiscalProfiles: /* @__PURE__ */ new Map(),
|
|
1672
|
+
subjects: /* @__PURE__ */ new Map(),
|
|
1622
1673
|
orders: [
|
|
1623
1674
|
{
|
|
1624
1675
|
id: "ord_seed_1",
|
|
@@ -1724,6 +1775,9 @@ function createMockStorefront(options = {}) {
|
|
|
1724
1775
|
if (list.length > 100) throw new HttpError(400, "validation", "At most 100 handles", "handles");
|
|
1725
1776
|
items = items.filter((p) => list.includes(p.handle));
|
|
1726
1777
|
}
|
|
1778
|
+
const tagsAny = (search.get("tags_any") ?? "").split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
|
|
1779
|
+
if (tagsAny.length > 5 || tagsAny.some((t) => t.length > 120)) throw new HttpError(400, "validation", "tags_any takes at most 5 tags of 120 characters", "tags_any");
|
|
1780
|
+
if (tagsAny.length > 0) items = items.filter((p) => p.tags.some((t) => tagsAny.includes(t.toLowerCase())));
|
|
1727
1781
|
const base = items;
|
|
1728
1782
|
if (search.get("available") === "true") items = items.filter((p) => p.available_for_sale);
|
|
1729
1783
|
const priceMin = search.get("price_min");
|
|
@@ -1851,6 +1905,7 @@ function createMockStorefront(options = {}) {
|
|
|
1851
1905
|
const expires = new Date(Date.now() + 24 * 36e5).toISOString();
|
|
1852
1906
|
return options.shippingRates.map((r) => ({ ...r, expires_at: r.expires_at ?? expires, cost: free ? fromCents(0, r.cost.currency_code) : r.cost }));
|
|
1853
1907
|
}
|
|
1908
|
+
if (free && options.shippingQuoteMode === "fixed_when_free") return [{ id: "flat", title: "Env\xEDo gratis", cost: fromCents(0, currency), eta: "2 a 5 d\xEDas h\xE1biles" }];
|
|
1854
1909
|
return [
|
|
1855
1910
|
{ id: "flat", title: free ? "Env\xEDo est\xE1ndar (gratis)" : "Env\xEDo est\xE1ndar", cost: fromCents(free ? 0 : flatShippingCents, currency), eta: "2 a 5 d\xEDas h\xE1biles" },
|
|
1856
1911
|
{ id: "express", title: "Env\xEDo expr\xE9s", cost: fromCents(flatShippingCents + 1e4, currency), eta: "1 a 2 d\xEDas h\xE1biles" }
|
|
@@ -2154,8 +2209,10 @@ function createMockStorefront(options = {}) {
|
|
|
2154
2209
|
route("GET", "/carts/:id/delivery-options", "cart:write", ({ params }) => {
|
|
2155
2210
|
const cart = renderCart(getCartRecord(params[0]));
|
|
2156
2211
|
const net = toCents(cart.cost.subtotal.amount) - toCents(cart.cost.discount.amount);
|
|
2212
|
+
const rates = shippingRates(net);
|
|
2213
|
+
const free = options.shippingQuoteMode === "fixed_when_free" && rates.length > 0 && rates.every((r) => toCents(r.cost.amount) === 0);
|
|
2157
2214
|
return {
|
|
2158
|
-
body: { rates:
|
|
2215
|
+
body: { rates, free, fixed: free, pickup: state.shop.branches.filter((b) => b.pickup?.enabled !== false).map((b) => ({ branch_id: b.id, name: b.name, address: b.address, city: b.city })), partial: false, unavailable: [] },
|
|
2159
2216
|
cache: "private"
|
|
2160
2217
|
};
|
|
2161
2218
|
});
|
|
@@ -2830,6 +2887,89 @@ function createMockStorefront(options = {}) {
|
|
|
2830
2887
|
mirrorDefaultProfile(c);
|
|
2831
2888
|
return { status: 204 };
|
|
2832
2889
|
});
|
|
2890
|
+
const MAX_SUBJECTS = 50;
|
|
2891
|
+
const subjectsOf = (c) => state.subjects.get(c.id) ?? [];
|
|
2892
|
+
const kinds = () => options.subjectKinds ?? [];
|
|
2893
|
+
const publicSubject = (s) => {
|
|
2894
|
+
const { private: priv, ...rest } = s;
|
|
2895
|
+
return { ...rest, attributes: { ...s.attributes, ...Object.fromEntries(Object.keys(priv).map((k) => [k, PRIVATE_MASK + (priv[k].length > 3 ? priv[k].slice(-2) : "")])) } };
|
|
2896
|
+
};
|
|
2897
|
+
const ownSubject = (c, id) => {
|
|
2898
|
+
const s = subjectsOf(c).find((x) => x.id === id);
|
|
2899
|
+
if (!s) throw new HttpError(404, "not_found", "Item not found", "id");
|
|
2900
|
+
return s;
|
|
2901
|
+
};
|
|
2902
|
+
const setDefaultSubject = (c, id) => {
|
|
2903
|
+
const kind = ownSubject(c, id).kind;
|
|
2904
|
+
state.subjects.set(c.id, subjectsOf(c).map((s) => s.kind !== kind ? s : s.id === id ? { ...s, is_default: true, last_used_at: now() } : { ...s, is_default: false }));
|
|
2905
|
+
};
|
|
2906
|
+
const resolveOrThrow = (kind, values) => {
|
|
2907
|
+
const r = resolveSubject(kind, values);
|
|
2908
|
+
if (!r.ok) {
|
|
2909
|
+
const [field, code] = Object.entries(r.problems)[0];
|
|
2910
|
+
throw new HttpError(422, "validation", `${field}: ${code}`, field === "_" ? "attributes" : `attributes.${field}`);
|
|
2911
|
+
}
|
|
2912
|
+
return r.subject;
|
|
2913
|
+
};
|
|
2914
|
+
const cleanNickname = (v) => {
|
|
2915
|
+
if (v === void 0 || v === null || v === "") return null;
|
|
2916
|
+
if (typeof v !== "string" || [...v.trim()].length > 40) throw new HttpError(422, "validation", "nickname is too long (max 40)", "nickname");
|
|
2917
|
+
return v.trim().split(/\s+/).join(" ");
|
|
2918
|
+
};
|
|
2919
|
+
route("GET", "/subject-kinds", "content:read", () => ({ body: { nodes: kinds() }, cache: "public" }));
|
|
2920
|
+
route("GET", "/customer/subjects", "customer:read", ({ headers, search }) => {
|
|
2921
|
+
const c = requireCustomer(headers);
|
|
2922
|
+
const kind = search.get("kind");
|
|
2923
|
+
const nodes = subjectsOf(c).filter((s) => !kind || s.kind === kind).sort((a, b) => a.kind.localeCompare(b.kind) || Number(b.is_default) - Number(a.is_default) || (b.last_used_at ?? "").localeCompare(a.last_used_at ?? ""));
|
|
2924
|
+
return { body: { nodes: nodes.map(publicSubject) }, cache: "private" };
|
|
2925
|
+
});
|
|
2926
|
+
route("POST", "/customer/subjects", "customer:write", ({ headers, body }) => {
|
|
2927
|
+
const c = requireCustomer(headers);
|
|
2928
|
+
const kind = kinds().find((k) => k.key === body.kind);
|
|
2929
|
+
if (!kind) throw new HttpError(422, "validation", "This store has no such kind", "kind");
|
|
2930
|
+
const attrs = body.attributes;
|
|
2931
|
+
if (!attrs || typeof attrs !== "object" || Array.isArray(attrs)) throw new HttpError(422, "validation", "attributes must be an object", "attributes");
|
|
2932
|
+
const r = resolveOrThrow(kind, attrs);
|
|
2933
|
+
const list = subjectsOf(c);
|
|
2934
|
+
if (list.length >= MAX_SUBJECTS || list.filter((s) => s.kind === kind.key).length >= kind.max_per_account) throw new HttpError(409, "conflict", "Limit reached; delete one first", "kind");
|
|
2935
|
+
if (r.fit_key && list.some((s) => s.kind === kind.key && s.fit_key === r.fit_key)) throw new HttpError(409, "conflict", "You already saved this item", "attributes");
|
|
2936
|
+
const subject = { id: nextId("subj"), kind: kind.key, label: r.label, nickname: cleanNickname(body.nickname), fit_key: r.fit_key, attributes: r.attributes, private: r.private, is_default: false, last_used_at: null, created_at: now() };
|
|
2937
|
+
state.subjects.set(c.id, [...list, subject]);
|
|
2938
|
+
if (!list.some((s) => s.kind === kind.key) || body.is_default === true) setDefaultSubject(c, subject.id);
|
|
2939
|
+
return { status: 201, body: publicSubject(ownSubject(c, subject.id)), cache: "private" };
|
|
2940
|
+
});
|
|
2941
|
+
route("PATCH", "/customer/subjects/:id", "customer:write", ({ headers, params, body }) => {
|
|
2942
|
+
const c = requireCustomer(headers);
|
|
2943
|
+
const cur = ownSubject(c, params[0]);
|
|
2944
|
+
if (body.is_default === false && cur.is_default) throw new HttpError(422, "validation", "Make another item the default instead", "is_default");
|
|
2945
|
+
let next = { ...cur };
|
|
2946
|
+
if ("nickname" in body) next.nickname = cleanNickname(body.nickname);
|
|
2947
|
+
if ("attributes" in body) {
|
|
2948
|
+
const kind = kinds().find((k) => k.key === cur.kind);
|
|
2949
|
+
if (!kind) throw new HttpError(422, "validation", "This store no longer offers this kind", "kind");
|
|
2950
|
+
const sent = body.attributes ?? {};
|
|
2951
|
+
const merged = { ...cur.attributes, ...cur.private };
|
|
2952
|
+
for (const [k, v] of Object.entries(sent)) {
|
|
2953
|
+
if (k in cur.private && isMasked(v)) continue;
|
|
2954
|
+
if (v === null) delete merged[k];
|
|
2955
|
+
else merged[k] = v;
|
|
2956
|
+
}
|
|
2957
|
+
const r = resolveOrThrow(kind, merged);
|
|
2958
|
+
if (r.fit_key && subjectsOf(c).some((s) => s.id !== cur.id && s.kind === cur.kind && s.fit_key === r.fit_key)) throw new HttpError(409, "conflict", "You already saved this item", "attributes");
|
|
2959
|
+
next = { ...next, label: r.label, fit_key: r.fit_key, attributes: r.attributes, private: r.private };
|
|
2960
|
+
}
|
|
2961
|
+
state.subjects.set(c.id, subjectsOf(c).map((s) => s.id === cur.id ? next : s));
|
|
2962
|
+
if (body.is_default === true && !cur.is_default) setDefaultSubject(c, cur.id);
|
|
2963
|
+
return { body: publicSubject(ownSubject(c, cur.id)), cache: "private" };
|
|
2964
|
+
});
|
|
2965
|
+
route("DELETE", "/customer/subjects/:id", "customer:write", ({ headers, params }) => {
|
|
2966
|
+
const c = requireCustomer(headers);
|
|
2967
|
+
const cur = ownSubject(c, params[0]);
|
|
2968
|
+
state.subjects.set(c.id, subjectsOf(c).filter((s) => s.id !== cur.id));
|
|
2969
|
+
const heir = subjectsOf(c).filter((s) => s.kind === cur.kind).sort((a, b) => (b.last_used_at ?? "").localeCompare(a.last_used_at ?? ""))[0];
|
|
2970
|
+
if (cur.is_default && heir) setDefaultSubject(c, heir.id);
|
|
2971
|
+
return { status: 204 };
|
|
2972
|
+
});
|
|
2833
2973
|
route("GET", "/customer/addresses", "customer:read", ({ headers }) => {
|
|
2834
2974
|
const c = requireCustomer(headers);
|
|
2835
2975
|
return { body: { nodes: state.addresses.get(c.id) ?? [] }, cache: "private" };
|
|
@@ -2979,6 +3119,6 @@ function createMockStorefront(options = {}) {
|
|
|
2979
3119
|
};
|
|
2980
3120
|
}
|
|
2981
3121
|
|
|
2982
|
-
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, createMockStorefront };
|
|
3122
|
+
export { MOCK_CSF_RULES, MOCK_CURRENCY, MOCK_CUSTOMER_EMAIL, MOCK_FISCAL_ID_MX, MOCK_IDENTITY_DOCUMENT_MX_CSF, MOCK_IMAGE_URL, MOCK_POSTAL_CENTROIDS, MOCK_REGION_MX, MOCK_SUBJECT_KINDS, createMockStorefront };
|
|
2983
3123
|
//# sourceMappingURL=mock.js.map
|
|
2984
3124
|
//# sourceMappingURL=mock.js.map
|