@stacksjs/commerce 0.70.54 → 0.70.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/package.json +3 -2
  2. package/src/carts/destroy.ts +54 -0
  3. package/src/carts/fetch.ts +20 -0
  4. package/src/carts/index.ts +13 -0
  5. package/src/carts/store.ts +75 -0
  6. package/src/carts/update.ts +78 -0
  7. package/src/coupons/destroy.ts +49 -0
  8. package/src/coupons/fetch.ts +510 -0
  9. package/src/coupons/index.ts +30 -0
  10. package/src/coupons/store.ts +46 -0
  11. package/src/coupons/update.ts +147 -0
  12. package/src/customers/destroy.ts +62 -0
  13. package/src/customers/fetch.ts +20 -0
  14. package/src/customers/index.ts +17 -0
  15. package/src/customers/store.ts +45 -0
  16. package/src/customers/update.ts +44 -0
  17. package/src/devices/destroy.ts +54 -0
  18. package/src/devices/export.ts +98 -0
  19. package/src/devices/fetch.ts +125 -0
  20. package/src/devices/index.ts +31 -0
  21. package/src/devices/store.ts +82 -0
  22. package/src/devices/update.ts +125 -0
  23. package/src/errors/destroy.ts +54 -0
  24. package/src/errors/fetch.ts +197 -0
  25. package/src/errors/index.ts +28 -0
  26. package/src/errors/update.ts +116 -0
  27. package/src/gift-cards/destroy.ts +87 -0
  28. package/src/gift-cards/fetch.ts +431 -0
  29. package/src/gift-cards/index.ts +26 -0
  30. package/src/gift-cards/store.ts +56 -0
  31. package/src/gift-cards/update.ts +102 -0
  32. package/src/index.ts +78 -0
  33. package/src/orders/destroy.ts +111 -0
  34. package/src/orders/events.ts +121 -0
  35. package/src/orders/export.ts +158 -0
  36. package/src/orders/fetch.ts +379 -0
  37. package/src/orders/guards.ts +261 -0
  38. package/src/orders/index.ts +91 -0
  39. package/src/orders/place-order.ts +274 -0
  40. package/src/orders/store.ts +51 -0
  41. package/src/orders/totals.ts +190 -0
  42. package/src/orders/update.ts +164 -0
  43. package/src/orders/webhook.ts +312 -0
  44. package/src/payments/destroy.ts +56 -0
  45. package/src/payments/fetch.ts +288 -0
  46. package/src/payments/index.ts +16 -0
  47. package/src/payments/store.ts +63 -0
  48. package/src/payments/update.ts +52 -0
  49. package/src/product.ts +0 -0
  50. package/src/products/categories/destroy.ts +103 -0
  51. package/src/products/categories/fetch.ts +285 -0
  52. package/src/products/categories/index.ts +36 -0
  53. package/src/products/categories/store.ts +87 -0
  54. package/src/products/categories/update.ts +251 -0
  55. package/src/products/index.ts +35 -0
  56. package/src/products/items/destroy.ts +54 -0
  57. package/src/products/items/fetch.ts +282 -0
  58. package/src/products/items/index.ts +26 -0
  59. package/src/products/items/store.ts +84 -0
  60. package/src/products/items/update.ts +324 -0
  61. package/src/products/manufacturers/destroy.ts +56 -0
  62. package/src/products/manufacturers/fetch.ts +117 -0
  63. package/src/products/manufacturers/index.ts +24 -0
  64. package/src/products/manufacturers/store.ts +82 -0
  65. package/src/products/manufacturers/update.ts +113 -0
  66. package/src/products/reviews/destroy.ts +56 -0
  67. package/src/products/reviews/fetch.ts +63 -0
  68. package/src/products/reviews/index.ts +25 -0
  69. package/src/products/reviews/store.ts +46 -0
  70. package/src/products/reviews/update.ts +82 -0
  71. package/src/products/units/destroy.ts +56 -0
  72. package/src/products/units/fetch.ts +20 -0
  73. package/src/products/units/index.ts +13 -0
  74. package/src/products/units/store.ts +162 -0
  75. package/src/products/units/update.ts +159 -0
  76. package/src/products/variants/destroy.ts +56 -0
  77. package/src/products/variants/fetch.ts +20 -0
  78. package/src/products/variants/index.ts +21 -0
  79. package/src/products/variants/store.ts +136 -0
  80. package/src/products/variants/update.ts +104 -0
  81. package/src/receipts/destroy.ts +54 -0
  82. package/src/receipts/export.ts +0 -0
  83. package/src/receipts/fetch.ts +222 -0
  84. package/src/receipts/index.ts +24 -0
  85. package/src/receipts/printer.ts +69 -0
  86. package/src/receipts/store.ts +69 -0
  87. package/src/receipts/update.ts +124 -0
  88. package/src/shippings/delivery-routes/destroy.ts +54 -0
  89. package/src/shippings/delivery-routes/fetch.ts +42 -0
  90. package/src/shippings/delivery-routes/index.ts +26 -0
  91. package/src/shippings/delivery-routes/store.ts +69 -0
  92. package/src/shippings/delivery-routes/update.ts +120 -0
  93. package/src/shippings/digital-deliveries/destroy.ts +111 -0
  94. package/src/shippings/digital-deliveries/fetch.ts +20 -0
  95. package/src/shippings/digital-deliveries/index.ts +26 -0
  96. package/src/shippings/digital-deliveries/store.ts +76 -0
  97. package/src/shippings/digital-deliveries/update.ts +129 -0
  98. package/src/shippings/drivers/destroy.ts +78 -0
  99. package/src/shippings/drivers/fetch.ts +20 -0
  100. package/src/shippings/drivers/index.ts +24 -0
  101. package/src/shippings/drivers/store.ts +69 -0
  102. package/src/shippings/drivers/update.ts +117 -0
  103. package/src/shippings/index.ts +39 -0
  104. package/src/shippings/license-keys/destroy.ts +111 -0
  105. package/src/shippings/license-keys/fetch.ts +20 -0
  106. package/src/shippings/license-keys/index.ts +26 -0
  107. package/src/shippings/license-keys/store.ts +77 -0
  108. package/src/shippings/license-keys/update.ts +117 -0
  109. package/src/shippings/shipping-methods/destroy.ts +111 -0
  110. package/src/shippings/shipping-methods/fetch.ts +62 -0
  111. package/src/shippings/shipping-methods/index.ts +28 -0
  112. package/src/shippings/shipping-methods/store.ts +123 -0
  113. package/src/shippings/shipping-methods/update.ts +120 -0
  114. package/src/shippings/shipping-rates/destroy.ts +102 -0
  115. package/src/shippings/shipping-rates/fetch.ts +320 -0
  116. package/src/shippings/shipping-rates/index.ts +36 -0
  117. package/src/shippings/shipping-rates/store.ts +102 -0
  118. package/src/shippings/shipping-rates/update.ts +139 -0
  119. package/src/shippings/shipping-zones/destroy.ts +111 -0
  120. package/src/shippings/shipping-zones/fetch.ts +96 -0
  121. package/src/shippings/shipping-zones/index.ts +26 -0
  122. package/src/shippings/shipping-zones/store.ts +74 -0
  123. package/src/shippings/shipping-zones/update.ts +156 -0
  124. package/src/tax/destroy.ts +54 -0
  125. package/src/tax/fetch.ts +20 -0
  126. package/src/tax/index.ts +24 -0
  127. package/src/tax/store.ts +69 -0
  128. package/src/tax/update.ts +119 -0
  129. package/src/tests/coupons.test.ts +90 -0
  130. package/src/tests/customers.test.ts +39 -0
  131. package/src/tests/device.test.ts +111 -0
  132. package/src/tests/digitals.test.ts +31 -0
  133. package/src/tests/drivers.test.ts +25 -0
  134. package/src/tests/gift-cards.test.ts +36 -0
  135. package/src/tests/license.test.ts +31 -0
  136. package/src/tests/manufacturers.test.ts +80 -0
  137. package/src/tests/orders.test.ts +25 -0
  138. package/src/tests/payments.test.ts +75 -0
  139. package/src/tests/rates.test.ts +367 -0
  140. package/src/tests/receipts.test.ts +287 -0
  141. package/src/tests/restaurant.test.ts +522 -0
  142. package/src/tests/reviews.test.ts +165 -0
  143. package/src/tests/routes.test.ts +51 -0
  144. package/src/tests/setup.ts +149 -0
  145. package/src/tests/shipping.test.ts +88 -0
  146. package/src/tests/tax.test.ts +25 -0
  147. package/src/tests/units.test.ts +110 -0
  148. package/src/tests/variants.test.ts +133 -0
  149. package/src/tests/waitlist.test.ts +303 -0
  150. package/src/tests/zones.test.ts +86 -0
  151. package/src/types.ts +312 -0
  152. package/src/utils/typed-stats.ts +67 -0
  153. package/src/waitlists/index.ts +19 -0
  154. package/src/waitlists/products/destroy.ts +65 -0
  155. package/src/waitlists/products/export.ts +113 -0
  156. package/src/waitlists/products/fetch.ts +362 -0
  157. package/src/waitlists/products/index.ts +33 -0
  158. package/src/waitlists/products/store.ts +112 -0
  159. package/src/waitlists/products/update.ts +125 -0
  160. package/src/waitlists/restaurant/destroy.ts +65 -0
  161. package/src/waitlists/restaurant/export.ts +107 -0
  162. package/src/waitlists/restaurant/fetch.ts +538 -0
  163. package/src/waitlists/restaurant/index.ts +43 -0
  164. package/src/waitlists/restaurant/store.ts +109 -0
  165. package/src/waitlists/restaurant/update.ts +198 -0
