@01.software/init 0.10.0 → 0.10.1

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.
@@ -1,7 +1,7 @@
1
- import type { CustomerSnapshot, ShippingAddress } from "../commerce/types.ts";
1
+ import type { CustomerSnapshot, ShippingAddress } from '../commerce/types.ts'
2
2
 
3
- const MAX_TEXT_LENGTH = 160;
4
- const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3
+ const MAX_TEXT_LENGTH = 160
4
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
5
5
 
6
6
  /**
7
7
  * Checkout collects only the buyer + shipping snapshot. The cart contents live
@@ -9,68 +9,78 @@ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
9
  * client-supplied lines or totals — the cart is the quote.
10
10
  */
11
11
  export type CheckoutPayload = {
12
- customerSnapshot: CustomerSnapshot;
13
- shippingAddress: ShippingAddress;
14
- };
12
+ customerSnapshot: CustomerSnapshot
13
+ shippingAddress?: ShippingAddress
14
+ }
15
15
 
16
- export function parseCheckoutPayload(input: unknown): CheckoutPayload {
17
- if (!input || typeof input !== "object") {
18
- throw new Error("Checkout payload must be an object");
16
+ export function parseCheckoutPayload(
17
+ input: unknown,
18
+ options: { requiresShipping?: boolean } = {},
19
+ ): CheckoutPayload {
20
+ if (!input || typeof input !== 'object') {
21
+ throw new Error('Checkout payload must be an object')
19
22
  }
20
23
 
21
24
  const body = input as {
22
- customerSnapshot?: unknown;
23
- shippingAddress?: unknown;
24
- };
25
- const customer = parseRecord(body.customerSnapshot, "customerSnapshot");
26
- const shipping = parseRecord(body.shippingAddress, "shippingAddress");
27
- const email = requiredString(customer.email, "email");
25
+ customerSnapshot?: unknown
26
+ shippingAddress?: unknown
27
+ }
28
+ const customer = parseRecord(body.customerSnapshot, 'customerSnapshot')
29
+ const email = requiredString(customer.email, 'email')
28
30
  if (!EMAIL_RE.test(email)) {
29
- throw new Error("email must be a valid email address");
31
+ throw new Error('email must be a valid email address')
30
32
  }
31
33
 
34
+ const customerSnapshot = {
35
+ name: requiredString(customer.name, 'customer name'),
36
+ email,
37
+ phone: requiredString(customer.phone, 'customer phone'),
38
+ }
39
+
40
+ if (options.requiresShipping === false) {
41
+ return { customerSnapshot }
42
+ }
43
+
44
+ const shipping = parseRecord(body.shippingAddress, 'shippingAddress')
45
+
32
46
  return {
33
- customerSnapshot: {
34
- name: requiredString(customer.name, "customer name"),
35
- email,
36
- phone: requiredString(customer.phone, "customer phone"),
37
- },
47
+ customerSnapshot,
38
48
  shippingAddress: {
39
- recipientName: requiredString(shipping.recipientName, "recipient name"),
40
- phone: requiredString(shipping.phone, "shipping phone"),
41
- postalCode: requiredString(shipping.postalCode, "postal code"),
42
- address: requiredString(shipping.address, "address"),
43
- detailAddress: requiredString(shipping.detailAddress, "address detail"),
49
+ recipientName: requiredString(shipping.recipientName, 'recipient name'),
50
+ phone: requiredString(shipping.phone, 'shipping phone'),
51
+ postalCode: requiredString(shipping.postalCode, 'postal code'),
52
+ address: requiredString(shipping.address, 'address'),
53
+ detailAddress: requiredString(shipping.detailAddress, 'address detail'),
44
54
  deliveryMessage: optionalString(
45
55
  shipping.deliveryMessage,
46
- "shipping message",
56
+ 'shipping message',
47
57
  ),
48
58
  },
49
- };
59
+ }
50
60
  }
51
61
 
52
62
  function parseRecord(input: unknown, field: string): Record<string, unknown> {
53
- if (!input || typeof input !== "object" || Array.isArray(input)) {
54
- throw new Error(`${field} must be an object`);
63
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
64
+ throw new Error(`${field} must be an object`)
55
65
  }
56
66
 
57
- return input as Record<string, unknown>;
67
+ return input as Record<string, unknown>
58
68
  }
