@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +83 -0
  3. package/dist/core/generated/operation-types.d.ts +2 -2
  4. package/dist/core/generated/operation-types.d.ts.map +1 -1
  5. package/dist/core/index.d.ts +2 -0
  6. package/dist/core/index.d.ts.map +1 -1
  7. package/dist/core/index.js +3 -0
  8. package/dist/core/middleware/forwarded-ip.d.ts +82 -0
  9. package/dist/core/middleware/forwarded-ip.d.ts.map +1 -0
  10. package/dist/core/middleware/forwarded-ip.js +109 -0
  11. package/dist/core/referral/cookie-config.d.ts +56 -0
  12. package/dist/core/referral/cookie-config.d.ts.map +1 -0
  13. package/dist/core/referral/cookie-config.js +83 -0
  14. package/dist/react/hooks/use-referral-capture.d.ts +9 -0
  15. package/dist/react/hooks/use-referral-capture.d.ts.map +1 -0
  16. package/dist/react/hooks/use-referral-capture.js +40 -0
  17. package/dist/react/index.d.ts +2 -0
  18. package/dist/react/index.d.ts.map +1 -1
  19. package/dist/react/index.js +5 -1
  20. package/dist/react/referral.d.ts +53 -0
  21. package/dist/react/referral.d.ts.map +1 -0
  22. package/dist/react/referral.js +51 -0
  23. package/dist/react/server/cookie-readers.d.ts +7 -0
  24. package/dist/react/server/cookie-readers.d.ts.map +1 -1
  25. package/dist/react/server/cookie-readers.js +10 -0
  26. package/dist/react/server/get-storefront-client.d.ts +19 -1
  27. package/dist/react/server/get-storefront-client.d.ts.map +1 -1
  28. package/dist/react/server/get-storefront-client.js +36 -2
  29. package/dist/react/server/index.d.ts +1 -1
  30. package/dist/react/server/index.d.ts.map +1 -1
  31. package/dist/react/server/index.js +2 -2
  32. package/package.json +1 -1
@@ -41,6 +41,7 @@ export { authMiddleware } from './middleware/auth';
41
41
  export { cartSecretMiddleware, serverCartSecretMiddleware, CART_SECRET_HEADER, } from './middleware/cart-secret';
42
42
  export { currencyMiddleware } from './middleware/currency';
43
43
  export { languageMiddleware } from './middleware/language';
44
+ export { forwardedIpMiddleware, forwardedIpSignedMessage, FORWARDED_IP_HEADER, FORWARDED_IP_TS_HEADER, FORWARDED_IP_SIG_HEADER, type ForwardedIpMiddlewareOptions, } from './middleware/forwarded-ip';
44
45
  export { botProtectionMiddleware, BOT_PROTECTION_HEADER, type BotProtectionTokenProvider, type BotProtectionConfig, type BotProtectionProviderConfig, type BotProtectionMiddlewareOptions, type FailStrategy, } from './middleware/bot-protection';
45
46
  export { retryMiddleware, type RetryOptions } from './middleware/retry';
46
47
  export { timeoutMiddleware, type TimeoutOptions } from './middleware/timeout';
