@doswiftly/storefront-sdk 21.0.1 → 22.1.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 +36 -0
- package/README.md +83 -0
- package/dist/core/generated/operation-types.d.ts +2 -2
- package/dist/core/generated/operation-types.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/core/middleware/forwarded-ip.d.ts +82 -0
- package/dist/core/middleware/forwarded-ip.d.ts.map +1 -0
- package/dist/core/middleware/forwarded-ip.js +109 -0
- package/dist/core/referral/cookie-config.d.ts +56 -0
- package/dist/core/referral/cookie-config.d.ts.map +1 -0
- package/dist/core/referral/cookie-config.js +83 -0
- package/dist/react/hooks/use-referral-capture.d.ts +9 -0
- package/dist/react/hooks/use-referral-capture.d.ts.map +1 -0
- package/dist/react/hooks/use-referral-capture.js +40 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/referral.d.ts +53 -0
- package/dist/react/referral.d.ts.map +1 -0
- package/dist/react/referral.js +51 -0
- package/dist/react/server/cookie-readers.d.ts +7 -0
- package/dist/react/server/cookie-readers.d.ts.map +1 -1
- package/dist/react/server/cookie-readers.js +10 -0
- package/dist/react/server/get-storefront-client.d.ts +19 -1
- package/dist/react/server/get-storefront-client.d.ts.map +1 -1
- package/dist/react/server/get-storefront-client.js +36 -2
- package/dist/react/server/index.d.ts +1 -1
- package/dist/react/server/index.d.ts.map +1 -1
- package/dist/react/server/index.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 22.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 32ee745: Forward the real buyer IP from server-rendered storefronts (per-buyer rate limiting)
|
|
8
|
+
|
|
9
|
+
A server-rendered (BFF) storefront fetches from its own server, so the API sees one
|
|
10
|
+
source IP for every buyer and per-IP rate limits collapse onto that single address.
|
|
11
|
+
The server client now forwards the real buyer IP to the API so rate limiting applies
|
|
12
|
+
per buyer again.
|
|
13
|
+
|
|
14
|
+
**Automatic on the server — no wiring, fully backward-compatible.** Call
|
|
15
|
+
`getStorefrontClient({ apiUrl, shopSlug })` as before; forwarding configures itself
|
|
16
|
+
and stays inert when it cannot apply, so existing setups are unaffected. Server-only.
|
|
17
|
+
|
|
18
|
+
Non-Next server runtimes can supply the buyer IP explicitly via `getBuyerIp` (and
|
|
19
|
+
`getForwardedIpSecret`). The lower-level `forwardedIpMiddleware` is also exported.
|
|
20
|
+
|
|
21
|
+
(`@doswiftly/storefront-operations` is a version-sync bump — no code change.)
|
|
22
|
+
|
|
23
|
+
## 22.0.0
|
|
24
|
+
|
|
25
|
+
### Major Changes
|
|
26
|
+
|
|
27
|
+
- 49139e2: Remove the `pendingPoints` field from `LoyaltyPointsSummary` (GraphQL schema, the `LoyaltyPointsSummary` fragment and the generated types). The field was never populated — it always returned `0` — because purchase points are credited directly when the payment is captured, so there is no "pending" state to report.
|
|
28
|
+
|
|
29
|
+
**Migration:** if your storefront reads `points.pendingPoints` (or selects `pendingPoints` in a custom query or fragment), remove that usage and re-run your codegen. No replacement field is needed — `currentPoints` has always been the spendable balance.
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- 49139e2: Referral program signup support — the loyalty referral program now works end-to-end:
|
|
34
|
+
- `customerSignup` accepts an optional `referralCode` in its input. When the shop's referral program is active, a valid code grants the configured bonus points to the new customer and, after their first paid order, rewards the referrer. An invalid, expired or own code is silently ignored — the signup always succeeds.
|
|
35
|
+
- New referral capture helpers in the SDK: `useReferralCapture()` / `captureReferralCode()` persist the `?ref=CODE` parameter from a referral landing link into the readable `referral-code` cookie (30 days by default), and `readReferralCodeCookie()` / `clearReferralCodeCookie()` read and clear it at signup time. A server-side `readReferralCodeCookie()` is available from `@doswiftly/storefront-sdk/react/server` for SSR-rendered signup forms. Core constants and the URL extractor (`REFERRAL_COOKIE_NAME`, `REFERRAL_COOKIE_MAX_AGE`, `extractReferralCodeFromUrl`) are exported from the framework-agnostic core entry.
|
|
36
|
+
|
|
37
|
+
The cookie lifetime is the _landing → signup_ window and is intentionally independent of the shop's referral validity setting, which starts at signup and is enforced by the API.
|
|
38
|
+
|
|
3
39
|
## 21.0.1
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -667,6 +667,50 @@ const {
|
|
|
667
667
|
});
|
|
668
668
|
```
|
|
669
669
|
|
|
670
|
+
## Referral program
|
|
671
|
+
|
|
672
|
+
When the shop's loyalty referral program is enabled, customers share links like
|
|
673
|
+
`https://shop.example/register?ref=REF-AB12CD34` (the code and share URL come
|
|
674
|
+
from the `referralStats` / `loyaltyGenerateReferralCode` GraphQL operations).
|
|
675
|
+
The SDK bridges the gap between landing on such a link and the actual signup:
|
|
676
|
+
|
|
677
|
+
```tsx
|
|
678
|
+
// 1. Capture the code on landing — mount once near the top of the app.
|
|
679
|
+
'use client';
|
|
680
|
+
import { useReferralCapture } from '@doswiftly/storefront-sdk/react';
|
|
681
|
+
|
|
682
|
+
export function ReferralCapture() {
|
|
683
|
+
useReferralCapture(); // reads ?ref=... and stores it in the `referral-code` cookie (30 days)
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
```tsx
|
|
689
|
+
// 2. At signup, pass the stored code in the customerSignup input and clear it on success.
|
|
690
|
+
import { getReferralCodeCookie, clearReferralCodeCookie } from '@doswiftly/storefront-sdk/react';
|
|
691
|
+
|
|
692
|
+
const referralCode = getReferralCodeCookie();
|
|
693
|
+
await signup({ email, password, firstName, lastName, referralCode: referralCode ?? undefined });
|
|
694
|
+
clearReferralCodeCookie();
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Codes are case-insensitive and an invalid, malformed, expired or own code is
|
|
698
|
+
silently ignored by the API — the signup always succeeds. The capture step only
|
|
699
|
+
persists values matching the platform code shape (letters, digits, dashes —
|
|
700
|
+
`REFERRAL_CODE_PATTERN`), so junk `?ref=` values never reach the cookie. Server
|
|
701
|
+
Components can read the stored code with the async `readReferralCodeCookie()`
|
|
702
|
+
from `@doswiftly/storefront-sdk/react/server` (e.g. to pre-fill an SSR-rendered
|
|
703
|
+
form) — note the deliberate naming split: synchronous `getReferralCodeCookie()`
|
|
704
|
+
on the client entry vs server-first `readReferralCodeCookie()` on the server
|
|
705
|
+
entry. Non-React runtimes can use the core helpers directly:
|
|
706
|
+
`extractReferralCodeFromUrl(url)` plus the `REFERRAL_COOKIE_NAME` /
|
|
707
|
+
`REFERRAL_COOKIE_MAX_AGE` constants.
|
|
708
|
+
|
|
709
|
+
The cookie lifetime (30 days, override via `captureReferralCode({ maxAge })`)
|
|
710
|
+
is the *landing → signup* window. It is independent of the shop's referral
|
|
711
|
+
validity setting — that one starts at signup, limits the time the referred
|
|
712
|
+
customer has to place their first order, and is enforced by the backend.
|
|
713
|
+
|
|
670
714
|
## Pre-built React components
|
|
671
715
|
|
|
672
716
|
Headless, accessibility-aware, zero styling — pass `className` to integrate with
|
|
@@ -728,6 +772,11 @@ Middleware that reads mutable state takes a **lazy getter**
|
|
|
728
772
|
(`authMiddleware(() => store.getState().accessToken)`) so rotated values are
|
|
729
773
|
picked up without rebuilding the client.
|
|
730
774
|
|
|
775
|
+
A server-only `forwardedIpMiddleware` is also available for server-rendered (BFF)
|
|
776
|
+
storefronts — it forwards the real buyer IP to the backend so per-IP rate limits
|
|
777
|
+
do not collapse onto the storefront server's address. See
|
|
778
|
+
[Server-side](#server-side-reactserver).
|
|
779
|
+
|
|
731
780
|
## Core API
|
|
732
781
|
|
|
733
782
|
### createStorefrontClient
|
|
@@ -1026,6 +1075,40 @@ middleware: [
|
|
|
1026
1075
|
],
|
|
1027
1076
|
```
|
|
1028
1077
|
|
|
1078
|
+
### Forwarding the buyer IP for rate limiting
|
|
1079
|
+
|
|
1080
|
+
When a storefront fetches on the server, the API sees the storefront server's IP for
|
|
1081
|
+
every buyer, so per-IP rate limits collapse onto a single address. The server client
|
|
1082
|
+
forwards the buyer's real IP so rate limiting applies per buyer again.
|
|
1083
|
+
|
|
1084
|
+
**Automatic on the server — nothing to wire.** Call `getStorefrontClient` as usual:
|
|
1085
|
+
|
|
1086
|
+
```typescript
|
|
1087
|
+
const client = getStorefrontClient({
|
|
1088
|
+
apiUrl: process.env.DOSWIFTLY_API_URL!,
|
|
1089
|
+
shopSlug: process.env.DOSWIFTLY_SHOP_SLUG!,
|
|
1090
|
+
});
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
It applies only inside a server request with forwarding configured by your
|
|
1094
|
+
deployment, and is otherwise an inert pass-through — existing setups are unaffected.
|
|
1095
|
+
**Server-only**: nothing IP-related reaches the browser.
|
|
1096
|
+
|
|
1097
|
+
**Non-Next server runtimes** — supply the buyer IP yourself (e.g. from
|
|
1098
|
+
`cf-connecting-ip`), plus the signing secret if your runtime has no `process.env`:
|
|
1099
|
+
|
|
1100
|
+
```typescript
|
|
1101
|
+
const client = getStorefrontClient({
|
|
1102
|
+
apiUrl,
|
|
1103
|
+
shopSlug,
|
|
1104
|
+
getBuyerIp: () => incomingRequest.headers.get('cf-connecting-ip'),
|
|
1105
|
+
// getForwardedIpSecret: () => ...
|
|
1106
|
+
});
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
The lower-level `forwardedIpMiddleware({ getBuyerIp, getSecret })` is also exported
|
|
1110
|
+
for fully custom clients.
|
|
1111
|
+
|
|
1029
1112
|
## Caching
|
|
1030
1113
|
|
|
1031
1114
|
```typescript
|
|
@@ -1553,6 +1553,8 @@ export type CustomerCreateInput = {
|
|
|
1553
1553
|
password: Scalars['String']['input'];
|
|
1554
1554
|
/** Phone number (free-form). */
|
|
1555
1555
|
phone?: InputMaybe<Scalars['String']['input']>;
|
|
1556
|
+
/** Referral code of an existing customer, usually taken from a referral link's `ref` URL parameter (codes are case-insensitive). Optional — an invalid, malformed, expired or own code is silently ignored and the signup still succeeds; only a value longer than 50 characters is rejected. When the shop's referral program is active, a valid code grants the configured bonus points to the new customer and, after their first paid order, rewards the referrer. */
|
|
1557
|
+
referralCode?: InputMaybe<Scalars['String']['input']>;
|
|
1556
1558
|
};
|
|
1557
1559
|
export type CustomerGroup = {
|
|
1558
1560
|
/** Group code */
|
|
@@ -2092,8 +2094,6 @@ export type LoyaltyPointsSummary = {
|
|
|
2092
2094
|
expiringPoints?: Maybe<Scalars['Int']['output']>;
|
|
2093
2095
|
/** Next expiry date (ISO 8601) */
|
|
2094
2096
|
nextExpiryDate?: Maybe<Scalars['DateTime']['output']>;
|
|
2095
|
-
/** Points pending (from incomplete orders) */
|
|
2096
|
-
pendingPoints: Scalars['Int']['output'];
|
|
2097
2097
|
/** Points redeemed */
|
|
2098
2098
|
redeemedPoints: Scalars['Int']['output'];
|
|
2099
2099
|
/** Total points earned all-time */
|