59
69
 
60
70
  function requiredString(input: unknown, field: string): string {
61
- const value = optionalString(input, field);
62
- if (!value) throw new Error(`${field} is required`);
63
- return value;
71
+ const value = optionalString(input, field)
72
+ if (!value) throw new Error(`${field} is required`)
73
+ return value
64
74
  }
65
75
 
66
76
  function optionalString(input: unknown, field: string): string {
67
- if (input === undefined || input === null) return "";
68
- if (typeof input !== "string") throw new Error(`${field} must be a string`);
77
+ if (input === undefined || input === null) return ''
78
+ if (typeof input !== 'string') throw new Error(`${field} must be a string`)
69
79
 
70
- const value = input.trim();
80
+ const value = input.trim()
71
81
  if (value.length > MAX_TEXT_LENGTH) {
72
- throw new Error(`${field} must be ${MAX_TEXT_LENGTH} characters or fewer`);
82
+ throw new Error(`${field} must be ${MAX_TEXT_LENGTH} characters or fewer`)
73
83
  }
74
84
 
75
- return value;
85
+ return value
76
86
  }
@@ -28,11 +28,21 @@ export async function startCheckout(input: {
28
28
  commerceProvider: CheckoutCommerceProvider
29
29
  paymentProvider: PaymentProvider
30
30
  }): Promise<StartCheckoutResult> {
31
- const payload: CheckoutPayload = parseCheckoutPayload(input.payload)
31
+ const cart = await input.commerceProvider.getCart(input.cartToken)
32
+ if (!cart) throw new Error('Cart not found')
33
+
34
+ const requiresShipping = cart.items.some(
35
+ (item) => item.requiresShipping !== false,
36
+ )
37
+ const payload: CheckoutPayload = parseCheckoutPayload(input.payload, {
38
+ requiresShipping,
39
+ })
32
40
  const pending = await input.commerceProvider.checkoutCart({
33
41
  cartToken: input.cartToken,
34
42
  customerSnapshot: payload.customerSnapshot,
35
- shippingAddress: payload.shippingAddress,
43
+ ...(payload.shippingAddress
44
+ ? { shippingAddress: payload.shippingAddress }
45
+ : {}),
36
46
  })
37
47
 
38
48
  const payment = await input.paymentProvider.requestPayment({
@@ -1,3 +1,6 @@
1
- import type { CommerceProvider } from "../commerce/provider.ts";
1
+ import type { CommerceProvider } from '../commerce/provider.ts'
2
2
 
3
- export type CheckoutCommerceProvider = Pick<CommerceProvider, "checkoutCart">;
3
+ export type CheckoutCommerceProvider = Pick<
4
+ CommerceProvider,
5
+ 'checkoutCart' | 'getCart'
6
+ >
@@ -1,7 +1,7 @@
1
- import seed from "../../../data/mock-catalog.json" with { type: "json" };
2
- import { normalizeCartLines } from "../../cart/normalize.ts";
3
- import type { CommerceProvider } from "../provider.ts";
4
- import { checkVariantStock } from "../stock.ts";
1
+ import seed from '../../../data/mock-catalog.json' with { type: 'json' }
2
+ import { normalizeCartLines } from '../../cart/normalize.ts'
3
+ import type { CommerceProvider } from '../provider.ts'
4
+ import { checkVariantStock } from '../stock.ts'
5
5
  import type {
6
6
  CartItemView,
7
7
  CartView,
@@ -11,7 +11,7 @@ import type {
11
11
  ProductDetail,
12
12
  ProductVariant,
13
13
  ShippingPolicy,
14
- } from "../types.ts";
14
+ } from '../types.ts'
15
15
 
16
16
  /**
17
17
  * In-memory server-cart emulation for the zero-backend demo. The mock stands in
@@ -20,41 +20,42 @@ import type {
20
20
  * lifetime of the dev server process — there is no file-based index.
21
21
  */
22
22
  type MockCartItem = {
23
- cartItemId: string;
24
- productId: string;
25
- variantId: string;
26
- optionId?: string;
27
- quantity: number;
28
- unitPrice: number;
29
- };
23
+ cartItemId: string
24
+ productId: string
25
+ variantId: string
26
+ optionId?: string
27
+ quantity: number
28
+ unitPrice: number
29
+ requiresShipping?: boolean | null
30
+ }
30
31
 
31
32
  type MockCart = {
32
- id: string;
33
- cartToken: string;
34
- currency: string;
35
- items: MockCartItem[];
36
- };
33
+ id: string
34
+ cartToken: string
35
+ currency: string
36
+ items: MockCartItem[]
37
+ }
37
38
 
38
- const carts = new Map<string, MockCart>();
39
- const orders = new Map<string, Order>();
39
+ const carts = new Map<string, MockCart>()
40
+ const orders = new Map<string, Order>()
40
41
 
41
42
  export function createMockCommerceProvider(): CommerceProvider {
42
- const products = seed.products as ProductDetail[];
43
- const shippingPolicy = seed.shippingPolicy as ShippingPolicy;
44
- const variants = allVariants(products);
45
- const variantById = new Map(variants.map((variant) => [variant.id, variant]));
43
+ const products = seed.products as ProductDetail[]
44
+ const shippingPolicy = seed.shippingPolicy as ShippingPolicy
45
+ const variants = allVariants(products)
46
+ const variantById = new Map(variants.map((variant) => [variant.id, variant]))
46
47
 
47
48
  function viewCart(cart: MockCart): CartView {
48
49
  const items: CartItemView[] = cart.items.map((item) => {
49
- const variant = variantById.get(item.variantId);
50
+ const variant = variantById.get(item.variantId)
50
51
  const detail = products.find(
51
52
  (entry) => entry.product.id === item.productId,
52
- );
53
+ )
53
54
  const image =
54
55
  variant?.images[0] ??
55
56
  detail?.product.thumbnail ??
56
57
  detail?.product.images[0] ??
57
- null;
58
+ null
58
59
  return {
59
60
  cartItemId: item.cartItemId,
60
61
  productId: item.productId,
@@ -62,20 +63,24 @@ export function createMockCommerceProvider(): CommerceProvider {
62
63
  quantity: item.quantity,
63
64
  unitAmount: item.unitPrice,
64
65
  lineAmount: item.unitPrice * item.quantity,
65
- productTitle: detail?.product.title ?? "Unknown product",
66
+ requiresShipping: item.requiresShipping,
67
+ productTitle: detail?.product.title ?? 'Unknown product',
66
68
  variantTitle: variant?.title,
67
69
  image,
68
- };
69
- });
70
+ }
71
+ })
70
72
 