@@ -67,6 +68,7 @@ export { AUTH_COOKIE_NAME, AUTH_COOKIE_DEFAULTS, type AuthCookieConfig, REFRESH_
67
68
  export { LANGUAGE_COOKIE_NAME, LANGUAGE_COOKIE_MAX_AGE, LANGUAGE_HEADER_NAME } from './language/cookie-config';
68
69
  export { CURRENCY_COOKIE_NAME, CURRENCY_COOKIE_MAX_AGE, CURRENCY_HEADER_NAME } from './currency/cookie-config';
69
70
  export { CART_COOKIE_NAME, CART_COOKIE_MAX_AGE, parseCartCookieValue, formatCartCookieValue, type CartCredentials, } from './cart/cookie-config';
71
+ export { REFERRAL_COOKIE_NAME, REFERRAL_COOKIE_MAX_AGE, REFERRAL_CODE_MAX_LENGTH, REFERRAL_CODE_PATTERN, extractReferralCodeFromUrl, } from './referral/cookie-config';
70
72
  export { matchesRoute, type RouteProtectionConfig } from './auth/routes';
71
73
  export { createSetTokenHandler, createClearTokenHandler, createWhoamiHandler, originAllowlistValidator, trustedForwardedHostValidator, } from './auth/handlers';
72
74
  export type { OriginValidator, OriginValidatorContext } from './auth/handlers';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,7 @@ export { authMiddleware } from './middleware/auth';
43
43
  export { cartSecretMiddleware, serverCartSecretMiddleware, CART_SECRET_HEADER, } from './middleware/cart-secret';
44
44
  export { currencyMiddleware } from './middleware/currency';
45
45
  export { languageMiddleware } from './middleware/language';
46
+ export { forwardedIpMiddleware, forwardedIpSignedMessage, FORWARDED_IP_HEADER, FORWARDED_IP_TS_HEADER, FORWARDED_IP_SIG_HEADER, } from './middleware/forwarded-ip';
46
47
  export { botProtectionMiddleware, BOT_PROTECTION_HEADER, } from './middleware/bot-protection';
47
48
  export { retryMiddleware } from './middleware/retry';
48
49
  export { timeoutMiddleware } from './middleware/timeout';
@@ -84,6 +85,8 @@ export { LANGUAGE_COOKIE_NAME, LANGUAGE_COOKIE_MAX_AGE, LANGUAGE_HEADER_NAME } f
84
85
  export { CURRENCY_COOKIE_NAME, CURRENCY_COOKIE_MAX_AGE, CURRENCY_HEADER_NAME } from './currency/cookie-config';
85
86
  // Cart cookie config + composite value parser/formatter (`<cartId>.<secret>`)
86
87
  export { CART_COOKIE_NAME, CART_COOKIE_MAX_AGE, parseCartCookieValue, formatCartCookieValue, } from './cart/cookie-config';
88
+ // Referral cookie config + URL extractor (loyalty referral program capture)
89
+ export { REFERRAL_COOKIE_NAME, REFERRAL_COOKIE_MAX_AGE, REFERRAL_CODE_MAX_LENGTH, REFERRAL_CODE_PATTERN, extractReferralCodeFromUrl, } from './referral/cookie-config';
87
90
  // Auth route matching
88
91
  export { matchesRoute } from './auth/routes';
89
92
  // Auth cookie handlers (API route factories)
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Forwarded-IP middleware — lets a SERVER-SIDE (BFF) storefront forward the real
3
+ * buyer IP to the backend, so per-buyer rate limiting works even though the
4
+ * backend's connection comes from the storefront server, not the browser.
5
+ *
6
+ * Why this exists: when a storefront renders/fetches on the server (server
7
+ * components, route handlers, server actions), the backend sees ONE source IP —
8
+ * the storefront server's — for every buyer. Per-IP limits then collapse onto
9
+ * that single address. This middleware carries the buyer's real IP (read from the
10
+ * incoming request, e.g. the `cf-connecting-ip` header) in a signed header the
11
+ * backend can trust.
12
+ *
13
+ * Trust model: the header is signed with HMAC-SHA256 over `${ip}.${ts}.${shopSlug}`
14
+ * using a shared platform secret. The backend recomputes the signature and only
15
+ * trusts the IP when it matches and the timestamp is fresh. Binding the shop slug
16
+ * stops a signature from one shop being replayed against another; the timestamp
17
+ * bounds replay. A spoofed header without a valid signature is ignored by the
18
+ * backend (it falls back to the connection IP).
19
+ *
20
+ * SERVER-ONLY: the secret must never reach the browser. Use this middleware only
21
+ * in server-side clients (`getStorefrontClient`), never in the browser pipeline.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { forwardedIpMiddleware } from '@doswiftly/storefront-sdk';
26
+ *
27
+ * // `buyerIp` comes from your server framework's incoming request — typically the
28
+ * // `cf-connecting-ip` request header. `serverSecret` is a server-only env value.
29
+ * getStorefrontClient({
30
+ * apiUrl,
31
+ * shopSlug,
32
+ * middleware: [
33
+ * forwardedIpMiddleware({
34
+ * getBuyerIp: () => buyerIp,
35
+ * getSecret: () => serverSecret,
36
+ * }),
37
+ * ],
38
+ * });
39
+ * ```
40
+ */
41
+ import type { Middleware } from '../client/types';
42
+ /** Header carrying the buyer's real IP, forwarded by a server-side storefront. */
43
+ export declare const FORWARDED_IP_HEADER = "x-doswiftly-buyer-ip";
44
+ /** Header carrying the signature timestamp (unix milliseconds) — bounds replay. */
45
+ export declare const FORWARDED_IP_TS_HEADER = "x-doswiftly-fwd-ts";
46
+ /** Header carrying the hex HMAC-SHA256 signature over `${ip}.${ts}.${shopSlug}`. */
47
+ export declare const FORWARDED_IP_SIG_HEADER = "x-doswiftly-fwd-sig";
48
+ /**
49
+ * Canonical signed message. The backend MUST build this identically before
50
+ * verifying the signature. Order and separators are load-bearing.
51
+ */
52
+ export declare function forwardedIpSignedMessage(ip: string, timestamp: string, shopSlug: string): string;
53
+ /** A getter that may be sync or async (e.g. `async () => (await headers()).get('cf-connecting-ip')`). */
54
+ type MaybeAsyncGetter = () => string | null | undefined | Promise<string | null | undefined>;
55
+ export interface ForwardedIpMiddlewareOptions {
56
+ /** Real buyer IP from the incoming request (e.g. the `cf-connecting-ip` header). */
57
+ getBuyerIp: MaybeAsyncGetter;
58
+ /** Shared platform secret used to sign. Server-side only — never expose to the browser. */
59
+ getSecret: MaybeAsyncGetter;
60
+ /**
61
+ * OPTIONAL override for the shop slug bound into the signature. Defaults to the
62
+ * `X-Shop-Slug` header the client already sends — so you normally don't pass it,
63
+ * and the signed slug is guaranteed to match what the backend verifies. Provide
64
+ * only for non-standard transports that don't set that header.
65
+ */
66
+ getShopSlug?: MaybeAsyncGetter;
67
+ /** Clock source (unix milliseconds). Injectable for tests; defaults to `Date.now`. */
68
+ now?: () => number;
69
+ }
70
+ /**
71
+ * Server-side forwarded-IP middleware. On every request it reads the buyer IP,
72
+ * shop slug and secret (lazy getters — a rotated secret is picked up without
73
+ * rebuilding the pipeline), signs `${ip}.${ts}.${shopSlug}` with HMAC-SHA256 and
74
+ * adds the three forwarded-IP headers. When any input is missing it adds nothing
75
+ * — the backend then keys the connection IP (the server-side default).
76
+ *
77
+ * The HMAC key is imported once per distinct secret value and reused across
78
+ * requests (re-imported only on rotation).
79
+ */
80
+ export declare function forwardedIpMiddleware(options: ForwardedIpMiddlewareOptions): Middleware;
81
+ export {};
82
+ //# sourceMappingURL=forwarded-ip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forwarded-ip.d.ts","sourceRoot":"","sources":["../../../src/core/middleware/forwarded-ip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAC1D,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAC3D,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAQ7D;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhG;AAED,yGAAyG;AACzG,KAAK,gBAAgB,GAAG,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE7F,MAAM,WAAW,4BAA4B;IAC3C,oFAAoF;IACpF,UAAU,EAAE,gBAAgB,CAAC;IAC7B,2FAA2F;IAC3F,SAAS,EAAE,gBAAgB,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAeD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,UAAU,CAiCvF"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Forwarded-IP middleware — lets a SERVER-SIDE (BFF) storefront forward the real
3
+ * buyer IP to the backend, so per-buyer rate limiting works even though the
4
+ * backend's connection comes from the storefront server, not the browser.
5
+ *
6
+ * Why this exists: when a storefront renders/fetches on the server (server
7
+ * components, route handlers, server actions), the backend sees ONE source IP —
8
+ * the storefront server's — for every buyer. Per-IP limits then collapse onto
9
+ * that single address. This middleware carries the buyer's real IP (read from the
10
+ * incoming request, e.g. the `cf-connecting-ip` header) in a signed header the
11
+ * backend can trust.
12
+ *
13
+ * Trust model: the header is signed with HMAC-SHA256 over `${ip}.${ts}.${shopSlug}`
14
+ * using a shared platform secret. The backend recomputes the signature and only
15
+ * trusts the IP when it matches and the timestamp is fresh. Binding the shop slug
16
+ * stops a signature from one shop being replayed against another; the timestamp
17
+ * bounds replay. A spoofed header without a valid signature is ignored by the
18
+ * backend (it falls back to the connection IP).
19
+ *
20
+ * SERVER-ONLY: the secret must never reach the browser. Use this middleware only
21
+ * in server-side clients (`getStorefrontClient`), never in the browser pipeline.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { forwardedIpMiddleware } from '@doswiftly/storefront-sdk';
26
+ *
27
+ * // `buyerIp` comes from your server framework's incoming request — typically the
28
+ * // `cf-connecting-ip` request header. `serverSecret` is a server-only env value.
29
+ * getStorefrontClient({
30
+ * apiUrl,
31
+ * shopSlug,
32
+ * middleware: [
33
+ * forwardedIpMiddleware({
34
+ * getBuyerIp: () => buyerIp,
35
+ * getSecret: () => serverSecret,
36
+ * }),
37
+ * ],
38
+ * });
39
+ * ```
40
+ */
41
+ /** Header carrying the buyer's real IP, forwarded by a server-side storefront. */
42
+ export const FORWARDED_IP_HEADER = 'x-doswiftly-buyer-ip';
43
+ /** Header carrying the signature timestamp (unix milliseconds) — bounds replay. */
44
+ export const FORWARDED_IP_TS_HEADER = 'x-doswiftly-fwd-ts';
45
+ /** Header carrying the hex HMAC-SHA256 signature over `${ip}.${ts}.${shopSlug}`. */
46
+ export const FORWARDED_IP_SIG_HEADER = 'x-doswiftly-fwd-sig';
47
+ /**
48
+ * Shop routing header the client already attaches to every request (see
49
+ * `create-client.ts`). The signature binds to THIS slug by default, so the signed
50
+ * value always matches what the backend verifies from the same header.
51
+ */
52
+ const SHOP_SLUG_HEADER = 'X-Shop-Slug';
53
+ /**
54
+ * Canonical signed message. The backend MUST build this identically before
55
+ * verifying the signature. Order and separators are load-bearing.
56
+ */
57
+ export function forwardedIpSignedMessage(ip, timestamp, shopSlug) {
58
+ return `${ip}.${timestamp}.${shopSlug}`;
59
+ }
60
+ async function importHmacKey(secret) {
61
+ return crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [
62
+ 'sign',
63
+ ]);
64
+ }
65
+ async function signHex(key, message) {
66
+ const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(message));
67
+ return Array.from(new Uint8Array(signature))
68
+ .map((byte) => byte.toString(16).padStart(2, '0'))
69
+ .join('');
70
+ }
71
+ /**
72
+ * Server-side forwarded-IP middleware. On every request it reads the buyer IP,
73
+ * shop slug and secret (lazy getters — a rotated secret is picked up without
74
+ * rebuilding the pipeline), signs `${ip}.${ts}.${shopSlug}` with HMAC-SHA256 and
75
+ * adds the three forwarded-IP headers. When any input is missing it adds nothing
76
+ * — the backend then keys the connection IP (the server-side default).
77
+ *
78
+ * The HMAC key is imported once per distinct secret value and reused across
79
+ * requests (re-imported only on rotation).
80
+ */
81
+ export function forwardedIpMiddleware(options) {
82
+ const { getBuyerIp, getShopSlug, getSecret, now = () => Date.now() } = options;
83
+ let cachedSecret = null;
84
+ let cachedKey = null;
85
+ return async (request, next) => {
86
+ // Getters may be async (e.g. reading the IP from `headers()` per request);
87
+ // `await` resolves sync values unchanged, so sync getters keep working.
88
+ const ip = await getBuyerIp();
89
+ // Slug defaults to the routing header the client already sends — so it always
90
+ // matches what the backend verifies. The optional getter only overrides it.
91
+ const shopSlug = (await getShopSlug?.()) ?? request.headers[SHOP_SLUG_HEADER];
92
+ const secret = await getSecret();
93
+ // Forward only when everything needed for a trusted, shop-bound signature is
94
+ // present. Missing any piece -> send nothing (backend falls back to conn IP).
95
+ if (ip && shopSlug && secret) {
96
+ const timestamp = String(now());
97
+ if (cachedSecret !== secret || cachedKey === null) {
98
+ cachedSecret = secret;
99
+ cachedKey = importHmacKey(secret);
100
+ }
101
+ const key = await cachedKey;
102
+ const signature = await signHex(key, forwardedIpSignedMessage(ip, timestamp, shopSlug));
103
+ request.headers[FORWARDED_IP_HEADER] = ip;
104
+ request.headers[FORWARDED_IP_TS_HEADER] = timestamp;
105
+ request.headers[FORWARDED_IP_SIG_HEADER] = signature;
106
+ }
107
+ return next(request);
108
+ };
109
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Referral cookie configuration — platform contract.
3
+ *
4
+ * When a visitor lands on the storefront through a referral link
5
+ * (`https://shop.example/register?ref=REF-AB12CD34`), the code must survive
6
+ * navigation until the visitor actually signs up — possibly minutes or days
7
+ * later. The SDK persists it in a readable first-party cookie and the signup
8
+ * form passes it to `customerSignup(input: { referralCode })`.
9
+ *
10
+ * Used by:
11
+ * - `captureReferralCode` / `useReferralCapture` (client-side capture on landing)
12
+ * - `readReferralCodeCookie` (client + server readers at signup time)
13
+ */
14
+ export declare const REFERRAL_COOKIE_NAME = "referral-code";
15
+ /**
16
+ * Default cookie lifetime: 30 days.
17
+ *
18
+ * This is the "landing → signup" window only. It is intentionally NOT tied to
19
+ * the shop's referral validity setting (days the referred customer has to
20
+ * place their first order) — that window starts at signup and is validated
21
+ * exclusively by the backend. Coupling the two would be wrong: a shop giving
22
+ * referred customers 7 days to order would silently lose visitors who sign up
23
+ * 8 days after clicking the link. Override per call via
24
+ * `captureReferralCode({ maxAge })` if you need a different capture window.
25
+ */
26
+ export declare const REFERRAL_COOKIE_MAX_AGE: number;
27
+ /**
28
+ * Maximum accepted code length — longer values are ignored as garbage.
29
+ * Mirrors the `customerSignup` input contract (the API rejects longer codes).
30
+ */
31
+ export declare const REFERRAL_CODE_MAX_LENGTH = 50;
32
+ /**
33
+ * Accepted code shape: letters, digits and dashes (codes are case-insensitive —
34
+ * the API uppercases on lookup). Mirrors the platform referral-code charset;
35
+ * a non-conforming value is never persisted to the cookie, because the API
36
+ * would silently ignore it at signup anyway — storing it for 30 days would
37
+ * just carry dead weight (or someone's injection attempt) around. Widen this
38
+ * pattern only together with the `customerSignup.referralCode` API contract.
39
+ */
40
+ export declare const REFERRAL_CODE_PATTERN: RegExp;
41
+ /**
42
+ * Extract a referral code from a URL or a search string.
43
+ *
44
+ * Accepts a full or relative URL (`https://shop.example/register?ref=CODE`,
45
+ * `/register?ref=CODE`), a search string starting with `?` (`?ref=CODE`) or a
46
+ * `URL` instance. Returns the trimmed code, or `null` when the parameter is
47
+ * absent, empty, longer than {@link REFERRAL_CODE_MAX_LENGTH} or outside
48
+ * {@link REFERRAL_CODE_PATTERN} (such values would be ignored by the API
49
+ * anyway, so they are never persisted).
50
+ *
51
+ * @param input - URL, `URL` instance or `?`-prefixed search string to inspect.
52
+ * @param paramName - Query parameter carrying the code (default `'ref'` —
53
+ * matches the share links generated by the platform).
54
+ */
55
+ export declare function extractReferralCodeFromUrl(input: string | URL, paramName?: string): string | null;
56
+ //# sourceMappingURL=cookie-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-config.d.ts","sourceRoot":"","sources":["../../../src/core/referral/cookie-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,QAAoB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,QAAoB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,SAAQ,GAAG,MAAM,GAAG,IAAI,CAyBhG"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Referral cookie configuration — platform contract.
3
+ *
4
+ * When a visitor lands on the storefront through a referral link
5
+ * (`https://shop.example/register?ref=REF-AB12CD34`), the code must survive
6
+ * navigation until the visitor actually signs up — possibly minutes or days
7
+ * later. The SDK persists it in a readable first-party cookie and the signup
8
+ * form passes it to `customerSignup(input: { referralCode })`.
9
+ *
10
+ * Used by:
11
+ * - `captureReferralCode` / `useReferralCapture` (client-side capture on landing)
12
+ * - `readReferralCodeCookie` (client + server readers at signup time)
13
+ */
14
+ export const REFERRAL_COOKIE_NAME = 'referral-code';
15
+ /**
16
+ * Default cookie lifetime: 30 days.
17
+ *
18
+ * This is the "landing → signup" window only. It is intentionally NOT tied to
19
+ * the shop's referral validity setting (days the referred customer has to
20
+ * place their first order) — that window starts at signup and is validated
21
+ * exclusively by the backend. Coupling the two would be wrong: a shop giving
22
+ * referred customers 7 days to order would silently lose visitors who sign up
23
+ * 8 days after clicking the link. Override per call via
24
+ * `captureReferralCode({ maxAge })` if you need a different capture window.
25
+ */
26
+ export const REFERRAL_COOKIE_MAX_AGE = 30 * 24 * 60 * 60; // 30 days
27
+ /**
28
+ * Maximum accepted code length — longer values are ignored as garbage.
29
+ * Mirrors the `customerSignup` input contract (the API rejects longer codes).
30
+ */
31
+ export const REFERRAL_CODE_MAX_LENGTH = 50;
32
+ /**
33
+ * Accepted code shape: letters, digits and dashes (codes are case-insensitive —
34
+ * the API uppercases on lookup). Mirrors the platform referral-code charset;
35
+ * a non-conforming value is never persisted to the cookie, because the API
36
+ * would silently ignore it at signup anyway — storing it for 30 days would
37
+ * just carry dead weight (or someone's injection attempt) around. Widen this
38
+ * pattern only together with the `customerSignup.referralCode` API contract.
39
+ */
40
+ export const REFERRAL_CODE_PATTERN = /^[A-Za-z0-9-]+$/;
41
+ /**
42
+ * Extract a referral code from a URL or a search string.
43
+ *
44
+ * Accepts a full or relative URL (`https://shop.example/register?ref=CODE`,
45
+ * `/register?ref=CODE`), a search string starting with `?` (`?ref=CODE`) or a
46
+ * `URL` instance. Returns the trimmed code, or `null` when the parameter is
47
+ * absent, empty, longer than {@link REFERRAL_CODE_MAX_LENGTH} or outside
48
+ * {@link REFERRAL_CODE_PATTERN} (such values would be ignored by the API
49
+ * anyway, so they are never persisted).
50
+ *
51
+ * @param input - URL, `URL` instance or `?`-prefixed search string to inspect.
52
+ * @param paramName - Query parameter carrying the code (default `'ref'` —
53
+ * matches the share links generated by the platform).
54
+ */
55
+ export function extractReferralCodeFromUrl(input, paramName = 'ref') {
56
+ let params;
57
+ if (input instanceof URL) {
58
+ params = input.searchParams;
59
+ }
60
+ else {
61
+ const raw = input.trim();
62
+ if (!raw)
63
+ return null;
64
+ if (raw.startsWith('?')) {
65
+ // Search-string form: "?ref=CODE&utm_source=x"
66
+ params = new URLSearchParams(raw.slice(1));
67
+ }
68
+ else {
69
+ // Full or relative URL — the base argument makes relative paths
70
+ // ("/register?ref=CODE") parse too.
71
+ try {
72
+ params = new URL(raw, 'http://localhost').searchParams;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ }
79
+ const value = params.get(paramName)?.trim() ?? '';
80
+ if (!value || value.length > REFERRAL_CODE_MAX_LENGTH || !REFERRAL_CODE_PATTERN.test(value))
81
+ return null;
82
+ return value;
83
+ }
@@ -0,0 +1,9 @@
1
+ import { type CaptureReferralCodeOptions } from '../referral';
2
+ export interface UseReferralCaptureOptions extends Pick<CaptureReferralCodeOptions, 'paramName' | 'maxAge'> {
3
+ }
4
+ /**
5
+ * Capture the referral code from the current URL into the `referral-code`
6
+ * cookie and return the active code (`null` when none).
7
+ */
8
+ export declare function useReferralCapture(options?: UseReferralCaptureOptions): string | null;
9
+ //# sourceMappingURL=use-referral-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-referral-capture.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-referral-capture.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,WAAW,yBACf,SAAQ,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,QAAQ,CAAC;CAAG;AAErE;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,yBAAyB,GAAG,MAAM,GAAG,IAAI,CAWrF"}
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ /**
3
+ * useReferralCapture — capture a referral code from the landing URL.
4
+ *
5
+ * Mount the hook once near the top of the app (layout or a dedicated client
6
+ * component). On mount it inspects the current URL for the referral parameter
7
+ * (`?ref=CODE` by default), persists it in the `referral-code` cookie and
8
+ * returns the active code (from the URL, or a previously stored one when the
9
+ * visitor navigated away from the landing URL before signing up).
10
+ *
11
+ * At signup time read the code with `readReferralCodeCookie()` (or use the
12
+ * value returned here), pass it as `customerSignup(input: { referralCode })`
13
+ * and call `clearReferralCodeCookie()` after success.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * 'use client';
18
+ * import { useReferralCapture } from '@doswiftly/storefront-sdk/react';
19
+ *
20
+ * export function ReferralCapture() {
21
+ * useReferralCapture();
22
+ * return null;
23
+ * }
24
+ * ```
25
+ */
26
+ import { useEffect, useState } from 'react';
27
+ import { captureReferralCode } from '../referral';
28
+ /**
29
+ * Capture the referral code from the current URL into the `referral-code`
30
+ * cookie and return the active code (`null` when none).
31
+ */
32
+ export function useReferralCapture(options) {
33
+ const [code, setCode] = useState(null);
34
+ const paramName = options?.paramName;
35
+ const maxAge = options?.maxAge;
36
+ useEffect(() => {
37
+ setCode(captureReferralCode({ paramName, maxAge }));
38
+ }, [paramName, maxAge]);
39
+ return code;
40
+ }
@@ -37,6 +37,8 @@ export type { ShopConfig } from './types/shop-config';
37
37
  export { selectCurrency, selectBaseCurrency, selectSupportedCurrencies, selectIsLoaded } from './stores/currency.store';
38
38
  export { selectLanguage, selectDefaultLanguage, selectSupportedLanguages, selectLanguageIsLoaded } from './stores/language.store';
39
39
  export { getCookie, setCookie, deleteCookie, createBrowserCartCookieStore, } from './cookies';
40
+ export { captureReferralCode, getReferralCodeCookie, clearReferralCodeCookie, type CaptureReferralCodeOptions, } from './referral';
41
+ export { useReferralCapture, type UseReferralCaptureOptions } from './hooks/use-referral-capture';
40
42
  export { useBotProtection } from './hooks/use-bot-protection';
41
43
  export { useHydrated } from './hooks/use-hydrated';
42
44
  export { useDebouncedValue } from './hooks/use-debounced-value';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -36,8 +36,12 @@ export { useLanguageStore, useLanguageStoreApi } from './stores/store-context';
36
36
  export { selectCurrency, selectBaseCurrency, selectSupportedCurrencies, selectIsLoaded } from './stores/currency.store';
37
37
  export { selectLanguage, selectDefaultLanguage, selectSupportedLanguages, selectLanguageIsLoaded } from './stores/language.store';
38
38
  // Cookie utilities (client-side). Server-side readers (readCartIdCookie,
39
- // readCurrencyCookie) live in `@doswiftly/storefront-sdk/react/server`.
39
+ // readCurrencyCookie, readReferralCodeCookie) live in
40
+ // `@doswiftly/storefront-sdk/react/server`.
40
41
  export { getCookie, setCookie, deleteCookie, createBrowserCartCookieStore, } from './cookies';
42
+ // Referral capture (loyalty referral program — landing-link `?ref=CODE` → cookie)
43
+ export { captureReferralCode, getReferralCodeCookie, clearReferralCodeCookie, } from './referral';
44
+ export { useReferralCapture } from './hooks/use-referral-capture';
41
45
  // Bot protection
42
46
  export { useBotProtection } from './hooks/use-bot-protection';
43
47
  // Generic hooks
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Client-side referral capture helpers.
3
+ *
4
+ * Flow: a visitor lands through a referral share link (`/register?ref=CODE`),
5
+ * `captureReferralCode()` persists the code in the readable `referral-code`
6
+ * cookie, and the signup form reads it back with `getReferralCodeCookie()`
7
+ * and passes it as `customerSignup(input: { referralCode })`. After a
8
+ * successful signup call `clearReferralCodeCookie()` so the code is not
9
+ * re-submitted by other browser tabs or future registrations.
10
+ *
11
+ * All helpers are SSR-safe (no-op / `null` without a `document`). For Server
12
+ * Components use the async `readReferralCodeCookie` from
13
+ * `@doswiftly/storefront-sdk/react/server` instead — the client helper is
14
+ * deliberately named differently (`get…`, synchronous) so the two cannot be
15
+ * confused in auto-imports.
16
+ */
17
+ export interface CaptureReferralCodeOptions {
18
+ /**
19
+ * URL (or search string) to inspect. Defaults to the current
20
+ * `window.location.href`.
21
+ */
22
+ url?: string | URL;
23
+ /** Query parameter carrying the code. Default `'ref'`. */
24
+ paramName?: string;
25
+ /**
26
+ * Cookie lifetime in seconds. Default {@link REFERRAL_COOKIE_MAX_AGE}
27
+ * (30 days) — the "landing → signup" window, independent of the shop's
28
+ * referral validity setting (see the constant's docs).
29
+ */
30
+ maxAge?: number;
31
+ }
32
+ /**
33
+ * Capture a referral code from the current URL (or an explicit one) into the
34
+ * `referral-code` cookie.
35
+ *
36
+ * Returns the captured code, or the previously stored one when the URL carries
37
+ * no code (so a visitor who navigated away from the landing URL still counts),
38
+ * or `null` when there is nothing to capture. A code present in the URL always
39
+ * wins over a previously stored one — the most recent referral link decides.
40
+ */
41
+ export declare function captureReferralCode(options?: CaptureReferralCodeOptions): string | null;
42
+ /**
43
+ * Read the stored referral code (client-side, synchronous). Returns `null`
44
+ * when absent or outside a browser environment. Server Components use the
45
+ * async `readReferralCodeCookie` from `@doswiftly/storefront-sdk/react/server`.
46
+ */
47
+ export declare function getReferralCodeCookie(): string | null;
48
+ /**
49
+ * Remove the stored referral code. Call after a successful signup so the code
50
+ * is not re-submitted by future registrations from the same browser.
51
+ */
52
+ export declare function clearReferralCodeCookie(): void;
53
+ //# sourceMappingURL=referral.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referral.d.ts","sourceRoot":"","sources":["../../src/react/referral.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,MAAM,GAAG,IAAI,CAU3F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAErD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Client-side referral capture helpers.
3
+ *
4
+ * Flow: a visitor lands through a referral share link (`/register?ref=CODE`),
5
+ * `captureReferralCode()` persists the code in the readable `referral-code`
6
+ * cookie, and the signup form reads it back with `getReferralCodeCookie()`
7
+ * and passes it as `customerSignup(input: { referralCode })`. After a
8
+ * successful signup call `clearReferralCodeCookie()` so the code is not
9
+ * re-submitted by other browser tabs or future registrations.
10
+ *
11
+ * All helpers are SSR-safe (no-op / `null` without a `document`). For Server
12
+ * Components use the async `readReferralCodeCookie` from
13
+ * `@doswiftly/storefront-sdk/react/server` instead — the client helper is
14
+ * deliberately named differently (`get…`, synchronous) so the two cannot be
15
+ * confused in auto-imports.
16
+ */
17
+ import { REFERRAL_COOKIE_NAME, REFERRAL_COOKIE_MAX_AGE, extractReferralCodeFromUrl, } from '../core/referral/cookie-config';
18
+ import { getCookie, setCookie, deleteCookie } from './cookies';
19
+ /**
20
+ * Capture a referral code from the current URL (or an explicit one) into the
21
+ * `referral-code` cookie.
22
+ *
23
+ * Returns the captured code, or the previously stored one when the URL carries
24
+ * no code (so a visitor who navigated away from the landing URL still counts),
25
+ * or `null` when there is nothing to capture. A code present in the URL always
26
+ * wins over a previously stored one — the most recent referral link decides.
27
+ */
28
+ export function captureReferralCode(options = {}) {
29
+ const url = options.url ?? (typeof window !== 'undefined' ? window.location.href : null);
30
+ const fromUrl = url ? extractReferralCodeFromUrl(url, options.paramName) : null;
31
+ if (fromUrl) {
32
+ setCookie(REFERRAL_COOKIE_NAME, fromUrl, { maxAge: options.maxAge ?? REFERRAL_COOKIE_MAX_AGE });
33
+ return fromUrl;
34
+ }
35
+ return getReferralCodeCookie();
36
+ }
37
+ /**
38
+ * Read the stored referral code (client-side, synchronous). Returns `null`
39
+ * when absent or outside a browser environment. Server Components use the
40
+ * async `readReferralCodeCookie` from `@doswiftly/storefront-sdk/react/server`.
41
+ */
42
+ export function getReferralCodeCookie() {
43
+ return getCookie(REFERRAL_COOKIE_NAME);
44
+ }
45
+ /**
46
+ * Remove the stored referral code. Call after a successful signup so the code
47
+ * is not re-submitted by future registrations from the same browser.
48
+ */
49
+ export function clearReferralCodeCookie() {
50
+ deleteCookie(REFERRAL_COOKIE_NAME);
51
+ }
@@ -46,4 +46,11 @@ export declare function readCartIdCookie(): Promise<string | null>;
46
46
  export declare function readCartCredentials(): Promise<CartCredentials | null>;
47
47
  /** Read the first-party `preferred-currency` cookie (server-first, client fallback). */
48
48
  export declare function readCurrencyCookie(): Promise<string | null>;
49
+ /**
50
+ * Read the first-party `referral-code` cookie (server-first, client fallback) —
51
+ * the referral code captured when the visitor landed through a referral share
52
+ * link. Useful for SSR-rendered signup forms that pre-fill the code field;
53
+ * pass it as `customerSignup(input: { referralCode })`.
54
+ */
55
+ export declare function readReferralCodeCookie(): Promise<string | null>;
49
56
  //# sourceMappingURL=cookie-readers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-readers.d.ts","sourceRoot":"","sources":["../../../src/react/server/cookie-readers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAqBvC,6EAA6E;AAC7E,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAE3E;AAED,wFAAwF;AACxF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEjE"}
1
+ {"version":3,"file":"cookie-readers.d.ts","sourceRoot":"","sources":["../../../src/react/server/cookie-readers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAsBvC,6EAA6E;AAC7E,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAE3E;AAED,wFAAwF;AACxF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEjE;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAErE"}