@develemit/commerce-server 0.6.0 → 0.8.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/CHANGELOG.md ADDED
@@ -0,0 +1,124 @@
1
+ ## 0.8.0 (2026-11-12)
2
+
3
+ ### Features
4
+
5
+ - `DELETE /admin/products/:id` permanently deletes a draft that has never been live and has no order lines (204), along with its image files and holds; otherwise 409 `PRODUCT_NOT_DELETABLE` with `reason: 'published' | 'has_orders'`. Migration `0009` adds `commerce_products.published_at` (set when a product first goes active; backfilled for existing non-draft products), and admin products report `everPublished`.
6
+ - `orderEmailLines` returns `reference`, the short order reference to print in order emails instead of the full id.
7
+ - `GET /admin/orders?q=` accepts the short reference (with or without the leading `#`).
8
+
9
+ ### Changes
10
+
11
+ - `GET /admin/products/counts`: `live` counts active pieces with stock, and `sold` counts active pieces at zero stock, so a sold piece is no longer counted in both. Each chip's count equals the list its filter shows (`status=active&stock=in` / `status=active&stock=sold`). Upgrading shows lower Live numbers.
12
+
13
+ ## 0.7.0 (2026-11-04)
14
+
15
+ ### Features
16
+
17
+ - `devCheckout` orders now complete with a buyer email (`dev-buyer@example.com`), a sample shipping address and the session's first shipping rate, so `onOrderPaid` hooks see real fields. New `devCheckout: { buyer }` option (object or `(sessionId) => buyer`) overrides them; `DevBuyer` and `DevBuyerSetting` types are exported.
18
+ - Drafts can be incomplete: migration `0008` makes `price_cents` and `category` nullable and gives `name` a `''` default. Publishing (`PATCH` to `status: 'active'`) with a missing name, price, category or photo answers 422 `PRODUCT_INCOMPLETE` with `missing`. New `admin.requirePhotoToPublish` option (default `true`).
19
+ - `GET /admin/products/counts` returns `{ all, live, draft, sold, archived, incompleteDrafts }`.
20
+ - Nameless drafts get a temporary `draft-<id>` slug, replaced once by the name slug the first time a name is set, then frozen.
21
+ - **Changed:** `GET /admin/products` pages newest first (`created_at` then `slug`, descending); cursors from before this change page in the wrong direction. New `excludeArchived=true` query param leaves archived products out, and `incomplete=true` keeps only drafts missing a name, price or category (the set `incompleteDrafts` counts).
22
+ - The public catalog skips active rows missing a price or category.
23
+
24
+ ### Features
25
+
26
+ - `PaymentGateway.preflight?()` is new and optional; `commercePlugin` awaits it during registration. `createStripeGateway` implements it, so a host with Stripe configured but the optional peer `stripe` missing fails at startup with `Run: pnpm add stripe` rather than at the first checkout.
27
+
28
+ ### Fixes
29
+
30
+ - `createDiskStorage` `put` and `delete` now accept a top-level key such as `x.png`; before, only nested keys worked. Every traversal guard is unchanged.
31
+ - **Changed:** `POST /admin/products` with `status: 'active'` now follows the photo rule too: while `admin.requirePhotoToPublish` is on (the default) it answers 422 `PRODUCT_INCOMPLETE` with `missing: ['images']`, because images can only upload after create. Create the product as a draft, add photos, then publish with `PATCH`. Hosts that create active products directly must do that or set `requirePhotoToPublish: false`.
32
+ - `checkoutUrls(...).success` no longer percent-encodes Stripe's `{CHECKOUT_SESSION_ID}` placeholder, so Stripe substitutes the real session id and buyers land on `/checkout/success?session_id=cs_…` with an id that matches their order. Hosts using `checkoutUrls` with Stripe on 0.5.0–0.6.x should upgrade. No API change.
33
+
34
+ ### Docs
35
+
36
+ - The package now ships `CHANGELOG.md` and declares `description`, `repository` and `homepage`. The README documents the lower-level exports (`catalogPlugin`, `ordersPlugin`, `ProductNotFoundError`, `WebhookSignatureError`, `SESSION_ID_PLACEHOLDER`, `isFakeGateway`, `createMemoryStorage`) and rate-limiting the public checkout routes.
37
+
38
+ ## 0.6.0 (2026-11-03)
39
+
40
+ ### Features
41
+
42
+ - `orderEmailLines(order, { locale, labels })` turns an `OrderDetail` into email-ready pieces: item, subtotal, discount (only when one applied), shipping and total lines, the ship-to address, and `text` / `html` renderings with every value HTML-escaped. Money is formatted like the storefront's `formatPrice`. New types `EmailLine`, `OrderEmailLabels`, `OrderEmailLines`, `OrderEmailLinesOptions`.
43
+
44
+ ## 0.5.0 (2026-11-02)
45
+
46
+ ### Features
47
+
48
+ - `resolvePayment(env = process.env)` picks the Stripe or fake gateway from `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `PAYMENT_GATEWAY` and `NODE_ENV`, and throws on half-configured Stripe or the fake gateway in production. `checkoutUrls(webUrl, { successPath, cancelPath })` builds the `urls` option. New types `CheckoutUrlPaths`, `PaymentEnv`.
49
+ - `commercePlugin({ devCheckout: true | { apiUrl } })` adds `GET /checkout/dev/:sessionId/complete` for the fake gateway: it completes the order through the webhook event path and redirects to the success URL (`?outcome=expired` releases it and redirects to the cancel URL), so a full purchase runs locally without Stripe. Registration throws when `NODE_ENV=production`; the route is skipped for any other gateway. New exports `isFakeGateway`, `DevCheckoutSetting`; `FakeGateway` gains `kind: 'fake'` and `useSessionUrl()`.
50
+ - `createDiskStorage({ dir, publicUrl })` is a `FileStorage` that writes under `dir` and rejects keys that escape it (`..`, absolute paths, encoded traversal, NUL bytes, symlinks pointing outside). `openStoredFile(dir, key)` opens a stored file for serving (stream, image content type, size) or returns `null`, with the same guard. New types `DiskStorageOptions`, `StoredFileStream`.
51
+
52
+ ### Fixes
53
+
54
+ - `createStripeGateway`'s one-off discount coupons now set `redeem_by` to the checkout session's expiry, so coupons from failed or abandoned sessions expire on their own.
55
+ - Admin order and discount list cursors keep Postgres's microsecond `created_at`, so paging never skips or repeats rows created within the same millisecond. Cursors issued by earlier versions (ISO milliseconds) are still accepted.
56
+ - Image upload (`POST /admin/products/:id/images`) applies the same trimmed, 500-character `alt` rule as the PATCH route (400 when longer).
57
+ - Editing a redeemed fixed discount with a case-only currency difference no longer answers 409 `DISCOUNT_IN_USE`.
58
+
59
+ ## 0.4.0 (2026-10-30)
60
+
61
+ ### Features
62
+
63
+ - Migration `0007` (`commerce_discounts`, `commerce_discount_redemptions`, and `discount_cents`/`discount_code` on `commerce_orders`; existing orders get `discount_cents = 0`). Apply it with `runCommerceMigrations` before serving traffic. Order totals are now subtotal − discount + shipping.
64
+ - `PATCH /admin/products/:id/images/:imageId` with `{ alt }` edits an existing image's alt text (404 `PRODUCT_NOT_FOUND` / `IMAGE_NOT_FOUND`, 400 on invalid input) and returns the updated product. Catalog ETags change with the new alt text.
65
+ - `GET|POST /admin/discounts` and `GET|PATCH /admin/discounts/:id` (only when `admin` and checkout are configured) list, create, edit and deactivate discount codes, with `redeemedCount` and `reservedCount`. Codes are unique (409 `DISCOUNT_CODE_TAKEN`); once redeemed, the code and value can't change (409 `DISCOUNT_IN_USE`) and `maxRedemptions` can't drop below usage (400). No hard delete. New contracts `adminDiscountListQuerySchema`, `adminDiscountPageSchema`, `discountStatusSchema`.
66
+ - `POST /checkout/discount/preview` (only when checkout is configured) prices a cart on the server and answers the discount a code would take off, or 422 `DISCOUNT_INVALID` with a `reason`. Inactive codes are reported as `not_found`. Rate-limit it at the host.
67
+ - `POST /checkout` accepts `discountCode`. The code is re-evaluated against server prices and a redemption is reserved in the same transaction as the stock holds, under a row lock on the discount, so `maxRedemptions` holds under concurrent checkouts. A rejected code answers 422 `DISCOUNT_INVALID` with a `reason` and creates no order. The order stores `discountCode`/`discountCents`. Payment commits the redemption; expiry, a failed async payment, the hold sweeper, or a failed gateway call releases it. A `needs_attention` order keeps it reserved until resolved (committed) or fully refunded (released). Refunds of paid orders leave it committed.
68
+ - `CreateCheckoutSessionInput` gains an optional `discount: { code, amountCents }` (new exported type `CheckoutDiscount`). It is an amount off the merchandise subtotal, never shipping. **Custom gateways should apply it**, or the buyer is charged the undiscounted price while the order records the discount. `createStripeGateway` creates a one-off coupon (`duration: 'once'`, `max_redemptions: 1`) and passes `discounts: [{ coupon }]`; `createFakeGateway` records it on `sessions`.
69
+
70
+ ## 0.3.0 (2026-10-30)
71
+
72
+ ### Features
73
+
74
+ - `GET /admin/products` accepts `?stock=in|sold` (stored quantity above 0, or 0; holds are ignored), combinable with `status` and `q`.
75
+ - Public catalog GETs send `Cache-Control` (default `public, max-age=30, stale-while-revalidate=300`) and a strong `ETag`, and answer `If-None-Match` with 304. New `catalogCache: { cacheControl?: string | false }` option on `commercePlugin`; catalog errors send `no-store`.
76
+
77
+ ### Breaking changes
78
+
79
+ - `GET /admin/products` now returns `{ items, nextCursor }` instead of an array. Page with `?limit=` (1-100, default 50) and `?cursor=`, filter with `?status=`, and search names with `?q=` (case-insensitive substring, `%`/`_` literal). Order is unchanged (`created_at`, then `slug`); a bad cursor gives 400 `INVALID_CURSOR`.
80
+ - `GET /catalog/products` now returns `{ items, nextCursor }` instead of an array. Items are cards (no description, materials or dimensions; only the first image). Page with `?limit=` (1-100, default 48) and `?cursor=`; a bad cursor gives 400 `INVALID_CURSOR`, a bad limit 400 `INVALID_QUERY`. Order is unchanged (`created_at`, then `slug`). `GET /catalog/products/:slug` is unchanged.
81
+
82
+ ### Fixes
83
+
84
+ - Published tarballs no longer include declaration maps (`*.d.ts.map`), which pointed at `src/` files that were not shipped. JS sourcemaps now embed `sourcesContent`.
85
+
86
+ ## 0.2.0 (2026-10-29)
87
+
88
+ First published release. 0.1.0 was tagged but never published to npm, so installing 0.2.0 gets everything in both entries below.
89
+
90
+ ### Breaking changes
91
+
92
+ - `PaymentGateway` has a new required method, `refund({ sessionId, amountCents, idempotencyKey })`, returning `{ gatewayRefundId }`. Custom gateways must implement it; `createStripeGateway` and `createFakeGateway` already do. The `PaymentEvent` union also gains a `payment.refunded` member (`GatewayRefundEvent`), which custom gateways may emit, so exhaustive switches over it need a new case.
93
+ - `POST /admin/orders/:id/refunds` requires an `Idempotency-Key` header (1-255 characters, else 400 `IDEMPOTENCY_KEY_REQUIRED`).
94
+
95
+ ### Features
96
+
97
+ - Migrations `0005` (`refunded` order status, fulfillment columns, `commerce_order_refunds`) and `0006` (refund `idempotency_key`, unique per order, and pending refund rows). Apply them with `runCommerceMigrations`.
98
+ - Admin order routes, behind `admin.authorize` and only when checkout is configured: `GET /admin/orders` (filter, search, cursor paging), `GET /admin/orders/:id`, `POST /admin/orders/:id/ship`, `POST /admin/orders/:id/resolve` and `POST /admin/orders/:id/refunds`.
99
+ - Refunds go through the gateway, can restock, and set the order `refunded` once the balance reaches zero. Errors: 400 `REFUND_EXCEEDS_BALANCE`, `RESTOCK_REQUIRES_FULL_REFUND` and `RESTOCK_NOT_APPLICABLE` (`needs_attention` orders never took stock), and 502 `REFUND_GATEWAY_ERROR`. Concurrent refunds count against the balance while in flight.
100
+ - `POST /admin/orders/:id/ship` records carrier and tracking and moves `paid` to `shipped`. The buyer's `GET /orders/session/:id` now returns `fulfillment`.
101
+ - `POST /admin/orders/:id/resolve` moves a `needs_attention` order to `paid` when stock is available, or answers 409 `INSUFFICIENT_STOCK` with `productIds`.
102
+ - Hooks `onOrderShipped` (first ship only) and `onOrderRefunded(order, refund)`. `onOrderPaid` also fires when an order is resolved.
103
+ - `charge.refunded` webhook: refunds made in the Stripe dashboard are recorded as `source: 'gateway'` refunds. Stripe refunds started by the kit carry `metadata.commerceRefundKey`, so the webhook echo matches the pending row instead of recording a second one. Enable `charge.refunded` on your Stripe webhook endpoint.
104
+ - An unknown refund outcome (gateway timeout, network error or 5xx) keeps the pending refund and answers 502 `REFUND_OUTCOME_UNKNOWN`, so a fresh `Idempotency-Key` can't refund twice; retry with the same key. Only a certain failure (Stripe 4xx) still deletes the row. Custom gateways signal certainty by throwing `RefundRejectedError`; the fake gateway gets `failNextRefundUncertain()`, and `failNextRefund()` now throws a `RefundRejectedError`.
105
+ - `GET /admin/orders?hasPendingRefund=true|false`, and pending refunds appear in `AdminOrderDetail.refunds` (`refundedCents` still counts confirmed ones only).
106
+ - Exported `RefundInput` and `GatewayRefund` types. The fake gateway records `refunds` and `refundCalls`, and has `failNextRefund()` and `refunded()`.
107
+
108
+ ## 0.1.0 (2026-10-29)
109
+
110
+ First release. Fastify plugins that own their Drizzle schema and migrations. Peers: `fastify`, `drizzle-orm`, `pg`. Optional peers: `stripe` (Stripe gateway) and `@fastify/multipart` (admin image upload).
111
+
112
+ ### Features
113
+
114
+ - Owned schema and migrations `0000`–`0004`, applied with `runCommerceMigrations`; `commerceSchema` is exported for host queries.
115
+ - `commercePlugin` (and the lower-level `catalogPlugin` / `ordersPlugin`): catalog routes, checkout and order lookup.
116
+ - Checkout re-reads prices server-side and reserves stock with no-oversell inventory holds. `startHoldSweeper` releases lapsed holds.
117
+ - Payment gateways: `createStripeGateway` and `createFakeGateway` (signed test webhooks). Implement `PaymentGateway` to add another.
118
+ - Webhook handling for completed, expired, async-succeeded and async-failed payments. A payment that arrives after its hold lapsed, or an unpaid async completion, no longer silently commits stock; the order moves to `needs_attention`.
119
+ - Shipping rates (`seedShippingRates`, `shippingCountries`) with the chosen rate and address stored on the order.
120
+ - Order hooks: `onOrderPaid`, `onOrderExpired`, `onOrderNeedsAttention`, `onOrderPaymentFailed` (async payment failed; stock already released). Hook failures are logged, not thrown.
121
+ - Catalog-only mode: omit `payment` and `urls` to mount the catalog without checkout.
122
+ - Admin product API (`AdminOptions`): host-supplied `authorize`, product CRUD, and image upload and ordering through the `FileStorage` interface (`createMemoryStorage` included).
123
+ - `seedProducts` for fixtures and demos.
124
+ - `categoryLabels` option (record or function) sets display labels for `GET /catalog/categories`; unmatched slugs fall back to the slug.
package/README.md CHANGED
@@ -4,11 +4,16 @@ Fastify plugins for a small shop: the public catalog, no-oversell inventory
4
4
  holds, Stripe hosted checkout, and orders. The package ships its own Drizzle
5
5
  schema and migrations, and the host owns the database pool and the secrets.
6
6
 
7
+ New here? Start with the
8
+ [getting-started guide](https://github.com/develemit/develemit-commerce/blob/main/docs/getting-started.md).
9
+ What changed in each version, including new migrations, is in
10
+ [CHANGELOG.md](CHANGELOG.md).
11
+
7
12
  ## Install
8
13
 
9
14
  ```sh