71
- const subtotalAmount = items.reduce((sum, item) => sum + item.lineAmount, 0);
73
+ const subtotalAmount = items.reduce((sum, item) => sum + item.lineAmount, 0)
74
+ const hasShippableItems = items.some(
75
+ (item) => item.requiresShipping !== false,
76
+ )
72
77
  const shippingAmount =
73
- shippingPolicy.freeAboveAmount !== undefined &&
74
- subtotalAmount >= shippingPolicy.freeAboveAmount
78
+ !hasShippableItems || subtotalAmount === 0
75
79
  ? 0
76
- : subtotalAmount > 0
77
- ? shippingPolicy.baseAmount
78
- : 0;
80
+ : shippingPolicy.freeAboveAmount !== undefined &&
81
+ subtotalAmount >= shippingPolicy.freeAboveAmount
82
+ ? 0
83
+ : shippingPolicy.baseAmount
79
84
 
80
85
  return {
81
86
  id: cart.id,
@@ -85,86 +90,88 @@ export function createMockCommerceProvider(): CommerceProvider {
85
90
  discountAmount: 0,
86
91
  totalAmount: subtotalAmount + shippingAmount,
87
92
  items,
88
- };
93
+ }
89
94
  }
90
95
 
91
96
  function requireCart(cartToken: string): MockCart {
92
- const cart = carts.get(cartToken);
93
- if (!cart) throw new Error("Cart not found");
94
- return cart;
97
+ const cart = carts.get(cartToken)
98
+ if (!cart) throw new Error('Cart not found')
99
+ return cart
95
100
  }
96
101
 
97
102
  return {
98
103
  async listProducts(input) {
99
- const limit = input?.limit ?? 24;
104
+ const limit = input?.limit ?? 24
100
105
  const publishedProducts = products.filter(
101
- (entry) => entry.product.status === "published",
102
- );
106
+ (entry) => entry.product.status === 'published',
107
+ )
103
108
  return {
104
109
  products: publishedProducts.slice(0, limit),
105
110
  total: publishedProducts.length,
106
- };
111
+ }
107
112
  },
