@01.software/sdk 0.29.0 → 0.31.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 +331 -77
- package/dist/analytics/react.cjs +4 -1
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js +4 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs +4 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js +4 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +1541 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +28 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +1518 -0
- package/dist/client.js.map +1 -0
- package/dist/collection-client-ByzY3hWK.d.ts +218 -0
- package/dist/collection-client-DFXXz0vk.d.cts +218 -0
- package/dist/{const-DAjQYNuM.d.ts → const-AytzliEu.d.cts} +5 -7
- package/dist/{const-Dsixdi6z.d.cts → const-BGCP-OJL.d.ts} +5 -7
- package/dist/index-BGEhoDUs.d.cts +106 -0
- package/dist/index-BGEhoDUs.d.ts +106 -0
- package/dist/index.cjs +1006 -1615
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -115
- package/dist/index.d.ts +11 -115
- package/dist/index.js +932 -1559
- package/dist/index.js.map +1 -1
- package/dist/metadata.cjs +91 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +58 -0
- package/dist/metadata.d.ts +58 -0
- package/dist/metadata.js +68 -0
- package/dist/metadata.js.map +1 -0
- package/dist/{payload-types-Ci-ZA7aM.d.cts → payload-types-Wa4-eC6x.d.cts} +794 -532
- package/dist/{payload-types-Ci-ZA7aM.d.ts → payload-types-Wa4-eC6x.d.ts} +794 -532
- package/dist/query.cjs +1841 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +244 -0
- package/dist/query.d.ts +244 -0
- package/dist/query.js +1836 -0
- package/dist/query.js.map +1 -0
- package/dist/realtime.cjs +4 -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 +4 -1
- package/dist/realtime.js.map +1 -1
- package/dist/{server-BINWywT8.d.cts → server-CrsPyqEc.d.cts} +14 -31
- package/dist/{server-BINWywT8.d.ts → server-CrsPyqEc.d.ts} +14 -31
- package/dist/server.cjs +430 -846
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +137 -7
- package/dist/server.d.ts +137 -7
- package/dist/server.js +430 -864
- package/dist/server.js.map +1 -1
- package/dist/{server-Cv0Q4dPQ.d.ts → types-BX2mqDf6.d.ts} +270 -743
- package/dist/{types-BWq_WlbB.d.ts → types-CVA10VC-.d.ts} +6 -2
- package/dist/{types-zKjATmDK.d.cts → types-CmLG-7RL.d.cts} +6 -2
- package/dist/{server-C0C8dtms.d.cts → types-DChFjQGz.d.cts} +270 -743
- package/dist/ui/canvas/server.cjs +7 -6
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +1 -3
- package/dist/ui/canvas/server.d.ts +1 -3
- package/dist/ui/canvas/server.js +7 -6
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +11 -10
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +29 -6
- package/dist/ui/canvas.d.ts +29 -6
- package/dist/ui/canvas.js +11 -10
- 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 +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +84 -15
package/README.md
CHANGED
|
@@ -42,10 +42,10 @@ export function App() {
|
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
|
-
// Main entry - browser client, query builder,
|
|
45
|
+
// Main entry - browser client, query builder, commerce helpers, utilities
|
|
46
46
|
import { createClient } from '@01.software/sdk'
|
|
47
47
|
|
|
48
|
-
// Server-only entry -
|
|
48
|
+
// Server-only entry - keep Secret Key code out of browser-facing imports
|
|
49
49
|
import { createServerClient } from '@01.software/sdk/server'
|
|
50
50
|
|
|
51
51
|
// Webhook only - webhook handlers
|
|
@@ -67,6 +67,45 @@ import { CanvasRenderer } from '@01.software/sdk/ui/canvas'
|
|
|
67
67
|
import { VideoPlayer } from '@01.software/sdk/ui/video'
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
The root entry keeps `createClient`, commerce helpers, collection helpers, and
|
|
71
|
+
types lightweight. Server, React Query, and UI features live behind explicit
|
|
72
|
+
sub-paths so consumers install feature peers only when they import the matching
|
|
73
|
+
entry.
|
|
74
|
+
|
|
75
|
+
| Import | Feature(s) | Install when used |
|
|
76
|
+
| ----------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `@01.software/sdk` | browser-safe `createClient`, commerce helpers, collection helpers, types | none |
|
|
78
|
+
| `@01.software/sdk/client` | browser-safe `createClient` entry | none |
|
|
79
|
+
| `@01.software/sdk/server` | `createServerClient`, server-only collection, commerce, and preview APIs | none; keep `secretKey` code on the server |
|
|
80
|
+
| `@01.software/sdk/query` | React Query hooks, cache helpers, `getQueryClient` | `@tanstack/react-query`, `react`, `react-dom` |
|
|
81
|
+
| `@01.software/sdk/realtime` | `RealtimeConnection`, `useRealtimeQuery` | `@tanstack/react-query`, `react`, `react-dom` |
|
|
82
|
+
| `@01.software/sdk/analytics/react` | `<Analytics />` | `react`, `react-dom` |
|
|
83
|
+
| `@01.software/sdk/ui/rich-text` | `RichTextContent`, `StyledRichTextContent` | `react`, `react-dom`, `@payloadcms/richtext-lexical` |
|
|
84
|
+
| `@01.software/sdk/ui/form` | `FormRenderer` | `react`, `react-dom` |
|
|
85
|
+
| `@01.software/sdk/ui/code-block` | `CodeBlock`, `highlight` | `react`, `react-dom`, `shiki`, `hast-util-to-jsx-runtime` |
|
|
86
|
+
| `@01.software/sdk/ui/canvas` | `CanvasRenderer`, `CanvasFrame`, `useCanvas`, `prefetchCanvas` | `react`, `react-dom`, `@tanstack/react-query`, `@xyflow/react`, `quickjs-emscripten`, `postcss`, `sucrase` |
|
|
87
|
+
| `@01.software/sdk/ui/canvas/server` | canvas server helpers | none |
|
|
88
|
+
| `@01.software/sdk/ui/video` | `VideoPlayer` | `react`, `react-dom`, `@mux/mux-player-react` |
|
|
89
|
+
| `@01.software/sdk/ui/image` | `Image` | `react`, `react-dom` |
|
|
90
|
+
|
|
91
|
+
If a feature is not listed here, it does not need a separate peer install.
|
|
92
|
+
For the full component-to-peer mapping, see
|
|
93
|
+
`packages/sdk/.claude/rules/components-reference.md`.
|
|
94
|
+
|
|
95
|
+
Migration quick reference:
|
|
96
|
+
|
|
97
|
+
- `createClient` remains available from `@01.software/sdk` and
|
|
98
|
+
`@01.software/sdk/client`.
|
|
99
|
+
- `createServerClient` must be imported from `@01.software/sdk/server`.
|
|
100
|
+
- React Query hooks and cache helpers must be imported from
|
|
101
|
+
`@01.software/sdk/query`.
|
|
102
|
+
- UI components must be imported from the specific `@01.software/sdk/ui/*`
|
|
103
|
+
sub-path and require only that row's peers.
|
|
104
|
+
- Console-shared pure ecommerce helpers live in private
|
|
105
|
+
`@01.software/contracts`. The public SDK keeps customer-facing helpers
|
|
106
|
+
self-contained and must not import private contracts; Console code should
|
|
107
|
+
import shared helpers from contracts directly.
|
|
108
|
+
|
|
70
109
|
## Getting Started
|
|
71
110
|
|
|
72
111
|
### Client
|
|
@@ -89,11 +128,13 @@ const { docs } = await client.collections.from('products').find({
|
|
|
89
128
|
|
|
90
129
|
```typescript
|
|
91
130
|
import { createServerClient } from '@01.software/sdk/server'
|
|
131
|
+
import { createServerQueryHooks } from '@01.software/sdk/query'
|
|
92
132
|
|
|
93
133
|
const server = createServerClient({
|
|
94
134
|
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY,
|
|
95
135
|
secretKey: process.env.SOFTWARE_SECRET_KEY, // sk01_... opaque API key from Console
|
|
96
136
|
})
|
|
137
|
+
const serverQuery = createServerQueryHooks(server)
|
|
97
138
|
|
|
98
139
|
// Create order (server only)
|
|
99
140
|
const order = await server.commerce.orders.create({
|
|
@@ -107,12 +148,29 @@ const order = await server.commerce.orders.create({
|
|
|
107
148
|
})
|
|
108
149
|
|
|
109
150
|
// SSR prefetch (server)
|
|
110
|
-
await
|
|
151
|
+
await serverQuery.prefetchQuery({
|
|
111
152
|
collection: 'products',
|
|
112
153
|
options: { limit: 10 },
|
|
113
154
|
})
|
|
114
155
|
```
|
|
115
156
|
|
|
157
|
+
Always import `createServerClient` from `@01.software/sdk/server` so generated
|
|
158
|
+
code and bundlers do not blur the Secret Key boundary.
|
|
159
|
+
|
|
160
|
+
Server-rendered preview routes can use `server.preview.detail()` with the
|
|
161
|
+
short-lived preview token issued by Console:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const preview = await server.preview.detail(
|
|
165
|
+
{ collection: 'products', id: previewId },
|
|
166
|
+
{ previewToken },
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
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.
|
|
173
|
+
|
|
116
174
|
## Getting product detail
|
|
117
175
|
|
|
118
176
|
The recommended way to fetch a single product is the shaped helper:
|
|
@@ -124,7 +182,9 @@ const client = createClient({
|
|
|
124
182
|
publishableKey: process.env.NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY!,
|
|
125
183
|
})
|
|
126
184
|
|
|
127
|
-
const product = await client.commerce.product.detail({
|
|
185
|
+
const product = await client.commerce.product.detail({
|
|
186
|
+
slug: 'every-peach-tee',
|
|
187
|
+
})
|
|
128
188
|
if (!product) {
|
|
129
189
|
return notFound() // returned null — product missing, unpublished, or not in this tenant
|
|
130
190
|
}
|
|
@@ -133,14 +193,120 @@ if (!product) {
|
|
|
133
193
|
|
|
134
194
|
`detail()` returns `ProductDetail | null`. A `null` result covers every "no result" reason: `not_found`, `not_published`, `tenant_mismatch`, `feature_disabled`. Render the same "not available" UI for all four. To recover the exact reason for triage, `404` maps to `null` rather than a thrown error — inspect `client.lastRequestId` and match against backend logs.
|
|
135
195
|
|
|
196
|
+
### Product selection helpers
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import {
|
|
200
|
+
buildProductHref,
|
|
201
|
+
buildProductOptionMatrixFromDetail,
|
|
202
|
+
getProductSelectionImages,
|
|
203
|
+
resolveProductSelectionFromMatrix,
|
|
204
|
+
} from '@01.software/sdk'
|
|
205
|
+
|
|
206
|
+
const matrix = buildProductOptionMatrixFromDetail(product)
|
|
207
|
+
const selection = resolveProductSelectionFromMatrix(
|
|
208
|
+
matrix,
|
|
209
|
+
{ search: '?opt.color=black&opt.size=s' },
|
|
210
|
+
undefined,
|
|
211
|
+
{ detail: product },
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
const images = getProductSelectionImages(selection) // object media only, deduped
|
|
215
|
+
const href = buildProductHref(product, {
|
|
216
|
+
optionSlug: 'color',
|
|
217
|
+
optionValueSlug: 'black',
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Selection media follows the resolved selection: a complete variant uses that
|
|
222
|
+
variant's media first; a partial option selection uses selected option-value
|
|
223
|
+
media first, then matching variant media, before falling back to listing or
|
|
224
|
+
product media. This keeps listing-card selection links and detail-page images
|
|
225
|
+
aligned without rebuilding media priority in storefront code.
|
|
226
|
+
|
|
227
|
+
`availableValuesByOptionSlug` / `availableValuesByOptionId` include
|
|
228
|
+
`availableStock`, `isUnlimited`, and `availableForSale` per value so option UIs
|
|
229
|
+
can render stock state without recalculating from variants.
|
|
230
|
+
|
|
136
231
|
### With React Query
|
|
137
232
|
|
|
138
233
|
```typescript
|
|
139
|
-
|
|
234
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
235
|
+
|
|
236
|
+
const query = createQueryHooks(client)
|
|
237
|
+
const { data: product, isLoading } = query.useProductDetailBySlug(slug)
|
|
140
238
|
```
|
|
141
239
|
|
|
142
240
|
Cache key is `['products', 'detail', { slug }]`. Mutations on products, product-variants, product-options, product-option-values, brands, brand-logos, images, and related collections automatically invalidate this cache.
|
|
143
241
|
|
|
242
|
+
### Selection URL contract
|
|
243
|
+
|
|
244
|
+
Use `createProductSelectionCodec(detail)` when product pages need to keep option
|
|
245
|
+
selection in the URL. Complete selections use `variant=<variantId>`; partial
|
|
246
|
+
selections use `opt.<optionId>=<valueId>`. Older
|
|
247
|
+
`opt.<optionSlug>=<valueSlug>` URLs still parse during Stage 1, but slugs are
|
|
248
|
+
compatibility metadata rather than canonical identity.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import {
|
|
252
|
+
createProductSelectionCodec,
|
|
253
|
+
resolveProductSelection,
|
|
254
|
+
} from '@01.software/sdk'
|
|
255
|
+
|
|
256
|
+
const codec = createProductSelectionCodec(product)
|
|
257
|
+
const normalizedSelection = codec.parse('?opt.option-color=color-black')
|
|
258
|
+
const selection = resolveProductSelection(product, normalizedSelection)
|
|
259
|
+
const selectionQuery = codec.stringify(normalizedSelection)
|
|
260
|
+
// selectionQuery === 'opt.option-color=color-black' for partial selections
|
|
261
|
+
// selectionQuery === 'variant=variant-black-large' once a complete variant is selected
|
|
262
|
+
// selection.selectedVariant, selection.price, selection.stock, selection.media
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Use IDs from `detail.options[].id` and `detail.options[].values[].id` when
|
|
266
|
+
building selection state. Slugs remain useful for display and old inbound URLs,
|
|
267
|
+
but new outbound URLs should use the codec output.
|
|
268
|
+
|
|
269
|
+
For listing cards, pass the listing group returned by
|
|
270
|
+
`buildProductListingGroupsByOption()` or the listing-groups endpoint into
|
|
271
|
+
`buildProductHref(product, group, { detail })`. The detail object lets the SDK
|
|
272
|
+
emit canonical `variant=<variantId>` or `opt.<optionId>=<valueId>` params. When
|
|
273
|
+
full detail is not available on a product-list page, pass the group without
|
|
274
|
+
`detail`; `buildProductHref()` still emits the best available selection hint and
|
|
275
|
+
the detail page can resolve it through `resolveProductSelection()`.
|
|
276
|
+
|
|
277
|
+
Do not use bare option query keys such as `?size=large`. The SDK rejects them
|
|
278
|
+
as ambiguous because product pages commonly share URLs with unrelated search,
|
|
279
|
+
filter, analytics, or framework parameters. Namespacing selection keys under
|
|
280
|
+
`opt.` lets the codec distinguish product-option state from ordinary query
|
|
281
|
+
parameters while still allowing unrelated parameters such as `utm_campaign` to
|
|
282
|
+
coexist without being interpreted as selection state.
|
|
283
|
+
|
|
284
|
+
### Product listing card helper
|
|
285
|
+
|
|
286
|
+
`buildProductListingCard(item, options?)` turns a single
|
|
287
|
+
`commerce.product.listingGroups()` response item into a render-ready
|
|
288
|
+
`ProductListingCard`. The card carries product-level hero media
|
|
289
|
+
(`product.thumbnail` -> first `product.images` -> `null`), an aggregated
|
|
290
|
+
price range across all option-value groups, and a `swatches[]` array
|
|
291
|
+
derived from groups when there is more than one. Single-group products
|
|
292
|
+
emit `swatches: []`; storefronts that disagree can read `item.groups`
|
|
293
|
+
directly.
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
import {
|
|
297
|
+
buildProductListingCard,
|
|
298
|
+
type ProductListingCard,
|
|
299
|
+
} from '@01.software/sdk'
|
|
300
|
+
|
|
301
|
+
const cards: ProductListingCard[] = response.docs.map((item) =>
|
|
302
|
+
buildProductListingCard(item, { basePath: '/shop' }),
|
|
303
|
+
)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Each swatch carries a hint-only option-value href
|
|
307
|
+
(`?opt.<optionId>=<valueId>`); the detail page resolves it through
|
|
308
|
+
`resolveProductSelection(detail, { search })`.
|
|
309
|
+
|
|
144
310
|
## Advanced: direct Payload queries (escape hatch)
|
|
145
311
|
|
|
146
312
|
Most consumers should use the helper APIs above (`commerce.product.detail`, etc.). The query builder below is the escape hatch for advanced cases the helpers do not cover: bulk operations, custom filter combinations, or fields the helper response does not expose.
|
|
@@ -171,7 +337,7 @@ await client.collections.from('products').find({
|
|
|
171
337
|
|
|
172
338
|
### `joins` — Payload join-field reverse-relations
|
|
173
339
|
|
|
174
|
-
`joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's schema, `products.variants`, `products.options`, `
|
|
340
|
+
`joins` is the correct control for Payload `type: 'join'` virtual reverse-relation fields. In this platform's public SDK schema, `products.variants`, `products.options`, `customers.orders`, `customers.addresses`, `posts.comments`, `article-authors.articles`, `orders.{items,transactions,fulfillments,returns}`, and similar reverse-relations 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 public SDK collection types.
|
|
175
341
|
|
|
176
342
|
```typescript
|
|
177
343
|
// Canonical product detail query — variants/options are join fields on Products
|
|
@@ -244,29 +410,40 @@ export default async function ProductPage({ params }) {
|
|
|
244
410
|
```typescript
|
|
245
411
|
const client = createClient({
|
|
246
412
|
publishableKey: string, // Required
|
|
413
|
+
apiUrl?: string, // Optional API origin override
|
|
247
414
|
})
|
|
248
415
|
|
|
249
416
|
const server = createServerClient({
|
|
250
417
|
publishableKey: string,
|
|
251
418
|
secretKey: string, // sk01_... or pat01_...
|
|
419
|
+
apiUrl?: string, // Optional API origin override
|
|
252
420
|
})
|
|
253
421
|
```
|
|
254
422
|
|
|
255
|
-
| Option | Type | Description
|
|
256
|
-
| ---------------- | -------- |
|
|
257
|
-
| `publishableKey` | `string` | API publishable key
|
|
258
|
-
| `secretKey` | `string` | API secret key (server only)
|
|
423
|
+
| Option | Type | Description |
|
|
424
|
+
| ---------------- | -------- | ------------------------------------------------------------- |
|
|
425
|
+
| `publishableKey` | `string` | API publishable key |
|
|
426
|
+
| `secretKey` | `string` | API secret key or PAT (server only) |
|
|
427
|
+
| `apiUrl` | `string` | Optional API origin override for staging, preview, or proxies |
|
|
428
|
+
|
|
429
|
+
Use `apiUrl: string` when an SDK instance should target a non-default API
|
|
430
|
+
origin.
|
|
259
431
|
|
|
260
|
-
API URL
|
|
432
|
+
API URL resolution order:
|
|
261
433
|
|
|
262
|
-
|
|
263
|
-
|
|
434
|
+
1. Explicit `apiUrl` passed to `createClient()` or `createServerClient()`
|
|
435
|
+
2. `SOFTWARE_API_URL` (server) or `NEXT_PUBLIC_SOFTWARE_API_URL` (browser)
|
|
436
|
+
3. Build-time default: `DEFAULT_API_URL` when injected at build time; otherwise dev-tagged SDK builds (`-dev.` versions) use `https://api.stg.01.software`, and regular releases use `https://api.01.software`
|
|
264
437
|
|
|
265
438
|
### Query Builder
|
|
266
439
|
|
|
267
440
|
Access collections via `client.collections.from(slug)`.
|
|
268
441
|
|
|
269
|
-
> **Note:** `client.collections.from()`
|
|
442
|
+
> **Note:** the root `client.collections.from()` type exposes the lightweight
|
|
443
|
+
> read surface (`find`, `findById`, `count`). Metadata helpers live behind the
|
|
444
|
+
> optional `@01.software/sdk/metadata` entry, and write operations (`create`,
|
|
445
|
+
> `update`, `remove`, `updateMany`, `removeMany`) are only available on
|
|
446
|
+
> `server.collections.from()`.
|
|
270
447
|
|
|
271
448
|
```typescript
|
|
272
449
|
// List query - returns PayloadFindResponse
|
|
@@ -295,11 +472,17 @@ const product = await client.collections.from('products').findById(id, {
|
|
|
295
472
|
|
|
296
473
|
// Single item query - returns document directly
|
|
297
474
|
const product = await client.collections.from('products').findById('id')
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Raw collection mutations are an escape hatch. For ecommerce product catalog
|
|
478
|
+
writes, prefer `server.commerce.product.upsert()` so options, option-values,
|
|
479
|
+
and variants are written through the domain transaction.
|
|
298
480
|
|
|
481
|
+
```typescript
|
|
299
482
|
// Create (server only) - returns PayloadMutationResponse
|
|
300
483
|
const { doc, message } = await server.collections
|
|
301
|
-
.from('
|
|
302
|
-
.create({
|
|
484
|
+
.from('articles')
|
|
485
|
+
.create({ title: 'Article' })
|
|
303
486
|
|
|
304
487
|
// Create with file upload (server only) - uses multipart/form-data
|
|
305
488
|
const { doc } = await server.collections
|
|
@@ -308,8 +491,8 @@ const { doc } = await server.collections
|
|
|
308
491
|
|
|
309
492
|
// Update (server only) - returns PayloadMutationResponse
|
|
310
493
|
const { doc } = await server.collections
|
|
311
|
-
.from('
|
|
312
|
-
.update('id', {
|
|
494
|
+
.from('articles')
|
|
495
|
+
.update('id', { title: 'Updated article' })
|
|
313
496
|
|
|
314
497
|
// Update with file replacement (server only)
|
|
315
498
|
await server.collections
|
|
@@ -317,28 +500,26 @@ await server.collections
|
|
|
317
500
|
.update('id', { alt: 'New alt' }, { file: newFile })
|
|
318
501
|
|
|
319
502
|
// Delete (server only) - returns document directly
|
|
320
|
-
const deletedDoc = await server.collections.from('
|
|
503
|
+
const deletedDoc = await server.collections.from('articles').remove('id')
|
|
321
504
|
|
|
322
505
|
// Count
|
|
323
506
|
const { totalDocs } = await client.collections.from('products').count()
|
|
324
507
|
|
|
325
|
-
// SEO Metadata (
|
|
326
|
-
|
|
327
|
-
.from('products')
|
|
328
|
-
.findMetadata(
|
|
329
|
-
{ where: { slug: { equals: 'my-product' } } },
|
|
330
|
-
{ siteName: 'My Store' },
|
|
331
|
-
)
|
|
508
|
+
// SEO Metadata (generate from a fetched document)
|
|
509
|
+
import { extractSeo, generateMetadata } from '@01.software/sdk/metadata'
|
|
332
510
|
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
511
|
+
const { docs } = await client.collections.from('products').find({
|
|
512
|
+
where: { slug: { equals: 'my-product' } },
|
|
513
|
+
limit: 1,
|
|
514
|
+
depth: 1,
|
|
515
|
+
})
|
|
516
|
+
const metadata = docs[0]
|
|
517
|
+
? generateMetadata(extractSeo(docs[0]), { siteName: 'My Store' })
|
|
518
|
+
: null
|
|
338
519
|
|
|
339
520
|
// Bulk operations (server only)
|
|
340
|
-
await server.collections.from('
|
|
341
|
-
await server.collections.from('
|
|
521
|
+
await server.collections.from('articles').updateMany(where, data)
|
|
522
|
+
await server.collections.from('articles').removeMany(where)
|
|
342
523
|
```
|
|
343
524
|
|
|
344
525
|
### API Response Types (Payload Native)
|
|
@@ -370,95 +551,112 @@ interface PayloadMutationResponse<T> {
|
|
|
370
551
|
// findById() / remove() returns T (document directly)
|
|
371
552
|
```
|
|
372
553
|
|
|
373
|
-
| Operation
|
|
374
|
-
|
|
|
375
|
-
| `find()`
|
|
376
|
-
| `findById()`
|
|
377
|
-
| `create()`
|
|
378
|
-
| `update()`
|
|
379
|
-
| `remove()`
|
|
380
|
-
| `count()`
|
|
381
|
-
| `findMetadata()` | `Metadata \| null` - Next.js Metadata (null if no match) |
|
|
382
|
-
| `findMetadataById()` | `Metadata` - Next.js Metadata (throws on 404) |
|
|
554
|
+
| Operation | Response Type |
|
|
555
|
+
| ------------ | ------------------------------------------------------------------ |
|
|
556
|
+
| `find()` | `PayloadFindResponse<T>` - `{ docs, totalDocs, hasNextPage, ... }` |
|
|
557
|
+
| `findById()` | `T` - document object directly |
|
|
558
|
+
| `create()` | `PayloadMutationResponse<T>` - `{ doc, message }` |
|
|
559
|
+
| `update()` | `PayloadMutationResponse<T>` - `{ doc, message }` |
|
|
560
|
+
| `remove()` | `T` - deleted document object directly |
|
|
561
|
+
| `count()` | `{ totalDocs: number }` |
|
|
383
562
|
|
|
384
563
|
### React Query Hooks
|
|
385
564
|
|
|
386
|
-
|
|
565
|
+
React Query helpers are opt-in through `@01.software/sdk/query`. Install
|
|
566
|
+
`@tanstack/react-query` (and React peers) only when your app imports this
|
|
567
|
+
sub-path. Browser components should use `createQueryHooks(client)` for
|
|
568
|
+
browser-safe reads and customer auth hooks. Collection writes belong in trusted
|
|
569
|
+
server code via `createServerClient`.
|
|
387
570
|
|
|
388
571
|
```typescript
|
|
572
|
+
import { createQueryHooks } from '@01.software/sdk/query'
|
|
573
|
+
|
|
574
|
+
const query = createQueryHooks(client)
|
|
575
|
+
|
|
389
576
|
// List query
|
|
390
|
-
const { data, isLoading } =
|
|
577
|
+
const { data, isLoading } = query.useQuery({
|
|
391
578
|
collection: 'products',
|
|
392
579
|
options: { limit: 10 },
|
|
393
580
|
})
|
|
394
581
|
|
|
395
582
|
// Suspense mode
|
|
396
|
-
const { data } =
|
|
583
|
+
const { data } = query.useSuspenseQuery({
|
|
397
584
|
collection: 'products',
|
|
398
585
|
options: { limit: 10 },
|
|
399
586
|
})
|
|
400
587
|
|
|
401
588
|
// Query by ID
|
|
402
|
-
const { data } =
|
|
589
|
+
const { data } = query.useQueryById({
|
|
403
590
|
collection: 'products',
|
|
404
591
|
id: 'product_id',
|
|
405
592
|
})
|
|
406
593
|
|
|
407
594
|
// Infinite scroll
|
|
408
|
-
const { data, fetchNextPage, hasNextPage } =
|
|
595
|
+
const { data, fetchNextPage, hasNextPage } = query.useInfiniteQuery({
|
|
409
596
|
collection: 'products',
|
|
410
597
|
options: { limit: 20 },
|
|
411
598
|
})
|
|
412
599
|
|
|
413
|
-
// Mutation hooks — ServerClient only (auto-invalidate cache on success)
|
|
414
|
-
const { mutate: create } = client.query.useCreate({ collection: 'images' })
|
|
415
|
-
const { mutate: update } = client.query.useUpdate({ collection: 'products' })
|
|
416
|
-
const { mutate: remove } = client.query.useRemove({ collection: 'products' })
|
|
417
|
-
|
|
418
|
-
create({ data: { alt: 'Hero' }, file: imageFile, filename: 'hero.jpg' })
|
|
419
|
-
update({ id: 'product_id', data: { title: 'Updated' } })
|
|
420
|
-
remove('product_id')
|
|
421
|
-
|
|
422
600
|
// SSR Prefetch
|
|
423
|
-
await
|
|
601
|
+
await query.prefetchQuery({
|
|
424
602
|
collection: 'products',
|
|
425
603
|
options: { limit: 10 },
|
|
426
604
|
})
|
|
427
|
-
await
|
|
605
|
+
await query.prefetchQueryById({
|
|
428
606
|
collection: 'products',
|
|
429
607
|
id: 'product_id',
|
|
430
608
|
})
|
|
431
|
-
await
|
|
609
|
+
await query.prefetchInfiniteQuery({
|
|
432
610
|
collection: 'products',
|
|
433
611
|
pageSize: 20,
|
|
434
612
|
})
|
|
435
613
|
|
|
436
614
|
// Cache utilities
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
615
|
+
query.invalidateQueries('products')
|
|
616
|
+
query.getQueryData('products', 'list', options)
|
|
617
|
+
query.setQueryData('products', 'detail', id, data)
|
|
440
618
|
|
|
441
619
|
// Customer auth hooks (Client only)
|
|
442
|
-
const { data: profile } =
|
|
443
|
-
const { mutate: login } =
|
|
444
|
-
const { mutate: register } =
|
|
445
|
-
const { mutate: logout } =
|
|
620
|
+
const { data: profile } = query.useCustomerMe()
|
|
621
|
+
const { mutate: login } = query.useCustomerLogin()
|
|
622
|
+
const { mutate: register } = query.useCustomerRegister()
|
|
623
|
+
const { mutate: logout } = query.useCustomerLogout()
|
|
446
624
|
|
|
447
625
|
login({ email: 'user@example.com', password: 'password' })
|
|
448
626
|
|
|
449
627
|
// Other customer mutations
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
628
|
+
query.useCustomerForgotPassword()
|
|
629
|
+
query.useCustomerResetPassword()
|
|
630
|
+
query.useCustomerChangePassword()
|
|
453
631
|
|
|
454
632
|
// Customer cache utilities
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
633
|
+
query.invalidateCustomerQueries()
|
|
634
|
+
query.getCustomerData()
|
|
635
|
+
query.setCustomerData(profile)
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
// Server action / API route for collection writes
|
|
640
|
+
import { createServerClient } from '@01.software/sdk/server'
|
|
641
|
+
|
|
642
|
+
const server = createServerClient({
|
|
643
|
+
publishableKey: process.env.SOFTWARE_PUBLISHABLE_KEY!,
|
|
644
|
+
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
await server.collections.from('articles').update('article_id', {
|
|
648
|
+
title: 'Updated article',
|
|
649
|
+
})
|
|
458
650
|
```
|
|
459
651
|
|
|
460
652
|
### Customer Auth
|
|
461
653
|
|
|
654
|
+
Customer auth methods currently cover local email/password flows: register,
|
|
655
|
+
login, refresh, password reset, profile read/update, and password change.
|
|
656
|
+
`CustomerProfile.authProvider` may contain `google`, `apple`, `kakao`, or
|
|
657
|
+
`naver` for accounts created through platform/provider integrations, but the
|
|
658
|
+
SDK does not expose social-login initiation or callback helpers yet.
|
|
659
|
+
|
|
462
660
|
Available on Client via `client.customer.auth.*`.
|
|
463
661
|
|
|
464
662
|
```typescript
|
|
@@ -548,7 +746,63 @@ await client.commerce.shipping.calculate({ shippingPolicyId?, orderAmount, posta
|
|
|
548
746
|
|
|
549
747
|
### Commerce Product
|
|
550
748
|
|
|
551
|
-
|
|
749
|
+
Product reads are available on both Client and ServerClient via `commerce.product.*`.
|
|
750
|
+
Product catalog writes are ServerClient-only.
|
|
751
|
+
Use `server.commerce.product.upsert()` for product catalog writes that include
|
|
752
|
+
options, option values, and variants. It is the tenant-admin safe path because
|
|
753
|
+
it applies the product/option/variant transaction that raw collection writes do
|
|
754
|
+
not provide.
|
|
755
|
+
|
|
756
|
+
```typescript
|
|
757
|
+
const result = await server.commerce.product.upsert({
|
|
758
|
+
product: {
|
|
759
|
+
title: 'Every Peach Tee',
|
|
760
|
+
slug: 'every-peach-tee',
|
|
761
|
+
status: 'published',
|
|
762
|
+
},
|
|
763
|
+
options: [
|
|
764
|
+
{
|
|
765
|
+
title: 'Color',
|
|
766
|
+
slug: 'color',
|
|
767
|
+
values: [
|
|
768
|
+
{ value: 'Black', slug: 'black', swatchColor: '#111111' },
|
|
769
|
+
{ value: 'White', slug: 'white', swatchColor: '#ffffff' },
|
|
770
|
+
],
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
title: 'Size',
|
|
774
|
+
slug: 'size',
|
|
775
|
+
values: [
|
|
776
|
+
{ value: 'Small', slug: 's' },
|
|
777
|
+
{ value: 'Medium', slug: 'm' },
|
|
778
|
+
],
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
variants: [
|
|
782
|
+
{
|
|
783
|
+
optionValues: { color: { valueSlug: 'black' }, size: { valueSlug: 's' } },
|
|
784
|
+
sku: 'TEE-BLK-S',
|
|
785
|
+
price: 29000,
|
|
786
|
+
stock: 10,
|
|
787
|
+
isActive: true,
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
optionValues: { color: { valueSlug: 'white' }, size: { valueSlug: 'm' } },
|
|
791
|
+
sku: 'TEE-WHT-M',
|
|
792
|
+
price: 29000,
|
|
793
|
+
stock: 8,
|
|
794
|
+
isActive: true,
|
|
795
|
+
},
|
|
796
|
+
],
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
if (!result.ok) {
|
|
800
|
+
throw new Error(result.message)
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
For updates to existing options or option-values, prefer `id` / `valueId` when
|
|
805
|
+
available so rename-safe updates do not depend on slugs.
|
|
552
806
|
|
|
553
807
|
```typescript
|
|
554
808
|
// Batch stock check (point-in-time read, NOT a reservation)
|
|
@@ -643,9 +897,9 @@ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 73)
|
|
|
643
897
|
| Tenant | `tenants`, `tenant-metadata`, `tenant-logos` |
|
|
644
898
|
| Products | `products`, `product-variants`, `product-options`, `product-option-values`, `product-categories`, `product-tags`, `product-collections`, `brands`, `brand-logos` |
|
|
645
899
|
| Orders | `orders`, `order-items`, `returns`, `return-items`, `fulfillments`, `fulfillment-items`, `transactions` |
|
|
646
|
-
| Customers | `customers`, `customer-profiles`, `customer-
|
|
900
|
+
| Customers | `customers`, `customer-profiles`, `customer-addresses` |
|
|
647
901
|
| Carts | `carts`, `cart-items` |
|
|
648
|
-
| Commerce | `discounts`, `shipping-policies`
|
|
902
|
+
| Commerce | `discounts`, `shipping-policies`, `shipping-zones` |
|
|
649
903
|
| Content | `documents`, `document-categories`, `document-types`, `articles`, `article-authors`, `article-categories`, `article-tags`, `links`, `link-categories`, `link-tags` |
|
|
650
904
|
| Playlists / Tracks | `playlists`, `playlist-categories`, `playlist-tags`, `tracks`, `track-categories`, `track-tags` |
|
|
651
905
|
| Galleries | `galleries`, `gallery-categories`, `gallery-tags`, `gallery-items` |
|
|
@@ -654,7 +908,7 @@ Source of truth: `packages/sdk/src/core/collection/const.ts` (`COLLECTIONS`: 73)
|
|
|
654
908
|
| Live Streams | `live-streams` |
|
|
655
909
|
| Media | `images` |
|
|
656
910
|
| Forms | `forms`, `form-submissions` |
|
|
657
|
-
| Community | `posts`, `comments`, `reactions`, `reaction-types`, `bookmarks`, `post-categories`
|
|
911
|
+
| Community | `posts`, `comments`, `reactions`, `reaction-types`, `bookmarks`, `post-categories`, `customer-profile-lists` |
|
|
658
912
|
| Events | `event-calendars`, `events`, `event-categories`, `event-occurrences`, `event-tags` |
|
|
659
913
|
|
|
660
914
|
Server-only collections: `customer-groups`, `reports`, and `community-bans`
|
package/dist/analytics/react.cjs
CHANGED
|
@@ -28,7 +28,10 @@ module.exports = __toCommonJS(react_exports);
|
|
|
28
28
|
var import_react = require("react");
|
|
29
29
|
|
|
30
30
|
// src/core/client/types.ts
|
|
31
|
-
function resolveApiUrl() {
|
|
31
|
+
function resolveApiUrl(apiUrl) {
|
|
32
|
+
if (apiUrl) {
|
|
33
|
+
return apiUrl.replace(/\/$/, "");
|
|
34
|
+
}
|
|
32
35
|
if (typeof process !== "undefined" && process.env) {
|
|
33
36
|
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
34
37
|
if (envUrl) {
|