@tagadapay/plugin-sdk 2.8.10 → 3.0.2

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 (51) hide show
  1. package/README.md +14 -14
  2. package/dist/index.js +1 -1
  3. package/dist/react/hooks/usePluginConfig.d.ts +1 -0
  4. package/dist/react/hooks/usePluginConfig.js +69 -18
  5. package/dist/react/providers/TagadaProvider.js +1 -4
  6. package/dist/v2/core/client.d.ts +18 -0
  7. package/dist/v2/core/client.js +45 -0
  8. package/dist/v2/core/config/environment.d.ts +8 -0
  9. package/dist/v2/core/config/environment.js +18 -0
  10. package/dist/v2/core/funnelClient.d.ts +84 -0
  11. package/dist/v2/core/funnelClient.js +252 -0
  12. package/dist/v2/core/index.d.ts +2 -0
  13. package/dist/v2/core/index.js +3 -0
  14. package/dist/v2/core/resources/apiClient.js +1 -1
  15. package/dist/v2/core/resources/funnel.d.ts +1 -0
  16. package/dist/v2/core/resources/offers.d.ts +182 -8
  17. package/dist/v2/core/resources/offers.js +25 -0
  18. package/dist/v2/core/resources/products.d.ts +5 -0
  19. package/dist/v2/core/resources/products.js +15 -1
  20. package/dist/v2/core/types.d.ts +1 -0
  21. package/dist/v2/core/utils/funnelQueryKeys.d.ts +23 -0
  22. package/dist/v2/core/utils/funnelQueryKeys.js +23 -0
  23. package/dist/v2/core/utils/index.d.ts +2 -0
  24. package/dist/v2/core/utils/index.js +2 -0
  25. package/dist/v2/core/utils/pluginConfig.js +44 -32
  26. package/dist/v2/core/utils/sessionStorage.d.ts +20 -0
  27. package/dist/v2/core/utils/sessionStorage.js +39 -0
  28. package/dist/v2/index.d.ts +4 -2
  29. package/dist/v2/index.js +1 -1
  30. package/dist/v2/react/components/DebugDrawer.js +99 -2
  31. package/dist/v2/react/hooks/__examples__/FunnelContextExample.d.ts +3 -0
  32. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +4 -3
  33. package/dist/v2/react/hooks/useClubOffers.d.ts +2 -2
  34. package/dist/v2/react/hooks/useFunnel.d.ts +27 -39
  35. package/dist/v2/react/hooks/useFunnel.js +22 -659
  36. package/dist/v2/react/hooks/useFunnelLegacy.d.ts +52 -0
  37. package/dist/v2/react/hooks/useFunnelLegacy.js +733 -0
  38. package/dist/v2/react/hooks/useOfferQuery.d.ts +109 -0
  39. package/dist/v2/react/hooks/useOfferQuery.js +483 -0
  40. package/dist/v2/react/hooks/useOffersQuery.d.ts +9 -75
  41. package/dist/v2/react/hooks/useProductsQuery.d.ts +1 -0
  42. package/dist/v2/react/hooks/useProductsQuery.js +10 -6
  43. package/dist/v2/react/index.d.ts +8 -4
  44. package/dist/v2/react/index.js +4 -2
  45. package/dist/v2/react/providers/TagadaProvider.d.ts +66 -5
  46. package/dist/v2/react/providers/TagadaProvider.js +120 -6
  47. package/dist/v2/standalone/index.d.ts +20 -0
  48. package/dist/v2/standalone/index.js +22 -0
  49. package/dist/v2/vue/index.d.ts +6 -0
  50. package/dist/v2/vue/index.js +10 -0
  51. package/package.json +6 -1
