@01.software/sdk 0.37.0 → 0.39.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/README.md +189 -84
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +1286 -109
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +8 -7
- package/dist/client.d.ts +8 -7
- package/dist/client.js +1286 -109
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-DyELGUcL.d.ts → collection-client-CaMgs5KE.d.ts} +18 -12
- package/dist/{collection-client-zOmnxwdA.d.cts → collection-client-DVfB0Em1.d.cts} +18 -12
- package/dist/const-6XHz_jej.d.ts +32 -0
- package/dist/const-B5KT72c7.d.cts +32 -0
- package/dist/errors.cjs +4 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +4 -1
- package/dist/errors.js.map +1 -1
- package/dist/{index-DRJs7QIh.d.cts → index-BOLQxveo.d.cts} +3 -3
- package/dist/{index-DTqoUZk_.d.ts → index-CSwR2HSg.d.ts} +3 -3
- package/dist/index.cjs +2861 -2714
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +2861 -2714
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-CREOjFNT.d.cts → payload-types-m3jjhxk9.d.cts} +418 -106
- package/dist/{payload-types-CREOjFNT.d.ts → payload-types-m3jjhxk9.d.ts} +418 -106
- package/dist/query.cjs +244 -1093
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +159 -34
- package/dist/query.d.ts +159 -34
- package/dist/query.js +244 -1093
- package/dist/query.js.map +1 -1
- package/dist/realtime.cjs +5 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +5 -1
- package/dist/realtime.js.map +1 -1
- package/dist/server.cjs +1191 -22
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +7 -7
- package/dist/server.d.ts +7 -7
- package/dist/server.js +1191 -22
- package/dist/server.js.map +1 -1
- package/dist/storefront-cache.cjs +144 -0
- package/dist/storefront-cache.cjs.map +1 -0
- package/dist/storefront-cache.d.cts +24 -0
- package/dist/storefront-cache.d.ts +24 -0
- package/dist/storefront-cache.js +121 -0
- package/dist/storefront-cache.js.map +1 -0
- package/dist/{types-DMvVHdb1.d.ts → types-BQo7UdI9.d.cts} +1608 -1215
- package/dist/{types-BWMUr3Zw.d.cts → types-CVf8sCZ-.d.ts} +1608 -1215
- package/dist/{types-CxzWHspI.d.ts → types-Cmrd1ezc.d.ts} +1 -15
- package/dist/{types-BkZNhuBh.d.cts → types-D0ubzQw0.d.cts} +1 -15
- package/dist/ui/canvas/server.cjs +5 -1
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.js +5 -1
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +5 -1
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.js +5 -1
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +4 -4
- package/dist/webhook.d.ts +4 -4
- package/package.json +11 -1
- package/dist/const-CK_FPaIn.d.cts +0 -32
- package/dist/const-Dqz05oaG.d.ts +0 -32
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ pnpm add @01.software/sdk
|
|
|
19
19
|
- Customer auth hooks (useCustomerMe, useCustomerLogin, etc.) with cache management
|
|
20
20
|
- Automatic retry with exponential backoff (non-retryable: 400, 401, 403, 404, 409, 422)
|
|
21
21
|
- Webhook handling with HMAC-SHA256 signature verification
|
|
22
|
-
- Sub-path imports (`./server`, `./webhook`, `./realtime`, `./ui/*`) for tree-shaking
|
|
22
|
+
- Sub-path imports (`./server`, `./webhook`, `./realtime`, `./storefront-cache`, `./ui/*`) for tree-shaking
|
|
23
23
|
- Type-safe read-only `collections.from()` for Client (compile-time write prevention)
|
|
24
24
|
|
|
25
25
|
### Sub-path Imports
|
|
@@ -57,6 +57,9 @@ import {
|
|
|
57
57
|
// Realtime only
|
|
58
58
|
import { createRealtimeClient } from '@01.software/sdk/realtime'
|
|
59
59
|
|
|
60
|
+
// Storefront cache resource names for SSG/ISR adapters
|
|
61
|
+
import { storefrontCacheResources } from '@01.software/sdk/storefront-cache'
|
|
62
|
+
|
|
60
63
|
// Components - sub-path imports per domain
|
|
61
64
|
import { Analytics } from '@01.software/sdk/analytics/react'
|
|
62
65
|
import { RichTextContent } from '@01.software/sdk/ui/rich-text'
|
|
@@ -79,6 +82,7 @@ entry.
|
|
|
79
82
|
| `@01.software/sdk/server` | `createServerClient`, server-only collection, commerce, and preview APIs | none; keep `secretKey` code on the server |
|
|
80
83
|
| `@01.software/sdk/query` | React Query hooks, cache helpers, `getQueryClient` | `@tanstack/react-query`, `react`, `react-dom` |
|
|
81
84
|
| `@01.software/sdk/realtime` | `RealtimeConnection`, `useRealtimeQuery` | `@tanstack/react-query`, `react`, `react-dom` |
|
|
85
|
+
| `@01.software/sdk/storefront-cache` | product storefront cache resource name helpers | none |
|
|
82
86
|
| `@01.software/sdk/analytics/react` | `<Analytics />` | `react`, `react-dom` |
|
|
83
87
|
| `@01.software/sdk/ui/rich-text` | `RichTextContent`, `StyledRichTextContent` | `react`, `react-dom`, `@payloadcms/richtext-lexical` |
|
|
84
88
|
| `@01.software/sdk/ui/form` | `FormRenderer` | `react`, `react-dom` |
|
|
@@ -99,6 +103,8 @@ Migration quick reference:
|
|
|
99
103
|
- `createServerClient` must be imported from `@01.software/sdk/server`.
|
|
100
104
|
- React Query hooks and cache helpers must be imported from
|
|
101
105
|
`@01.software/sdk/query`.
|
|
106
|
+
- Product storefront cache resource helpers must be imported from
|
|
107
|
+
`@01.software/sdk/storefront-cache`.
|
|
102
108
|
- UI components must be imported from the specific `@01.software/sdk/ui/*`
|
|
103
109
|
sub-path and require only that row's peers.
|
|
104
110
|
- Console-shared pure ecommerce helpers live in private
|
|
@@ -140,8 +146,16 @@ const serverQuery = createServerQueryHooks(server)
|
|
|
140
146
|
const order = await server.commerce.orders.create({
|
|
141
147
|
orderNumber: generateOrderNumber(),
|
|
142
148
|
customerSnapshot: { email: 'user@example.com' },
|
|
143
|
-
shippingAddress: {
|
|
144
|
-
|
|
149
|
+
shippingAddress: {
|
|
150
|
+
recipientName: 'John',
|
|
151
|
+
phone: '010-1234-5678',
|
|
152
|
+
postalCode: '12345',
|
|
153
|
+
address: 'Seoul',
|
|
154
|
+
detailAddress: 'Apt 101',
|
|
155
|
+
},
|
|
156
|
+
items: [
|
|
157
|
+
{ product: productId, variant: variantId, option: optionId, quantity: 1 },
|
|
158
|
+
],
|
|
145
159
|
totalAmount: 10000,
|
|
146
160
|
pgPaymentId: 'provider-payment-id', // optional (omit for free orders)
|
|
147
161
|
discountCode: 'WELCOME10', // optional
|
|
@@ -176,6 +190,61 @@ previewToken })` returns the raw product detail payload for the saved
|
|
|
176
190
|
draft/unpublished record addressed by the preview token. `detail()` wraps the
|
|
177
191
|
published storefront payload in a `{ found, product | reason }` result.
|
|
178
192
|
|
|
193
|
+
## Getting storefront content
|
|
194
|
+
|
|
195
|
+
Use shaped content helpers when a browser storefront needs relationship-backed
|
|
196
|
+
media for common public content. These helpers use the publishable key only;
|
|
197
|
+
the server resolves the tenant, enforces the owning feature, excludes drafts,
|
|
198
|
+
and returns allowlisted DTOs instead of raw Payload documents.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { createClient } from '@01.software/sdk'
|
|
202
|
+
|
|
203
|
+
const client = createClient({ publishableKey: '<publishable-key>' })
|
|
204
|
+
|
|
205
|
+
const links = await client.content.links.list({
|
|
206
|
+
limit: 10,
|
|
207
|
+
categorySlug: 'social',
|
|
208
|
+
tagSlug: 'footer',
|
|
209
|
+
featured: true,
|
|
210
|
+
sort: 'title',
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
const gallery = await client.content.galleryItems.list({
|
|
214
|
+
gallerySlug: 'spring-lookbook',
|
|
215
|
+
limit: 24,
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`content.links.list()` calls `GET /api/links/storefront`; link DTOs include
|
|
220
|
+
display fields, categories/tags, thumbnail, and icon media. Operator-only
|
|
221
|
+
fields such as tenant, metadata, click counters, and private storage details
|
|
222
|
+
are omitted. Use `categorySlug` and `tagSlug` for stable storefront URLs when
|
|
223
|
+
available; `categoryId` and `tagId` are also supported. Expired links are
|
|
224
|
+
excluded by default, though visible link DTOs may include `expiresAt` so
|
|
225
|
+
storefronts can render time-sensitive copy.
|
|
226
|
+
|
|
227
|
+
`content.galleryItems.list()` calls `GET /api/gallery-items/storefront`;
|
|
228
|
+
gallery item DTOs include title, description, content, gallery reference, and
|
|
229
|
+
image media. Tenant, metadata, draft status, and storage/provider internals are
|
|
230
|
+
omitted. Gallery item reads require either `gallerySlug` or `galleryId`; prefer
|
|
231
|
+
`gallerySlug` for storefront routes. The default gallery item sort is `manual`,
|
|
232
|
+
matching curator order from the Admin Panel.
|
|
233
|
+
|
|
234
|
+
Both helpers return Payload-style pagination (`docs`, `totalDocs`, `page`,
|
|
235
|
+
`limit`, etc.). `limit` is bounded server-side to `1..100`; invalid query,
|
|
236
|
+
publishable-key, feature, rate-limit, and server errors surface through the
|
|
237
|
+
SDK's typed error classes and preserve request IDs on `client.lastRequestId`.
|
|
238
|
+
SDK sort inputs are typed public allowlists: links support created/updated/
|
|
239
|
+
published dates, title, and featured ordering; gallery items support manual
|
|
240
|
+
curator order, created/updated dates, and title.
|
|
241
|
+
|
|
242
|
+
Use `client.collections.from(...).find()` only as the advanced raw collection
|
|
243
|
+
escape hatch. Browser publishable-key raw reads stay shallow (`depth: 0`,
|
|
244
|
+
`joins: false`) and are not for relationship-expanded media. Use shaped helpers
|
|
245
|
+
for storefront content media, or `createServerClient()` when a server route
|
|
246
|
+
needs full raw collection access with server credentials.
|
|
247
|
+
|
|
179
248
|
## Getting product detail
|
|
180
249
|
|
|
181
250
|
The recommended way to fetch a single product is the shaped helper:
|
|
@@ -219,14 +288,13 @@ catalog/stock split instead of reading `variant.stock` from a cached
|
|
|
219
288
|
`detail()` response (inventory in that payload can lag for the catalog TTL).
|
|
220
289
|
|
|
221
290
|
```typescript
|
|
222
|
-
import {
|
|
223
|
-
createClient,
|
|
224
|
-
mergeProductDetailWithStock,
|
|
225
|
-
} from '@01.software/sdk'
|
|
291
|
+
import { createClient, mergeProductDetailWithStock } from '@01.software/sdk'
|
|
226
292
|
|
|
227
293
|
const client = createClient({ publishableKey: '<publishable-key>' })
|
|
228
294
|
|
|
229
|
-
const catalog = await client.commerce.product.detailCatalog({
|
|
295
|
+
const catalog = await client.commerce.product.detailCatalog({
|
|
296
|
+
slug: 'every-peach-tee',
|
|
297
|
+
})
|
|
230
298
|
if (!catalog.found) return notFound()
|
|
231
299
|
|
|
232
300
|
const variantIds = catalog.product.variants.map((v) => v.id)
|
|
@@ -238,46 +306,63 @@ const { product, stockMergeStatus } = mergeProductDetailWithStock(
|
|
|
238
306
|
// stockMergeStatus: 'complete' | 'partial' — partial when a variant id is missing from snapshot
|
|
239
307
|
```
|
|
240
308
|
|
|
241
|
-
Listing
|
|
242
|
-
|
|
309
|
+
Listing UIs use the same pattern: `listingPage()` for PLP/search grids, or
|
|
310
|
+
`listingGroupsCatalog({ productIds })` when curated product IDs are already
|
|
311
|
+
known, then `stockSnapshot()` (or `stock-check` at cart/checkout) for live
|
|
243
312
|
availability. `detail()` and POST detail/listing endpoints are unchanged during
|
|
244
313
|
the migration window; see ADR 0012 addendum in `docs/decisions/0012-sdk-public-commerce-contract.md`.
|
|
245
314
|
|
|
246
315
|
### Product Listing Pages (PLP) — join-safe queries
|
|
247
316
|
|
|
248
|
-
**Recommended path:** Use `commerce.product.
|
|
249
|
-
`
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
317
|
+
**Recommended path:** Use `commerce.product.listingPage()` (or
|
|
318
|
+
`createQueryHooks(client).useProductListingPage()`) for greenfield storefront
|
|
319
|
+
PLPs. It wraps the cacheable listing-groups query endpoint, keeps the raw
|
|
320
|
+
Payload pagination response, and adds `cards` built with
|
|
321
|
+
`buildProductListingCard()`. The endpoint returns pre-grouped listing data and
|
|
322
|
+
avoids the top-level `products.options` / `products.variants` join truncation
|
|
323
|
+
that raw REST product queries hit by default.
|
|
254
324
|
|
|
255
325
|
```typescript
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
type ProductListingCard,
|
|
259
|
-
} from '@01.software/sdk'
|
|
260
|
-
|
|
261
|
-
const response = await client.commerce.product.listingGroupsCatalog({
|
|
262
|
-
where: { status: { equals: 'published' } },
|
|
326
|
+
const response = await client.commerce.product.listingPage({
|
|
327
|
+
search: 'shirt',
|
|
263
328
|
limit: 24,
|
|
329
|
+
filters: {
|
|
330
|
+
categoryIds: ['category-1'],
|
|
331
|
+
price: { min: 10000, max: 50000 },
|
|
332
|
+
availableForSale: true,
|
|
333
|
+
},
|
|
334
|
+
basePath: '/shop',
|
|
264
335
|
})
|
|
265
336
|
|
|
266
|
-
const cards
|
|
267
|
-
|
|
268
|
-
|
|
337
|
+
const cards = response.cards
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Use `commerce.product.listingGroupsCatalog({ productIds })` when product IDs
|
|
341
|
+
are already known, for example curated rails, recommendations, or editorial
|
|
342
|
+
sections:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
const response = await client.commerce.product.listingGroupsCatalog({
|
|
346
|
+
productIds: ['product-1', 'product-2'],
|
|
347
|
+
})
|
|
269
348
|
```
|
|
270
349
|
|
|
271
|
-
**
|
|
272
|
-
(bulk operations, custom filters, fields the helper
|
|
350
|
+
**Server-auth escape hatch:** When server code deliberately needs raw
|
|
351
|
+
`products` collection reads (bulk operations, custom filters, fields the helper
|
|
352
|
+
does not expose), use `createServerClient()` and spread
|
|
273
353
|
`PRODUCT_PLP_FIND_OPTIONS` to raise the default Payload join limit of 10:
|
|
274
354
|
|
|
275
355
|
```typescript
|
|
276
|
-
import {
|
|
277
|
-
|
|
278
|
-
} from '@01.software/sdk'
|
|
356
|
+
import { PRODUCT_PLP_FIND_OPTIONS } from '@01.software/sdk'
|
|
357
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
279
358
|
|
|
280
|
-
const
|
|
359
|
+
const server = createServerClient({
|
|
360
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
361
|
+
apiUrl: process.env.SOFTWARE_API_URL!,
|
|
362
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
const { docs } = await server.collections.from('products').find({
|
|
281
366
|
...PRODUCT_PLP_FIND_OPTIONS,
|
|
282
367
|
where: { status: { equals: 'published' } },
|
|
283
368
|
limit: 24,
|
|
@@ -288,7 +373,10 @@ const { docs } = await client.collections.from('products').find({
|
|
|
288
373
|
limits with `sort: '_order'`. It cures top-level `products.options` and
|
|
289
374
|
`products.variants` join truncation but **cannot** cure the nested
|
|
290
375
|
`options[].values.docs` join — the Payload REST `joins` param is flat and
|
|
291
|
-
nested join limits require the listing-groups endpoint.
|
|
376
|
+
nested join limits require the listing-groups endpoint. This preset is not
|
|
377
|
+
accepted by publishable `createClient().collections.from('products').find()`
|
|
378
|
+
because browser-public raw reads are constrained to `depth: 0` and
|
|
379
|
+
`joins: false`, and cannot use `populate`.
|
|
292
380
|
|
|
293
381
|
### Product selection helpers
|
|
294
382
|
|
|
@@ -325,9 +413,9 @@ aligned without rebuilding media priority in storefront code.
|
|
|
325
413
|
|
|
326
414
|
For new storefront work, prefer pool-pointer and gallery-aware resolution from
|
|
327
415
|
`commerce.product.detail()` + `resolveProductSelection()` (and
|
|
328
|
-
`getProductSelectionImages()` when a list is needed). Direct
|
|
416
|
+
`getProductSelectionImages()` when a list is needed). Direct pre-ADR-0025 fields like
|
|
329
417
|
`variant.thumbnail` and option-value direct `images` are still accepted as
|
|
330
|
-
|
|
418
|
+
transitional input, but are no longer primary storefront media sources.
|
|
331
419
|
|
|
332
420
|
`availableValuesByOptionSlug` / `availableValuesByOptionId` include
|
|
333
421
|
`availableStock`, `isUnlimited`, and `availableForSale` per value so option UIs
|
|
@@ -393,9 +481,13 @@ By default, partial selections (for example color only) leave
|
|
|
393
481
|
`selectedOrFirstAvailableVariant` behavior with `fillDefaults: true`:
|
|
394
482
|
|
|
395
483
|
```typescript
|
|
396
|
-
const resolution = resolveProductSelection(
|
|
397
|
-
|
|
398
|
-
|
|
484
|
+
const resolution = resolveProductSelection(
|
|
485
|
+
product,
|
|
486
|
+
codec.parse('?opt.color=ivory'),
|
|
487
|
+
{
|
|
488
|
+
fillDefaults: true,
|
|
489
|
+
},
|
|
490
|
+
)
|
|
399
491
|
// resolution.selectedVariant is concrete; unselected options are filled
|
|
400
492
|
// using the same available-by-order rules as listing selectionHintVariant.
|
|
401
493
|
```
|
|
@@ -446,8 +538,8 @@ Selection query params are share/deep-link state, not index targets.
|
|
|
446
538
|
### Product listing card helper
|
|
447
539
|
|
|
448
540
|
`buildProductListingCard(item, options?)` turns a single
|
|
449
|
-
`commerce.product.
|
|
450
|
-
`ProductListingCard`. Each item includes `listingGroupingState` (`grouped`,
|
|
541
|
+
`commerce.product.listingPage()` or `listingGroupsCatalog()` response item into
|
|
542
|
+
a render-ready `ProductListingCard`. Each item includes `listingGroupingState` (`grouped`,
|
|
451
543
|
`no_primary_option`, or `empty`) and, when empty, `listingGroupingEmptyReason`
|
|
452
544
|
(`primary_option_not_linked`, `primary_option_has_no_values`, or
|
|
453
545
|
`no_variants_for_primary_option`). Each group includes public-safe
|
|
@@ -488,14 +580,17 @@ the card should deep-link a complete hint variant.
|
|
|
488
580
|
|
|
489
581
|
## Storefront performance defaults
|
|
490
582
|
|
|
491
|
-
- **PLP:** prefer `
|
|
583
|
+
- **PLP:** prefer `commerce.product.listingPage()` or `useProductListingPage()` (GET `/api/products/listing-groups/query/catalog`, CDN-cacheable, card-ready). Use `listingGroupsCatalog({ productIds })` only when IDs are already known. Treat the full listing-groups response shape as a server-auth escape hatch because it can include operational stock fields. Avoid fetching a product list and then calling `detail()` per card.
|
|
492
584
|
- **PDP:** prefer `useProductDetailBySlug()` / `commerce.product.detail()`. Override `staleTime` / `retry` on the hook when you need fresher catalog data or faster failure on errors.
|
|
493
585
|
- **CDN-friendly reads:** server/edge code can use `detailCatalog()` and `listingGroupsCatalog()` (GET, cacheable) plus batched `stockSnapshot()` for live inventory.
|
|
494
586
|
- **React Query in the browser:** default `getQueryClient()` keeps SSR data fresh forever (`staleTime: Infinity`). For client-only storefronts, use `getStorefrontQueryClient()` (~1 minute staleTime) when creating query hooks:
|
|
495
587
|
|
|
496
588
|
```ts
|
|
497
589
|
import { createClient } from '@01.software/sdk'
|
|
498
|
-
import {
|
|
590
|
+
import {
|
|
591
|
+
createQueryHooks,
|
|
592
|
+
getStorefrontQueryClient,
|
|
593
|
+
} from '@01.software/sdk/query'
|
|
499
594
|
|
|
500
595
|
const client = createClient({ publishableKey: '...' })
|
|
501
596
|
const query = createQueryHooks(client, getStorefrontQueryClient())
|
|
@@ -507,11 +602,12 @@ Most consumers should use the helper APIs above (`commerce.product.detail`, etc.
|
|
|
507
602
|
|
|
508
603
|
### `depth` — how deep to populate relationship fields
|
|
509
604
|
|
|
510
|
-
`depth` is the primary control for populating relationships like `category`, `images`, `brand`.
|
|
605
|
+
`depth` is the primary control for populating relationships like `category`, `images`, `brand`. Browser publishable-key raw collection reads are constrained to `depth: 0` with `joins: false` and no `populate`; relationship-rich storefront reads should use shaped helpers such as `commerce.product.detail()` / `listingPage()`, or a `createServerClient()` raw query when server credentials are appropriate. Browser SDK raw reads add those safe defaults automatically and reject relationship-expanded raw read options before making a request.
|
|
511
606
|
|
|
512
607
|
```typescript
|
|
513
608
|
const product = await client.collections.from('products').findById(id, {
|
|
514
|
-
depth:
|
|
609
|
+
depth: 0,
|
|
610
|
+
joins: false,
|
|
515
611
|
})
|
|
516
612
|
```
|
|
517
613
|
|
|
@@ -520,7 +616,7 @@ const product = await client.collections.from('products').findById(id, {
|
|
|
520
616
|
`populate` controls which fields are returned per collection. It does NOT decide which relationships to populate — that is `depth`.
|
|
521
617
|
|
|
522
618
|
```typescript
|
|
523
|
-
await
|
|
619
|
+
await server.collections.from('products').find({
|
|
524
620
|
depth: 2,
|
|
525
621
|
populate: {
|
|
526
622
|
categories: { title: true, slug: true },
|
|
@@ -531,11 +627,11 @@ await client.collections.from('products').find({
|
|
|
531
627
|
|
|
532
628
|
### `joins` — Payload join-field reverse-relations
|
|
533
629
|
|
|
534
|
-
`joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's
|
|
630
|
+
`joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's SDK schema, browser-public relations such as `products.variants`, `products.options`, and `article-authors.articles`, plus server-auth relations such as `customers.orders`, `customers.addresses`, `posts.comments`, and `orders.{items,transactions,fulfillments,returns}`, are all join fields — you must use `joins` (not `depth`/`populate`) to control their pagination, sorting, filtering, and count. Internal backing joins such as product collection memberships are intentionally omitted from SDK collection types.
|
|
535
631
|
|
|
536
632
|
```typescript
|
|
537
633
|
// Canonical product detail query — variants/options are join fields on Products
|
|
538
|
-
await
|
|
634
|
+
await server.collections.from('products').find({
|
|
539
635
|
where: { slug: { equals } },
|
|
540
636
|
joins: {
|
|
541
637
|
variants: { limit: 50, sort: '_order' },
|
|
@@ -546,11 +642,14 @@ await client.collections.from('products').find({
|
|
|
546
642
|
|
|
547
643
|
// Disable all join-field population for a lightweight list query
|
|
548
644
|
await client.collections.from('products').find({
|
|
645
|
+
depth: 0,
|
|
549
646
|
joins: false,
|
|
550
647
|
})
|
|
551
648
|
```
|
|
552
649
|
|
|
553
|
-
Each join field defaults to **limit 10** when `joins` is omitted. `depth` does not raise that cap — storefront PLPs that call `products.find()` with only `depth` and then `buildProductListingGroupsByOption()` can silently drop color swatches. Prefer `
|
|
650
|
+
Each join field defaults to **limit 10** when `joins` is omitted. `depth` does not raise that cap — storefront PLPs that call `products.find()` with only `depth` and then `buildProductListingGroupsByOption()` can silently drop color swatches. Prefer `listingPage()` for PLP cards, or use `PRODUCT_PLP_FIND_OPTIONS` only in server-auth raw product queries (see [PLP join-safe queries](#product-listing-pages-plp--join-safe-queries) above).
|
|
651
|
+
|
|
652
|
+
Publishable-key browser raw reads must keep `depth: 0`, `joins: false`, and omit `populate`; relationship-expanded public storefront reads belong behind shaped helpers. Use `createServerClient()` for raw `joins` queries that need server credentials.
|
|
554
653
|
|
|
555
654
|
`joins` does NOT populate normal relationship fields. Keys that do not match a `type: 'join'` field on the queried collection are silently ignored — e.g. `joins: { category: {} }` on Products is a no-op because `category` is not a join field there. For normal relationships use `depth` (and optionally `populate`).
|
|
556
655
|
|
|
@@ -651,7 +750,7 @@ const { docs, totalDocs, hasNextPage } = await client.collections
|
|
|
651
750
|
page: 1,
|
|
652
751
|
sort: '-createdAt',
|
|
653
752
|
where: { status: { equals: 'published' } },
|
|
654
|
-
depth:
|
|
753
|
+
depth: 0,
|
|
655
754
|
select: { title: true, slug: true },
|
|
656
755
|
})
|
|
657
756
|
|
|
@@ -661,8 +760,8 @@ const { docs } = await client.collections.from('products').find({
|
|
|
661
760
|
joins: false, // disable joins for lightweight list
|
|
662
761
|
})
|
|
663
762
|
|
|
664
|
-
// Override relationship populate
|
|
665
|
-
const product = await
|
|
763
|
+
// Override relationship populate and join expansion (server credentials only)
|
|
764
|
+
const product = await server.collections.from('products').findById(id, {
|
|
666
765
|
populate: { brands: { name: true, logo: true } },
|
|
667
766
|
joins: { variants: { limit: 50 } },
|
|
668
767
|
})
|
|
@@ -708,7 +807,7 @@ import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
|
|
|
708
807
|
const { docs } = await client.collections.from('products').find({
|
|
709
808
|
where: { slug: { equals: 'my-product' } },
|
|
710
809
|
limit: 1,
|
|
711
|
-
depth:
|
|
810
|
+
depth: 0,
|
|
712
811
|
})
|
|
713
812
|
const metadata = docs[0]
|
|
714
813
|
? generateMetadata(extractSeo(docs[0]), { siteName: 'My Store' })
|
|
@@ -893,7 +992,7 @@ await client.customer.auth.changePassword(currentPassword, newPassword)
|
|
|
893
992
|
|
|
894
993
|
### Commerce Orders (ServerClient-only writes)
|
|
895
994
|
|
|
896
|
-
Available on ServerClient via `server.commerce.orders.*`.
|
|
995
|
+
Available on ServerClient via `server.commerce.orders.*`. `checkout` and `listMine` are also on Client and return sanitized customer-facing order DTOs. Use `server.collections.from('orders')` for raw operational order documents.
|
|
897
996
|
|
|
898
997
|
```typescript
|
|
899
998
|
// Orders
|
|
@@ -927,6 +1026,8 @@ await server.commerce.orders.confirmPayment({
|
|
|
927
1026
|
|
|
928
1027
|
// Low-level transaction annotation / compatibility path. Prefer confirmPayment()
|
|
929
1028
|
// for normal provider-verified paid transitions.
|
|
1029
|
+
// status: 'failed' records a retryable PG failure on the Transaction only; the
|
|
1030
|
+
// Order stays pending until verified payment succeeds or the merchant cancels it.
|
|
930
1031
|
await server.commerce.orders.updateTransaction({ pgPaymentId, status, paymentMethod, receiptUrl })
|
|
931
1032
|
|
|
932
1033
|
// Returns
|
|
@@ -974,15 +1075,15 @@ not provide.
|
|
|
974
1075
|
documents, Payload saves product fields first; upsert receives `productId`,
|
|
975
1076
|
`graphRevision` (required when updating an existing product via `productId` -
|
|
976
1077
|
load from `GET /api/products/:id/composer-draft`), options, and variants. Do
|
|
977
|
-
not send removed
|
|
1078
|
+
not send removed pre-ADR-0025 media inputs (`optionValue.thumbnail`,
|
|
978
1079
|
`optionValue.images`, `variant.thumbnail`); use `swatch.mediaItemId` and
|
|
979
1080
|
variant `images[]`. Unknown keys are not part of the published upsert contract.
|
|
980
1081
|
|
|
981
|
-
| Payload
|
|
982
|
-
|
|
|
983
|
-
| Create
|
|
984
|
-
| Edit graph
|
|
985
|
-
| Edit
|
|
1082
|
+
| Payload | Valid | Invalid |
|
|
1083
|
+
| ------------- | -------------------------------------- | ------------------------------------------------------------------------ |
|
|
1084
|
+
| Create | `product: { title, ... }` + graph | `productId` on create; missing `product.title` |
|
|
1085
|
+
| Edit graph | `productId` + `graphRevision?` + graph | `product.title` / SEO on upsert; conflicting `productId` vs `product.id` |
|
|
1086
|
+
| Edit existing | `product: { id }` + graph only | `product: { id, title }` on edit |
|
|
986
1087
|
|
|
987
1088
|
```typescript
|
|
988
1089
|
const result = await server.commerce.product.upsert({
|
|
@@ -1076,7 +1177,7 @@ for (const item of results) {
|
|
|
1076
1177
|
|
|
1077
1178
|
### Commerce Cart
|
|
1078
1179
|
|
|
1079
|
-
Available on both Client and ServerClient via `commerce.cart.*`.
|
|
1180
|
+
Available on both Client and ServerClient via `commerce.cart.*`. These helpers return sanitized customer-facing cart DTOs; use `server.collections.from('carts' | 'cart-items')` for raw operational cart documents.
|
|
1080
1181
|
|
|
1081
1182
|
```typescript
|
|
1082
1183
|
// Add item to cart
|
|
@@ -1094,7 +1195,7 @@ await client.commerce.cart.updateItem({ cartItemId, quantity })
|
|
|
1094
1195
|
// Remove item from cart
|
|
1095
1196
|
await client.commerce.cart.removeItem({ cartItemId })
|
|
1096
1197
|
|
|
1097
|
-
// Other cart operations
|
|
1198
|
+
// Other cart operations return sanitized customer-facing cart DTOs.
|
|
1098
1199
|
await client.commerce.cart.get(cartId)
|
|
1099
1200
|
await client.commerce.cart.applyDiscount({ cartId, discountCode })
|
|
1100
1201
|
await client.commerce.cart.removeDiscount({ cartId })
|
|
@@ -1170,7 +1271,10 @@ const customerAuthHandler = createCustomerAuthWebhookHandler({
|
|
|
1170
1271
|
// Semantic order-change events keep operation as "update" for compatibility.
|
|
1171
1272
|
// Use isOrderChangedWebhookEvent when you need to distinguish manual ordering
|
|
1172
1273
|
// from content field edits.
|
|
1173
|
-
import {
|
|
1274
|
+
import {
|
|
1275
|
+
handleWebhook,
|
|
1276
|
+
isOrderChangedWebhookEvent,
|
|
1277
|
+
} from '@01.software/sdk/webhook'
|
|
1174
1278
|
|
|
1175
1279
|
function getWebhookSecret(): string {
|
|
1176
1280
|
const secret = process.env.WEBHOOK_SECRET
|
|
@@ -1203,31 +1307,32 @@ join-order surface and does not emit a semantic order-change webhook.
|
|
|
1203
1307
|
|
|
1204
1308
|
## Supported Collections
|
|
1205
1309
|
|
|
1206
|
-
Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`:
|
|
1310
|
+
Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 53).
|
|
1207
1311
|
|
|
1208
|
-
| Category |
|
|
1312
|
+
| Category | Browser-public generic collections |
|
|
1209
1313
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1210
|
-
| Tenant | `tenants`, `tenant-metadata
|
|
1211
|
-
| Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands
|
|
1212
|
-
|
|
|
1213
|
-
| Customers | `customers`, `customer-profiles`, `customer-addresses` |
|
|
1214
|
-
| Carts | `carts`, `cart-items` |
|
|
1314
|
+
| Tenant | `tenants`, `tenant-metadata` |
|
|
1315
|
+
| Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands` |
|
|
1316
|
+
| Customers | `customer-profiles` |
|
|
1215
1317
|
| Commerce | `discounts`, `shipping-policies`, `shipping-zones` |
|
|
1216
1318
|
| Content | `documents`, `document-categories`, `document-types`, `articles`, `article-authors`, `article-categories`, `article-tags`, `links`, `link-categories`, `link-tags` |
|
|
1217
1319
|
| Playlists / Tracks | `playlists`, `playlist-categories`, `playlist-tags`, `tracks`, `track-categories`, `track-tags` |
|
|
1218
1320
|
| Galleries | `galleries`, `gallery-categories`, `gallery-tags`, `gallery-items` |
|
|
1219
1321
|
| Canvas | `canvases`, `canvas-node-types`, `canvas-edge-types`, `canvas-categories`, `canvas-tags`, `canvas-nodes`, `canvas-edges` |
|
|
1220
|
-
| Videos | `
|
|
1221
|
-
|
|
|
1222
|
-
|
|
|
1223
|
-
| Forms | `forms`, `form-submissions` |
|
|
1224
|
-
| Community | `posts`, `comments`, `reactions`, `reaction-types`, `bookmarks`, `post-categories`, `customer-profile-lists` |
|
|
1322
|
+
| Videos | `video-categories`, `video-tags` |
|
|
1323
|
+
| Forms | `forms` |
|
|
1324
|
+
| Community | `reaction-types`, `post-categories`, `post-tags`, `customer-profile-lists` |
|
|
1225
1325
|
| Events | `event-calendars`, `events`, `event-categories`, `event-occurrences`, `event-tags` |
|
|
1226
1326
|
|
|
1227
|
-
Server-only collections
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1327
|
+
Server-only collections include raw media/logo records, customer and order
|
|
1328
|
+
operational records, raw cart/cart-item records, form submissions, raw community documents
|
|
1329
|
+
(`posts`, `comments`, `reactions`, `bookmarks`), live stream provider records,
|
|
1330
|
+
segmentation records, and moderation records. They remain available from
|
|
1331
|
+
`createServerClient().collections` with secret/PAT credentials. Shaped
|
|
1332
|
+
browser/customer helpers such as `commerce.cart.*`,
|
|
1333
|
+
`commerce.orders.listMine()`, and `commerce.orders.checkout()` remain available
|
|
1334
|
+
where customer-facing DTOs are needed, but raw slugs are intentionally absent
|
|
1335
|
+
from browser collection discovery.
|
|
1231
1336
|
|
|
1232
1337
|
## Utilities
|
|
1233
1338
|
|
|
@@ -1362,18 +1467,18 @@ API keys created without explicit scopes use the default `['read', 'write']`. Co
|
|
|
1362
1467
|
`swatch` (`type`, `color`, `mediaItemId`). Legacy public fields are removed
|
|
1363
1468
|
from SDK input/output helpers and from listing/detail contracts.
|
|
1364
1469
|
|
|
1365
|
-
| Surface
|
|
1366
|
-
|
|
|
1367
|
-
| Upsert option value
|
|
1368
|
-
| Listing groups
|
|
1369
|
-
| Product detail option value | `swatchColor`, `thumbnail`, `images`
|
|
1470
|
+
| Surface | Removed | Replacement |
|
|
1471
|
+
| --------------------------- | --------------------------------------------------------------------- | --------------------------------------------------- |
|
|
1472
|
+
| Upsert option value | `swatchColor`, `thumbnail`, `images` | `swatch.type`, `swatch.color`, `swatch.mediaItemId` |
|
|
1473
|
+
| Listing groups | `optionValueSwatchColor`, `optionValueThumbnail`, `optionValueImages` | `optionValueSwatch` |
|
|
1474
|
+
| Product detail option value | `swatchColor`, `thumbnail`, `images` | `swatch` |
|
|
1370
1475
|
|
|
1371
1476
|
Migration steps:
|
|
1372
1477
|
|
|
1373
1478
|
1. Replace color-only values with `swatch: { type: 'color', color: '#111111' }`.
|
|
1374
1479
|
2. Replace thumbnail/gallery values with `swatch: { type: 'media', mediaItemId: '<product-pool-image-id>' }`.
|
|
1375
1480
|
3. Ensure media swatches reference an image already attached to the parent product (`products.images` / `products.thumbnail`).
|
|
1376
|
-
4. Remove reads of
|
|
1481
|
+
4. Remove reads of pre-ADR-0025 response fields; consume `optionValueSwatch` / `optionValue.swatch` instead.
|
|
1377
1482
|
|
|
1378
1483
|
## Migration Guide
|
|
1379
1484
|
|