108
113
 
109
114
  async getProductBySlug(slug) {
110
115
  return (
111
116
  products.find(
112
117
  (entry) =>
113
- entry.product.slug === slug &&
114
- entry.product.status === "published",
118
+ entry.product.slug === slug && entry.product.status === 'published',
115
119
  ) ?? null
116
- );
120
+ )
117
121
  },
118
122
 
119
123
  async getVariantsByIds(ids) {
120
- const idSet = new Set(ids);
121
- return variants.filter((variant) => idSet.has(variant.id));
124
+ const idSet = new Set(ids)
125
+ return variants.filter((variant) => idSet.has(variant.id))
122
126
  },
123
127
 
124
128
  async getShippingPolicy() {
125
- return shippingPolicy;
129
+ return shippingPolicy
126
130
  },
127
131
 
128
132
  async checkStock(items) {
129
133
  return checkVariantStock({
130
134
  lines: normalizeCartLines(items),
131
135
  variants,
132
- });
136
+ })
133
137
  },
134
138
 
135
139
  async createCart() {
136
- const cartToken = `mock_cart_${crypto.randomUUID()}`;
140
+ const cartToken = `mock_cart_${crypto.randomUUID()}`
137
141
  carts.set(cartToken, {
138
142
  id: `cart_${crypto.randomUUID()}`,
139
143
  cartToken,
140
144
  currency: shippingPolicy.currency,
141
145
  items: [],
142
- });
143
- return { cartToken };
146
+ })
147
+ return { cartToken }
144
148
  },
145
149
 
146
150
  async getCart(cartToken) {
147
- const cart = carts.get(cartToken);
148
- return cart ? viewCart(cart) : null;
151
+ const cart = carts.get(cartToken)
152
+ return cart ? viewCart(cart) : null
149
153
  },
150
154
 
151
155
  async addCartItem({ cartToken, item }) {
152
- const cart = requireCart(cartToken);
153
- const variant = variantById.get(item.variantId);
154
- if (!variant) throw new Error("Variant not found");
156
+ const cart = requireCart(cartToken)
157
+ const variant = variantById.get(item.variantId)
158
+ if (!variant) throw new Error('Variant not found')
155
159
 
156
160
  const existing = cart.items.find(
157
161
  (line) => line.variantId === item.variantId,
158
- );
159
- const nextQuantity = (existing?.quantity ?? 0) + item.quantity;
162
+ )
163
+ const nextQuantity = (existing?.quantity ?? 0) + item.quantity
160
164
  const stock = checkVariantStock({
161
165
  lines: [{ variantId: item.variantId, quantity: nextQuantity }],
162
166
  variants,
163
- });
164
- if (!stock.ok) throw new Error("Insufficient stock");
167
+ })
168
+ if (!stock.ok) throw new Error('Insufficient stock')
165
169
 
166
170
  if (existing) {
167
- existing.quantity = Math.min(99, nextQuantity);
171
+ existing.quantity = Math.min(99, nextQuantity)
172
+ existing.requiresShipping =
173
+ existing.requiresShipping !== false ||
174
+ variant.requiresShipping !== false
168
175
  } else {
169
176
  cart.items.push({
170
177
  cartItemId: `item_${crypto.randomUUID()}`,
@@ -173,110 +180,111 @@ export function createMockCommerceProvider(): CommerceProvider {
173
180
  optionId: item.optionId,
174
181
  quantity: Math.min(99, item.quantity),
175
182
  unitPrice: variant.price,
176
- });
183
+ requiresShipping: variant.requiresShipping !== false,
184
+ })
177
185
  }
178
- return viewCart(cart);
186
+ return viewCart(cart)
179
187
  },
180
188
 
181
189
  async updateCartItem({ cartToken, cartItemId, quantity }) {
182
- const cart = requireCart(cartToken);
183
- const line = cart.items.find((entry) => entry.cartItemId === cartItemId);
184
- if (!line) throw new Error("Cart item not found");
190
+ const cart = requireCart(cartToken)
191
+ const line = cart.items.find((entry) => entry.cartItemId === cartItemId)
192
+ if (!line) throw new Error('Cart item not found')
185
193
  if (quantity <= 0) {
186
194
  cart.items = cart.items.filter(
187
195
  (entry) => entry.cartItemId !== cartItemId,
188
- );
196
+ )
189
197
  } else {
190
- line.quantity = Math.min(99, Math.floor(quantity));
198
+ line.quantity = Math.min(99, Math.floor(quantity))
191
199
  }