10
15
  pnpm add @develemit/commerce-server fastify drizzle-orm pg
11
- pnpm add stripe # only if you use createStripeGateway
16
+ pnpm add stripe # whenever the Stripe keys are set, including via resolvePayment
12
17
  pnpm add @fastify/multipart # only if you pass `admin`
13
18
  ```
14
19
 
@@ -127,6 +132,9 @@ onOrderPaid: async (order) => {
127
132
  },
128
133
  ```
129
134
 
135
+ `lines.reference` is the short order reference (`#A1B2C3D4`, from
136
+ `formatOrderRef` in contracts); print it instead of the full id.
137
+
130
138
  The result also has structured `items`, `subtotal`, `discount` (absent when
131
139
  no discount applied), `shipping`, `total` and `address` (absent for pickup).
132
140
  HTML output is `<tr>` / `<p>` fragments with `& < > " '` escaped. Override
@@ -204,7 +212,33 @@ Relative to the host's prefix:
204
212
  | `POST /checkout` | `{ items: [{ productId, quantity }] }` → `{ url }`, or 409 |
205
213
  | `POST /checkout/discount/preview` | `{ code, items }` → discount preview, or 422 `DISCOUNT_INVALID` |
206
214
  | `POST /orders/webhooks/stripe` | Stripe webhook (raw body, `stripe-signature` header) |
