@doswiftly/storefront-sdk 4.4.0 → 4.5.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/dist/core/cart/types.d.ts +53 -20
- package/dist/core/cart/types.d.ts.map +1 -1
- package/dist/core/cart/types.js +3 -0
- package/dist/core/operations/cart.d.ts +15 -9
- package/dist/core/operations/cart.d.ts.map +1 -1
- package/dist/core/operations/cart.js +130 -58
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -4
- package/src/__tests__/contract/storefront-api.contract.test.ts +0 -450
- package/src/__tests__/unit/auth-client.test.ts +0 -210
- package/src/__tests__/unit/bot-protection.test.ts +0 -461
- package/src/__tests__/unit/cart-client.test.ts +0 -233
- package/src/__tests__/unit/cart-store.test.ts +0 -349
- package/src/__tests__/unit/create-client.test.ts +0 -356
- package/src/__tests__/unit/helpers.test.ts +0 -377
- package/src/__tests__/unit/middleware.test.ts +0 -374
- package/src/__tests__/unit/test-helpers.ts +0 -103
- package/src/core/auth/auth-client.ts +0 -123
- package/src/core/auth/cookie-config.ts +0 -23
- package/src/core/auth/handlers.ts +0 -168
- package/src/core/auth/routes.ts +0 -26
- package/src/core/auth/token-client.ts +0 -51
- package/src/core/auth/types.ts +0 -54
- package/src/core/bot-protection/abstract-manager.ts +0 -185
- package/src/core/bot-protection/create-manager.ts +0 -37
- package/src/core/bot-protection/eucaptcha-manager.ts +0 -88
- package/src/core/bot-protection/fallback-manager.ts +0 -43
- package/src/core/bot-protection/turnstile-manager.ts +0 -92
- package/src/core/bot-protection/types/eucaptcha.d.ts +0 -28
- package/src/core/bot-protection/types/turnstile.d.ts +0 -33
- package/src/core/cache.ts +0 -102
- package/src/core/cart/cart-client.ts +0 -150
- package/src/core/cart/cookie-config.ts +0 -13
- package/src/core/cart/types.ts +0 -104
- package/src/core/client/compose.ts +0 -15
- package/src/core/client/create-client.ts +0 -129
- package/src/core/client/dedupe.ts +0 -19
- package/src/core/client/execute.ts +0 -70
- package/src/core/client/hash.ts +0 -21
- package/src/core/client/operation-name.ts +0 -12
- package/src/core/client/types.ts +0 -171
- package/src/core/currency/cookie-config.ts +0 -13
- package/src/core/errors.ts +0 -67
- package/src/core/format.ts +0 -254
- package/src/core/helpers/assert-no-user-errors.ts +0 -21
- package/src/core/helpers/normalize-connection.ts +0 -48
- package/src/core/helpers/sanitize-html.ts +0 -42
- package/src/core/image.ts +0 -22
- package/src/core/index.ts +0 -174
- package/src/core/language/cookie-config.ts +0 -13
- package/src/core/middleware/auth.ts +0 -27
- package/src/core/middleware/bot-protection.ts +0 -140
- package/src/core/middleware/currency.ts +0 -27
- package/src/core/middleware/errors.ts +0 -86
- package/src/core/middleware/language.ts +0 -30
- package/src/core/middleware/retry.ts +0 -75
- package/src/core/middleware/timeout.ts +0 -61
- package/src/core/operations/auth.ts +0 -123
- package/src/core/operations/cart.ts +0 -185
- package/src/index.ts +0 -25
- package/src/react/bot-protection/bot-protection-context.ts +0 -17
- package/src/react/bot-protection/bot-protection-widget.tsx +0 -46
- package/src/react/cookies.ts +0 -89
- package/src/react/helpers/create-store-context.ts +0 -56
- package/src/react/hooks/use-auth.ts +0 -218
- package/src/react/hooks/use-bot-protection.ts +0 -31
- package/src/react/hooks/use-cart-manager.ts +0 -236
- package/src/react/hooks/use-currency.ts +0 -23
- package/src/react/hooks/use-debounced-value.ts +0 -30
- package/src/react/hooks/use-hydrated.ts +0 -20
- package/src/react/hooks/use-storefront-client.ts +0 -12
- package/src/react/index.ts +0 -71
- package/src/react/providers/currency-provider.tsx +0 -30
- package/src/react/providers/language-provider.tsx +0 -34
- package/src/react/providers/storefront-client-provider.tsx +0 -107
- package/src/react/providers/storefront-provider.tsx +0 -99
- package/src/react/server/get-storefront-client.ts +0 -60
- package/src/react/server/index.ts +0 -1
- package/src/react/stores/auth.store.ts +0 -112
- package/src/react/stores/cart.context.ts +0 -10
- package/src/react/stores/cart.store.ts +0 -254
- package/src/react/stores/currency.store.ts +0 -93
- package/src/react/stores/index.ts +0 -17
- package/src/react/stores/language.store.ts +0 -90
- package/src/react/stores/store-context.tsx +0 -103
- package/src/react/types/shop-config.ts +0 -22
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -14
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timeout middleware — aborts request after timeout via AbortController.
|
|
3
|
-
*
|
|
4
|
-
* Edge-safe (no Node.js APIs). Default: 5 seconds.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { timeoutMiddleware } from '@doswiftly/storefront-sdk';
|
|
9
|
-
*
|
|
10
|
-
* client.use(timeoutMiddleware({ timeout: 10000 })); // 10s
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { Middleware } from '../client/types';
|
|
15
|
-
import { StorefrontError, ErrorCodes } from '../errors';
|
|
16
|
-
|
|
17
|
-
export interface TimeoutOptions {
|
|
18
|
-
/** Timeout in milliseconds (default: 5000) */
|
|
19
|
-
timeout?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function timeoutMiddleware(options: TimeoutOptions = {}): Middleware {
|
|
23
|
-
const { timeout = 5000 } = options;
|
|
24
|
-
|
|
25
|
-
return async (request, next) => {
|
|
26
|
-
const controller = new AbortController();
|
|
27
|
-
|
|
28
|
-
// If request already has a signal, link them
|
|
29
|
-
if (request.signal) {
|
|
30
|
-
if (request.signal.aborted) {
|
|
31
|
-
controller.abort(request.signal.reason);
|
|
32
|
-
} else {
|
|
33
|
-
request.signal.addEventListener('abort', () => {
|
|
34
|
-
controller.abort(request.signal!.reason);
|
|
35
|
-
}, { once: true });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
request.signal = controller.signal;
|
|
40
|
-
|
|
41
|
-
const timer = setTimeout(() => {
|
|
42
|
-
controller.abort(new Error(`Request timed out after ${timeout}ms`));
|
|
43
|
-
}, timeout);
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const response = await next(request);
|
|
47
|
-
return response;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
if (controller.signal.aborted) {
|
|
50
|
-
throw new StorefrontError({
|
|
51
|
-
code: ErrorCodes.TIMEOUT,
|
|
52
|
-
message: `Request timed out after ${timeout}ms`,
|
|
53
|
-
cause: error,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
throw error;
|
|
57
|
-
} finally {
|
|
58
|
-
clearTimeout(timer);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth/Customer GraphQL operations — manual query strings (no codegen).
|
|
3
|
-
*
|
|
4
|
-
* These match the backend storefront-graphql schema exactly.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Fragments
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
const CUSTOMER_ACCESS_TOKEN_FRAGMENT = `
|
|
12
|
-
fragment CustomerAccessTokenFields on CustomerAccessToken {
|
|
13
|
-
accessToken
|
|
14
|
-
expiresAt
|
|
15
|
-
}
|
|
16
|
-
`;
|
|
17
|
-
|
|
18
|
-
const USER_ERROR_FRAGMENT = `
|
|
19
|
-
fragment UserErrorFields on UserError {
|
|
20
|
-
message
|
|
21
|
-
field
|
|
22
|
-
code
|
|
23
|
-
}
|
|
24
|
-
`;
|
|
25
|
-
|
|
26
|
-
const CUSTOMER_FRAGMENT = `
|
|
27
|
-
fragment CustomerFields on Customer {
|
|
28
|
-
id
|
|
29
|
-
email
|
|
30
|
-
firstName
|
|
31
|
-
lastName
|
|
32
|
-
displayName
|
|
33
|
-
phone
|
|
34
|
-
emailVerified
|
|
35
|
-
emailMarketingState
|
|
36
|
-
defaultAddress {
|
|
37
|
-
id
|
|
38
|
-
address1
|
|
39
|
-
address2
|
|
40
|
-
city
|
|
41
|
-
company
|
|
42
|
-
country
|
|
43
|
-
countryCode
|
|
44
|
-
firstName
|
|
45
|
-
lastName
|
|
46
|
-
phone
|
|
47
|
-
province
|
|
48
|
-
provinceCode
|
|
49
|
-
zip
|
|
50
|
-
isDefault
|
|
51
|
-
}
|
|
52
|
-
ordersCount
|
|
53
|
-
totalSpent {
|
|
54
|
-
amount
|
|
55
|
-
currencyCode
|
|
56
|
-
}
|
|
57
|
-
createdAt
|
|
58
|
-
updatedAt
|
|
59
|
-
}
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Mutations
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
export const CUSTOMER_LOGIN = `
|
|
67
|
-
mutation CustomerLogin($input: CustomerAccessTokenCreateInput!) {
|
|
68
|
-
customerAccessTokenCreate(input: $input) {
|
|
69
|
-
customerAccessToken { ...CustomerAccessTokenFields }
|
|
70
|
-
userErrors { ...UserErrorFields }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
${CUSTOMER_ACCESS_TOKEN_FRAGMENT}
|
|
74
|
-
${USER_ERROR_FRAGMENT}
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
export const CUSTOMER_LOGOUT = `
|
|
78
|
-
mutation CustomerLogout($customerAccessToken: String!) {
|
|
79
|
-
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
|
|
80
|
-
deletedAccessToken
|
|
81
|
-
deletedCustomerAccessTokenId
|
|
82
|
-
userErrors { ...UserErrorFields }
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
${USER_ERROR_FRAGMENT}
|
|
86
|
-
`;
|
|
87
|
-
|
|
88
|
-
export const CUSTOMER_TOKEN_RENEW = `
|
|
89
|
-
mutation CustomerTokenRenew($customerAccessToken: String!) {
|
|
90
|
-
customerAccessTokenRenew(customerAccessToken: $customerAccessToken) {
|
|
91
|
-
customerAccessToken { ...CustomerAccessTokenFields }
|
|
92
|
-
userErrors { ...UserErrorFields }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
${CUSTOMER_ACCESS_TOKEN_FRAGMENT}
|
|
96
|
-
${USER_ERROR_FRAGMENT}
|
|
97
|
-
`;
|
|
98
|
-
|
|
99
|
-
export const CUSTOMER_CREATE = `
|
|
100
|
-
mutation CustomerCreate($input: CustomerCreateInput!) {
|
|
101
|
-
customerCreate(input: $input) {
|
|
102
|
-
customer { ...CustomerFields }
|
|
103
|
-
customerAccessToken { ...CustomerAccessTokenFields }
|
|
104
|
-
userErrors { ...UserErrorFields }
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
${CUSTOMER_FRAGMENT}
|
|
108
|
-
${CUSTOMER_ACCESS_TOKEN_FRAGMENT}
|
|
109
|
-
${USER_ERROR_FRAGMENT}
|
|
110
|
-
`;
|
|
111
|
-
|
|
112
|
-
// ---------------------------------------------------------------------------
|
|
113
|
-
// Queries
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
|
|
116
|
-
export const CUSTOMER_QUERY = `
|
|
117
|
-
query Customer($customerAccessToken: String!) {
|
|
118
|
-
customer(customerAccessToken: $customerAccessToken) {
|
|
119
|
-
...CustomerFields
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
${CUSTOMER_FRAGMENT}
|
|
123
|
-
`;
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cart GraphQL operations — manual query strings (no codegen).
|
|
3
|
-
*
|
|
4
|
-
* These match the backend storefront-graphql schema exactly.
|
|
5
|
-
* Cart mutations always return full Cart + userErrors.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Fragments
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
const MONEY_FRAGMENT = `
|
|
13
|
-
fragment Money on Money {
|
|
14
|
-
amount
|
|
15
|
-
currencyCode
|
|
16
|
-
}
|
|
17
|
-
`;
|
|
18
|
-
|
|
19
|
-
const CART_COST_FRAGMENT = `
|
|
20
|
-
fragment CartCost on CartCost {
|
|
21
|
-
totalAmount { ...Money }
|
|
22
|
-
subtotalAmount { ...Money }
|
|
23
|
-
totalTaxAmount { ...Money }
|
|
24
|
-
totalDutyAmount { ...Money }
|
|
25
|
-
}
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
const CART_LINE_FRAGMENT = `
|
|
29
|
-
fragment CartLineFields on CartLine {
|
|
30
|
-
id
|
|
31
|
-
quantity
|
|
32
|
-
merchandise {
|
|
33
|
-
id
|
|
34
|
-
title
|
|
35
|
-
sku
|
|
36
|
-
image { url altText }
|
|
37
|
-
price { ...Money }
|
|
38
|
-
compareAtPrice { ...Money }
|
|
39
|
-
}
|
|
40
|
-
cost {
|
|
41
|
-
totalAmount { ...Money }
|
|
42
|
-
amountPerQuantity { ...Money }
|
|
43
|
-
compareAtAmountPerQuantity { ...Money }
|
|
44
|
-
}
|
|
45
|
-
attributes { key value }
|
|
46
|
-
productId
|
|
47
|
-
productTitle
|
|
48
|
-
productHandle
|
|
49
|
-
}
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
const CART_FRAGMENT = `
|
|
53
|
-
fragment CartFields on Cart {
|
|
54
|
-
id
|
|
55
|
-
checkoutUrl
|
|
56
|
-
totalQuantity
|
|
57
|
-
note
|
|
58
|
-
createdAt
|
|
59
|
-
updatedAt
|
|
60
|
-
cost { ...CartCost }
|
|
61
|
-
lines(first: 100) {
|
|
62
|
-
edges {
|
|
63
|
-
node { ...CartLineFields }
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
buyerIdentity {
|
|
67
|
-
email
|
|
68
|
-
phone
|
|
69
|
-
countryCode
|
|
70
|
-
}
|
|
71
|
-
discountCodes {
|
|
72
|
-
code
|
|
73
|
-
applicable
|
|
74
|
-
}
|
|
75
|
-
discountAllocations {
|
|
76
|
-
discountedAmount { ...Money }
|
|
77
|
-
}
|
|
78
|
-
attributes { key value }
|
|
79
|
-
}
|
|
80
|
-
${MONEY_FRAGMENT}
|
|
81
|
-
${CART_COST_FRAGMENT}
|
|
82
|
-
${CART_LINE_FRAGMENT}
|
|
83
|
-
`;
|
|
84
|
-
|
|
85
|
-
const USER_ERROR_FRAGMENT = `
|
|
86
|
-
fragment UserErrorFields on UserError {
|
|
87
|
-
message
|
|
88
|
-
field
|
|
89
|
-
code
|
|
90
|
-
}
|
|
91
|
-
`;
|
|
92
|
-
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
// Queries
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
|
|
97
|
-
export const CART_QUERY = `
|
|
98
|
-
query Cart($id: ID!) {
|
|
99
|
-
cart(id: $id) {
|
|
100
|
-
...CartFields
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
${CART_FRAGMENT}
|
|
104
|
-
`;
|
|
105
|
-
|
|
106
|
-
// ---------------------------------------------------------------------------
|
|
107
|
-
// Mutations
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
|
|
110
|
-
export const CART_CREATE = `
|
|
111
|
-
mutation CartCreate($input: CartCreateInput) {
|
|
112
|
-
cartCreate(input: $input) {
|
|
113
|
-
cart { ...CartFields }
|
|
114
|
-
userErrors { ...UserErrorFields }
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
${CART_FRAGMENT}
|
|
118
|
-
${USER_ERROR_FRAGMENT}
|
|
119
|
-
`;
|
|
120
|
-
|
|
121
|
-
export const CART_LINES_ADD = `
|
|
122
|
-
mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
123
|
-
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
124
|
-
cart { ...CartFields }
|
|
125
|
-
userErrors { ...UserErrorFields }
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
${CART_FRAGMENT}
|
|
129
|
-
${USER_ERROR_FRAGMENT}
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
export const CART_LINES_UPDATE = `
|
|
133
|
-
mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
134
|
-
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
135
|
-
cart { ...CartFields }
|
|
136
|
-
userErrors { ...UserErrorFields }
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
${CART_FRAGMENT}
|
|
140
|
-
${USER_ERROR_FRAGMENT}
|
|
141
|
-
`;
|
|
142
|
-
|
|
143
|
-
export const CART_LINES_REMOVE = `
|
|
144
|
-
mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
|
145
|
-
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
146
|
-
cart { ...CartFields }
|
|
147
|
-
userErrors { ...UserErrorFields }
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
${CART_FRAGMENT}
|
|
151
|
-
${USER_ERROR_FRAGMENT}
|
|
152
|
-
`;
|
|
153
|
-
|
|
154
|
-
export const CART_DISCOUNT_CODES_UPDATE = `
|
|
155
|
-
mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]!) {
|
|
156
|
-
cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
|
|
157
|
-
cart { ...CartFields }
|
|
158
|
-
userErrors { ...UserErrorFields }
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
${CART_FRAGMENT}
|
|
162
|
-
${USER_ERROR_FRAGMENT}
|
|
163
|
-
`;
|
|
164
|
-
|
|
165
|
-
export const CART_NOTE_UPDATE = `
|
|
166
|
-
mutation CartNoteUpdate($cartId: ID!, $note: String!) {
|
|
167
|
-
cartNoteUpdate(cartId: $cartId, note: $note) {
|
|
168
|
-
cart { ...CartFields }
|
|
169
|
-
userErrors { ...UserErrorFields }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
${CART_FRAGMENT}
|
|
173
|
-
${USER_ERROR_FRAGMENT}
|
|
174
|
-
`;
|
|
175
|
-
|
|
176
|
-
export const CART_BUYER_IDENTITY_UPDATE = `
|
|
177
|
-
mutation CartBuyerIdentityUpdate($cartId: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
|
|
178
|
-
cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {
|
|
179
|
-
cart { ...CartFields }
|
|
180
|
-
userErrors { ...UserErrorFields }
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
${CART_FRAGMENT}
|
|
184
|
-
${USER_ERROR_FRAGMENT}
|
|
185
|
-
`;
|
package/src/index.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @doswiftly/storefront-sdk v4.0.0
|
|
3
|
-
*
|
|
4
|
-
* Layered SDK — Hydrogen-aligned architecture.
|
|
5
|
-
*
|
|
6
|
-
* Entry points:
|
|
7
|
-
* "." → Core (framework-agnostic, 0 deps)
|
|
8
|
-
* "./react" → React adapter (providers, stores, hooks)
|
|
9
|
-
* "./react/stores" → Zustand stores (direct import)
|
|
10
|
-
* "./react/server" → Server-side client factory
|
|
11
|
-
* "./cache" → Cache strategies
|
|
12
|
-
*
|
|
13
|
-
* @example Core (CLI, Edge, Remix, Astro — no React)
|
|
14
|
-
* ```typescript
|
|
15
|
-
* import { createStorefrontClient, CartClient, errorMiddleware } from '@doswiftly/storefront-sdk';
|
|
16
|
-
* ```
|
|
17
|
-
*
|
|
18
|
-
* @example React
|
|
19
|
-
* ```tsx
|
|
20
|
-
* import { StorefrontProvider, useAuth, useCartManager } from '@doswiftly/storefront-sdk/react';
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
// Re-export everything from core
|
|
25
|
-
export * from './core';
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bot protection React context.
|
|
3
|
-
*
|
|
4
|
-
* Provides access to the BotProtectionTokenProvider manager
|
|
5
|
-
* for the useBotProtection() escape hatch hook.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import { createContext } from 'react';
|
|
11
|
-
import type { BotProtectionTokenProvider } from '../../core/middleware/bot-protection';
|
|
12
|
-
|
|
13
|
-
export interface BotProtectionContextValue {
|
|
14
|
-
manager: BotProtectionTokenProvider | null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const BotProtectionContext = createContext<BotProtectionContextValue | null>(null);
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BotProtectionWidget — invisible React wrapper that mounts the bot protection manager.
|
|
3
|
-
*
|
|
4
|
-
* Renders a hidden div and mounts/unmounts the manager lifecycle.
|
|
5
|
-
* Triggers lazy script preload on mount.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import React, { useEffect, useRef } from 'react';
|
|
11
|
-
import type { BotProtectionTokenProvider } from '../../core/middleware/bot-protection';
|
|
12
|
-
import type { AbstractBotProtectionManager } from '../../core/bot-protection/abstract-manager';
|
|
13
|
-
|
|
14
|
-
interface BotProtectionWidgetProps {
|
|
15
|
-
manager: BotProtectionTokenProvider | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function BotProtectionWidget({ manager }: BotProtectionWidgetProps) {
|
|
19
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
20
|
-
const mountedRef = useRef(false);
|
|
21
|
-
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
if (!manager || !containerRef.current || mountedRef.current) return;
|
|
24
|
-
|
|
25
|
-
mountedRef.current = true;
|
|
26
|
-
|
|
27
|
-
// Trigger lazy script preload + mount container
|
|
28
|
-
const mgr = manager as AbstractBotProtectionManager;
|
|
29
|
-
if (typeof mgr.loadScript === 'function') {
|
|
30
|
-
mgr.loadScript().catch(() => {
|
|
31
|
-
// Script load failure is handled gracefully in execute()
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
if (typeof mgr.mount === 'function') {
|
|
35
|
-
mgr.mount(containerRef.current);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return () => {
|
|
39
|
-
mountedRef.current = false;
|
|
40
|
-
};
|
|
41
|
-
}, [manager]);
|
|
42
|
-
|
|
43
|
-
if (!manager) return null;
|
|
44
|
-
|
|
45
|
-
return <div ref={containerRef} style={{ display: 'none' }} aria-hidden="true" />;
|
|
46
|
-
}
|
package/src/react/cookies.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cookie utilities for SDK consumers.
|
|
3
|
-
*
|
|
4
|
-
* Simple, framework-agnostic cookie read/write for client-side.
|
|
5
|
-
* For Next.js server-side cookies, use `cookies()` from next/headers.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get cookie value by name (client-side only).
|
|
10
|
-
*/
|
|
11
|
-
export function getCookie(name: string): string | null {
|
|
12
|
-
if (typeof document === 'undefined') return null;
|
|
13
|
-
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
|
|
14
|
-
return match ? decodeURIComponent(match[1]) : null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Set cookie (client-side only).
|
|
19
|
-
*
|
|
20
|
-
* `secure` defaults to auto-detect from `location.protocol` (true on HTTPS).
|
|
21
|
-
*/
|
|
22
|
-
export function setCookie(
|
|
23
|
-
name: string,
|
|
24
|
-
value: string,
|
|
25
|
-
options: { maxAge?: number; path?: string; sameSite?: string; secure?: boolean } = {},
|
|
26
|
-
): void {
|
|
27
|
-
if (typeof document === 'undefined') return;
|
|
28
|
-
const { maxAge = 365 * 24 * 60 * 60, path = '/', sameSite = 'lax' } = options;
|
|
29
|
-
const secure = options.secure ??
|
|
30
|
-
(typeof location !== 'undefined' && location.protocol === 'https:');
|
|
31
|
-
const securePart = secure ? ';secure' : '';
|
|
32
|
-
document.cookie = `${name}=${encodeURIComponent(value)};max-age=${maxAge};path=${path};samesite=${sameSite}${securePart}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Delete cookie (client-side only).
|
|
37
|
-
*/
|
|
38
|
-
export function deleteCookie(name: string, path = '/'): void {
|
|
39
|
-
if (typeof document === 'undefined') return;
|
|
40
|
-
document.cookie = `${name}=;max-age=0;path=${path}`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
import { CURRENCY_COOKIE_NAME } from '../core/currency/cookie-config';
|
|
44
|
-
import { CART_COOKIE_NAME } from '../core/cart/cookie-config';
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get preferred currency from cookie (async — works with Next.js cookies()).
|
|
48
|
-
* Falls back to document.cookie on client.
|
|
49
|
-
*/
|
|
50
|
-
export async function getCurrencyFromCookieAsync(): Promise<string | null> {
|
|
51
|
-
// Server-side: try Next.js cookies()
|
|
52
|
-
try {
|
|
53
|
-
const { cookies } = await import('next/headers');
|
|
54
|
-
const cookieStore = await cookies();
|
|
55
|
-
return cookieStore.get(CURRENCY_COOKIE_NAME)?.value ?? null;
|
|
56
|
-
} catch {
|
|
57
|
-
// Not in a server context or next not available
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Client-side fallback
|
|
61
|
-
return getCookie(CURRENCY_COOKIE_NAME);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get cart ID from cookie (async — works with Next.js cookies()).
|
|
66
|
-
* Falls back to document.cookie on client.
|
|
67
|
-
*
|
|
68
|
-
* Use in Server Components for SSR cart badge:
|
|
69
|
-
* ```typescript
|
|
70
|
-
* const cartId = await getCartIdFromCookieAsync();
|
|
71
|
-
* if (cartId) {
|
|
72
|
-
* const cart = await fetchCart(cartId);
|
|
73
|
-
* // Render cart badge with real totalQuantity — no skeleton needed
|
|
74
|
-
* }
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export async function getCartIdFromCookieAsync(): Promise<string | null> {
|
|
78
|
-
// Server-side: try Next.js cookies()
|
|
79
|
-
try {
|
|
80
|
-
const { cookies } = await import('next/headers');
|
|
81
|
-
const cookieStore = await cookies();
|
|
82
|
-
return cookieStore.get(CART_COOKIE_NAME)?.value ?? null;
|
|
83
|
-
} catch {
|
|
84
|
-
// Not in a server context or next not available
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Client-side fallback
|
|
88
|
-
return getCookie(CART_COOKIE_NAME);
|
|
89
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext, createElement, type ReactNode } from 'react';
|
|
4
|
-
import { useStore, type StoreApi } from 'zustand';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Create a Context-based Zustand store pattern.
|
|
8
|
-
*
|
|
9
|
-
* Returns Provider, useStore hook, and useApi hook — eliminates
|
|
10
|
-
* module-level singleton boilerplate (Turbopack duplication bug).
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* const { Provider: CartProvider, useStore: useCartStore, useApi: useCartStoreApi } =
|
|
15
|
-
* createStoreContext<CartState>('CartStore');
|
|
16
|
-
*
|
|
17
|
-
* // In layout:
|
|
18
|
-
* const cartStore = useRef(createCartStore());
|
|
19
|
-
* <CartProvider store={cartStore.current}>{children}</CartProvider>
|
|
20
|
-
*
|
|
21
|
-
* // In components:
|
|
22
|
-
* const isOpen = useCartStore((s) => s.isOpen);
|
|
23
|
-
* const api = useCartStoreApi(); // for .getState() in callbacks
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export function createStoreContext<T>(displayName: string) {
|
|
27
|
-
const StoreContext = createContext<StoreApi<T> | null>(null);
|
|
28
|
-
StoreContext.displayName = displayName;
|
|
29
|
-
|
|
30
|
-
function Provider({ store, children }: { store: StoreApi<T>; children: ReactNode }) {
|
|
31
|
-
return createElement(StoreContext.Provider, { value: store }, children);
|
|
32
|
-
}
|
|
33
|
-
Provider.displayName = `${displayName}Provider`;
|
|
34
|
-
|
|
35
|
-
// Overloads: no selector → full state, with selector → selected slice
|
|
36
|
-
function useStoreHook(): T;
|
|
37
|
-
function useStoreHook<U>(selector: (s: T) => U): U;
|
|
38
|
-
function useStoreHook<U>(selector?: (s: T) => U) {
|
|
39
|
-
const store = useContext(StoreContext);
|
|
40
|
-
if (!store) {
|
|
41
|
-
throw new Error(`${displayName}: useStore must be used within its Provider`);
|
|
42
|
-
}
|
|
43
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
44
|
-
return selector ? useStore(store, selector) : useStore(store);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function useApi(): StoreApi<T> {
|
|
48
|
-
const store = useContext(StoreContext);
|
|
49
|
-
if (!store) {
|
|
50
|
-
throw new Error(`${displayName}: useApi must be used within its Provider`);
|
|
51
|
-
}
|
|
52
|
-
return store;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return { Provider, useStore: useStoreHook, useApi } as const;
|
|
56
|
-
}
|