@@ -0,0 +1,252 @@
1
+ import { FunnelResource } from './resources/funnel';
2
+ import { EventDispatcher } from './utils/eventDispatcher';
3
+ import { getFunnelSessionCookie, setFunnelSessionCookie } from './utils/sessionStorage';
4
+ import { detectEnvironment } from './config/environment';
5
+ export class FunnelClient {
6
+ constructor(config) {
7
+ this.eventDispatcher = new EventDispatcher();
8
+ // Guards
9
+ this.isInitializing = false;
10
+ this.initializationAttempted = false;
11
+ this.config = config;
12
+ this.resource = new FunnelResource(config.apiClient);
13
+ this.state = {
14
+ context: null,
15
+ isLoading: false,
16
+ isInitialized: false,
17
+ isNavigating: false,
18
+ error: null,
19
+ sessionError: null,
20
+ };
21
+ }
22
+ /**
23
+ * Update configuration (e.g. when plugin config loads)
24
+ */
25
+ setConfig(config) {
26
+ this.config = { ...this.config, ...config };
27
+ }
28
+ /**
29
+ * Subscribe to state changes
30
+ */
31
+ subscribe(listener) {
32
+ return this.eventDispatcher.subscribe(listener);
33
+ }
34
+ /**
35
+ * Get current state
36
+ */
37
+ getState() {
38
+ return this.state;
39
+ }
40
+ /**
41
+ * Initialize session with automatic detection (cookies, URL, etc.)
42
+ */
43
+ async autoInitialize(authSession, store, funnelId) {
44
+ if (this.state.context)
45
+ return this.state.context;
46
+ if (this.isInitializing)
47
+ return null;
48
+ if (this.initializationAttempted)
49
+ return null;
50
+ this.initializationAttempted = true;
51
+ this.isInitializing = true;
52
+ this.updateState({ isLoading: true, error: null });
53
+ try {
54
+ // URL params
55
+ const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
56
+ const urlFunnelId = params.get('funnelId');
57
+ const effectiveFunnelId = urlFunnelId || funnelId;
58
+ let existingSessionId = params.get('funnelSessionId');
59
+ // Cookie fallback
60
+ if (!existingSessionId) {
61
+ existingSessionId = getFunnelSessionCookie() || null;
62
+ }
63
+ if (this.config.debugMode) {
64
+ console.log('🚀 [FunnelClient] Auto-initializing...', { existingSessionId, effectiveFunnelId });
65
+ }
66
+ // Note: We proceed even without funnelId/sessionId - the backend will create a new anonymous session if needed
67
+ const response = await this.resource.initialize({
68
+ cmsSession: {
69
+ customerId: authSession.customerId,
70
+ sessionId: authSession.sessionId,
71
+ storeId: store.id,
72
+ accountId: store.accountId,
73
+ },
74
+ funnelId: effectiveFunnelId,
75
+ existingSessionId: existingSessionId || undefined,
76
+ currentUrl: typeof window !== 'undefined' ? window.location.href : undefined,
77
+ });
78
+ if (response.success && response.context) {
79
+ const enriched = this.enrichContext(response.context);
80
+ this.handleSessionSuccess(enriched);
81
+ return enriched;
82
+ }
83
+ else {
84
+ throw new Error(response.error || 'Failed to initialize funnel session');
85
+ }
86
+ }
87
+ catch (error) {
88
+ const err = error instanceof Error ? error : new Error(String(error));
89
+ this.updateState({ error: err, isLoading: false });
90
+ if (this.config.debugMode) {
91
+ console.error('❌ [FunnelClient] Init failed:', err);
92
+ }
93
+ throw err;
94
+ }
95
+ finally {
96
+ this.isInitializing = false;
97
+ }
98
+ }
99
+ /**
100
+ * Manual initialization
101
+ */
102
+ async initialize(authSession, store, funnelId, entryStepId) {
103
+ this.updateState({ isLoading: true, error: null });
104
+ try {
105
+ const response = await this.resource.initialize({
106
+ cmsSession: {
107
+ customerId: authSession.customerId,
108
+ sessionId: authSession.sessionId,
109
+ storeId: store.id,
110
+ accountId: store.accountId,
111
+ },
112
+ funnelId: funnelId,
113
+ entryStepId,
114
+ currentUrl: typeof window !== 'undefined' ? window.location.href : undefined,
115
+ });
116
+ if (response.success && response.context) {
117
+ const enriched = this.enrichContext(response.context);
118
+ this.handleSessionSuccess(enriched);
119
+ return enriched;
120
+ }
121
+ else {
122
+ throw new Error(response.error || 'Failed to initialize');
123
+ }
124
+ }
125
+ catch (error) {
126
+ const err = error instanceof Error ? error : new Error(String(error));
127
+ this.updateState({ error: err, isLoading: false });
128
+ throw err;
129
+ }
130
+ }
131
+ /**
132
+ * Navigate
133
+ */
134
+ async navigate(event) {
135
+ if (!this.state.context?.sessionId)
136
+ throw new Error('No active session');
137
+ this.updateState({ isNavigating: true, isLoading: true });
138
+ try {
139
+ const response = await this.resource.navigate({
140
+ sessionId: this.state.context.sessionId,
141
+ event
142
+ });
143
+ if (response.success && response.result) {
144
+ // Refresh session to get updated context
145
+ await this.refreshSession();
146
+ this.updateState({ isNavigating: false, isLoading: false });
147
+ const result = response.result;
148
+ // Auto-redirect if enabled (default: true) and result has a URL
149
+ const shouldAutoRedirect = this.config.autoRedirect !== false; // Default to true
150
+ if (shouldAutoRedirect && result?.url && typeof window !== 'undefined') {
151
+ if (this.config.debugMode) {
152
+ console.log('🚀 [FunnelClient] Auto-redirecting to:', result.url);
153
+ }
154
+ window.location.href = result.url;
155
+ }
156
+ return result;
157
+ }
158
+ throw new Error(response.error || 'Navigation failed');
159
+ }
160
+ catch (error) {
161
+ const err = error instanceof Error ? error : new Error(String(error));
162
+ this.updateState({ error: err, isNavigating: false, isLoading: false });
163
+ throw err;
164
+ }
165
+ }
166
+ /**
167
+ * Refresh session data
168
+ */
169
+ async refreshSession() {
170
+ if (!this.state.context?.sessionId)
171
+ return;
172
+ try {
173
+ const response = await this.resource.getSession(this.state.context.sessionId);
174
+ if (response.success && response.context) {
175
+ const enriched = this.enrichContext(response.context);
176
+ this.updateState({ context: enriched, sessionError: null });
177
+ return enriched;
178
+ }
179
+ }
180
+ catch (error) {
181
+ this.updateState({ sessionError: error instanceof Error ? error : new Error(String(error)) });
182
+ }
183
+ }
184
+ /**
185
+ * Update context data
186
+ */
187
+ async updateContext(updates) {
188
+ if (!this.state.context?.sessionId)
189
+ throw new Error('No active session');
190
+ this.updateState({ isLoading: true });
191
+ try {
192
+ const response = await this.resource.updateContext(this.state.context.sessionId, { contextUpdates: updates });
193
+ if (response.success) {
194
+ await this.refreshSession();
195
+ }
196
+ else {
197
+ throw new Error(response.error || 'Failed to update context');
198
+ }
199
+ }
200
+ finally {
201
+ this.updateState({ isLoading: false });
202
+ }
203
+ }
204
+ /**
205
+ * End session
206
+ */
207
+ async endSession() {
208
+ if (!this.state.context?.sessionId)
209
+ return;
210
+ try {
211
+ await this.resource.endSession(this.state.context.sessionId);
212
+ }
213
+ finally {
214
+ this.state.context = null;
215
+ this.updateState({ context: null, isInitialized: false });
216
+ if (typeof document !== 'undefined') {
217
+ // Clear cookie via import or manually if needed, but we have utility for that
218
+ // We should probably import clearFunnelSessionCookie
219
+ }
220
+ }
221
+ }
222
+ // Private helpers
223
+ updateState(updates) {
224
+ this.state = { ...this.state, ...updates };
225
+ this.eventDispatcher.notify(this.state);
226
+ }
227
+ handleSessionSuccess(context) {
228
+ setFunnelSessionCookie(context.sessionId);
229
+ this.updateState({
230
+ context,
231
+ isLoading: false,
232
+ isInitialized: true,
233
+ error: null,
234
+ sessionError: null
235
+ });
236
+ }
237
+ enrichContext(ctx) {
238
+ const env = this.config.environment?.environment || detectEnvironment();
239
+ if (env !== 'local')
240
+ return ctx;
241
+ const localResources = this.config.pluginConfig?.staticResources || {};
242
+ if (Object.keys(localResources).length === 0)
243
+ return ctx;
244
+ return {
245
+ ...ctx,
246
+ static: {
247
+ ...localResources,
248
+ ...(ctx.static || {})
249
+ }
250
+ };
251
+ }
252
+ }
@@ -4,6 +4,8 @@
4
4
  */