207
- | `GET /orders/session/:sessionId` | `orderSummarySchema` for the success page |
215
+ | `GET /orders/session/:sessionId` | `orderSummarySchema` for the success page (no shipping address) |
216
+
217
+ With `devCheckout`, the fake gateway also gets
218
+ `GET /checkout/dev/:sessionId/complete` (see [Local checkout](docs/payments.md#local-checkout-without-stripe)).
219
+ The dev order gets a sample buyer email, address and the first shipping rate;
220
+ `devCheckout: { buyer }` overrides them.
221
+ Admin routes are listed in [docs/admin.md](docs/admin.md).
222
+
223
+ `POST /checkout` and `POST /checkout/discount/preview` are public and write or
224
+ price carts, so rate-limit both at the host (for example `@fastify/rate-limit`
225
+ with a route config).
226
+
227
+ ## Lower-level exports
228
+
229
+ - `catalogPlugin` and `ordersPlugin` are the two halves `commercePlugin`
230
+ registers. Use them only to mount the catalog without checkout, or to put
231
+ them under different prefixes.
232
+ - `ProductNotFoundError` is thrown by the catalog service. The route turns it
233
+ into a 404.
234
+ - `WebhookSignatureError` is what a `PaymentGateway`'s `verifyWebhook` should
235
+ throw for a bad signature. The webhook route answers 400 for it.
236
+ - `SESSION_ID_PLACEHOLDER` (`{CHECKOUT_SESSION_ID}`): checkout builds the
237
+ success URL as `urls.success(SESSION_ID_PLACEHOLDER)`. Stripe fills it in
238
+ itself, but a custom gateway must replace it with its session id.
239
+ - `isFakeGateway(gateway)` narrows a `PaymentGateway` to the fake one (for
240
+ dev-only wiring).
241
+ - `createMemoryStorage()` is an in-memory `FileStorage` for tests.
208
242
 
209
243
  ## Discounts
210
244
 
package/dist/index.d.ts CHANGED
@@ -52,7 +52,7 @@ declare const commerceSchema: {
52
52
  data: string;
53
53
  driverParam: string;
54
54
  notNull: true;
55
- hasDefault: false;
55
+ hasDefault: true;
56
56
  isPrimaryKey: false;
57
57
  isAutoincrement: false;
58
58
  hasRuntimeDefault: false;
@@ -119,7 +119,7 @@ declare const commerceSchema: {
119
119
  columnType: "PgInteger";
120
120
  data: number;
121
121
  driverParam: string | number;
122
- notNull: true;
122
+ notNull: false;
123
123
  hasDefault: false;
124
124
  isPrimaryKey: false;
125
125
  isAutoincrement: false;
@@ -153,7 +153,7 @@ declare const commerceSchema: {
153
153
  columnType: "PgText";
154
154
  data: string;
155
155
  driverParam: string;
156
- notNull: true;
156
+ notNull: false;
157
157
  hasDefault: false;
158
158
  isPrimaryKey: false;
159
159
  isAutoincrement: false;
@@ -214,6 +214,23 @@ declare const commerceSchema: {
214
214
  identity: undefined;
215
215
  generated: undefined;
216
216
  }, {}, {}>;
217
+ publishedAt: drizzle_orm_pg_core.PgColumn<{
218
+ name: "published_at";
219
+ tableName: "commerce_products";
220
+ dataType: "date";
221
+ columnType: "PgTimestamp";
222
+ data: Date;
223
+ driverParam: string;
224
+ notNull: false;
225
+ hasDefault: false;
226
+ isPrimaryKey: false;
227
+ isAutoincrement: false;
228
+ hasRuntimeDefault: false;
229
+ enumValues: undefined;
230
+ baseColumn: never;
231
+ identity: undefined;
232
+ generated: undefined;
233
+ }, {}, {}>;
217
234
  createdAt: drizzle_orm_pg_core.PgColumn<{
218
235
  name: "created_at";
219
236
  tableName: "commerce_products";
@@ -1643,6 +1660,7 @@ interface FileStorage {
1643
1660
  interface AdminOptions {
1644
1661
  authorize(req: FastifyRequest): Promise<boolean>;
1645
1662
  storage: FileStorage;
1663
+ requirePhotoToPublish?: boolean;
1646
1664
  }
1647
1665
 
1648
1666
  interface CatalogCacheOptions {
@@ -1745,6 +1763,7 @@ interface GatewayRefund {
1745
1763
  gatewayRefundId: string;
1746
1764
  }
1747
1765
  interface PaymentGateway {
1766
+ preflight?(): Promise<void>;
1748
1767
  createCheckoutSession(input: CreateCheckoutSessionInput): Promise<CheckoutSession>;
1749
1768
  verifyWebhook(rawBody: Buffer, signature: string | undefined): Promise<PaymentEvent>;
1750
1769
  refund(input: RefundInput): Promise<GatewayRefund>;
@@ -1775,6 +1794,13 @@ interface OrderHooks {
1775
1794
  onOrderRefunded?(order: OrderDetail, refund: OrderRefund): Promise<void>;
1776
1795
  }
1777
1796
 
1797
+ interface DevBuyer {
1798
+ email?: string;
1799
+ name?: string;
1800
+ address?: ShippingAddress;
1801
+ }
1802
+ type DevBuyerSetting = DevBuyer | ((sessionId: string) => DevBuyer);
1803
+
1778
1804
  interface CheckoutOptions {
1779
1805
  payment: PaymentGateway;
1780
1806
  urls: CheckoutUrls;
@@ -1784,6 +1810,7 @@ interface CheckoutOptions {
1784
1810
  }
1785
1811
  interface DevCheckoutSetting {
1786
1812
  apiUrl?: string;
1813
+ buyer?: DevBuyerSetting;
1787
1814
  }
1788
1815
  interface CatalogOnlyOptions {
1789
1816
  payment?: undefined;
@@ -1889,6 +1916,7 @@ interface OrderEmailLinesHtml {
1889
1916
  address: string;
1890
1917
  }
1891
1918
  interface OrderEmailLines {
1919
+ reference: string;
1892
1920
  items: EmailLine[];
1893
1921
  subtotal: EmailLine;
1894
1922
  discount?: EmailLine;
@@ -1966,4 +1994,4 @@ interface CheckoutUrlPaths {
1966
1994
  declare function checkoutUrls(webUrl: string, { successPath, cancelPath, }?: CheckoutUrlPaths): CheckoutUrls;
1967
1995
 
1968
1996
  export { ProductNotFoundError, SESSION_ID_PLACEHOLDER, WebhookSignatureError, catalogPlugin, checkoutUrls, commercePlugin, commerceSchema, createDiskStorage, createFakeGateway, createMemoryStorage, createStripeGateway, isFakeGateway, openStoredFile, orderEmailLines, ordersPlugin, resolvePayment, runCommerceMigrations, seedProducts, seedShippingRates, startHoldSweeper };
1969
- export type { AdminOptions, CatalogCacheOptions, CatalogPluginOptions, CategoryLabels, CheckoutDiscount, CheckoutLine, CheckoutSession, CheckoutUrlPaths, CheckoutUrls, ChosenShipping, CommerceDb, CommercePluginOptions, CommerceSchema, CreateCheckoutSessionInput, DevCheckoutSetting, DiskStorageOptions, EmailLine, FakeGateway, FakeGatewayOptions, FileStorage, GatewayRefund, HoldSweeper, HoldSweeperOptions, MemoryStorage, OrderEmailLabels, OrderEmailLines, OrderEmailLinesOptions, OrderHooks, OrdersPluginOptions, PaymentEnv, PaymentEvent, PaymentGateway, RefundInput, SeedProduct, SeedShippingRate, ShippingAddress, ShippingRateOption, SignedWebhook, StoredFile, StoredFileStream, StripeGatewayOptions };
1997
+ export type { AdminOptions, CatalogCacheOptions, CatalogPluginOptions, CategoryLabels, CheckoutDiscount, CheckoutLine, CheckoutSession, CheckoutUrlPaths, CheckoutUrls, ChosenShipping, CommerceDb, CommercePluginOptions, CommerceSchema, CreateCheckoutSessionInput, DevBuyer, DevBuyerSetting, DevCheckoutSetting, DiskStorageOptions, EmailLine, FakeGateway, FakeGatewayOptions, FileStorage, GatewayRefund, HoldSweeper, HoldSweeperOptions, MemoryStorage, OrderEmailLabels, OrderEmailLines, OrderEmailLinesOptions, OrderHooks, OrdersPluginOptions, PaymentEnv, PaymentEvent, PaymentGateway, RefundInput, SeedProduct, SeedShippingRate, ShippingAddress, ShippingRateOption, SignedWebhook, StoredFile, StoredFileStream, StripeGatewayOptions };