192
- return viewCart(cart);
200
+ return viewCart(cart)
193
201
  },
194
202
 
195
203
  async removeCartItem({ cartToken, cartItemId }) {
196
- const cart = requireCart(cartToken);
197
- cart.items = cart.items.filter((entry) => entry.cartItemId !== cartItemId);
198
- return viewCart(cart);
204
+ const cart = requireCart(cartToken)
205
+ cart.items = cart.items.filter((entry) => entry.cartItemId !== cartItemId)
206
+ return viewCart(cart)
199
207
  },
200
208
 
201
209
  async clearCart({ cartToken }) {
202
- const cart = requireCart(cartToken);
203
- cart.items = [];
204
- return viewCart(cart);
210
+ const cart = requireCart(cartToken)
211
+ cart.items = []
212
+ return viewCart(cart)
205
213
  },
206
214
 
207
215
  async checkoutCart(input) {
208
- return checkoutMockCart({ input, products, viewCart, requireCart });
216
+ return checkoutMockCart({ input, products, viewCart, requireCart })
209
217
  },
210
218
 
211
219
  async getOrderByPaymentId({ paymentId }) {
212
- return orders.get(paymentId) ?? null;
220
+ return orders.get(paymentId) ?? null
213
221
  },
214
222
 
215
223
  async confirmOrderPayment(input) {
216
- const order = orders.get(input.paymentId);
217
- if (!order) throw new Error("Order not found");
224
+ const order = orders.get(input.paymentId)
225
+ if (!order) throw new Error('Order not found')
218
226
  const paidOrder: Order = {
219
227
  ...order,
220
- displayStatus: "paid",
228
+ displayStatus: 'paid',
221
229
  transactions: order.transactions.map((transaction) =>
222
230
  transaction.paymentId === input.paymentId
223
- ? { ...transaction, provider: input.provider, status: "paid" }
231
+ ? { ...transaction, provider: input.provider, status: 'paid' }
224
232
  : transaction,
225
233
  ),
226
- };
227
- orders.set(input.paymentId, paidOrder);
228
- return paidOrder;
234
+ }
235
+ orders.set(input.paymentId, paidOrder)
236
+ return paidOrder
229
237
  },
230
238
 
231
239
  async markPaymentFailed(input) {
232
- const order = orders.get(input.paymentId);
233
- if (!order) return null;
240
+ const order = orders.get(input.paymentId)
241
+ if (!order) return null
234
242
  const failedOrder: Order = {
235
243
  ...order,
236
244
  transactions: order.transactions.map((transaction) =>
237
245
  transaction.paymentId === input.paymentId
238
- ? { ...transaction, provider: input.provider, status: "failed" }
246
+ ? { ...transaction, provider: input.provider, status: 'failed' }
239
247
  : transaction,
240
248
  ),
241
- };
242
- orders.set(input.paymentId, failedOrder);
243
- return failedOrder;
249
+ }
250
+ orders.set(input.paymentId, failedOrder)
251
+ return failedOrder
244
252
  },
245
253
 
246
254
  async cancelPendingOrder(input) {
247
- const order = orders.get(input.paymentId);
248
- if (!order) return null;
255
+ const order = orders.get(input.paymentId)
256
+ if (!order) return null
249
257
  const canceledOrder: Order = {
250
258
  ...order,
251
- displayStatus: "canceled",
259
+ displayStatus: 'canceled',
252
260
  transactions: order.transactions.map((transaction) =>
253
261
  transaction.paymentId === input.paymentId
254
- ? { ...transaction, provider: input.provider, status: "canceled" }
262
+ ? { ...transaction, provider: input.provider, status: 'canceled' }
255
263
  : transaction,
256
264
  ),
257
- };
258
- orders.set(input.paymentId, canceledOrder);
259
- return canceledOrder;
265
+ }
266
+ orders.set(input.paymentId, canceledOrder)
267
+ return canceledOrder
260
268
  },
261
- };
269
+ }
262
270
  }
263
271
 