5
5
  export * from './utils';
6
6
  export * from './resources';
7
+ export * from './client';
8
+ export * from './funnelClient';
7
9
  export * from './pathRemapping';
8
10
  export * from './googleAutocomplete';
9
11
  export * from './isoData';
@@ -6,6 +6,9 @@
6
6
  export * from './utils';
7
7
  // Export resources (axios-based API clients)
8
8
  export * from './resources';
9
+ // Export clients (stateful logic)
10
+ export * from './client';
11
+ export * from './funnelClient';
9
12
  // Export path remapping helpers (framework-agnostic)
10
13
  export * from './pathRemapping';
11
14
  // Export legacy files that are still needed
@@ -10,7 +10,7 @@ export class ApiClient {
10
10
  // Circuit breaker state
11
11
  this.requestHistory = new Map();
12
12
  this.WINDOW_MS = 5000; // 5 seconds window
13
- this.MAX_REQUESTS = 5; // Max 5 requests per endpoint in window
13
+ this.MAX_REQUESTS = 30; // Max 30 requests per endpoint in window
14
14
  this.axios = axios.create({
15
15
  baseURL: config.baseURL,
16
16
  timeout: config.timeout || 30000,
@@ -344,6 +344,7 @@ export interface FunnelNavigationAction {
344
344
  }
345
345
  export interface FunnelNavigationResult {
346
346
  stepId: string;
347
+ url?: string;
347
348
  action: FunnelNavigationAction;
348
349
  context: SimpleFunnelContext;
349
350
  tracking?: {
@@ -3,44 +3,205 @@
3
3
  * Axios-based API client for offers endpoints
4
4
  */
5
5
  import { ApiClient } from './apiClient';
6
- export interface OfferItem {
6
+ import type { CheckoutSessionState, OrderSummary, OrderSummaryItem, VariantOption } from './postPurchases';
7
+ export type { CheckoutSessionState, OrderSummary, OrderSummaryItem, VariantOption };
8
+ /**
9
+ * Currency option with rate, amount, and lock info
10
+ */
11
+ export interface OfferCurrencyOption {
12
+ rate: number;
13
+ amount: number;
14
+ lock?: boolean;
15
+ date?: string;
16
+ }
17
+ /**
18
+ * Product info within an offer
19
+ */
20
+ export interface OfferProduct {
21
+ id: string;
22
+ name: string;
23
+ description: string | null;
24
+ storeId?: string;
25
+ externalProductId?: string | null;
26
+ externalCollectionIds?: string[] | null;
27
+ active?: boolean;
28
+ isShippable?: boolean;
29
+ isTaxable?: boolean;
30
+ taxCategory?: string;
31
+ unitLabel?: string;
32
+ accountingCode?: string;
33
+ accountId?: string;
34
+ autoSync?: boolean;
35
+ creditPrice?: number | null;
36
+ createdAt?: string;
37
+ updatedAt?: string;
38
+ storefrontRawData?: any;
39
+ }
40
+ /**
41
+ * Variant info within an offer
42
+ */
43
+ export interface OfferVariant {
44
+ id: string;
45
+ productId?: string;
46
+ default?: boolean;
47
+ name: string;
48
+ description?: string | null;
49
+ sku?: string;
50
+ externalVariantId?: string | null;
51
+ price?: number | null;
52
+ currency?: string | null;
53
+ compareAtPrice?: number | null;
54
+ grams?: number | null;
55
+ imageUrl: string | null;
56
+ active?: boolean;
57
+ accountId?: string | null;
58
+ creditPrice?: number | null;
59
+ createdAt?: string;
60
+ updatedAt?: string;
61
+ product?: OfferProduct | null;
62
+ }
63
+ /**
64
+ * Price info within an offer line item
65
+ */
66
+ export interface OfferLineItemPrice {
7
67
  id: string;
68
+ createdAt?: string;
69
+ updatedAt?: string;
70
+ accountId?: string;
71
+ productId?: string;
72
+ variantId: string;
73
+ default?: boolean;
74
+ currencyOptions: Record<string, OfferCurrencyOption>;
75
+ recurring?: boolean;
76
+ billingTiming?: string;
77
+ interval?: string;
78
+ intervalCount?: number;
79
+ rebillWithOrder?: boolean;
80
+ rebillMode?: string | null;
81
+ rebillStepIntervalMs?: number | null;
82
+ isExternalSellingPlan?: boolean;
83
+ isInternalSellingPlan?: boolean;
84
+ shopifySellingPlanId?: string | null;
85
+ creditsPerRebill?: number | null;
86
+ deliverySettings?: Record<string, any>;
87
+ priceSettings?: Record<string, any>;
88
+ variant?: OfferVariant | null;
89
+ }
90
+ /**
91
+ * Line item within an offer
92
+ */
93
+ export interface OfferLineItem {
94
+ id: string;
95
+ offerId?: string;
96
+ quantity: number;
97
+ priceId?: string;
98
+ variantId?: string;
99
+ productId?: string;
100
+ price: OfferLineItemPrice | null;
101
+ }
102
+ /**
103
+ * Summary item within an offer summary
104
+ */
105
+ export interface OfferSummaryItem {
106
+ id: string;
107
+ productId: string;
108
+ variantId: string;
109
+ priceId?: string;
8
110
  product: {
9
111
  name: string;
10
112
  description: string;
11
113
  };
12
114
  variant: {
13
115
  name: string;
14
- imageUrl: string;
116
+ description?: string;
117
+ imageUrl: string | null;
118
+ grams?: number | null;
15
119
  };
16
- quantity: number;
120
+ sku?: string;
17
121
  unitAmount: number;
122
+ quantity: number;
18
123
  amount: number;
19
124
  adjustedAmount: number;
20
125
  currency: string;
126
+ adjustments?: any[];
127
+ recurring?: boolean;
128
+ rebillMode?: string | null;
129
+ interval?: string;
130
+ intervalCount?: number;
131
+ totalBillingCycles?: number;
132
+ unitAmountAfterFirstCycle?: number;
133
+ isExternalSellingPlan?: boolean;
134
+ isInternalSellingPlan?: boolean;
135
+ deliverySettings?: Record<string, any>;
136
+ priceSettings?: Record<string, any>;
137
+ orderLineItemProduct?: {
138
+ name: string;
139
+ };
140
+ orderLineItemVariant?: {
141
+ name: string;
142
+ imageUrl: string | null;
143
+ };
144
+ metadata?: Record<string, any>;
21
145
  }
146
+ /**
147
+ * Offer summary containing items and totals
148
+ */
22
149
  export interface OfferSummary {
23
- items: OfferItem[];
150
+ items: OfferSummaryItem[];
151
+ totalWeight?: number;
152
+ subtotalAmount?: number;
153
+ lineItemsPromotionAmount?: number;
154
+ subtotalAdjustedAmount?: number;
155
+ totalPromotionAmount: number;
156
+ lineItemsTaxAmount?: number;
157
+ totalTaxAmount?: number;
158
+ shippingCost?: number;
159
+ shippingCostIsFree?: boolean;
24
160
  totalAmount: number;
25
161
  totalAdjustedAmount: number;
26
- totalPromotionAmount: number;
27
- currency: string;
28
162
  adjustments: {
29
163
  type: string;
30
164
  description: string;
31
165
  amount: number;
32
166
  }[];
167
+ currency: string;
168
+ _consolidated?: boolean;
169
+ }
170
+ /**
171
+ * Offer promotion association
172
+ */
173
+ export interface OfferPromotion {
174
+ id: string;
175
+ promotionId: string;
176
+ promotion?: {
177
+ id: string;
178
+ name: string;
179
+ code?: string | null;
180
+ automatic?: boolean;
181
+ };
33
182
  }
34
- import type { CurrencyOptions, VariantOption, OrderSummaryItem, OrderSummary, CheckoutSessionState } from './postPurchases';
35
- export type { CurrencyOptions, VariantOption, OrderSummaryItem, OrderSummary, CheckoutSessionState };
183
+ /**
184
+ * Main Offer type
185
+ */
36
186
  export interface Offer {
37
187
  id: string;
188
+ createdAt?: string;
189
+ updatedAt?: string;
190
+ storeId?: string;
191
+ type?: string;
38
192
  titleTrans: Record<string, string>;
193
+ expiryDate?: string | null;
39
194
  summaries: OfferSummary[];
195
+ offerLineItems?: OfferLineItem[];
196
+ promotions?: OfferPromotion[];
40
197
  }
41
198
  export declare class OffersResource {
42
199
  private apiClient;
43
200
  constructor(apiClient: ApiClient);
201
+ /**
202
+ * Get a single offer by ID
203
+ */
204
+ getOfferById(offerId: string): Promise<Offer>;
44
205
  /**
45
206
  * Get offers for a store
46
207
  */
@@ -70,6 +231,19 @@ export declare class OffersResource {
70
231
  */
71
232
  payOffer(offerId: string, orderId?: string): Promise<any>;
72
233
  /**
234
+ * Transform offer to checkout session with dynamic variant selection
235
+ * Uses lineItems from the offer to create a new checkout session
236
+ */
237
+ transformToCheckoutSession(offerId: string, lineItems: {
238
+ variantId: string;
239
+ quantity: number;
240
+ }[], mainOrderId: string, returnUrl?: string): Promise<{
241
+ checkoutUrl: string;
242
+ checkoutSessionId?: string;
243
+ customerId?: string;
244
+ }>;
245
+ /**
246
+ * @deprecated Use transformToCheckoutSession instead
73
247
  * Transform offer to checkout session with dynamic variant selection
74
248
  */
75
249
  transformToCheckout(offerId: string, returnUrl?: string): Promise<{
@@ -6,6 +6,13 @@ export class OffersResource {
6
6
  constructor(apiClient) {
7
7
  this.apiClient = apiClient;
8
8
  }
9
+ /**
10
+ * Get a single offer by ID
11
+ */
12
+ async getOfferById(offerId) {
13
+ const response = await this.apiClient.get(`/api/v1/offers/${offerId}`);
14
+ return response;
15
+ }
9
16
  /**
10
17
  * Get offers for a store
11
18
  */
@@ -77,6 +84,24 @@ export class OffersResource {
77
84
  });
78
85
  }
79
86
  /**
87
+ * Transform offer to checkout session with dynamic variant selection
88
+ * Uses lineItems from the offer to create a new checkout session
89
+ */
90
+ async transformToCheckoutSession(offerId, lineItems, mainOrderId, returnUrl) {
91
+ const response = await this.apiClient.post(`/api/v1/offers/${offerId}/transform-to-checkout`, {
92
+ offerId,
93
+ lineItems,
94
+ mainOrderId,
95
+ returnUrl: returnUrl || (typeof window !== 'undefined' ? window.location.origin : ''),
96
+ });
97
+ return {
98
+ checkoutUrl: response.checkoutUrl,
99
+ checkoutSessionId: response.checkoutSessionId,
100
+ customerId: response.customerId,
101
+ };
102
+ }
103
+ /**
104
+ * @deprecated Use transformToCheckoutSession instead
80
105
  * Transform offer to checkout session with dynamic variant selection
81
106
  */
82
107
  async transformToCheckout(offerId, returnUrl) {
@@ -7,6 +7,7 @@ import { ApiClient } from './apiClient';
7
7
  export interface GetProductsOptions {
8
8
  storeId: string;
9
9
  productIds?: string[];
10
+ variantIds?: string[];
10
11
  includeVariants?: boolean;
11
12
  includePrices?: boolean;
12
13
  }
@@ -26,4 +27,8 @@ export declare class ProductsResource {
26
27
  * Get multiple products by IDs
27
28
  */
28
29
  getProductsByIds(productIds: string[], options: GetProductsOptions): Promise<Product[]>;
30
+ /**
31
+ * Get products by variant IDs (efficient filtering on the backend)
32
+ */
33
+ getProductsByVariantIds(variantIds: string[], options: GetProductsOptions): Promise<Product[]>;
29
34
  }
@@ -10,9 +10,10 @@ export class ProductsResource {
10
10
  * Get all products for a store
11
11
  */
12
12
  async getProducts(options) {
13
- const { storeId, includeVariants = true, includePrices = true } = options;
13
+ const { storeId, variantIds, includeVariants = true, includePrices = true } = options;
14
14
  const response = await this.apiClient.post('/api/v1/products', {
15
15
  storeId,
16
+ variantIds,
16
17
  includeVariants,
17
18
  includePrices,
18
19
  });
@@ -46,4 +47,17 @@ export class ProductsResource {
46
47
  const results = await Promise.all(fetchPromises);
47
48
  return results.filter((product) => product !== null);
48
49
  }
50
+ /**
51
+ * Get products by variant IDs (efficient filtering on the backend)
52
+ */
53
+ async getProductsByVariantIds(variantIds, options) {
54
+ const { storeId, includeVariants = true, includePrices = true } = options;
55
+ const response = await this.apiClient.post('/api/v1/products', {
56
+ storeId,
57
+ variantIds, // Backend will filter products that have these variants
58
+ includeVariants,
59
+ includePrices,
60
+ });
61
+ return Array.isArray(response) ? response : response.items ?? [];
62
+ }
49
63
  }
@@ -60,6 +60,7 @@ export interface Currency {
60
60
  }
61
61
  export interface Store {
62
62
  id: string;
63
+ accountId: string;
63
64
  name: string;
64
65
  domain: string;
65
66
  currency: string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Funnel Query Keys - Core SDK (Framework-agnostic)
3
+ * Standardized query keys for caching funnel data
4
+ * Can be used with any query library (TanStack Query, SWR, etc.)
5
+ */
6
+ /**
7
+ * Query keys for funnel operations
8
+ * These keys are used for caching and invalidation strategies
9
+ */
10
+ export declare const funnelQueryKeys: {
11
+ /**
12
+ * Key for a specific funnel session
13
+ */
14
+ session: (sessionId: string) => readonly ["funnel", "session", string];
15
+ /**
16
+ * Key for all funnel sessions
17
+ */
18
+ allSessions: () => readonly ["funnel", "sessions"];
19
+ /**
20
+ * Key for funnel metadata/configuration
21
+ */
22
+ funnelMeta: (funnelId: string) => readonly ["funnel", "meta", string];
23
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Funnel Query Keys - Core SDK (Framework-agnostic)
3
+ * Standardized query keys for caching funnel data
4
+ * Can be used with any query library (TanStack Query, SWR, etc.)
5
+ */
6
+ /**
7
+ * Query keys for funnel operations
8
+ * These keys are used for caching and invalidation strategies
9
+ */
10
+ export const funnelQueryKeys = {
11
+ /**
12
+ * Key for a specific funnel session
13
+ */
14
+ session: (sessionId) => ['funnel', 'session', sessionId],
15
+ /**
16
+ * Key for all funnel sessions
17
+ */
18
+ allSessions: () => ['funnel', 'sessions'],
19
+ /**
20
+ * Key for funnel metadata/configuration
21
+ */
22
+ funnelMeta: (funnelId) => ['funnel', 'meta', funnelId],
23
+ };
@@ -10,3 +10,5 @@ export * from './checkout';
10
10
  export * from './promotions';
11
11
  export * from './postPurchases';
12
12
  export * from './orderBump';
13
+ export * from './sessionStorage';
14
+ export * from './funnelQueryKeys';
@@ -10,3 +10,5 @@ export * from './checkout';
10
10
  export * from './promotions';
11
11
  export * from './postPurchases';
12
12
  export * from './orderBump';
13
+ export * from './sessionStorage';
14
+ export * from './funnelQueryKeys';