@@ -0,0 +1,111 @@
1
+ import { db } from '@stacksjs/database'
2
+
3
+ /**
4
+ * Delete an order by ID
5
+ *
6
+ * @param id The ID of the order to delete
7
+ * @returns True if the order was deleted, false otherwise
8
+ */
9
+ export async function destroy(id: number): Promise<boolean> {
10
+ try {
11
+ const result = await db
12
+ .deleteFrom('orders')
13
+ .where('id', '=', id)
14
+ .executeTakeFirst()
15
+
16
+ return Number(result.numDeletedRows) > 0
17
+ }
18
+ catch (error) {
19
+ if (error instanceof Error) {
20
+ throw new TypeError(`Failed to delete order: ${error.message}`)
21
+ }
22
+
23
+ throw error
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Soft delete an order by ID (updates status to CANCELED)
29
+ *
30
+ * @param id The ID of the order to soft delete
31
+ * @returns True if the order was soft deleted, false otherwise
32
+ */
33
+ export async function softDelete(id: number): Promise<boolean> {
34
+ try {
35
+ // Update the status to CANCELED instead of deleting
36
+ const result = await db
37
+ .updateTable('orders')
38
+ .set({ status: 'CANCELED' })
39
+ .where('id', '=', id)
40
+ .executeTakeFirst()
41
+
42
+ // Return true if any row was affected (updated)
43
+ return Number(result.numUpdatedRows) > 0
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error) {
47
+ throw new TypeError(`Failed to soft delete order: ${error.message}`)
48
+ }
49
+
50
+ throw error
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Delete multiple orders by ID
56
+ *
57
+ * @param ids Array of order IDs to delete
58
+ * @returns Number of orders deleted
59
+ */
60
+ export async function bulkDestroy(ids: number[]): Promise<number> {
61
+ if (!ids.length)
62
+ return 0
63
+
64
+ try {
65
+ // Perform the delete operation
66
+ const result = await db
67
+ .deleteFrom('orders')
68
+ .where('id', 'in', ids)
69
+ .executeTakeFirst()
70
+
71
+ // Return the number of deleted rows
72
+ return Number(result.numDeletedRows)
73
+ }
74
+ catch (error) {
75
+ if (error instanceof Error) {
76
+ throw new TypeError(`Failed to delete orders: ${error.message}`)
77
+ }
78
+
79
+ throw error
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Soft delete multiple orders by ID (updates status to CANCELED)
85
+ *
86
+ * @param ids Array of order IDs to soft delete
87
+ * @returns Number of orders soft deleted
88
+ */
89
+ export async function bulkSoftDelete(ids: number[]): Promise<number> {
90
+ if (!ids.length)
91
+ return 0
92
+
93
+ try {
94
+ // Update the status to CANCELED instead of deleting
95
+ const result = await db
96
+ .updateTable('orders')
97
+ .set({ status: 'CANCELED' })
98
+ .where('id', 'in', ids)
99
+ .executeTakeFirst()
100
+
101
+ // Return the number of updated rows
102
+ return Number(result.numUpdatedRows)
103
+ }
104
+ catch (error) {
105
+ if (error instanceof Error) {
106
+ throw new TypeError(`Failed to soft delete orders: ${error.message}`)
107
+ }
108
+
109
+ throw error
110
+ }
111
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Commerce → events bus integration (stacksjs/stacks#1879 Co-18).
3
+ *
4
+ * Background: orders moved through created → paid → shipped →
5
+ * delivered states with no `events.dispatch(...)` calls. Every
6
+ * downstream service (email confirmation, inventory replenishment,
7
+ * analytics, fraud detection) had to poll the orders table because
8
+ * there was no subscription path. The framework's event bus from
9
+ * `@stacksjs/events` has been ready since #1878 — commerce just
10
+ * wasn't wired into it.
11
+ *
12
+ * This module wraps the dispatch calls so they:
13
+ * - Lazy-import the events package so commerce stays usable in
14
+ * environments that don't load it (CLI scripts, migrations)
15
+ * - Silently no-op when the events package isn't installed
16
+ * - Use a structured payload shape callers can subscribe with type safety
17
+ * - Never throw — emission failures shouldn't abort the order write
18
+ *
19
+ * Status-transition guard (stacksjs/stacks#1879 Co-4) lives here
20
+ * too so the rules are co-located with the events they trigger.
21
+ */
22
+
23
+ export type OrderStatus = 'PENDING' | 'PROCESSING' | 'SHIPPED' | 'DELIVERED' | 'CANCELLED' | 'REFUNDED'
24
+
25
+ /**
26
+ * Whitelisted state transitions. Forward-only progressions and
27
+ * branch-to-cancel/refund from any non-terminal state. SHIPPED →
28
+ * PENDING and similar nonsense are rejected.
29
+ *
30
+ * REFUNDED and DELIVERED are terminal — nothing transitions out
31
+ * of them. CANCELLED can be re-entered (rare admin recovery), so
32
+ * we allow CANCELLED → PROCESSING but not the other terminals.
33
+ */
34
+ const TRANSITIONS: Record<OrderStatus, OrderStatus[]> = {
35
+ PENDING: ['PROCESSING', 'CANCELLED'],
36
+ PROCESSING: ['SHIPPED', 'CANCELLED', 'REFUNDED'],
37
+ SHIPPED: ['DELIVERED', 'REFUNDED'],
38
+ DELIVERED: ['REFUNDED'], // post-delivery refund is the only legal exit
39
+ CANCELLED: ['PROCESSING'], // admin recovery — rare but supported
40
+ REFUNDED: [], // terminal
41
+ }
42
+
43
+ /**
44
+ * Check whether `from → to` is a legal status transition
45
+ * (stacksjs/stacks#1879 Co-4). Use in `updateStatus` to reject
46
+ * illegal jumps like SHIPPED → PENDING before they hit the database.
47
+ */
48
+ export function canTransition(from: OrderStatus, to: OrderStatus): boolean {
49
+ if (from === to) return true // no-op
50
+ return TRANSITIONS[from]?.includes(to) ?? false
51
+ }
52
+
53
+ /**
54
+ * Best-effort event dispatch. Catches the lazy-import miss (events
55
+ * package not installed) and any per-listener throw — order writes
56
+ * MUST NOT depend on the events bus's health.
57
+ */
58
+ async function emitOrderEvent(eventName: string, payload: Record<string, unknown>): Promise<void> {
59
+ try {
60
+ const mod = await import('@stacksjs/events').catch(() => null)
61
+ if (!mod) return
62
+ const dispatch = (mod as { dispatch?: (t: string, p: unknown) => void }).dispatch
63
+ if (typeof dispatch !== 'function') return
64
+ dispatch(eventName, payload)
65
+ }
66
+ catch {
67
+ // Swallow — the events bus is a notification side-channel, not
68
+ // the source of truth. A listener that throws shouldn't be able
69
+ // to abort an order write. The events package itself logs handler
70
+ // errors via #1876 O-2 / #1878 E-1 paths.
71
+ }
72
+ }
73
+
74
+ /** Emit `order:created` after a successful placeOrder / store call. */
75
+ export async function emitOrderCreated(order: Record<string, unknown>): Promise<void> {
76
+ await emitOrderEvent('order:created', { order })
77
+ }
78
+
79
+ /** Emit `order:paid` after payment confirmation lands. */
80
+ export async function emitOrderPaid(order: Record<string, unknown>, payment?: Record<string, unknown>): Promise<void> {
81
+ await emitOrderEvent('order:paid', { order, payment })
82
+ }
83
+
84
+ /** Emit `order:shipped` after fulfillment marks the order shipped. */
85
+ export async function emitOrderShipped(order: Record<string, unknown>): Promise<void> {
86
+ await emitOrderEvent('order:shipped', { order })
87
+ }
88
+
89
+ /** Emit `order:delivered` after the carrier confirms delivery. */
90
+ export async function emitOrderDelivered(order: Record<string, unknown>): Promise<void> {
91
+ await emitOrderEvent('order:delivered', { order })
92
+ }
93
+
94
+ /** Emit `order:cancelled` on cancellation (pre- or post-payment). */
95
+ export async function emitOrderCancelled(order: Record<string, unknown>, reason?: string): Promise<void> {
96
+ await emitOrderEvent('order:cancelled', { order, reason })
97
+ }
98
+
99
+ /** Emit `order:refunded` after a refund settles. */
100
+ export async function emitOrderRefunded(order: Record<string, unknown>, refundAmount?: number): Promise<void> {
101
+ await emitOrderEvent('order:refunded', { order, refundAmount })
102
+ }
103
+
104
+ /**
105
+ * Convenience dispatch keyed by status. Used from `updateStatus`
106
+ * so a single function fires the right event for whichever status
107
+ * we just transitioned into.
108
+ */
109
+ export async function emitForStatus(status: OrderStatus, order: Record<string, unknown>): Promise<void> {
110
+ switch (status) {
111
+ case 'PROCESSING': return emitOrderPaid(order)
112
+ case 'SHIPPED': return emitOrderShipped(order)
113
+ case 'DELIVERED': return emitOrderDelivered(order)
114
+ case 'CANCELLED': return emitOrderCancelled(order)
115
+ case 'REFUNDED': return emitOrderRefunded(order)
116
+ // PENDING fires no event — the initial state is already covered
117
+ // by `order:created` from the placeOrder path.
118
+ case 'PENDING':
119
+ default:
120
+ }
121
+ }
@@ -0,0 +1,158 @@
1
+ import type { SpreadsheetWrapper } from 'ts-spreadsheets'
2
+ import type { OrderWithTotals } from '../types'
3
+ import { db } from '@stacksjs/database'
4
+ import { createSpreadsheet } from 'ts-spreadsheets'
5
+
6
+ /**
7
+ * Represents the structure of an exported order
8
+ */
9
+ export interface ExportedOrder {
10
+ 'Order ID': number
11
+ 'Customer': string
12
+ 'Date': string
13
+ 'Total': number
14
+ 'Status': string
15
+ 'Items': string
16
+ }
17
+
18
+ /**
19
+ * Export orders to a spreadsheet
20
+ * @param format The format of the spreadsheet (default is CSV)
21
+ * @returns Spreadsheet object ready for download or storage
22
+ */
23
+ export async function exportOrders(format: 'csv' | 'excel' = 'csv'): Promise<SpreadsheetWrapper> {
24
+ // Fetch all orders with their details
25
+ const orders = await fetchAllWithDetails()
26
+
27
+ // Prepare data for spreadsheet
28
+ const spreadsheetData = prepareOrdersForExport(orders)
29
+
30
+ // Create and return spreadsheet
31
+ return createSpreadsheet(spreadsheetData, { type: format })
32
+ }
33
+
34
+ /**
35
+ * Fetch all orders with customer names and order items
36
+ */
37
+ async function fetchAllWithDetails(): Promise<OrderWithTotals[] | []> {
38
+ const ordersWithCustomers = await db
39
+ .selectFrom('orders')
40
+ .leftJoin('customers', 'customers.id', '=', 'orders.customer_id')
41
+ .selectAll()
42
+ .select(['customers.name as customer_name', 'customers.email as customer_email'])
43
+ .execute()
44
+
45
+ const orderIds = ordersWithCustomers.map((order: any) => order.id)
46
+
47
+ if (orderIds.length === 0) {
48
+ return []
49
+ }
50
+
51
+ // Fetch order items related only to the fetched orders
52
+ const allOrderItems = await db
53
+ .selectFrom('order_items')
54
+ .where('order_items.order_id', 'in', orderIds)
55
+ .select([
56
+ 'order_items.order_id',
57
+ 'order_items.quantity',
58
+ 'order_items.price',
59
+ ])
60
+ .execute()
61
+
62
+ const orderItemsMap = allOrderItems.reduce(
63
+ (acc: any, item: any) => {
64
+ if (!item.order_id)
65
+ return acc // Skip items without order_id
66
+
67
+ if (!acc[item.order_id]) {
68
+ acc[item.order_id] = {
69
+ totalItems: 0,
70
+ totalPrice: 0,
71
+ }
72
+ }
73
+
74
+ acc[item.order_id].totalItems += item.quantity
75
+ acc[item.order_id].totalPrice += item.price * item.quantity
76
+
77
+ return acc
78
+ },
79
+ {},
80
+ )
81
+
82
+ const enrichedOrders = ordersWithCustomers.map((order: any) => {
83
+ const orderItemData = orderItemsMap[order.id] || { totalItems: 0, totalPrice: 0 }
84
+
85
+ return {
86
+ ...order,
87
+ totalItems: orderItemData.totalItems,
88
+ totalPrice: orderItemData.totalPrice,
89
+ }
90
+ })
91
+
92
+ return enrichedOrders as OrderWithTotals[]
93
+ }
94
+
95
+ /**
96
+ * Prepare orders data for spreadsheet export
97
+ * @param orders Array of order objects
98
+ * @returns Spreadsheet data structure
99
+ */
100
+ function prepareOrdersForExport(orders: OrderWithTotals[]) {
101
+ // Define headings
102
+ const headings: (keyof ExportedOrder)[] = [
103
+ 'Order ID',
104
+ 'Customer',
105
+ 'Date',
106
+ 'Total',
107
+ 'Status',
108
+ 'Items',
109
+ ]
110
+
111
+ // Transform orders into export format
112
+ const data = orders.map((order: any) => {
113
+ // Convert items to a readable string
114
+ const itemsString = order.order_items
115
+ ?.map((item: any) => `${item.product?.name} (Qty: ${item.quantity}, Price: $${item.price})`)
116
+ .join(' | ') || 'No Items'
117
+
118
+ return [
119
+ order.id,
120
+ order.customer?.name || 'N/A',
121
+ order.created_at,
122
+ order.total_amount,
123
+ order.status,
124
+ itemsString,
125
+ ]
126
+ })
127
+
128
+ return { headings, data }
129
+ }
130
+
131
+ /**
132
+ * Export orders and automatically download
133
+ * @param format The format of the spreadsheet (default is CSV)
134
+ * @param filename Optional filename for the download
135
+ * @returns Download response
136
+ */
137
+ export async function downloadOrders(format: 'csv' | 'excel' = 'csv', filename?: string): Promise<Response> {
138
+ const spreadsheet = await exportOrders(format)
139
+
140
+ // Use default filename if not provided
141
+ const defaultFilename = `orders_export_${new Date().toISOString().split('T')[0]}.${format}`
142
+ return spreadsheet.download(filename || defaultFilename)
143
+ }
144
+
145
+ /**
146
+ * Store orders export to disk
147
+ * @param format The format of the spreadsheet (default is CSV)
148
+ * @param path Optional path to store the file
149
+ * @returns Path where the file is stored
150
+ */
151
+ export async function storeOrdersExport(format: 'csv' | 'excel' = 'csv', path?: string): Promise<void> {
152
+ const spreadsheet = await exportOrders(format)
153
+
154
+ // Use default path and filename if not provided
155
+ const defaultPath = `orders_export_${new Date().toISOString().split('T')[0]}.${format}`
156
+
157
+ spreadsheet.store(path || defaultPath)
158
+ }