@01.software/sdk 0.32.0 → 0.33.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.
Files changed (46) hide show
  1. package/README.md +43 -20
  2. package/dist/client.cjs +31 -2
  3. package/dist/client.cjs.map +1 -1
  4. package/dist/client.d.cts +6 -6
  5. package/dist/client.d.ts +6 -6
  6. package/dist/client.js +31 -2
  7. package/dist/client.js.map +1 -1
  8. package/dist/{collection-client-DPGXnhoF.d.ts → collection-client-B6SlhzIP.d.ts} +3 -3
  9. package/dist/{collection-client-CORhppPb.d.cts → collection-client-De6eKW1J.d.cts} +3 -3
  10. package/dist/{const-Brk2Ff0q.d.cts → const-DwmSDeWq.d.ts} +2 -2
  11. package/dist/{const-DcY2_z9O.d.ts → const-sPR2IkCe.d.cts} +2 -2
  12. package/dist/index.cjs +256 -90
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +7 -7
  15. package/dist/index.d.ts +7 -7
  16. package/dist/index.js +256 -90
  17. package/dist/index.js.map +1 -1
  18. package/dist/{payload-types-DVK1QCeU.d.cts → payload-types-dkeQyrDC.d.cts} +1562 -1436
  19. package/dist/{payload-types-DVK1QCeU.d.ts → payload-types-dkeQyrDC.d.ts} +1562 -1436
  20. package/dist/query.cjs.map +1 -1
  21. package/dist/query.d.cts +9 -9
  22. package/dist/query.d.ts +9 -9
  23. package/dist/query.js.map +1 -1
  24. package/dist/realtime.d.cts +2 -2
  25. package/dist/realtime.d.ts +2 -2
  26. package/dist/server.cjs +37 -6
  27. package/dist/server.cjs.map +1 -1
  28. package/dist/server.d.cts +7 -7
  29. package/dist/server.d.ts +7 -7
  30. package/dist/server.js +37 -6
  31. package/dist/server.js.map +1 -1
  32. package/dist/{types-ByMrR_Z_.d.cts → types-B3YT092I.d.cts} +1 -1
  33. package/dist/{types-CYMSBkJC.d.ts → types-BHh0YLmq.d.ts} +27 -10
  34. package/dist/{types-CAkWqIr6.d.cts → types-BZKxss8Y.d.cts} +27 -10
  35. package/dist/{types-DUPC7Xn6.d.ts → types-Cel_4L9t.d.ts} +1 -1
  36. package/dist/ui/form.d.cts +1 -1
  37. package/dist/ui/form.d.ts +1 -1
  38. package/dist/ui/video.d.cts +1 -1
  39. package/dist/ui/video.d.ts +1 -1
  40. package/dist/webhook.cjs +94 -0
  41. package/dist/webhook.cjs.map +1 -1
  42. package/dist/webhook.d.cts +82 -7
  43. package/dist/webhook.d.ts +82 -7
  44. package/dist/webhook.js +94 -0
  45. package/dist/webhook.js.map +1 -1
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -168,8 +168,9 @@ const preview = await server.preview.detail(
168
168
  ```
169
169
 
170
170
  For product pages, `server.commerce.product.previewDetail({ id }, {
171
- previewToken })` returns the same shaped product detail as `detail()`, but allows
172
- the saved draft/unpublished record addressed by the preview token.
171
+ previewToken })` returns the raw product detail payload for the saved
172
+ draft/unpublished record addressed by the preview token. `detail()` wraps the
173
+ published storefront payload in a `{ found, product | reason }` result.
173
174
 
174
175
  ## Getting product detail
175
176
 
@@ -179,19 +180,33 @@ The recommended way to fetch a single product is the shaped helper:
179
180
  import { createClient } from '@01.software/sdk'
180
181
 
181
182
  const client = createClient({
182
- publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
183
+ publishableKey: '<publishable-key>',
183
184
  })
184
185
 
185
- const product = await client.commerce.product.detail({
186
+ const result = await client.commerce.product.detail({
186
187
  slug: 'every-peach-tee',
187
188
  })
188
- if (!product) {
189
- return notFound() // returned null — product missing, unpublished, or not in this tenant
189
+ if (!result.found) {
190
+ return notFound()
190
191
  }
192
+
193
+ const { product } = result
191
194
  // product: { product, variants, options, brand, categories, tags, images, videos, listing }
192
195
  ```
193
196
 
194
- `detail()` returns `ProductDetail | null`. A `null` result covers every "no result" reason: `not_found`, `not_published`, `feature_disabled`. Render the same "not available" UI for all three. To recover the exact reason for triage, `404` maps to `null` rather than a thrown error — inspect `client.lastRequestId` and match against backend logs.
197
+ `detail()` returns `ProductDetailResult`, a discriminated union:
198
+ `{ found: true, product: ProductDetail } | { found: false, reason }`. The
199
+ `reason` value is one of `not_found`, `not_published`, or
200
+ `feature_disabled`, so storefronts can choose between a standard 404, preview
201
+ CTA, or feature gating UI. Permission/auth errors, including 403 tenant
202
+ mismatches, still throw
203
+ typed `SDKError` subclasses and preserve request IDs through the existing
204
+ `lastRequestId` / `onRequestId` path.
205
+
206
+ The successful product payload exposes inventory rollups without sentinel
207
+ values: `product.totalInventory` is the tracked stock sum across non-unlimited
208
+ variants, `null` when no variants are tracked, and
209
+ `product.hasUnlimitedVariant` signals whether any variant is unlimited.
195
210
 
196
211
  ### Product selection helpers
197
212
 
@@ -291,12 +306,12 @@ coexist without being interpreted as selection state.
291
306
  inspect grouped variant fields without a follow-up fetch. The by-ids response
292
307
  also returns `missing: string[]` for requested product IDs that were not found,
293
308
  not published, or not accessible; `docs` preserve the input `productIds` order
294
- for returned products. The card carries product-level hero media
295
- (`product.thumbnail` -> first `product.images` -> `null`), an aggregated
296
- price range across all option-value groups, and a `swatches[]` array
297
- derived from groups when there is more than one. Single-group products
298
- emit `swatches: []`; storefronts that disagree can read `item.groups`
299
- directly.
309
+ for returned products. The helper populates optional `representativeVariant`, a
310
+ PDP-seeded `href`, representative media (`product.thumbnail` -> first
311
+ `product.images` -> representative variant media -> `null`), an aggregated price
312
+ range across all option-value groups, and a `swatches[]` array derived from
313
+ groups when there is more than one. Single-group products emit `swatches: []`;
314
+ storefronts that disagree can read `item.groups` directly.
300
315
 
301
316
  ```ts
302
317
  import {
@@ -309,9 +324,10 @@ const cards: ProductListingCard[] = response.docs.map((item) =>
309
324
  )
310
325
  ```
311
326
 
312
- Each swatch carries a hint-only option-value href
313
- (`?opt.<optionId>=<valueId>`); the detail page resolves it through
314
- `resolveProductSelection(detail, { search })`.
327
+ The card href seeds the same representative variant that the PDP resolves by
328
+ default through `resolveProductSelection(detail)`. Each swatch carries a
329
+ hint-only option-value href (`?opt.<optionId>=<valueId>`); the detail page
330
+ resolves it through `resolveProductSelection(detail, { search })`.
315
331
 
316
332
  ## Advanced: direct Payload queries (escape hatch)
317
333
 
@@ -401,10 +417,11 @@ export const revalidate = 60 // ISR — adjust per page freshness need
401
417
 
402
418
  export default async function ProductPage({ params }) {
403
419
  const client = createClient({
404
- publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
420
+ publishableKey: '<publishable-key>',
405
421
  })
406
- const product = await client.commerce.product.detail({ slug: params.slug })
407
- if (!product) return notFound()
422
+ const result = await client.commerce.product.detail({ slug: params.slug })
423
+ if (!result.found) return notFound()
424
+ const { product } = result
408
425
  // ...
409
426
  }
410
427
  ```
@@ -739,7 +756,13 @@ await server.commerce.orders.updateTransaction({ pgPaymentId, status, paymentMet
739
756
  // Returns
740
757
  await server.commerce.orders.createReturn({ orderNumber, returnItems, refundAmount, reason? })
741
758
  await server.commerce.orders.updateReturn({ returnId, status })
742
- await server.commerce.orders.returnWithRefund({ orderNumber, returnItems, refundAmount, pgPaymentId })
759
+ await server.commerce.orders.returnWithRefund({
760
+ orderNumber,
761
+ returnItems: [{ orderItem, quantity, restockingFee? }],
762
+ refundAmount,
763
+ returnShippingFee?,
764
+ pgPaymentId,
765
+ })
743
766
  ```
744
767
 
745
768
  ### Commerce Discounts / Shipping
package/dist/client.cjs CHANGED
@@ -1375,6 +1375,30 @@ var CartApi = class {
1375
1375
  }
1376
1376
  };
1377
1377
 
1378
+ // src/core/api/product-api.ts
1379
+ var PRODUCT_DETAIL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
1380
+ "not_found",
1381
+ "not_published",
1382
+ "feature_disabled"
1383
+ ]);
1384
+ function isRecord(value) {
1385
+ return typeof value === "object" && value !== null;
1386
+ }
1387
+ function readProductDetailUnavailableReason(value) {
1388
+ if (!isRecord(value)) return void 0;
1389
+ const directReason = value.reason ?? value.code;
1390
+ if (typeof directReason === "string" && PRODUCT_DETAIL_UNAVAILABLE_REASONS.has(directReason)) {
1391
+ return directReason;
1392
+ }
1393
+ return readProductDetailUnavailableReason(value.body);
1394
+ }
1395
+ function productDetailResultFromError(error) {
1396
+ if (!(error instanceof SDKError) || error.status !== 404) return void 0;
1397
+ const reason = readProductDetailUnavailableReason(error.details);
1398
+ if (!reason) return void 0;
1399
+ return { found: false, reason };
1400
+ }
1401
+
1378
1402
  // src/core/commerce/commerce-client.ts
1379
1403
  var CommerceClient = class {
1380
1404
  constructor(options) {
@@ -1409,9 +1433,14 @@ var CommerceClient = class {
1409
1433
  listingGroups: (params) => execute("/api/products/listing-groups", params),
1410
1434
  detail: async (params) => {
1411
1435
  try {
1412
- return await execute("/api/products/detail", params);
1436
+ const product = await execute(
1437
+ "/api/products/detail",
1438
+ params
1439
+ );
1440
+ return { found: true, product };
1413
1441
  } catch (err) {
1414
- if (err instanceof NotFoundError) return null;
1442
+ const notFoundResult = productDetailResultFromError(err);
1443
+ if (notFoundResult) return notFoundResult;
1415
1444
  throw err;
1416
1445
  }
1417
1446
  }