@cimplify/sdk 0.44.12 → 0.44.16
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/dist/advanced.d.mts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +21 -21
- package/dist/advanced.mjs +2 -2
- package/dist/{chunk-6TVQQYQ5.mjs → chunk-6AZZ6AOF.mjs} +21 -1
- package/dist/{chunk-EZK2FXTM.mjs → chunk-7NML226B.mjs} +1 -1
- package/dist/{chunk-BAWKQRIB.js → chunk-CFD4J32N.js} +21 -21
- package/dist/{chunk-OMAP327U.js → chunk-MBGQP6FZ.js} +62 -61
- package/dist/{chunk-V2O5AO7I.js → chunk-MNIKXG7W.js} +26 -0
- package/dist/{chunk-3WD6U5RD.mjs → chunk-UXLQ7WXI.mjs} +4 -3
- package/dist/chunk-XL4CX75J.js +9370 -0
- package/dist/chunk-ZLEFMQC6.mjs +9318 -0
- package/dist/cli/commands/deploy.mjs +9 -1
- package/dist/{client-lvLvB1_K.d.ts → client-D6ZisKco.d.ts} +7 -0
- package/dist/{client-CkGlXZRd.d.mts → client-Dh-bHyhU.d.mts} +7 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +106 -106
- package/dist/index.mjs +3 -3
- package/dist/mock/cli.mjs +172 -23
- package/dist/mock/library.d.mts +2 -2
- package/dist/mock/library.d.ts +2 -2
- package/dist/mock/library.js +172 -23
- package/dist/mock/library.mjs +172 -23
- package/dist/mock/msw.d.mts +1 -1
- package/dist/mock/msw.d.ts +1 -1
- package/dist/mock/msw.js +172 -23
- package/dist/mock/msw.mjs +172 -23
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +80 -80
- package/dist/react.mjs +3 -3
- package/dist/server-CWpUlLOQ.d.mts +1003 -0
- package/dist/server-CWpUlLOQ.d.ts +1003 -0
- package/dist/{server-CUbuQxOV.d.ts → server-CnECAQDQ.d.mts} +28 -6
- package/dist/{server-CUbuQxOV.d.mts → server-CnECAQDQ.d.ts} +28 -6
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +5 -5
- package/dist/server.mjs +3 -3
- package/dist/testing/suite.d.mts +60 -0
- package/dist/testing/suite.d.ts +60 -0
- package/dist/testing/suite.js +169 -0
- package/dist/testing/suite.mjs +165 -0
- package/dist/testing.d.mts +1359 -0
- package/dist/testing.d.ts +1359 -0
- package/dist/testing.js +213 -0
- package/dist/testing.mjs +4 -0
- package/dist/utils.js +28 -28
- package/dist/utils.mjs +1 -1
- package/package.json +18 -3
- package/registry/account.json +15 -0
- package/registry/billing-plan-selector.json +16 -0
- package/registry/cart-drawer.json +17 -0
- package/registry/currency-selector.json +15 -0
- package/registry/customer-input-fields.json +15 -0
- package/registry/delivery-estimate.json +16 -0
- package/registry/index.json +105 -0
- package/registry/location-picker.json +15 -0
- package/registry/recently-viewed.json +16 -0
- package/registry/recommendation-carousel.json +16 -0
- package/registry/resource-picker.json +15 -0
- package/registry/session-message-banner.json +15 -0
- package/templates/storefront-bakery/AGENTS.md +12 -0
- package/templates/storefront-bakery/__tests__/brand.test.ts +4 -0
- package/templates/storefront-bakery/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-bakery/__tests__/contract.test.ts +4 -0
- package/templates/storefront-bakery/package.json +11 -3
- package/templates/storefront-bakery/vitest.config.ts +9 -0
- package/templates/storefront-fashion/AGENTS.md +21 -1
- package/templates/storefront-fashion/__tests__/brand.test.ts +4 -0
- package/templates/storefront-fashion/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-fashion/__tests__/contract.test.ts +4 -0
- package/templates/storefront-fashion/package.json +11 -3
- package/templates/storefront-fashion/vitest.config.ts +9 -0
- package/templates/storefront-grocery/AGENTS.md +12 -0
- package/templates/storefront-grocery/__tests__/brand.test.ts +4 -0
- package/templates/storefront-grocery/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-grocery/__tests__/contract.test.ts +4 -0
- package/templates/storefront-grocery/package.json +11 -3
- package/templates/storefront-grocery/vitest.config.ts +9 -0
- package/templates/storefront-restaurant/AGENTS.md +12 -0
- package/templates/storefront-restaurant/__tests__/brand.test.ts +4 -0
- package/templates/storefront-restaurant/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-restaurant/__tests__/contract.test.ts +4 -0
- package/templates/storefront-restaurant/package.json +11 -3
- package/templates/storefront-restaurant/vitest.config.ts +9 -0
- package/templates/storefront-retail/AGENTS.md +12 -0
- package/templates/storefront-retail/__tests__/brand.test.ts +4 -0
- package/templates/storefront-retail/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-retail/__tests__/contract.test.ts +4 -0
- package/templates/storefront-retail/package.json +11 -3
- package/templates/storefront-retail/vitest.config.ts +9 -0
- package/templates/storefront-services/AGENTS.md +12 -0
- package/templates/storefront-services/__tests__/brand.test.ts +4 -0
- package/templates/storefront-services/__tests__/cart-flow.test.ts +4 -0
- package/templates/storefront-services/__tests__/contract.test.ts +4 -0
- package/templates/storefront-services/package.json +11 -3
- package/templates/storefront-services/vitest.config.ts +9 -0
package/dist/mock/library.js
CHANGED
|
@@ -2139,6 +2139,44 @@ function authMiddleware(deps) {
|
|
|
2139
2139
|
await next();
|
|
2140
2140
|
};
|
|
2141
2141
|
}
|
|
2142
|
+
var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
|
|
2143
|
+
var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2144
|
+
function idempotencyMiddleware(deps) {
|
|
2145
|
+
return async (c, next) => {
|
|
2146
|
+
if (c.req.method !== "POST" || !IDEMPOTENT_PATH_RE.test(c.req.path)) {
|
|
2147
|
+
return next();
|
|
2148
|
+
}
|
|
2149
|
+
const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
|
|
2150
|
+
if (!key || key.length > 200) return next();
|
|
2151
|
+
const session = c.var.session;
|
|
2152
|
+
const recordId = `${session.id}:${key}`;
|
|
2153
|
+
const existing = deps.registry.idempotency.get(recordId);
|
|
2154
|
+
if (existing) {
|
|
2155
|
+
const expiresAt = Date.parse(existing.expires_at);
|
|
2156
|
+
if (Number.isFinite(expiresAt) && expiresAt > Date.now()) {
|
|
2157
|
+
return c.json(existing.response, existing.status);
|
|
2158
|
+
}
|
|
2159
|
+
deps.registry.idempotency.delete(recordId);
|
|
2160
|
+
}
|
|
2161
|
+
await next();
|
|
2162
|
+
if (c.res.status >= 200 && c.res.status < 300) {
|
|
2163
|
+
try {
|
|
2164
|
+
const cloned = c.res.clone();
|
|
2165
|
+
const response = await cloned.json();
|
|
2166
|
+
const now = Date.now();
|
|
2167
|
+
deps.registry.idempotency.put(recordId, {
|
|
2168
|
+
key: recordId,
|
|
2169
|
+
request_hash: "",
|
|
2170
|
+
response,
|
|
2171
|
+
status: c.res.status,
|
|
2172
|
+
created_at: new Date(now).toISOString(),
|
|
2173
|
+
expires_at: new Date(now + IDEMPOTENCY_TTL_MS).toISOString()
|
|
2174
|
+
});
|
|
2175
|
+
} catch {
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2142
2180
|
function chaosMiddleware(deps) {
|
|
2143
2181
|
return async (c, next) => {
|
|
2144
2182
|
const path = c.req.path;
|
|
@@ -2189,6 +2227,7 @@ function buildV1(deps) {
|
|
|
2189
2227
|
const app = new Hono2();
|
|
2190
2228
|
app.use("*", chaosMiddleware(deps));
|
|
2191
2229
|
app.use("*", authMiddleware(deps));
|
|
2230
|
+
app.use("*", idempotencyMiddleware(deps));
|
|
2192
2231
|
app.get("/bootstrap", (c) => {
|
|
2193
2232
|
const session = c.var.session;
|
|
2194
2233
|
return tryRoute(c, () => ({
|
|
@@ -2265,9 +2304,27 @@ function buildV1(deps) {
|
|
|
2265
2304
|
(c) => tryRoute(c, () => ({ results: deps.catalogue.searchKnowledgeBase(deps.defaultBusinessId, c.req.query("q") ?? ""), query: c.req.query("q") ?? "" }))
|
|
2266
2305
|
);
|
|
2267
2306
|
app.get("/catalogue/products/:product_id/attributes", (c) => tryRoute(c, () => deps.catalogue.getProductAttributes(c.req.param("product_id"))));
|
|
2268
|
-
app.get(
|
|
2269
|
-
|
|
2270
|
-
|
|
2307
|
+
app.get(
|
|
2308
|
+
"/catalogue/products/:product_id/available-now",
|
|
2309
|
+
(c) => tryRoute(c, () => {
|
|
2310
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2311
|
+
return { product_id: c.req.param("product_id"), is_available: p?.is_available ?? false };
|
|
2312
|
+
})
|
|
2313
|
+
);
|
|
2314
|
+
app.get(
|
|
2315
|
+
"/catalogue/products/:product_id/billing-plans/eligible",
|
|
2316
|
+
(c) => tryRoute(c, () => {
|
|
2317
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2318
|
+
return { plans: p?.billing_plans ?? [] };
|
|
2319
|
+
})
|
|
2320
|
+
);
|
|
2321
|
+
app.get(
|
|
2322
|
+
"/catalogue/products/:product_id/schedules",
|
|
2323
|
+
(c) => tryRoute(c, () => {
|
|
2324
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2325
|
+
return { schedules: p?.metadata?.["time_profiles"] ?? [] };
|
|
2326
|
+
})
|
|
2327
|
+
);
|
|
2271
2328
|
app.get("/catalogue/deals", (c) => tryRoute(c, () => deps.catalogue.listDeals(deps.defaultBusinessId)));
|
|
2272
2329
|
app.get("/catalogue/deals/on-sale", (c) => tryRoute(c, () => deps.catalogue.listOnSale(deps.defaultBusinessId)));
|
|
2273
2330
|
app.post("/catalogue/deals/validate", async (c) => {
|
|
@@ -2350,8 +2407,14 @@ function buildV1(deps) {
|
|
|
2350
2407
|
});
|
|
2351
2408
|
app.get("/inventory/products/:product_id/stock", (c) => tryRoute(c, () => deps.inventory.productStock(c.req.param("product_id"))));
|
|
2352
2409
|
app.get("/inventory/variants/:variant_id/stock", (c) => tryRoute(c, () => deps.inventory.variantStock(c.req.param("variant_id"))));
|
|
2353
|
-
app.get(
|
|
2354
|
-
|
|
2410
|
+
app.get(
|
|
2411
|
+
"/inventory/products/:product_id/availability",
|
|
2412
|
+
(c) => tryRoute(c, () => deps.inventory.productAvailability(c.req.param("product_id"), getQueryNum(c, "quantity") ?? 1))
|
|
2413
|
+
);
|
|
2414
|
+
app.get(
|
|
2415
|
+
"/inventory/variants/:variant_id/availability",
|
|
2416
|
+
(c) => tryRoute(c, () => deps.inventory.variantAvailability(c.req.param("variant_id"), getQueryNum(c, "quantity") ?? 1))
|
|
2417
|
+
);
|
|
2355
2418
|
app.get(
|
|
2356
2419
|
"/delivery/fee",
|
|
2357
2420
|
(c) => tryRoute(
|
|
@@ -3375,7 +3438,14 @@ function createAuthService(opts) {
|
|
|
3375
3438
|
},
|
|
3376
3439
|
status(token) {
|
|
3377
3440
|
const s = this.resolveSession(token);
|
|
3378
|
-
|
|
3441
|
+
if (!s) return { is_authenticated: false, user_type: "guest" };
|
|
3442
|
+
return {
|
|
3443
|
+
is_authenticated: s.is_authenticated,
|
|
3444
|
+
account_id: s.customer_id,
|
|
3445
|
+
user_type: s.is_authenticated ? "customer" : "guest",
|
|
3446
|
+
session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
|
|
3447
|
+
session: s
|
|
3448
|
+
};
|
|
3379
3449
|
}
|
|
3380
3450
|
};
|
|
3381
3451
|
}
|
|
@@ -4179,13 +4249,22 @@ function computeBundlePrice(bundle, componentSum) {
|
|
|
4179
4249
|
}
|
|
4180
4250
|
return componentSum;
|
|
4181
4251
|
}
|
|
4182
|
-
function computeCompositePrice(composite, componentsTotal, highestUnit) {
|
|
4252
|
+
function computeCompositePrice(composite, componentsTotal, highestUnit, perGroupHighest, selectionCount) {
|
|
4183
4253
|
switch (composite.pricing_mode) {
|
|
4184
4254
|
case "highest_overall":
|
|
4185
4255
|
return add(composite.base_price, highestUnit ?? "0.00");
|
|
4256
|
+
case "highest_per_group": {
|
|
4257
|
+
let sum = "0.00";
|
|
4258
|
+
for (const v of Object.values(perGroupHighest ?? {})) sum = add(sum, v);
|
|
4259
|
+
return add(composite.base_price, sum);
|
|
4260
|
+
}
|
|
4261
|
+
case "tiered": {
|
|
4262
|
+
const tiers = composite.tiers ?? [];
|
|
4263
|
+
const count = selectionCount ?? 0;
|
|
4264
|
+
const matched = tiers.filter((t) => count >= t.min_count).sort((a, b) => b.min_count - a.min_count)[0];
|
|
4265
|
+
return matched ? add(composite.base_price, matched.price) : add(composite.base_price, componentsTotal);
|
|
4266
|
+
}
|
|
4186
4267
|
case "additive":
|
|
4187
|
-
case "highest_per_group":
|
|
4188
|
-
case "tiered":
|
|
4189
4268
|
default:
|
|
4190
4269
|
return add(composite.base_price, componentsTotal);
|
|
4191
4270
|
}
|
|
@@ -4319,7 +4398,11 @@ function toUICart(registry, cart) {
|
|
|
4319
4398
|
// Billing / discounts / metadata
|
|
4320
4399
|
billing_plan_id: i.billing_plan_id,
|
|
4321
4400
|
applied_discount_ids: i.applied_discount_ids,
|
|
4322
|
-
discount_details:
|
|
4401
|
+
discount_details: i.applied_discount_ids.length > 0 ? {
|
|
4402
|
+
applied_count: i.applied_discount_ids.length,
|
|
4403
|
+
total_amount: i.item_discount_amount,
|
|
4404
|
+
discount_ids: i.applied_discount_ids
|
|
4405
|
+
} : void 0,
|
|
4323
4406
|
metadata: i.metadata,
|
|
4324
4407
|
created_at: i.created_at,
|
|
4325
4408
|
updated_at: i.updated_at
|
|
@@ -4354,7 +4437,7 @@ function toUICart(registry, cart) {
|
|
|
4354
4437
|
var CART_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4355
4438
|
var ZERO2 = "0.00";
|
|
4356
4439
|
function createCartService(opts) {
|
|
4357
|
-
const { registry, clock, ids, bus } = opts;
|
|
4440
|
+
const { registry, clock, ids, bus, inventory } = opts;
|
|
4358
4441
|
const findOrCreate = (session) => {
|
|
4359
4442
|
const existing = registry.carts.list(
|
|
4360
4443
|
(c) => c.session_id === session.id && c.status === "active"
|
|
@@ -4436,7 +4519,12 @@ function createCartService(opts) {
|
|
|
4436
4519
|
}
|
|
4437
4520
|
const { stored, componentsTotal } = resolveCompositeSelections(registry, selections, composite.groups);
|
|
4438
4521
|
const highestUnit = stored.reduce((max, s) => parseFloat(s.unit_price) > parseFloat(max) ? s.unit_price : max, ZERO2);
|
|
4439
|
-
const
|
|
4522
|
+
const perGroupHighest = {};
|
|
4523
|
+
for (const s of stored) {
|
|
4524
|
+
const cur = perGroupHighest[s.group_id];
|
|
4525
|
+
if (!cur || parseFloat(s.unit_price) > parseFloat(cur)) perGroupHighest[s.group_id] = s.unit_price;
|
|
4526
|
+
}
|
|
4527
|
+
const finalPrice = computeCompositePrice(composite, componentsTotal, highestUnit, perGroupHighest, totalQty);
|
|
4440
4528
|
return {
|
|
4441
4529
|
unitPrice: finalPrice,
|
|
4442
4530
|
configuration: {
|
|
@@ -4514,6 +4602,7 @@ function createCartService(opts) {
|
|
|
4514
4602
|
bundleSelections: body.bundle_selections,
|
|
4515
4603
|
compositeSelections: body.composite_selections
|
|
4516
4604
|
});
|
|
4605
|
+
inventory?.reserve(body.item_id, body.variant_id, body.quantity);
|
|
4517
4606
|
const existing = cart.items.find((i) => i.line_key === lineKey);
|
|
4518
4607
|
if (existing) {
|
|
4519
4608
|
existing.quantity += body.quantity;
|
|
@@ -4553,9 +4642,13 @@ function createCartService(opts) {
|
|
|
4553
4642
|
const cart = findOrCreate(session);
|
|
4554
4643
|
const item = cart.items.find((i) => i.id === cartItemId);
|
|
4555
4644
|
if (!item) throw notFound(`Cart item ${cartItemId} not found`);
|
|
4645
|
+
const delta = quantity - item.quantity;
|
|
4556
4646
|
if (quantity <= 0) {
|
|
4647
|
+
inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4557
4648
|
cart.items = cart.items.filter((i) => i.id !== cartItemId);
|
|
4558
4649
|
} else {
|
|
4650
|
+
if (delta > 0) inventory?.reserve(item.item_id, item.variant_id, delta);
|
|
4651
|
+
else if (delta < 0) inventory?.release(item.item_id, item.variant_id, -delta);
|
|
4559
4652
|
item.quantity = quantity;
|
|
4560
4653
|
item.updated_at = clock.iso();
|
|
4561
4654
|
}
|
|
@@ -4564,12 +4657,15 @@ function createCartService(opts) {
|
|
|
4564
4657
|
},
|
|
4565
4658
|
removeItem(session, cartItemId) {
|
|
4566
4659
|
const cart = findOrCreate(session);
|
|
4660
|
+
const item = cart.items.find((i) => i.id === cartItemId);
|
|
4661
|
+
if (item) inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4567
4662
|
cart.items = cart.items.filter((i) => i.id !== cartItemId);
|
|
4568
4663
|
const updated = recalc(cart);
|
|
4569
4664
|
return { success: true, cart: toUICart(registry, updated) };
|
|
4570
4665
|
},
|
|
4571
4666
|
clear(session) {
|
|
4572
4667
|
const cart = findOrCreate(session);
|
|
4668
|
+
for (const item of cart.items) inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4573
4669
|
cart.items = [];
|
|
4574
4670
|
cart.applied_discount_codes = [];
|
|
4575
4671
|
cart.applied_discount_ids = [];
|
|
@@ -4645,18 +4741,24 @@ function createCheckoutService(opts) {
|
|
|
4645
4741
|
bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
|
|
4646
4742
|
bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
|
|
4647
4743
|
}
|
|
4744
|
+
const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
4648
4745
|
const result = {
|
|
4649
4746
|
order_id: orderId,
|
|
4650
4747
|
order_number: orderNumber,
|
|
4651
4748
|
payment_status: requiresAuth ? "pending" : "succeeded",
|
|
4652
4749
|
payment_reference: reference,
|
|
4653
4750
|
requires_authorization: requiresAuth,
|
|
4751
|
+
authorization_type: requiresAuth ? "otp" : void 0,
|
|
4654
4752
|
next_action: requiresAuth ? { type: "authorization", authorization_type: "otp", display_text: "Approve on your mobile money app" } : { type: "none" },
|
|
4655
|
-
provider: body.payment_method
|
|
4753
|
+
provider: body.payment_method,
|
|
4754
|
+
bill_token: billToken
|
|
4656
4755
|
};
|
|
4657
4756
|
if (isCard) {
|
|
4658
4757
|
result.next_action = { type: "redirect", authorization_url: `https://mock.cimplify.io/pay/${reference}` };
|
|
4659
4758
|
result.authorization_url = `https://mock.cimplify.io/pay/${reference}`;
|
|
4759
|
+
result.client_secret = `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}`;
|
|
4760
|
+
const proc = globalThis.process;
|
|
4761
|
+
result.public_key = proc?.env?.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? "pk_test_mock_publishable_key";
|
|
4660
4762
|
}
|
|
4661
4763
|
return result;
|
|
4662
4764
|
},
|
|
@@ -4862,19 +4964,66 @@ function createSchedulingService(opts) {
|
|
|
4862
4964
|
}
|
|
4863
4965
|
|
|
4864
4966
|
// src/mock/domain/inventory.ts
|
|
4865
|
-
|
|
4967
|
+
var DEFAULT_INITIAL_STOCK = 50;
|
|
4968
|
+
function createInventoryService(opts) {
|
|
4969
|
+
const { registry } = opts;
|
|
4970
|
+
const stockMap = /* @__PURE__ */ new Map();
|
|
4971
|
+
const stockKey = (productId, variantId) => variantId ? `${productId}:${variantId}` : productId;
|
|
4972
|
+
const initialStock = (productId) => {
|
|
4973
|
+
const product = registry.products.get(productId);
|
|
4974
|
+
if (!product) return 0;
|
|
4975
|
+
if (product.product_type === "service" || product.product_type === "digital") {
|
|
4976
|
+
return Number.MAX_SAFE_INTEGER;
|
|
4977
|
+
}
|
|
4978
|
+
const seeded = product.metadata?.["stock_level"];
|
|
4979
|
+
if (typeof seeded === "number") return seeded;
|
|
4980
|
+
return DEFAULT_INITIAL_STOCK;
|
|
4981
|
+
};
|
|
4982
|
+
const ensure = (productId, variantId) => {
|
|
4983
|
+
const key = stockKey(productId, variantId);
|
|
4984
|
+
if (!stockMap.has(key)) stockMap.set(key, initialStock(productId));
|
|
4985
|
+
return key;
|
|
4986
|
+
};
|
|
4866
4987
|
return {
|
|
4867
4988
|
productStock(productId) {
|
|
4868
|
-
|
|
4989
|
+
const key = ensure(productId);
|
|
4990
|
+
const stock_level = stockMap.get(key) ?? 0;
|
|
4991
|
+
return { product_id: productId, stock_level, in_stock: stock_level > 0 };
|
|
4869
4992
|
},
|
|
4870
4993
|
variantStock(variantId) {
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4994
|
+
const variant = registry.variants.get(variantId);
|
|
4995
|
+
const productId = variant?.product_id ?? "";
|
|
4996
|
+
const key = ensure(productId, variantId);
|
|
4997
|
+
const stock_level = stockMap.get(key) ?? 0;
|
|
4998
|
+
return { variant_id: variantId, stock_level, in_stock: stock_level > 0 };
|
|
4999
|
+
},
|
|
5000
|
+
productAvailability(productId, quantity = 1) {
|
|
5001
|
+
const key = ensure(productId);
|
|
5002
|
+
const have = stockMap.get(key) ?? 0;
|
|
5003
|
+
return { product_id: productId, quantity, is_available: have >= quantity };
|
|
5004
|
+
},
|
|
5005
|
+
variantAvailability(variantId, quantity = 1) {
|
|
5006
|
+
const variant = registry.variants.get(variantId);
|
|
5007
|
+
const productId = variant?.product_id ?? "";
|
|
5008
|
+
const key = ensure(productId, variantId);
|
|
5009
|
+
const have = stockMap.get(key) ?? 0;
|
|
5010
|
+
return { variant_id: variantId, quantity, is_available: have >= quantity };
|
|
5011
|
+
},
|
|
5012
|
+
reserve(productId, variantId, quantity) {
|
|
5013
|
+
const product = registry.products.get(productId);
|
|
5014
|
+
if (!product) return;
|
|
5015
|
+
if (product.product_type === "service" || product.product_type === "digital") return;
|
|
5016
|
+
const key = ensure(productId, variantId);
|
|
5017
|
+
const have = stockMap.get(key) ?? 0;
|
|
5018
|
+
stockMap.set(key, Math.max(0, have - quantity));
|
|
4875
5019
|
},
|
|
4876
|
-
|
|
4877
|
-
|
|
5020
|
+
release(productId, variantId, quantity) {
|
|
5021
|
+
const product = registry.products.get(productId);
|
|
5022
|
+
if (!product) return;
|
|
5023
|
+
if (product.product_type === "service" || product.product_type === "digital") return;
|
|
5024
|
+
const key = stockKey(productId, variantId);
|
|
5025
|
+
const have = stockMap.get(key) ?? 0;
|
|
5026
|
+
stockMap.set(key, have + quantity);
|
|
4878
5027
|
}
|
|
4879
5028
|
};
|
|
4880
5029
|
}
|
|
@@ -8626,12 +8775,12 @@ function createDeps(options = {}) {
|
|
|
8626
8775
|
});
|
|
8627
8776
|
const business = createBusinessService({ registry, defaultBusinessId });
|
|
8628
8777
|
const catalogue = createCatalogueService({ registry, clock, ids });
|
|
8629
|
-
const
|
|
8778
|
+
const inventory = createInventoryService({ registry });
|
|
8779
|
+
const cart = createCartService({ registry, clock, ids, bus, inventory });
|
|
8630
8780
|
const orders = createOrderService({ registry, clock, bus });
|
|
8631
8781
|
const checkout = createCheckoutService({ registry, clock, ids, bus});
|
|
8632
8782
|
const subscriptions = createSubscriptionService({ registry, clock, bus });
|
|
8633
8783
|
const scheduling = createSchedulingService({ registry, clock, bus });
|
|
8634
|
-
const inventory = createInventoryService();
|
|
8635
8784
|
const fx = createFxService({ registry, clock, ids });
|
|
8636
8785
|
const places = createPlacesService();
|
|
8637
8786
|
const delivery = createDeliveryService();
|
package/dist/mock/library.mjs
CHANGED
|
@@ -2137,6 +2137,44 @@ function authMiddleware(deps) {
|
|
|
2137
2137
|
await next();
|
|
2138
2138
|
};
|
|
2139
2139
|
}
|
|
2140
|
+
var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
|
|
2141
|
+
var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2142
|
+
function idempotencyMiddleware(deps) {
|
|
2143
|
+
return async (c, next) => {
|
|
2144
|
+
if (c.req.method !== "POST" || !IDEMPOTENT_PATH_RE.test(c.req.path)) {
|
|
2145
|
+
return next();
|
|
2146
|
+
}
|
|
2147
|
+
const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
|
|
2148
|
+
if (!key || key.length > 200) return next();
|
|
2149
|
+
const session = c.var.session;
|
|
2150
|
+
const recordId = `${session.id}:${key}`;
|
|
2151
|
+
const existing = deps.registry.idempotency.get(recordId);
|
|
2152
|
+
if (existing) {
|
|
2153
|
+
const expiresAt = Date.parse(existing.expires_at);
|
|
2154
|
+
if (Number.isFinite(expiresAt) && expiresAt > Date.now()) {
|
|
2155
|
+
return c.json(existing.response, existing.status);
|
|
2156
|
+
}
|
|
2157
|
+
deps.registry.idempotency.delete(recordId);
|
|
2158
|
+
}
|
|
2159
|
+
await next();
|
|
2160
|
+
if (c.res.status >= 200 && c.res.status < 300) {
|
|
2161
|
+
try {
|
|
2162
|
+
const cloned = c.res.clone();
|
|
2163
|
+
const response = await cloned.json();
|
|
2164
|
+
const now = Date.now();
|
|
2165
|
+
deps.registry.idempotency.put(recordId, {
|
|
2166
|
+
key: recordId,
|
|
2167
|
+
request_hash: "",
|
|
2168
|
+
response,
|
|
2169
|
+
status: c.res.status,
|
|
2170
|
+
created_at: new Date(now).toISOString(),
|
|
2171
|
+
expires_at: new Date(now + IDEMPOTENCY_TTL_MS).toISOString()
|
|
2172
|
+
});
|
|
2173
|
+
} catch {
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2140
2178
|
function chaosMiddleware(deps) {
|
|
2141
2179
|
return async (c, next) => {
|
|
2142
2180
|
const path = c.req.path;
|
|
@@ -2187,6 +2225,7 @@ function buildV1(deps) {
|
|
|
2187
2225
|
const app = new Hono2();
|
|
2188
2226
|
app.use("*", chaosMiddleware(deps));
|
|
2189
2227
|
app.use("*", authMiddleware(deps));
|
|
2228
|
+
app.use("*", idempotencyMiddleware(deps));
|
|
2190
2229
|
app.get("/bootstrap", (c) => {
|
|
2191
2230
|
const session = c.var.session;
|
|
2192
2231
|
return tryRoute(c, () => ({
|
|
@@ -2263,9 +2302,27 @@ function buildV1(deps) {
|
|
|
2263
2302
|
(c) => tryRoute(c, () => ({ results: deps.catalogue.searchKnowledgeBase(deps.defaultBusinessId, c.req.query("q") ?? ""), query: c.req.query("q") ?? "" }))
|
|
2264
2303
|
);
|
|
2265
2304
|
app.get("/catalogue/products/:product_id/attributes", (c) => tryRoute(c, () => deps.catalogue.getProductAttributes(c.req.param("product_id"))));
|
|
2266
|
-
app.get(
|
|
2267
|
-
|
|
2268
|
-
|
|
2305
|
+
app.get(
|
|
2306
|
+
"/catalogue/products/:product_id/available-now",
|
|
2307
|
+
(c) => tryRoute(c, () => {
|
|
2308
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2309
|
+
return { product_id: c.req.param("product_id"), is_available: p?.is_available ?? false };
|
|
2310
|
+
})
|
|
2311
|
+
);
|
|
2312
|
+
app.get(
|
|
2313
|
+
"/catalogue/products/:product_id/billing-plans/eligible",
|
|
2314
|
+
(c) => tryRoute(c, () => {
|
|
2315
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2316
|
+
return { plans: p?.billing_plans ?? [] };
|
|
2317
|
+
})
|
|
2318
|
+
);
|
|
2319
|
+
app.get(
|
|
2320
|
+
"/catalogue/products/:product_id/schedules",
|
|
2321
|
+
(c) => tryRoute(c, () => {
|
|
2322
|
+
const p = deps.registry.products.get(c.req.param("product_id"));
|
|
2323
|
+
return { schedules: p?.metadata?.["time_profiles"] ?? [] };
|
|
2324
|
+
})
|
|
2325
|
+
);
|
|
2269
2326
|
app.get("/catalogue/deals", (c) => tryRoute(c, () => deps.catalogue.listDeals(deps.defaultBusinessId)));
|
|
2270
2327
|
app.get("/catalogue/deals/on-sale", (c) => tryRoute(c, () => deps.catalogue.listOnSale(deps.defaultBusinessId)));
|
|
2271
2328
|
app.post("/catalogue/deals/validate", async (c) => {
|
|
@@ -2348,8 +2405,14 @@ function buildV1(deps) {
|
|
|
2348
2405
|
});
|
|
2349
2406
|
app.get("/inventory/products/:product_id/stock", (c) => tryRoute(c, () => deps.inventory.productStock(c.req.param("product_id"))));
|
|
2350
2407
|
app.get("/inventory/variants/:variant_id/stock", (c) => tryRoute(c, () => deps.inventory.variantStock(c.req.param("variant_id"))));
|
|
2351
|
-
app.get(
|
|
2352
|
-
|
|
2408
|
+
app.get(
|
|
2409
|
+
"/inventory/products/:product_id/availability",
|
|
2410
|
+
(c) => tryRoute(c, () => deps.inventory.productAvailability(c.req.param("product_id"), getQueryNum(c, "quantity") ?? 1))
|
|
2411
|
+
);
|
|
2412
|
+
app.get(
|
|
2413
|
+
"/inventory/variants/:variant_id/availability",
|
|
2414
|
+
(c) => tryRoute(c, () => deps.inventory.variantAvailability(c.req.param("variant_id"), getQueryNum(c, "quantity") ?? 1))
|
|
2415
|
+
);
|
|
2353
2416
|
app.get(
|
|
2354
2417
|
"/delivery/fee",
|
|
2355
2418
|
(c) => tryRoute(
|
|
@@ -3373,7 +3436,14 @@ function createAuthService(opts) {
|
|
|
3373
3436
|
},
|
|
3374
3437
|
status(token) {
|
|
3375
3438
|
const s = this.resolveSession(token);
|
|
3376
|
-
|
|
3439
|
+
if (!s) return { is_authenticated: false, user_type: "guest" };
|
|
3440
|
+
return {
|
|
3441
|
+
is_authenticated: s.is_authenticated,
|
|
3442
|
+
account_id: s.customer_id,
|
|
3443
|
+
user_type: s.is_authenticated ? "customer" : "guest",
|
|
3444
|
+
session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
|
|
3445
|
+
session: s
|
|
3446
|
+
};
|
|
3377
3447
|
}
|
|
3378
3448
|
};
|
|
3379
3449
|
}
|
|
@@ -4177,13 +4247,22 @@ function computeBundlePrice(bundle, componentSum) {
|
|
|
4177
4247
|
}
|
|
4178
4248
|
return componentSum;
|
|
4179
4249
|
}
|
|
4180
|
-
function computeCompositePrice(composite, componentsTotal, highestUnit) {
|
|
4250
|
+
function computeCompositePrice(composite, componentsTotal, highestUnit, perGroupHighest, selectionCount) {
|
|
4181
4251
|
switch (composite.pricing_mode) {
|
|
4182
4252
|
case "highest_overall":
|
|
4183
4253
|
return add(composite.base_price, highestUnit ?? "0.00");
|
|
4254
|
+
case "highest_per_group": {
|
|
4255
|
+
let sum = "0.00";
|
|
4256
|
+
for (const v of Object.values(perGroupHighest ?? {})) sum = add(sum, v);
|
|
4257
|
+
return add(composite.base_price, sum);
|
|
4258
|
+
}
|
|
4259
|
+
case "tiered": {
|
|
4260
|
+
const tiers = composite.tiers ?? [];
|
|
4261
|
+
const count = selectionCount ?? 0;
|
|
4262
|
+
const matched = tiers.filter((t) => count >= t.min_count).sort((a, b) => b.min_count - a.min_count)[0];
|
|
4263
|
+
return matched ? add(composite.base_price, matched.price) : add(composite.base_price, componentsTotal);
|
|
4264
|
+
}
|
|
4184
4265
|
case "additive":
|
|
4185
|
-
case "highest_per_group":
|
|
4186
|
-
case "tiered":
|
|
4187
4266
|
default:
|
|
4188
4267
|
return add(composite.base_price, componentsTotal);
|
|
4189
4268
|
}
|
|
@@ -4317,7 +4396,11 @@ function toUICart(registry, cart) {
|
|
|
4317
4396
|
// Billing / discounts / metadata
|
|
4318
4397
|
billing_plan_id: i.billing_plan_id,
|
|
4319
4398
|
applied_discount_ids: i.applied_discount_ids,
|
|
4320
|
-
discount_details:
|
|
4399
|
+
discount_details: i.applied_discount_ids.length > 0 ? {
|
|
4400
|
+
applied_count: i.applied_discount_ids.length,
|
|
4401
|
+
total_amount: i.item_discount_amount,
|
|
4402
|
+
discount_ids: i.applied_discount_ids
|
|
4403
|
+
} : void 0,
|
|
4321
4404
|
metadata: i.metadata,
|
|
4322
4405
|
created_at: i.created_at,
|
|
4323
4406
|
updated_at: i.updated_at
|
|
@@ -4352,7 +4435,7 @@ function toUICart(registry, cart) {
|
|
|
4352
4435
|
var CART_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4353
4436
|
var ZERO2 = "0.00";
|
|
4354
4437
|
function createCartService(opts) {
|
|
4355
|
-
const { registry, clock, ids, bus } = opts;
|
|
4438
|
+
const { registry, clock, ids, bus, inventory } = opts;
|
|
4356
4439
|
const findOrCreate = (session) => {
|
|
4357
4440
|
const existing = registry.carts.list(
|
|
4358
4441
|
(c) => c.session_id === session.id && c.status === "active"
|
|
@@ -4434,7 +4517,12 @@ function createCartService(opts) {
|
|
|
4434
4517
|
}
|
|
4435
4518
|
const { stored, componentsTotal } = resolveCompositeSelections(registry, selections, composite.groups);
|
|
4436
4519
|
const highestUnit = stored.reduce((max, s) => parseFloat(s.unit_price) > parseFloat(max) ? s.unit_price : max, ZERO2);
|
|
4437
|
-
const
|
|
4520
|
+
const perGroupHighest = {};
|
|
4521
|
+
for (const s of stored) {
|
|
4522
|
+
const cur = perGroupHighest[s.group_id];
|
|
4523
|
+
if (!cur || parseFloat(s.unit_price) > parseFloat(cur)) perGroupHighest[s.group_id] = s.unit_price;
|
|
4524
|
+
}
|
|
4525
|
+
const finalPrice = computeCompositePrice(composite, componentsTotal, highestUnit, perGroupHighest, totalQty);
|
|
4438
4526
|
return {
|
|
4439
4527
|
unitPrice: finalPrice,
|
|
4440
4528
|
configuration: {
|
|
@@ -4512,6 +4600,7 @@ function createCartService(opts) {
|
|
|
4512
4600
|
bundleSelections: body.bundle_selections,
|
|
4513
4601
|
compositeSelections: body.composite_selections
|
|
4514
4602
|
});
|
|
4603
|
+
inventory?.reserve(body.item_id, body.variant_id, body.quantity);
|
|
4515
4604
|
const existing = cart.items.find((i) => i.line_key === lineKey);
|
|
4516
4605
|
if (existing) {
|
|
4517
4606
|
existing.quantity += body.quantity;
|
|
@@ -4551,9 +4640,13 @@ function createCartService(opts) {
|
|
|
4551
4640
|
const cart = findOrCreate(session);
|
|
4552
4641
|
const item = cart.items.find((i) => i.id === cartItemId);
|
|
4553
4642
|
if (!item) throw notFound(`Cart item ${cartItemId} not found`);
|
|
4643
|
+
const delta = quantity - item.quantity;
|
|
4554
4644
|
if (quantity <= 0) {
|
|
4645
|
+
inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4555
4646
|
cart.items = cart.items.filter((i) => i.id !== cartItemId);
|
|
4556
4647
|
} else {
|
|
4648
|
+
if (delta > 0) inventory?.reserve(item.item_id, item.variant_id, delta);
|
|
4649
|
+
else if (delta < 0) inventory?.release(item.item_id, item.variant_id, -delta);
|
|
4557
4650
|
item.quantity = quantity;
|
|
4558
4651
|
item.updated_at = clock.iso();
|
|
4559
4652
|
}
|
|
@@ -4562,12 +4655,15 @@ function createCartService(opts) {
|
|
|
4562
4655
|
},
|
|
4563
4656
|
removeItem(session, cartItemId) {
|
|
4564
4657
|
const cart = findOrCreate(session);
|
|
4658
|
+
const item = cart.items.find((i) => i.id === cartItemId);
|
|
4659
|
+
if (item) inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4565
4660
|
cart.items = cart.items.filter((i) => i.id !== cartItemId);
|
|
4566
4661
|
const updated = recalc(cart);
|
|
4567
4662
|
return { success: true, cart: toUICart(registry, updated) };
|
|
4568
4663
|
},
|
|
4569
4664
|
clear(session) {
|
|
4570
4665
|
const cart = findOrCreate(session);
|
|
4666
|
+
for (const item of cart.items) inventory?.release(item.item_id, item.variant_id, item.quantity);
|
|
4571
4667
|
cart.items = [];
|
|
4572
4668
|
cart.applied_discount_codes = [];
|
|
4573
4669
|
cart.applied_discount_ids = [];
|
|
@@ -4643,18 +4739,24 @@ function createCheckoutService(opts) {
|
|
|
4643
4739
|
bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
|
|
4644
4740
|
bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
|
|
4645
4741
|
}
|
|
4742
|
+
const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
4646
4743
|
const result = {
|
|
4647
4744
|
order_id: orderId,
|
|
4648
4745
|
order_number: orderNumber,
|
|
4649
4746
|
payment_status: requiresAuth ? "pending" : "succeeded",
|
|
4650
4747
|
payment_reference: reference,
|
|
4651
4748
|
requires_authorization: requiresAuth,
|
|
4749
|
+
authorization_type: requiresAuth ? "otp" : void 0,
|
|
4652
4750
|
next_action: requiresAuth ? { type: "authorization", authorization_type: "otp", display_text: "Approve on your mobile money app" } : { type: "none" },
|
|
4653
|
-
provider: body.payment_method
|
|
4751
|
+
provider: body.payment_method,
|
|
4752
|
+
bill_token: billToken
|
|
4654
4753
|
};
|
|
4655
4754
|
if (isCard) {
|
|
4656
4755
|
result.next_action = { type: "redirect", authorization_url: `https://mock.cimplify.io/pay/${reference}` };
|
|
4657
4756
|
result.authorization_url = `https://mock.cimplify.io/pay/${reference}`;
|
|
4757
|
+
result.client_secret = `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}`;
|
|
4758
|
+
const proc = globalThis.process;
|
|
4759
|
+
result.public_key = proc?.env?.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? "pk_test_mock_publishable_key";
|
|
4658
4760
|
}
|
|
4659
4761
|
return result;
|
|
4660
4762
|
},
|
|
@@ -4860,19 +4962,66 @@ function createSchedulingService(opts) {
|
|
|
4860
4962
|
}
|
|
4861
4963
|
|
|
4862
4964
|
// src/mock/domain/inventory.ts
|
|
4863
|
-
|
|
4965
|
+
var DEFAULT_INITIAL_STOCK = 50;
|
|
4966
|
+
function createInventoryService(opts) {
|
|
4967
|
+
const { registry } = opts;
|
|
4968
|
+
const stockMap = /* @__PURE__ */ new Map();
|
|
4969
|
+
const stockKey = (productId, variantId) => variantId ? `${productId}:${variantId}` : productId;
|
|
4970
|
+
const initialStock = (productId) => {
|
|
4971
|
+
const product = registry.products.get(productId);
|
|
4972
|
+
if (!product) return 0;
|
|
4973
|
+
if (product.product_type === "service" || product.product_type === "digital") {
|
|
4974
|
+
return Number.MAX_SAFE_INTEGER;
|
|
4975
|
+
}
|
|
4976
|
+
const seeded = product.metadata?.["stock_level"];
|
|
4977
|
+
if (typeof seeded === "number") return seeded;
|
|
4978
|
+
return DEFAULT_INITIAL_STOCK;
|
|
4979
|
+
};
|
|
4980
|
+
const ensure = (productId, variantId) => {
|
|
4981
|
+
const key = stockKey(productId, variantId);
|
|
4982
|
+
if (!stockMap.has(key)) stockMap.set(key, initialStock(productId));
|
|
4983
|
+
return key;
|
|
4984
|
+
};
|
|
4864
4985
|
return {
|
|
4865
4986
|
productStock(productId) {
|
|
4866
|
-
|
|
4987
|
+
const key = ensure(productId);
|
|
4988
|
+
const stock_level = stockMap.get(key) ?? 0;
|
|
4989
|
+
return { product_id: productId, stock_level, in_stock: stock_level > 0 };
|
|
4867
4990
|
},
|
|
4868
4991
|
variantStock(variantId) {
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4992
|
+
const variant = registry.variants.get(variantId);
|
|
4993
|
+
const productId = variant?.product_id ?? "";
|
|
4994
|
+
const key = ensure(productId, variantId);
|
|
4995
|
+
const stock_level = stockMap.get(key) ?? 0;
|
|
4996
|
+
return { variant_id: variantId, stock_level, in_stock: stock_level > 0 };
|
|
4997
|
+
},
|
|
4998
|
+
productAvailability(productId, quantity = 1) {
|
|
4999
|
+
const key = ensure(productId);
|
|
5000
|
+
const have = stockMap.get(key) ?? 0;
|
|
5001
|
+
return { product_id: productId, quantity, is_available: have >= quantity };
|
|
5002
|
+
},
|
|
5003
|
+
variantAvailability(variantId, quantity = 1) {
|
|
5004
|
+
const variant = registry.variants.get(variantId);
|
|
5005
|
+
const productId = variant?.product_id ?? "";
|
|
5006
|
+
const key = ensure(productId, variantId);
|
|
5007
|
+
const have = stockMap.get(key) ?? 0;
|
|
5008
|
+
return { variant_id: variantId, quantity, is_available: have >= quantity };
|
|
5009
|
+
},
|
|
5010
|
+
reserve(productId, variantId, quantity) {
|
|
5011
|
+
const product = registry.products.get(productId);
|
|
5012
|
+
if (!product) return;
|
|
5013
|
+
if (product.product_type === "service" || product.product_type === "digital") return;
|
|
5014
|
+
const key = ensure(productId, variantId);
|
|
5015
|
+
const have = stockMap.get(key) ?? 0;
|
|
5016
|
+
stockMap.set(key, Math.max(0, have - quantity));
|
|
4873
5017
|
},
|
|
4874
|
-
|
|
4875
|
-
|
|
5018
|
+
release(productId, variantId, quantity) {
|
|
5019
|
+
const product = registry.products.get(productId);
|
|
5020
|
+
if (!product) return;
|
|
5021
|
+
if (product.product_type === "service" || product.product_type === "digital") return;
|
|
5022
|
+
const key = stockKey(productId, variantId);
|
|
5023
|
+
const have = stockMap.get(key) ?? 0;
|
|
5024
|
+
stockMap.set(key, have + quantity);
|
|
4876
5025
|
}
|
|
4877
5026
|
};
|
|
4878
5027
|
}
|
|
@@ -8624,12 +8773,12 @@ function createDeps(options = {}) {
|
|
|
8624
8773
|
});
|
|
8625
8774
|
const business = createBusinessService({ registry, defaultBusinessId });
|
|
8626
8775
|
const catalogue = createCatalogueService({ registry, clock, ids });
|
|
8627
|
-
const
|
|
8776
|
+
const inventory = createInventoryService({ registry });
|
|
8777
|
+
const cart = createCartService({ registry, clock, ids, bus, inventory });
|
|
8628
8778
|
const orders = createOrderService({ registry, clock, bus });
|
|
8629
8779
|
const checkout = createCheckoutService({ registry, clock, ids, bus});
|
|
8630
8780
|
const subscriptions = createSubscriptionService({ registry, clock, bus });
|
|
8631
8781
|
const scheduling = createSchedulingService({ registry, clock, bus });
|
|
8632
|
-
const inventory = createInventoryService();
|
|
8633
8782
|
const fx = createFxService({ registry, clock, ids });
|
|
8634
8783
|
const places = createPlacesService();
|
|
8635
8784
|
const delivery = createDeliveryService();
|
package/dist/mock/msw.d.mts
CHANGED
package/dist/mock/msw.d.ts
CHANGED