264
272
  function checkoutMockCart(args: {
265
- input: CheckoutCartInput;
266
- products: ProductDetail[];
267
- viewCart: (cart: MockCart) => CartView;
268
- requireCart: (cartToken: string) => MockCart;
273
+ input: CheckoutCartInput
274
+ products: ProductDetail[]
275
+ viewCart: (cart: MockCart) => CartView
276
+ requireCart: (cartToken: string) => MockCart
269
277
  }): CreatePendingOrderResult {
270
- const cart = args.requireCart(args.input.cartToken);
271
- const view = args.viewCart(cart);
272
- if (view.items.length === 0) throw new Error("Cart is empty");
278
+ const cart = args.requireCart(args.input.cartToken)
279
+ const view = args.viewCart(cart)
280
+ if (view.items.length === 0) throw new Error('Cart is empty')
273
281
 
274
- const paymentId = `mock_${Date.now()}_${Math.random().toString(16).slice(2)}`;
275
- const orderNumber = `ORD-${Date.now().toString().slice(-8)}`;
282
+ const paymentId = `mock_${Date.now()}_${Math.random().toString(16).slice(2)}`
283
+ const orderNumber = `ORD-${Date.now().toString().slice(-8)}`
276
284
  const order: Order = {
277
285
  id: `order_${paymentId}`,
278
286
  orderNumber,
279
- displayStatus: "pending",
287
+ displayStatus: 'pending',
280
288
  items: view.items.map((item) => ({
281
289
  variantId: item.variantId,
282
290
  productTitle: item.productTitle,
@@ -286,24 +294,26 @@ function checkoutMockCart(args: {
286
294
  lineAmount: item.lineAmount,
287
295
  })),
288
296
  customerSnapshot: args.input.customerSnapshot,
289
- shippingAddress: args.input.shippingAddress,
297
+ ...(args.input.shippingAddress
298
+ ? { shippingAddress: args.input.shippingAddress }
299
+ : {}),
290
300
  subtotalAmount: view.subtotalAmount,
291
301
  shippingAmount: view.shippingAmount,
292
302
  totalAmount: view.totalAmount,
293
303
  transactions: [
294
304
  {
295
305
  paymentId,
296
- provider: "mock",
297
- status: "pending",
306
+ provider: 'mock',
307
+ status: 'pending',
298
308
  amount: view.totalAmount,
299
309
  },
300
310
  ],
301
- };
311
+ }
302
312
 
303
- orders.set(paymentId, order);
313
+ orders.set(paymentId, order)
304
314
  // The cart is consumed by checkout (Shopify cart→checkout boundary); the
305
315
  // browser cookie will resolve to no active cart and mint a fresh one.
306
- carts.delete(args.input.cartToken);
316
+ carts.delete(args.input.cartToken)
307
317
 
308
318
  return {
309
319
  order,
@@ -311,26 +321,26 @@ function checkoutMockCart(args: {
311
321
  paymentName: buildPaymentName(order.items.map((item) => item.productTitle)),
312
322
  amount: view.totalAmount,
313
323
  currency: view.currency,
314
- };
324
+ }
315
325
  }
316
326
 
317
327
  function allVariants(products: ProductDetail[]): ProductVariant[] {
318
- return products.flatMap((entry) => entry.variants);
328
+ return products.flatMap((entry) => entry.variants)
319
329
  }
320
330
 
321
331
  function buildPaymentName(productTitles: string[]): string {
322
- const [first, ...rest] = productTitles;
323
- if (!first) return "Order";
324
- return rest.length === 0 ? first : `${first} and ${rest.length} more`;
332
+ const [first, ...rest] = productTitles
333
+ if (!first) return 'Order'
334
+ return rest.length === 0 ? first : `${first} and ${rest.length} more`
325
335
  }
326
336
 
327
337
  /** Test helper: reset the in-memory stores between cases. */
328
338
  export function resetMockCommerceState(): void {
329
- carts.clear();
330
- orders.clear();
339
+ carts.clear()
340
+ orders.clear()
331
341
  }
332
342
 
333
343
  /** Test helper: read a stored mock order by its mock payment id. */
334
344
  export function getMockOrder(paymentId: string): Order | null {
335
- return orders.get(paymentId) ?? null;
345
+ return orders.get(paymentId) ?? null
336
346
  }