@asteby/storefront 0.9.0 → 0.10.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 +33 -1
- package/dist/index.cjs +18 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/mock.cjs +113 -0
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.d.cts +19 -1
- package/dist/mock.d.ts +19 -1
- package/dist/mock.js +113 -0
- package/dist/mock.js.map +1 -1
- package/dist/mockup.d.cts +1 -1
- package/dist/mockup.d.ts +1 -1
- package/dist/{types-iGGZ9EiC.d.ts → types-COSwo4ND.d.ts} +41 -1
- package/dist/{types-CSMuPSQA.d.cts → types-DdLzlxw4.d.cts} +41 -1
- package/package.json +1 -1
package/dist/mock.cjs
CHANGED
|
@@ -438,6 +438,15 @@ function createMockStorefront(options = {}) {
|
|
|
438
438
|
const currency = options.currency ?? MOCK_CURRENCY;
|
|
439
439
|
const imageUrl = options.imageUrl ?? MOCK_IMAGE_URL;
|
|
440
440
|
const otpCode = options.otpCode ?? "000000";
|
|
441
|
+
const capabilities = {
|
|
442
|
+
password: true,
|
|
443
|
+
email_verification: true,
|
|
444
|
+
otp_channels: ["email"],
|
|
445
|
+
...options.customerCapabilities
|
|
446
|
+
};
|
|
447
|
+
const maxLoginAttempts = options.maxLoginAttempts ?? 5;
|
|
448
|
+
const MIN_PASSWORD = 10;
|
|
449
|
+
const MAX_PASSWORD = 128;
|
|
441
450
|
const discountCodes = Object.fromEntries(
|
|
442
451
|
Object.entries(options.discountCodes ?? { BIENVENIDA10: 10 }).map(([k, v]) => [k.toUpperCase(), v])
|
|
443
452
|
);
|
|
@@ -470,6 +479,9 @@ function createMockStorefront(options = {}) {
|
|
|
470
479
|
}
|
|
471
480
|
],
|
|
472
481
|
otps: /* @__PURE__ */ new Map(),
|
|
482
|
+
passwords: /* @__PURE__ */ new Map(),
|
|
483
|
+
pendingRegistrations: /* @__PURE__ */ new Map(),
|
|
484
|
+
loginFailures: /* @__PURE__ */ new Map(),
|
|
473
485
|
sessions: /* @__PURE__ */ new Map(),
|
|
474
486
|
idempotency: /* @__PURE__ */ new Map(),
|
|
475
487
|
checkouts: /* @__PURE__ */ new Map(),
|
|
@@ -754,6 +766,39 @@ function createMockStorefront(options = {}) {
|
|
|
754
766
|
if (email && !/^[^@\s]+@[^@\s]+$/.test(email)) throw new HttpError(400, "validation", "Invalid email", "email");
|
|
755
767
|
return email ? `email:${email}` : `phone:${phone}`;
|
|
756
768
|
}
|
|
769
|
+
function emailOf(body) {
|
|
770
|
+
const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : "";
|
|
771
|
+
if (!email || !/^[^@\s]+@[^@\s]+$/.test(email)) throw new HttpError(400, "validation", "Invalid email", "email");
|
|
772
|
+
return email;
|
|
773
|
+
}
|
|
774
|
+
function passwordOf(body, field = "password") {
|
|
775
|
+
const value = body[field];
|
|
776
|
+
if (typeof value !== "string" || value.length < MIN_PASSWORD || value.length > MAX_PASSWORD) {
|
|
777
|
+
throw new HttpError(400, "validation", `${field} must be ${MIN_PASSWORD}-${MAX_PASSWORD} characters`, field);
|
|
778
|
+
}
|
|
779
|
+
return value;
|
|
780
|
+
}
|
|
781
|
+
function requirePasswordEnabled() {
|
|
782
|
+
if (!capabilities.password) throw new HttpError(403, "forbidden", "Password accounts are disabled for this store");
|
|
783
|
+
}
|
|
784
|
+
function sessionFor(email, extra = {}) {
|
|
785
|
+
let customer = state.customers.find((c) => c.email === email);
|
|
786
|
+
if (!customer) {
|
|
787
|
+
customer = {
|
|
788
|
+
id: nextId("cus"),
|
|
789
|
+
email,
|
|
790
|
+
phone: null,
|
|
791
|
+
first_name: extra.first_name ?? null,
|
|
792
|
+
last_name: extra.last_name ?? null,
|
|
793
|
+
default_address_id: null,
|
|
794
|
+
created_at: now()
|
|
795
|
+
};
|
|
796
|
+
state.customers.push(customer);
|
|
797
|
+
}
|
|
798
|
+
const token = `ct_${customer.id}_${Math.random().toString(36).slice(2)}`;
|
|
799
|
+
state.sessions.set(token, customer.id);
|
|
800
|
+
return { token, expires_at: new Date(Date.now() + 30 * 864e5).toISOString(), customer };
|
|
801
|
+
}
|
|
757
802
|
function validateAddress(body, partial = false) {
|
|
758
803
|
const required = ["address1", "city", "zip", "country_code"];
|
|
759
804
|
if (!partial) {
|
|
@@ -1169,6 +1214,74 @@ function createMockStorefront(options = {}) {
|
|
|
1169
1214
|
cache: "private"
|
|
1170
1215
|
};
|
|
1171
1216
|
});
|
|
1217
|
+
route("GET", "/customer/capabilities", "customer:read", () => ({ body: capabilities, cache: "private" }));
|
|
1218
|
+
route("POST", "/customer/register", "customer:write", ({ body }) => {
|
|
1219
|
+
requirePasswordEnabled();
|
|
1220
|
+
const email = emailOf(body);
|
|
1221
|
+
const password = passwordOf(body);
|
|
1222
|
+
const text = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
|
|
1223
|
+
const first_name = text(body.first_name);
|
|
1224
|
+
const last_name = text(body.last_name);
|
|
1225
|
+
if (state.passwords.has(email)) throw new HttpError(409, "conflict", "An account with this email already exists", "email");
|
|
1226
|
+
if (capabilities.email_verification) {
|
|
1227
|
+
state.pendingRegistrations.set(email, { password, first_name, last_name });
|
|
1228
|
+
state.otps.set(`email:${email}`, otpCode);
|
|
1229
|
+
return { status: 202, body: { status: "verification_required" }, cache: "private" };
|
|
1230
|
+
}
|
|
1231
|
+
state.passwords.set(email, password);
|
|
1232
|
+
return { status: 201, body: sessionFor(email, { first_name, last_name }), cache: "private" };
|
|
1233
|
+
});
|
|
1234
|
+
route("POST", "/customer/register/verify", "customer:write", ({ body }) => {
|
|
1235
|
+
requirePasswordEnabled();
|
|
1236
|
+
const email = emailOf(body);
|
|
1237
|
+
const pending = state.pendingRegistrations.get(email);
|
|
1238
|
+
if (!pending || typeof body.code !== "string" || state.otps.get(`email:${email}`) !== body.code) {
|
|
1239
|
+
throw new HttpError(401, "unauthorized", "Invalid or expired code", "code");
|
|
1240
|
+
}
|
|
1241
|
+
state.pendingRegistrations.delete(email);
|
|
1242
|
+
state.otps.delete(`email:${email}`);
|
|
1243
|
+
state.passwords.set(email, pending.password);
|
|
1244
|
+
return { status: 201, body: sessionFor(email, pending), cache: "private" };
|
|
1245
|
+
});
|
|
1246
|
+
route("POST", "/customer/login", "customer:write", ({ body }) => {
|
|
1247
|
+
requirePasswordEnabled();
|
|
1248
|
+
const email = emailOf(body);
|
|
1249
|
+
if ((state.loginFailures.get(email) ?? 0) >= maxLoginAttempts) {
|
|
1250
|
+
throw new HttpError(429, "rate_limited", "Too many failed attempts, try again later");
|
|
1251
|
+
}
|
|
1252
|
+
const stored = state.passwords.get(email);
|
|
1253
|
+
if (stored === void 0 || typeof body.password !== "string" || body.password !== stored) {
|
|
1254
|
+
state.loginFailures.set(email, (state.loginFailures.get(email) ?? 0) + 1);
|
|
1255
|
+
throw new HttpError(401, "unauthorized", "Invalid email or password");
|
|
1256
|
+
}
|
|
1257
|
+
state.loginFailures.delete(email);
|
|
1258
|
+
return { status: 201, body: sessionFor(email), cache: "private" };
|
|
1259
|
+
});
|
|
1260
|
+
route("PUT", "/customer/password", "customer:write", ({ headers, body }) => {
|
|
1261
|
+
requirePasswordEnabled();
|
|
1262
|
+
const customer = requireCustomer(headers);
|
|
1263
|
+
const password = passwordOf(body);
|
|
1264
|
+
const email = customer.email;
|
|
1265
|
+
if (!email) throw new HttpError(422, "invalid_state", "The account has no email", "email");
|
|
1266
|
+
const current = state.passwords.get(email);
|
|
1267
|
+
if (current !== void 0 && body.current_password !== current) {
|
|
1268
|
+
throw new HttpError(401, "unauthorized", "current_password is missing or wrong", "current_password");
|
|
1269
|
+
}
|
|
1270
|
+
state.passwords.set(email, password);
|
|
1271
|
+
return { status: 204 };
|
|
1272
|
+
});
|
|
1273
|
+
route("POST", "/customer/password/reset", "customer:write", ({ body }) => {
|
|
1274
|
+
requirePasswordEnabled();
|
|
1275
|
+
const email = emailOf(body);
|
|
1276
|
+
const password = passwordOf(body);
|
|
1277
|
+
if (typeof body.code !== "string" || state.otps.get(`email:${email}`) !== body.code || !state.customers.some((c) => c.email === email)) {
|
|
1278
|
+
throw new HttpError(401, "unauthorized", "Invalid or expired code", "code");
|
|
1279
|
+
}
|
|
1280
|
+
state.otps.delete(`email:${email}`);
|
|
1281
|
+
state.passwords.set(email, password);
|
|
1282
|
+
state.loginFailures.delete(email);
|
|
1283
|
+
return { status: 204 };
|
|
1284
|
+
});
|
|
1172
1285
|
route("GET", "/customer", "customer:read", ({ headers }) => ({ body: requireCustomer(headers), cache: "private" }));
|
|
1173
1286
|
const stripOrder = ({ customer_id: _c, ...o }) => o;
|
|
1174
1287
|
route("GET", "/customer/orders", "orders:read", ({ headers, search }) => {
|