@classytic/commerce-client 0.2.0 → 0.3.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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-10-24
4
+
5
+ Add merchant `stock.read`, batched `stock.query` and guarded `stock.setQuantity` over Arc Next, product stock-tracking input and public per-variant availability types. Count retries require an explicit stable key. Browser bundles remain independent of server kernels.
6
+
3
7
  ## 0.2.0 — 2026-10-23
4
8
 
5
9
  Add typed merchant collection CRUD and public collection list/detail methods with
package/README.md CHANGED
@@ -170,3 +170,5 @@ Arc Next sends `If-Match` for this version. A 412 response means another editor
170
170
  changed the collection: reload and review the latest record before saving again.
171
171
  Keep the original version with an open draft; do not silently retry with a newer
172
172
  version.
173
+
174
+ Quantity inventory is documented in [management.md](management.md#stock).
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DeliveryArea, DeliveryDistrict, DeliveryQuote, DeliveryQuoteInput } from "./delivery-types.js";
2
- import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
2
+ import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-Bz-eFZb2.js";
3
3
  import { ArcClientConfig } from "@classytic/arc-next/client";
4
4
  export interface StorefrontClientOptions {
5
5
  /** Full API mount, for example https://api.example.com/api/v1. */
@@ -1,7 +1,10 @@
1
- import { l as StorefrontOrder, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
1
+ import { l as StorefrontOrder, u as StorefrontProduct } from "./types-Bz-eFZb2.js";
2
2
  import { Address } from "@classytic/primitives/address";
3
3
  import { Money } from "@classytic/primitives/money";
4
4
  export interface ProductInput {
5
+ inventory?: {
6
+ stockTracked: boolean;
7
+ };
5
8
  name: string;
6
9
  slug: string;
7
10
  productType: 'physical';
@@ -35,6 +38,7 @@ export interface ProductInput {
35
38
  }>;
36
39
  }
37
40
  export interface MerchantProduct extends ProductInput {
41
+ stock?: ProductStock;
38
42
  _id: string;
39
43
  updatedAt: string;
40
44
  }
@@ -245,4 +249,18 @@ export interface MerchantOverview {
245
249
  count: number;
246
250
  orderValue: number;
247
251
  }>;
252
+ }
253
+ export interface ProductStock {
254
+ productId: string;
255
+ tracked: boolean;
256
+ items: Array<{
257
+ variantSku?: string;
258
+ attributes: Record<string, string>;
259
+ quantity: number;
260
+ }>;
261
+ }
262
+ export interface StockCountInput {
263
+ variantSku?: string;
264
+ quantity: number;
265
+ expectedQuantity: number;
248
266
  }
@@ -1,5 +1,5 @@
1
1
  import { DeliveryDistrict, DeliverySettings, DeliverySettingsUpdate } from "./delivery-types.js";
2
- import { AnalyticsWindow, ApiKeyInput, ApiKeyOptions, CategoryInput, CollectionInput, IntegrationProvider, IssuedApiKey, MerchantApiKey, MerchantCategory, MerchantCategoryTree, MerchantCollection, MerchantIntegration, MerchantMedia, MerchantOrder, MerchantOverview, MerchantProduct, NotificationDelivery, OrderCommand, ProductInput, StoreSettings } from "./management-types.js";
2
+ import { AnalyticsWindow, ApiKeyInput, ApiKeyOptions, CategoryInput, CollectionInput, IntegrationProvider, IssuedApiKey, MerchantApiKey, MerchantCategory, MerchantCategoryTree, MerchantCollection, MerchantIntegration, MerchantMedia, MerchantOrder, MerchantOverview, MerchantProduct, NotificationDelivery, OrderCommand, ProductInput, ProductStock, StockCountInput, StoreSettings } from "./management-types.js";
3
3
  import { BaseApi } from "@classytic/arc-next/api";
4
4
  import { ArcClientConfig } from "@classytic/arc-next/client";
5
5
  export interface MerchantClientOptions {
@@ -43,6 +43,14 @@ export declare function createMerchantClient(options: MerchantClientOptions): {
43
43
  update: (data: DeliverySettingsUpdate) => Promise<DeliverySettings>;
44
44
  districts(signal?: AbortSignal): Promise<DeliveryDistrict[]>;
45
45
  };
46
+ stock: {
47
+ read: (productId: string, signal?: AbortSignal) => Promise<ProductStock>;
48
+ query(productIds: string[], signal?: AbortSignal): Promise<ProductStock[]>;
49
+ setQuantity(productId: string, data: StockCountInput, idempotencyKey: string): Promise<{
50
+ productId: string;
51
+ quantity: number;
52
+ }>;
53
+ };
46
54
  products: BaseApi<MerchantProduct, ProductInput, Partial<ProductInput>>;
47
55
  collections: BaseApi<MerchantCollection, CollectionInput, Partial<Omit<CollectionInput, "slug">>>;
48
56
  categories: BaseApi<MerchantCategory, CategoryInput, Partial<Omit<CategoryInput, "slug">>> & {
@@ -1,5 +1,38 @@
1
1
  import { BaseApi } from "@classytic/arc-next/api";
2
2
  import { createClient } from "@classytic/arc-next/client";
3
+ // src/management-stock.ts
4
+ function createMerchantStock(client) {
5
+ const api = new BaseApi("inventory/products", {
6
+ client,
7
+ basePath: "",
8
+ cache: "no-store"
9
+ });
10
+ const id = (value) => {
11
+ if (!/^[a-f\d]{24}$/i.test(value)) throw new Error("A product ID is required.");
12
+ return value;
13
+ };
14
+ return { stock: {
15
+ read: (productId, signal) => api.request("GET", `${api.baseUrl}/${id(productId)}`, { options: { signal } }),
16
+ async query(productIds, signal) {
17
+ if (!productIds.length) return [];
18
+ if (productIds.length > 50) throw new Error("Read stock for at most 50 products at once.");
19
+ return (await api.request("POST", `${api.baseUrl}/query`, {
20
+ data: { productIds: productIds.map(id) },
21
+ options: { signal }
22
+ })).data;
23
+ },
24
+ setQuantity(productId, data, idempotencyKey) {
25
+ if (!idempotencyKey.trim()) throw new Error("Preserve an explicit idempotency key for stock retries.");
26
+ return api.request("POST", `${api.baseUrl}/${id(productId)}/action`, {
27
+ data: {
28
+ ...data,
29
+ action: "set_quantity"
30
+ },
31
+ options: { headerOptions: { "Idempotency-Key": idempotencyKey } }
32
+ });
33
+ }
34
+ } };
35
+ }
3
36
  // src/management-delivery.ts
4
37
  function createMerchantDelivery(client) {
5
38
  const api = new BaseApi("stores/delivery", {
@@ -180,6 +213,7 @@ function createMerchantClient(options) {
180
213
  const stores = new BaseApi("stores", config);
181
214
  return {
182
215
  ...createMerchantAccess(client),
216
+ ...createMerchantStock(client),
183
217
  ...createMerchantDelivery(client),
184
218
  products,
185
219
  collections: new BaseApi("collections", config),
@@ -2,6 +2,14 @@ import { Address } from "@classytic/primitives/address";
2
2
  import { Money } from "@classytic/primitives/money";
3
3
  /** Public wire projections; merchant-only catalog and buyer fields are absent. */
4
4
  interface StorefrontProduct {
5
+ stock?: {
6
+ tracked: boolean;
7
+ quantity?: number;
8
+ variants?: Array<{
9
+ sku?: string;
10
+ quantity: number;
11
+ }>;
12
+ };
5
13
  id: string;
6
14
  name: string;
7
15
  slug: string;
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-CwAQqf0Z.js";
1
+ import { a as StorefrontCategory, c as StorefrontCollectionQuery, d as StorefrontProductPage, f as StorefrontProductQuery, i as GuestOrderAccess, l as StorefrontOrder, m as StorefrontRequestOptions, n as GuestCheckoutOptions, o as StorefrontCollection, p as StorefrontProfile, r as GuestCheckoutResult, s as StorefrontCollectionPage, t as GuestCheckoutInput, u as StorefrontProduct } from "./types-Bz-eFZb2.js";
2
2
  export { GuestCheckoutInput, GuestCheckoutOptions, GuestCheckoutResult, GuestOrderAccess, StorefrontCategory, StorefrontCollection, StorefrontCollectionPage, StorefrontCollectionQuery, StorefrontOrder, StorefrontProduct, StorefrontProductPage, StorefrontProductQuery, StorefrontProfile, StorefrontRequestOptions };
package/management.md CHANGED
@@ -127,3 +127,31 @@ memberships. Upload cover images with `merchant.media.uploadImage(file,
127
127
  Existing string labels require the Brihot server's `collections:migrate` script
128
128
  before switching an existing storefront to managed collection navigation. It is a
129
129
  per-store dry run by default; it creates metadata without rewriting memberships.
130
+
131
+ ## Stock
132
+
133
+ Brihot uses available quantities per product/variant. New products default to
134
+ tracked; existing products keep their saved setting. Untracked products have no
135
+ quantity limit. Set counts before enabling tracking on an existing product.
136
+
137
+ ```ts
138
+ const stock = await merchant.stock.read(productId);
139
+ const item = stock.items[0];
140
+ await merchant.stock.setQuantity(productId, {
141
+ variantSku: item.variantSku, // omit for a simple product
142
+ quantity: 12,
143
+ expectedQuantity: item.quantity,
144
+ }, crypto.randomUUID()); // create once; reuse on a transport retry
145
+ await merchant.products.update({ id: productId, data: {
146
+ inventory: { stockTracked: true },
147
+ } });
148
+ ```
149
+
150
+ Use `stock.query(productIds)` for a table page (maximum 50 products), rather than
151
+ one request per row. A stale count returns 409; reload before submitting a new
152
+ count. Checkout deducts atomically and refuses insufficient stock. Canceling an
153
+ unshipped order restores its quantity once; shipping does not deduct again.
154
+
155
+ Storefront products optionally expose `stock: { tracked, quantity, variants }`.
156
+ Use a selected variant's quantity, disable sold-out options and cap the quantity
157
+ input. These values inform the UI; checkout always validates current stock.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/commerce-client",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Typed storefront and merchant clients for the Classytic Commerce API",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -63,7 +63,7 @@
63
63
  "esbuild": "^0.28.1",
64
64
  "publint": "^0.3.25",
65
65
  "tsdown": "^0.23.1",
66
- "typescript": "^7.1.0-beta",
66
+ "typescript": "7.1.0-beta",
67
67
  "vitest": "^5.0.5"
68
68
  },
69
69
  "publishConfig": {