@01.software/sdk 0.51.0 → 0.65.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 +313 -0
- package/README.md +228 -102
- package/dist/client.cjs +556 -60
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -6
- package/dist/client.d.ts +6 -6
- package/dist/client.js +556 -60
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-CWAeuzV4.d.cts → collection-client-B7rsTtVj.d.cts} +6 -6
- package/dist/{collection-client-hYvVfHIm.d.ts → collection-client-C2sMvfme.d.ts} +6 -6
- package/dist/{const-JD8obaKg.d.ts → const-CRxPTDGt.d.ts} +2 -2
- package/dist/{const-D8LixhAJ.d.cts → const-D6ZRQLxI.d.cts} +2 -2
- package/dist/{index-B0Hjh3Ey.d.ts → index-1auUEYrl.d.ts} +24 -7
- package/dist/{index-Bu0kCc6o.d.cts → index-Bbpf5t-l.d.cts} +24 -7
- package/dist/index.cjs +573 -260
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -12
- package/dist/index.d.ts +19 -12
- package/dist/index.js +573 -260
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-BOsckg8B.d.cts → payload-types-BnIET0xU.d.cts} +567 -438
- package/dist/{payload-types-BOsckg8B.d.ts → payload-types-BnIET0xU.d.ts} +567 -438
- package/dist/query.cjs +62 -3
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +5 -5
- package/dist/query.d.ts +5 -5
- package/dist/query.js +62 -3
- package/dist/query.js.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/server.cjs +557 -20
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +14 -8
- package/dist/server.d.ts +14 -8
- package/dist/server.js +557 -20
- package/dist/server.js.map +1 -1
- package/dist/{types-5ZlcAFuU.d.ts → types-D1y4TgQm.d.ts} +2 -2
- package/dist/{types-Bj8j7VHU.d.ts → types-DnRC3nLG.d.ts} +464 -16
- package/dist/{types-B14sEt1z.d.cts → types-Dp1oYpD3.d.cts} +2 -2
- package/dist/{types-CM5qiR2k.d.cts → types-Dzn0LD2N.d.cts} +464 -16
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.cjs +4 -2
- package/dist/ui/video.cjs.map +1 -1
- package/dist/ui/video.d.cts +4 -2
- package/dist/ui/video.d.ts +4 -2
- package/dist/ui/video.js +4 -2
- package/dist/ui/video.js.map +1 -1
- package/dist/webhook.cjs +13 -2
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +5 -5
- package/dist/webhook.d.ts +5 -5
- package/dist/webhook.js +13 -2
- package/dist/webhook.js.map +1 -1
- package/package.json +9 -28
- package/dist/analytics/react.cjs +0 -344
- package/dist/analytics/react.cjs.map +0 -1
- package/dist/analytics/react.d.cts +0 -24
- package/dist/analytics/react.d.ts +0 -24
- package/dist/analytics/react.js +0 -319
- package/dist/analytics/react.js.map +0 -1
- package/dist/analytics.cjs +0 -238
- package/dist/analytics.cjs.map +0 -1
- package/dist/analytics.d.cts +0 -39
- package/dist/analytics.d.ts +0 -39
- package/dist/analytics.js +0 -215
- package/dist/analytics.js.map +0 -1
package/README.md
CHANGED
|
@@ -50,7 +50,6 @@ import { receiveEmbeddedAdminSession } from '@01.software/sdk/embedded-admin'
|
|
|
50
50
|
import { verifyEmbeddedAdminSession } from '@01.software/sdk/embedded-admin/server'
|
|
51
51
|
|
|
52
52
|
// Components - sub-path imports per domain
|
|
53
|
-
import { Analytics } from '@01.software/sdk/analytics/react'
|
|
54
53
|
import { RichTextContent } from '@01.software/sdk/ui/rich-text'
|
|
55
54
|
import { Image } from '@01.software/sdk/ui/image'
|
|
56
55
|
import { FormRenderer } from '@01.software/sdk/ui/form'
|
|
@@ -70,7 +69,6 @@ entry.
|
|
|
70
69
|
| `@01.software/sdk/client` | browser-safe `createClient` entry and SDK error guards | none |
|
|
71
70
|
| `@01.software/sdk/server` | `createServerClient`, server-only APIs, and SDK error guards | none; keep `secretKey` code on the server |
|
|
72
71
|
| `@01.software/sdk/errors` | SDK error classes and guards | none |
|
|
73
|
-
| `@01.software/sdk/analytics` | browser analytics client and typed event helpers | none |
|
|
74
72
|
| `@01.software/sdk/metadata` | SEO metadata extraction and generation helpers | none |
|
|
75
73
|
| `@01.software/sdk/webhook` | webhook handlers, event guards, and webhook types | none |
|
|
76
74
|
| `@01.software/sdk/query` | React Query hooks, cache helpers, `getQueryClient` | `@tanstack/react-query`, `react`, `react-dom` |
|
|
@@ -78,7 +76,6 @@ entry.
|
|
|
78
76
|
| `@01.software/sdk/storefront-cache` | product storefront cache resource name helpers | none |
|
|
79
77
|
| `@01.software/sdk/embedded-admin` | embedded app browser handshake and shared protocol types | none |
|
|
80
78
|
| `@01.software/sdk/embedded-admin/server` | assertion verification and JWKS loading | none; keep receiver session creation on the server |
|
|
81
|
-
| `@01.software/sdk/analytics/react` | `<Analytics />` | `react`, `react-dom` |
|
|
82
79
|
| `@01.software/sdk/ui/rich-text` | `RichTextContent`, `StyledRichTextContent` | `react`, `react-dom`, `@payloadcms/richtext-lexical` |
|
|
83
80
|
| `@01.software/sdk/ui/form` | `FormRenderer` | `react`, `react-dom` |
|
|
84
81
|
| `@01.software/sdk/ui/code-block` | `CodeBlock`, `highlight` | `react`, `react-dom`, `shiki`, `hast-util-to-jsx-runtime` |
|
|
@@ -87,6 +84,18 @@ entry.
|
|
|
87
84
|
| `@01.software/sdk/ui/video` | `VideoPlayer` | `react`, `react-dom`, `@mux/mux-player-react` |
|
|
88
85
|
| `@01.software/sdk/ui/image` | `Image` | `react`, `react-dom` |
|
|
89
86
|
|
|
87
|
+
### Video playback admission
|
|
88
|
+
|
|
89
|
+
On your server, call `createServerClient(...).video.playback(video.id)` after
|
|
90
|
+
applying your viewer's content-access policy. Pass its `streamUrl` to
|
|
91
|
+
`<VideoPlayer video={video} streamUrl={streamUrl} />`.
|
|
92
|
+
The bearer URL expires after two hours; refresh it before the deadline during
|
|
93
|
+
long playback or after a long pause. Free playback uses a quota-metered gateway and can return 403 when the
|
|
94
|
+
monthly allowance is exhausted or 503 when admission cannot be verified.
|
|
95
|
+
`muxPlaybackId`, `getVideoStreamUrl`, and raw Mux thumbnail/MP4 helpers are
|
|
96
|
+
legacy public-ID paths for unmigrated paid assets. A signed asset's retained
|
|
97
|
+
`muxPlaybackId` is historical usage evidence and is not a playable URL.
|
|
98
|
+
|
|
90
99
|
`CanvasRenderer` renders reserved `shape` nodes (`terminator`, `process`,
|
|
91
100
|
`decision`, `io`, `subprocess`) with first-party SVG geometry, without a tenant
|
|
92
101
|
catalog row or QuickJS template.
|
|
@@ -162,91 +171,7 @@ Migration quick reference:
|
|
|
162
171
|
self-contained and must not import private contracts; Console code should
|
|
163
172
|
import shared helpers from contracts directly.
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
// Pageviews + custom events (browser)
|
|
169
|
-
import { createAnalytics } from '@01.software/sdk/analytics'
|
|
170
|
-
|
|
171
|
-
const analytics = createAnalytics({ publishableKey: 'pk01_xxx' })
|
|
172
|
-
// auto-tracks pageviews; call analytics.pageview('/custom-path') manually if needed
|
|
173
|
-
analytics.track('signup', { plan: 'pro', trial: false })
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
**Custom events must be registered first.** Each custom event name, its
|
|
177
|
-
dimensions, and any enum/boolean value sets are defined per workspace in
|
|
178
|
-
Console → Analytics. An unregistered or mistyped event is accepted by the
|
|
179
|
-
browser (200/204) and then **silently dropped server-side** — no client error.
|
|
180
|
-
Register the event before firing it.
|
|
181
|
-
|
|
182
|
-
**Typed events (compile-time safety).** Declare an event map as a plain `type`
|
|
183
|
-
(do **not** `extends AnalyticsEventMap` — an index signature would defeat
|
|
184
|
-
typo detection) and pass it as the generic. Mistyped names and out-of-enum
|
|
185
|
-
values then fail to compile:
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
import {
|
|
189
|
-
createAnalytics,
|
|
190
|
-
defineAnalyticsEvents,
|
|
191
|
-
} from '@01.software/sdk/analytics'
|
|
192
|
-
|
|
193
|
-
type ShopEvents = {
|
|
194
|
-
signup: { plan: 'free' | 'pro'; trial: boolean }
|
|
195
|
-
add_to_cart: { productId: string; price: number }
|
|
196
|
-
checkout_start: undefined // no props
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const analytics = createAnalytics<ShopEvents>({ publishableKey: 'pk01_xxx' })
|
|
200
|
-
analytics.track('signup', { plan: 'pro', trial: false }) // ✅
|
|
201
|
-
analytics.track('checkout_start') // ✅ no props
|
|
202
|
-
// analytics.track('signpu', { plan: 'pro', trial: false }) // ❌ unknown event
|
|
203
|
-
// analytics.track('signup', { plan: 'team', trial: false }) // ❌ enum violation
|
|
204
|
-
|
|
205
|
-
// Bind the map once to avoid repeating the generic:
|
|
206
|
-
const create = defineAnalyticsEvents<ShopEvents>()
|
|
207
|
-
const a2 = create({ publishableKey: 'pk01_xxx' })
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
**React.** Use `AnalyticsProvider` + `useAnalytics()` to fire events from
|
|
211
|
-
components. The provider auto-tracks pageviews like `<Analytics/>` and owns one
|
|
212
|
-
instance for its subtree:
|
|
213
|
-
|
|
214
|
-
Reusing the `ShopEvents` type from above:
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
import {
|
|
218
|
-
AnalyticsProvider,
|
|
219
|
-
useAnalytics,
|
|
220
|
-
} from '@01.software/sdk/analytics/react'
|
|
221
|
-
|
|
222
|
-
function Root() {
|
|
223
|
-
return (
|
|
224
|
-
<AnalyticsProvider>
|
|
225
|
-
<App />
|
|
226
|
-
</AnalyticsProvider>
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function SignupButton() {
|
|
231
|
-
const { track, pageview } = useAnalytics<ShopEvents>()
|
|
232
|
-
// pageview(path?) is available for manual SPA pageviews; the provider already auto-tracks pageviews
|
|
233
|
-
return (
|
|
234
|
-
<button onClick={() => track('signup', { plan: 'pro', trial: false })}>
|
|
235
|
-
Sign up
|
|
236
|
-
</button>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
`<Analytics />` remains available as a pageview-only mount helper for apps that
|
|
242
|
-
do not fire custom events from components.
|
|
243
|
-
|
|
244
|
-
**Send mode.** `mode: 'auto'` (default) suppresses sends on local hosts
|
|
245
|
-
(`localhost` / `127.0.0.1` / `*.local`); `'production'` always sends, even
|
|
246
|
-
locally; `'development'` never sends. The hosted `<script>` snippet uses the
|
|
247
|
-
same vocabulary via `window.__01_analytics__.mode` or `data-mode`; its legacy
|
|
248
|
-
`captureOnLocalhost: true` flag is equivalent to `mode: 'production'` and is
|
|
249
|
-
honored only when `mode` is unset.
|
|
174
|
+
For website analytics, integrate an external tool such as GA4, Plausible, or Vercel Analytics in your own frontend. The SDK no longer provides analytics collection helpers.
|
|
250
175
|
|
|
251
176
|
## Getting Started
|
|
252
177
|
|
|
@@ -390,11 +315,26 @@ storefronts can render time-sensitive copy.
|
|
|
390
315
|
|
|
391
316
|
`content.galleryItems.list()` calls `GET /api/gallery-items/storefront`;
|
|
392
317
|
gallery item DTOs include title, description, content, gallery reference, and
|
|
393
|
-
image
|
|
318
|
+
optional `mediaType` (`image` or `video`). Treat an omitted `mediaType` as `image`
|
|
319
|
+
for older responses. Image items retain `image` media. Video items include a
|
|
320
|
+
safe `video` descriptor with `availability: 'ready' | 'processing' | 'unavailable'`,
|
|
321
|
+
and may include a video document `id` and uploaded `thumbnail`. Disabled or
|
|
322
|
+
plan-blocked video features, missing/deleted targets, and failed videos return
|
|
323
|
+
only `{ availability: 'unavailable' }`, preserving each item's position and
|
|
324
|
+
pagination. The image relationship is not used as a video poster.
|
|
325
|
+
Tenant, metadata, draft status, and storage/provider internals are
|
|
394
326
|
omitted. Gallery item reads require either `gallerySlug` or `galleryId`; prefer
|
|
395
327
|
`gallerySlug` for storefront routes. The default gallery item sort is `manual`,
|
|
396
328
|
matching curator order from the Console.
|
|
397
329
|
|
|
330
|
+
The descriptor's `ready` state is preparation metadata, not playback authority.
|
|
331
|
+
Your application server must verify that the requested video belongs to the
|
|
332
|
+
published gallery/item and apply its viewer policy before calling
|
|
333
|
+
`server.video.playback(video.id)`. Deliver that response's `streamUrl` to the
|
|
334
|
+
player; playback rechecks the current tenant, video state, and feature/plan.
|
|
335
|
+
Keep server credentials on the application server. Gallery responses do not
|
|
336
|
+
include playback URLs, provider IDs, or tokens.
|
|
337
|
+
|
|
398
338
|
Both helpers return Payload-style pagination (`docs`, `totalDocs`, `page`,
|
|
399
339
|
`limit`, etc.). `limit` is bounded server-side to `1..100`; invalid query,
|
|
400
340
|
publishable-key, feature, rate-limit, and server errors surface through the
|
|
@@ -779,8 +719,8 @@ Membership self-service is split by credential boundary:
|
|
|
779
719
|
- Browser/customer JWT clients can read the signed-in customer's current or
|
|
780
720
|
latest membership and request cancellation for active, trialing, or past-due
|
|
781
721
|
memberships.
|
|
782
|
-
- Server clients with a Secret Key
|
|
783
|
-
|
|
722
|
+
- Server clients with a Secret Key prepare a paid customer subscription before
|
|
723
|
+
the tenant's PSP executes the first charge.
|
|
784
724
|
|
|
785
725
|
```typescript
|
|
786
726
|
import { createClient } from '@01.software/sdk'
|
|
@@ -801,28 +741,115 @@ const server = createServerClient({
|
|
|
801
741
|
secretKey: process.env.SOFTWARE_SECRET_KEY!,
|
|
802
742
|
})
|
|
803
743
|
|
|
804
|
-
await server.memberships.subscribe({
|
|
744
|
+
const prepared = await server.memberships.subscribe({
|
|
805
745
|
planId: 'plan_123',
|
|
746
|
+
priceId: 'price_monthly_123',
|
|
806
747
|
customerId: 'customer_123',
|
|
807
748
|
customerRef: 'cus_provider_123',
|
|
808
|
-
pgPaymentId: 'pay_123',
|
|
809
|
-
amount: 9900,
|
|
810
|
-
currency: 'KRW',
|
|
811
749
|
subscribeIntentKey: 'checkout-session-123',
|
|
812
750
|
card: { last4: '4242', brand: 'visa' },
|
|
813
751
|
})
|
|
752
|
+
|
|
753
|
+
if (prepared.status === 'incomplete') {
|
|
754
|
+
await tenantPsp.charge({
|
|
755
|
+
idempotencyKey: prepared.firstCharge.idempotencyKey,
|
|
756
|
+
amount: prepared.firstCharge.amount,
|
|
757
|
+
currency: prepared.firstCharge.currency,
|
|
758
|
+
})
|
|
759
|
+
// Post the PSP outcome to Console's existing signed charge-result webhook.
|
|
760
|
+
}
|
|
814
761
|
```
|
|
815
762
|
|
|
816
763
|
`subscribeIntentKey` is sent as `X-Idempotency-Key`; it is not part of the JSON
|
|
817
|
-
body.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
764
|
+
body. A paid response is `incomplete` and contains the immutable first-charge
|
|
765
|
+
instruction. The tenant charges its PSP with `firstCharge.idempotencyKey`, then
|
|
766
|
+
posts the signed outcome through the configured charge-result webhook. A trial
|
|
767
|
+
response is immediately `trialing` and has no `firstCharge` field. Before a paid
|
|
768
|
+
subscribe, configure an active delegated billing connection for
|
|
769
|
+
the tenant; otherwise Console returns `409 conflict` without lifecycle writes.
|
|
770
|
+
Do not start
|
|
771
|
+
the PSP payment at or after `expiresAt`; deduplicate PSP work with
|
|
772
|
+
`firstCharge.idempotencyKey`. A late paid result remains evidence but cannot
|
|
773
|
+
activate the Membership, so the tenant handles any refund outside this API. Replays use
|
|
774
|
+
the same plan, price, customer reference, and idempotency key while the first
|
|
775
|
+
charge is pending. A replay after settlement or `expiresAt` returns `409`
|
|
776
|
+
instead of a payment instruction.
|
|
822
777
|
`memberships.me()` and browser `memberships.cancel()` require the customer JWT
|
|
823
778
|
configured on the client. Refund webhook `refundedAmount` values are cumulative
|
|
824
779
|
charge totals, not per-event deltas.
|
|
825
780
|
|
|
781
|
+
### Source-aware customer groups (server only)
|
|
782
|
+
|
|
783
|
+
Manage manual Customer Group membership through the shaped server API. The
|
|
784
|
+
read result includes every current reason a customer qualifies, so revoking a
|
|
785
|
+
manual source does not hide a still-active Membership source.
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
const page = await server.customerGroups.listMembers({
|
|
789
|
+
groupId: '00000000-0000-4000-8000-000000000002',
|
|
790
|
+
limit: 100,
|
|
791
|
+
})
|
|
792
|
+
|
|
793
|
+
await server.customerGroups.grantMember({
|
|
794
|
+
groupId: '00000000-0000-4000-8000-000000000002',
|
|
795
|
+
customerId: '00000000-0000-4000-8000-000000000003',
|
|
796
|
+
reason: 'support-approved',
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
await server.customerGroups.revokeMember({
|
|
800
|
+
groupId: '00000000-0000-4000-8000-000000000002',
|
|
801
|
+
customerId: '00000000-0000-4000-8000-000000000003',
|
|
802
|
+
})
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
Keep these calls in trusted server code. Do not mutate `customers.groups`
|
|
806
|
+
directly: after source-authority activation it is a read-compatible projection
|
|
807
|
+
and direct writes return `customer_groups_direct_write_retired`.
|
|
808
|
+
|
|
809
|
+
### External Customer links (server only)
|
|
810
|
+
|
|
811
|
+
`server.externalCustomerLinks` connects a platform Customer to an external
|
|
812
|
+
Customer ID within the namespace and environment derived from an active
|
|
813
|
+
integration registration. Your app supplies a normalized stable `accountScope`
|
|
814
|
+
to reserve identity; namespace-bound keys authorize all accounts in that
|
|
815
|
+
namespace, and account scope is not an account ACL. All six methods (`connect`,
|
|
816
|
+
`get`, `list`,
|
|
817
|
+
`unlink`, `capturePreviousState`, `clearPreviousState`) require server
|
|
818
|
+
credentials and Customer read or write scope.
|
|
819
|
+
|
|
820
|
+
```typescript
|
|
821
|
+
const link = await server.externalCustomerLinks.connect({
|
|
822
|
+
integrationId,
|
|
823
|
+
customerId,
|
|
824
|
+
accountScope,
|
|
825
|
+
externalCustomerId: 'contact-123',
|
|
826
|
+
requestId: crypto.randomUUID(),
|
|
827
|
+
})
|
|
828
|
+
const capture = {
|
|
829
|
+
integrationId,
|
|
830
|
+
customerId,
|
|
831
|
+
linkId: link.id,
|
|
832
|
+
requestId: crypto.randomUUID(),
|
|
833
|
+
expectedVersion: link.version,
|
|
834
|
+
previousState: { tier: 'silver' },
|
|
835
|
+
}
|
|
836
|
+
await server.externalCustomerLinks.capturePreviousState(capture)
|
|
837
|
+
// After an interrupted response, retry this exact request, including expectedVersion.
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
The bounded previous state accepts up to 16 flat string/number/boolean/null
|
|
841
|
+
fields, 512 characters per string, and 2048 UTF-8 JSON bytes. It records only
|
|
842
|
+
what your server supplies; the platform performs no external provider I/O.
|
|
843
|
+
Before restoring provider state, check the current active owner with
|
|
844
|
+
`list({ integrationId, accountScope, externalCustomerId, state: 'active' })`
|
|
845
|
+
and evaluate whether the restoration is still appropriate. Lists accept optional Customer,
|
|
846
|
+
account scope, external ID, and state filters (external ID requires account
|
|
847
|
+
scope), `limit` (default 25, maximum 100), and `afterId`;
|
|
848
|
+
use `nextCursor` for the next page. Unlink and clear also require a fresh
|
|
849
|
+
request UUID and the current version; exact retries retain the original values.
|
|
850
|
+
A privacy-erased snapshot has state `erased`, null `externalCustomerId`, and
|
|
851
|
+
null `previousState`. Keep these calls and snapshots in trusted server code.
|
|
852
|
+
|
|
826
853
|
## Storefront performance defaults
|
|
827
854
|
|
|
828
855
|
- **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.
|
|
@@ -1094,6 +1121,49 @@ await server.collections.from('articles').updateMany(where, data)
|
|
|
1094
1121
|
await server.collections.from('articles').removeMany(where)
|
|
1095
1122
|
```
|
|
1096
1123
|
|
|
1124
|
+
Collection `find`, `count`, `updateMany`, and `removeMany` share filter validation.
|
|
1125
|
+
`equals: null` and `not_equals: null` retain their null meaning through Payload
|
|
1126
|
+
REST, including text fields; null-containing filters use Payload's native JSON
|
|
1127
|
+
`where` query parameter. Other filters keep bracketed encoding. The SDK throws
|
|
1128
|
+
`ValidationError` before a request for undefined values, null outside those two
|
|
1129
|
+
operators, empty condition objects, empty arrays (including `and`/`or`), nesting
|
|
1130
|
+
beyond 10 levels, and arrays with more than 1000 elements. Omit `where` for an
|
|
1131
|
+
unfiltered read. For a dynamically empty ID list, return an empty result before
|
|
1132
|
+
calling the SDK rather than passing `in: []`. False, zero, empty strings, and
|
|
1133
|
+
Date conditions remain valid.
|
|
1134
|
+
|
|
1135
|
+
Check your bulk filters carefully: calls that previously failed with HTTP 400
|
|
1136
|
+
now perform updates or deletions. Large `in`/`not_in`
|
|
1137
|
+
lists can exceed URL length limits (around 250 UUIDs); split work into smaller
|
|
1138
|
+
batches. For `not_in`, keep the full exclusion condition in every batch rather
|
|
1139
|
+
than splitting the exclusions across separate mutations.
|
|
1140
|
+
|
|
1141
|
+
### Orders update compatibility
|
|
1142
|
+
|
|
1143
|
+
Orders accept one-document updates only. Admin Panel bulk edits and generic
|
|
1144
|
+
`server.collections.from('orders').updateMany(...)` are rejected before any
|
|
1145
|
+
Order changes with HTTP 400 (`errors[0].data.code: order_bulk_update_unsupported`).
|
|
1146
|
+
Use `update(orderId, data)` for an unconditional edit. For a conditional update,
|
|
1147
|
+
keep the entire where predicate in the raw `PATCH /api/orders` query and set
|
|
1148
|
+
`limit=1`; send the field data as the body. Local API callers use numeric
|
|
1149
|
+
`limit: 1`, or a strict UUID `id.equals` that bounds the complete predicate.
|
|
1150
|
+
An ID in only one OR branch is insufficient. SDK `updateMany` has no limit
|
|
1151
|
+
option and is not a supported Orders CAS path.
|
|
1152
|
+
|
|
1153
|
+
A conditional miss returns `docs: [], errors: []`. A collected hook failure
|
|
1154
|
+
instead returns HTTP 500 (`errors[0].data.code: order_update_failed`), with a
|
|
1155
|
+
localized message and rollback of the supported single operation. Access and
|
|
1156
|
+
tenant checks remain in force. Do not replace a conditional update with an
|
|
1157
|
+
unconditional ID update or automatically retry payment capture. Separate
|
|
1158
|
+
per-order edits do not promise atomic success across the whole selection.
|
|
1159
|
+
|
|
1160
|
+
Console Local API Orders updates also require a live transaction. Setting
|
|
1161
|
+
`disableTransaction: true` without a live external transaction rejects both
|
|
1162
|
+
top-ID and where updates before writes with HTTP 400
|
|
1163
|
+
(`errors[0].data.code: order_transaction_required`). Remove that option to
|
|
1164
|
+
use Payload's normal transaction, or pass a live external transaction;
|
|
1165
|
+
a request transaction ID by itself is not sufficient.
|
|
1166
|
+
|
|
1097
1167
|
### API Response Types (Payload Native)
|
|
1098
1168
|
|
|
1099
1169
|
The SDK returns Payload CMS native response types without wrapping:
|
|
@@ -1296,7 +1366,6 @@ const { token, customer } = await client.customer.auth.login({
|
|
|
1296
1366
|
// Profile & token management
|
|
1297
1367
|
const profile = await client.customer.auth.me()
|
|
1298
1368
|
client.customer.auth.isAuthenticated()
|
|
1299
|
-
client.customer.auth.logout()
|
|
1300
1369
|
|
|
1301
1370
|
// Authenticated customer's own orders (Client-only)
|
|
1302
1371
|
const orders = await client.commerce.orders.listMine({
|
|
@@ -1305,12 +1374,22 @@ const orders = await client.commerce.orders.listMine({
|
|
|
1305
1374
|
financialStatus: 'paid',
|
|
1306
1375
|
})
|
|
1307
1376
|
|
|
1377
|
+
await client.customer.auth.logoutAsync()
|
|
1378
|
+
|
|
1308
1379
|
// Password
|
|
1309
1380
|
await client.customer.auth.forgotPassword('john@example.com')
|
|
1310
1381
|
await client.customer.auth.resetPassword(token, newPassword)
|
|
1311
1382
|
await client.customer.auth.changePassword(currentPassword, newPassword)
|
|
1312
1383
|
```
|
|
1313
1384
|
|
|
1385
|
+
`logoutAsync()` revokes the direct refresh-token family on the server and clears
|
|
1386
|
+
local credentials even when the request fails. Handle a rejection as incomplete
|
|
1387
|
+
server revocation. The older synchronous `logout(): void` clears local state
|
|
1388
|
+
immediately but cannot revoke the server session. `useCustomerLogout()` awaits
|
|
1389
|
+
the server request and clears authenticated customer query data on success or
|
|
1390
|
+
failure. An issued access token can remain valid for up to 15 minutes; legacy
|
|
1391
|
+
bearer-only refresh remains a separate compatibility path.
|
|
1392
|
+
|
|
1314
1393
|
`forgotPassword()` keeps the same SDK signature in both delivery modes. A
|
|
1315
1394
|
tenant administrator chooses either backward-compatible signed webhook
|
|
1316
1395
|
delivery or platform-managed email in the Console customer-authentication
|
|
@@ -1319,6 +1398,31 @@ the platform appends the `token` query parameter and sends through the verified
|
|
|
1319
1398
|
tenant sender or the platform fallback. Do not add reset tokens or dynamic
|
|
1320
1399
|
redirect URLs to the SDK call.
|
|
1321
1400
|
|
|
1401
|
+
#### Self-withdrawal
|
|
1402
|
+
|
|
1403
|
+
A signed-in customer with a local password can withdraw using their current
|
|
1404
|
+
password. With an authenticated client:
|
|
1405
|
+
|
|
1406
|
+
```ts
|
|
1407
|
+
const { withdrawnAt } = await client.customer.auth.withdraw(currentPassword)
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
This calls `POST /api/customers/withdraw` with the publishable key and Customer
|
|
1411
|
+
JWT. Success archives the account immediately and clears the SDK's local
|
|
1412
|
+
credentials. Clear any application-owned session cookies and customer cache as
|
|
1413
|
+
well. Archived accounts cannot use existing sessions or authenticate again.
|
|
1414
|
+
Withdrawal preserves order, payment and membership-charge history; it does not
|
|
1415
|
+
erase personal data or free the email for a new registration.
|
|
1416
|
+
|
|
1417
|
+
Branch on `SDKError.apiReason`: `active_membership_exists` (409) requires the
|
|
1418
|
+
membership to end first, including scheduled cancellations still in an active
|
|
1419
|
+
status; `reauthentication_unavailable` (409) means no local password is set and
|
|
1420
|
+
the customer should contact the tenant administrator. Incorrect passwords use
|
|
1421
|
+
the shared login attempt counter and temporary lock (`account_locked`, 423).
|
|
1422
|
+
Refusal leaves the SDK credentials intact. Server keys cannot withdraw on a
|
|
1423
|
+
customer's behalf. Operator restore continues to follow the existing archive
|
|
1424
|
+
rules; the withdrawal timestamp is retained.
|
|
1425
|
+
|
|
1322
1426
|
### Market discovery
|
|
1323
1427
|
|
|
1324
1428
|
Discover a tenant's active buyer markets and resolve the default market. These
|
|
@@ -1970,6 +2074,28 @@ await server.community.moderation.banCustomer({ customerId, isPermanent?, banned
|
|
|
1970
2074
|
await server.community.moderation.unbanCustomer({ customerId })
|
|
1971
2075
|
```
|
|
1972
2076
|
|
|
2077
|
+
### Installation credentials (server only)
|
|
2078
|
+
|
|
2079
|
+
`createServerClient().installationCredentials` stores one bounded ciphertext
|
|
2080
|
+
for a registered non-payment OAuth installation. A tenant administrator must
|
|
2081
|
+
register the installation and explicitly bind the server key; an ordinary
|
|
2082
|
+
`sk01_` key or external-effect claim grant is insufficient. Encrypt before
|
|
2083
|
+
`create`, decrypt only in your server, and keep OAuth and provider calls there.
|
|
2084
|
+
|
|
2085
|
+
```typescript
|
|
2086
|
+
const current = await server.installationCredentials.get({ installationId })
|
|
2087
|
+
const next = await server.installationCredentials.replace({
|
|
2088
|
+
installationId,
|
|
2089
|
+
expectedVersion: current.version,
|
|
2090
|
+
ciphertext: newlyEncryptedCredential,
|
|
2091
|
+
})
|
|
2092
|
+
```
|
|
2093
|
+
|
|
2094
|
+
Replacement uses a version condition; after an uncertain result, read back
|
|
2095
|
+
before retrying. `dispose` clears the platform copy but does not revoke a
|
|
2096
|
+
credential already read by your server. A platform database restore requires
|
|
2097
|
+
re-authorization before older installations become readable again.
|
|
2098
|
+
|
|
1973
2099
|
### Webhook
|
|
1974
2100
|
|
|
1975
2101
|
Webhooks created in Console start inactive. Use this setup order so no event is
|
|
@@ -2243,7 +2369,7 @@ SOFTWARE_SECRET_KEY=sk01_... # Server only — opaque API key from Console
|
|
|
2243
2369
|
|
|
2244
2370
|
> `secretKey` may be an `sk01_...` API key or a `pat01_...` personal access token. Server SDK calls must also send the matching `publishableKey`; PAT tenant selection is pinned server-side, and callers must not send `X-Tenant-Id`.
|
|
2245
2371
|
|
|
2246
|
-
API keys created without explicit scopes use the default `['read', 'write']`. Console API-key creation can request narrower `scopes[]`;
|
|
2372
|
+
API keys created without explicit scopes use the default `['read', 'write']`. Console API-key creation can request narrower `scopes[]`; tenant admins may grant only `read` and `write`. `webhook` and `super-admin` require a super-admin session. Requests containing the retired `analytics` scope, alone or mixed, are rejected for every role. Previously issued keys keep their identity; an analytics-only key has no effective access, while a mixed key keeps its other scopes.
|
|
2247
2373
|
|
|
2248
2374
|
> **SDK 0.9.0**: Server auth now uses opaque bearer tokens (`sk01_...`). Generate API keys from the Console. `createServerToken`, `createApiKey`, and `parseApiKey` are no longer part of the SDK surface.
|
|
2249
2375
|
|