@fast-simon/dashboard-utilities 1.0.167-beta.1 → 1.0.168-beta.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.
@@ -63,4 +63,11 @@ export interface AutocompleteReasoningResponse {
63
63
  query_understanding?: QueryUnderstanding;
64
64
  reformulation_source?: string;
65
65
  request_url?: string;
66
+ rpi_methodology_assigned?: string | null;
67
+ rpi_methodology_preview_requested?: boolean;
68
+ rpi_methodology_effective?: string | null;
69
+ rpi_methodology_fallback_reason?: string | null;
70
+ rpi_candidate_available?: boolean;
71
+ rpi_candidate_unavailable_reason?: string | null;
72
+ rpi_candidate_readiness_status?: string | null;
66
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"autocomplete.js","sourceRoot":"","sources":["../../src/@types/autocomplete.ts"],"names":[],"mappings":"","sourcesContent":["import {ServerProduct} from \"./product\";\nimport {Facet, FastCategory, FastTurbolinks} from \"./facets\";\nimport {PopularSearch, ContextFilteringData, QueryUnderstanding} from \"./results\";\nimport {SortBy} from \"./sortBy\";\n\n\n\nexport interface AutocompleteResults {\n totalResults: number,\n categories?: FastCategory[],\n products: ServerProduct[]\n turbolinks?: FastTurbolinks[]\n popularSearches: PopularSearch[]\n query: string\n}\n\nexport interface autocompleteProps {\n storeID: number\n uuid: string\n siteKey?:string\n cdn?: number\n term: string,\n userToken: string,\n sessionStart: number,\n withAttributes?: boolean\n segments?: string[],\n hasShopifyMarkets?: boolean,\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n force_and?: boolean\n ps?: 'new' | 'returning' | null\n spv?: string | null // Session Product Views JSON for personalization preview\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n}\nexport interface AutocompleteReasoningResponse {\n auto_facets?: boolean\n isp_quick_view_mode?: number\n products: ServerProduct[]\n merch: {\n prorduct:any,\n rules:any,\n strategy:any,\n visual:any\n }\n personalized_products:any[]\n term:string\n totalResults: number\n uuid:string\n\n antonyms?: Record<string, number[]>\n\n //all bellow wont be never field but to keep consistent with all the AIExplainer implementation\n sortBy?:SortBy\n within_search_results_for?: string,\n isNoResults?:any,\n pageCount?:number,\n narrow?: [string, string, string][],\n facets?: Facet[],\n page?: number,\n results_for?: string,\n id_list?:number[]\n turbolink?: string\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n}"]}
1
+ {"version":3,"file":"autocomplete.js","sourceRoot":"","sources":["../../src/@types/autocomplete.ts"],"names":[],"mappings":"","sourcesContent":["import {ServerProduct} from \"./product\";\nimport {Facet, FastCategory, FastTurbolinks} from \"./facets\";\nimport {PopularSearch, ContextFilteringData, QueryUnderstanding} from \"./results\";\nimport {SortBy} from \"./sortBy\";\n\n\n\nexport interface AutocompleteResults {\n totalResults: number,\n categories?: FastCategory[],\n products: ServerProduct[]\n turbolinks?: FastTurbolinks[]\n popularSearches: PopularSearch[]\n query: string\n}\n\nexport interface autocompleteProps {\n storeID: number\n uuid: string\n siteKey?:string\n cdn?: number\n term: string,\n userToken: string,\n sessionStart: number,\n withAttributes?: boolean\n segments?: string[],\n hasShopifyMarkets?: boolean,\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n force_and?: boolean\n ps?: 'new' | 'returning' | null\n spv?: string | null // Session Product Views JSON for personalization preview\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n}\nexport interface AutocompleteReasoningResponse {\n auto_facets?: boolean\n isp_quick_view_mode?: number\n products: ServerProduct[]\n merch: {\n prorduct:any,\n rules:any,\n strategy:any,\n visual:any\n }\n personalized_products:any[]\n term:string\n totalResults: number\n uuid:string\n\n antonyms?: Record<string, number[]>\n\n //all bellow wont be never field but to keep consistent with all the AIExplainer implementation\n sortBy?:SortBy\n within_search_results_for?: string,\n isNoResults?:any,\n pageCount?:number,\n narrow?: [string, string, string][],\n facets?: Facet[],\n page?: number,\n results_for?: string,\n id_list?:number[]\n turbolink?: string\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n rpi_methodology_assigned?: string | null\n rpi_methodology_preview_requested?: boolean\n rpi_methodology_effective?: string | null\n rpi_methodology_fallback_reason?: string | null\n rpi_candidate_available?: boolean\n rpi_candidate_unavailable_reason?: string | null\n rpi_candidate_readiness_status?: string | null\n}\n"]}
@@ -115,12 +115,14 @@ export interface smartCollectionsResults {
115
115
  reformulation_source?: string;
116
116
  request_url?: string;
117
117
  rpi_methodology_assigned?: string | null;
118
+ rpi_methodology_preview_requested?: boolean;
118
119
  rpi_methodology_effective?: string | null;
119
120
  rpi_methodology_fallback_reason?: string | null;
120
121
  rpi_snapshot_computed_at?: string | null;
121
122
  rpi_snapshot_covered_until?: string | null;
122
123
  rpi_candidate_available?: boolean;
123
124
  rpi_candidate_unavailable_reason?: string | null;
125
+ rpi_candidate_readiness_status?: string | null;
124
126
  }
125
127
  export type inventoryCallback = ({}: {
126
128
  action: "products inventory";
@@ -1 +1 @@
1
- {"version":3,"file":"categories.js","sourceRoot":"","sources":["../../src/@types/categories.ts"],"names":[],"mappings":"","sourcesContent":["import {SortBy} from \"./sortBy\";\nimport {Narrow, ServerNarrow} from \"./narrow\";\nimport { ServerProduct} from \"./product\";\nimport {ServerFacet} from \"./facets\";\nimport {ContextFilteringData, QueryUnderstanding} from \"./results\";\n\n\nexport type AncestorCategory = [string, string, string] // category_id, category_name, category_url\nexport interface RelatedCategory {\n id: string\n l: string\n t: string\n u: string\n}\nexport type CategoryAncestor = [string, string, string]\nexport interface Category {\n d: string\n id: string\n l: string\n p_id: string\n t: string\n u: string\n parent_name?: string\n}\nexport interface FastCategory {\n d: string\n id: string\n l: string\n p_id: string\n t: string\n u: string\n parent_name?: string\n}\nexport interface smartCollectionsProps {\n storeID: number\n uuid: string\n siteKey?:string\n cdn: number\n categoryID: string,\n userToken: string,\n sessionStart: number,\n page?: number\n productsPerPage?: number\n sortBy?: SortBy\n narrowBy?: ServerNarrow[]\n facetRequired?: FacetRequired\n withAttributes?: boolean\n recent?: string\n rec?: boolean\n searchWithinSearch?: string,\n segments?: string[],\n hasShopifyMarkets?: boolean,\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n custom_sort_settings?:{personalization_rank: number, creation_date: number, popularity: number, inventory: number}\n with_product_analytics?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n ps?: 'new' | 'returning' | null // Personalization segment for user segment coefficient preview (new/returning user)\n spv?: string | null // Session Product Views JSON for personalization preview (e.g., '{\"pid1\":ts,\"pid2\":ts}')\n spc?: string | null // Session Product Cart-adds JSON for size personalization preview (e.g., '{\"pid::vid\":ts}'); collection-only\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n force_and?: boolean\n session_token?: string // Session token override for AI Explainer (sent as st param)\n}\nexport type FacetRequired = 0 | 1 | 2\nexport type smartCollectionsCallback = ({}: {\n action: \"products\" | \"facets and products\"\n payload: smartCollectionsResults\n}) => void\nexport interface smartCollectionsResults {\n ancestors: CategoryAncestor[]\n categoryID: string\n name: string\n category_name: string\n products: ServerProduct[]\n facets?: ServerFacet[]\n totalResults: number\n sortBy?: SortBy,\n narrow?: Narrow[],\n isNoResults?: boolean\n page?: number\n pageCount?: number\n avoidPrefixList?: string[]\n antonyms?: Record<string, number[]>\n merch?: any\n bannerHTML?: string\n turbolink?:string\n hiddenProducts?:ServerProduct[]\n within_search_results_for?:string\n results_for?: string\n id_list?:number[]\n personalized_products?: number[] // Product IDs affected by RPI personalization\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n analytics?: {\n badges?: {\n winners?: Record<string, string> // Product ID -> tooltip message\n overexposed?: Record<string, string> // Product ID -> tooltip message\n new_arrivals?: string[]\n }\n key_hidden_winners?: string\n key_overexposed?: string\n }\n matching_sets?: Record<string, string> // Product ID -> set name\n group_strategy?: Record<string, string> // Product ID -> group name\n variety_strategy?: number[] // Product IDs affected by variety strategy\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n rpi_methodology_assigned?: string | null\n rpi_methodology_effective?: string | null\n rpi_methodology_fallback_reason?: string | null\n rpi_snapshot_computed_at?: string | null\n rpi_snapshot_covered_until?: string | null\n rpi_candidate_available?: boolean\n rpi_candidate_unavailable_reason?: string | null\n\n}\nexport type inventoryCallback = ({}: {\n action: \"products inventory\"\n payload: {productsInventory: productsInventoryResults[]}\n}) => void\nexport interface productsInventoryResults {\n id: string;\n totalInventory: number;\n}\n"]}
1
+ {"version":3,"file":"categories.js","sourceRoot":"","sources":["../../src/@types/categories.ts"],"names":[],"mappings":"","sourcesContent":["import {SortBy} from \"./sortBy\";\nimport {Narrow, ServerNarrow} from \"./narrow\";\nimport { ServerProduct} from \"./product\";\nimport {ServerFacet} from \"./facets\";\nimport {ContextFilteringData, QueryUnderstanding} from \"./results\";\n\n\nexport type AncestorCategory = [string, string, string] // category_id, category_name, category_url\nexport interface RelatedCategory {\n id: string\n l: string\n t: string\n u: string\n}\nexport type CategoryAncestor = [string, string, string]\nexport interface Category {\n d: string\n id: string\n l: string\n p_id: string\n t: string\n u: string\n parent_name?: string\n}\nexport interface FastCategory {\n d: string\n id: string\n l: string\n p_id: string\n t: string\n u: string\n parent_name?: string\n}\nexport interface smartCollectionsProps {\n storeID: number\n uuid: string\n siteKey?:string\n cdn: number\n categoryID: string,\n userToken: string,\n sessionStart: number,\n page?: number\n productsPerPage?: number\n sortBy?: SortBy\n narrowBy?: ServerNarrow[]\n facetRequired?: FacetRequired\n withAttributes?: boolean\n recent?: string\n rec?: boolean\n searchWithinSearch?: string,\n segments?: string[],\n hasShopifyMarkets?: boolean,\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n custom_sort_settings?:{personalization_rank: number, creation_date: number, popularity: number, inventory: number}\n with_product_analytics?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n ps?: 'new' | 'returning' | null // Personalization segment for user segment coefficient preview (new/returning user)\n spv?: string | null // Session Product Views JSON for personalization preview (e.g., '{\"pid1\":ts,\"pid2\":ts}')\n spc?: string | null // Session Product Cart-adds JSON for size personalization preview (e.g., '{\"pid::vid\":ts}'); collection-only\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n force_and?: boolean\n session_token?: string // Session token override for AI Explainer (sent as st param)\n}\nexport type FacetRequired = 0 | 1 | 2\nexport type smartCollectionsCallback = ({}: {\n action: \"products\" | \"facets and products\"\n payload: smartCollectionsResults\n}) => void\nexport interface smartCollectionsResults {\n ancestors: CategoryAncestor[]\n categoryID: string\n name: string\n category_name: string\n products: ServerProduct[]\n facets?: ServerFacet[]\n totalResults: number\n sortBy?: SortBy,\n narrow?: Narrow[],\n isNoResults?: boolean\n page?: number\n pageCount?: number\n avoidPrefixList?: string[]\n antonyms?: Record<string, number[]>\n merch?: any\n bannerHTML?: string\n turbolink?:string\n hiddenProducts?:ServerProduct[]\n within_search_results_for?:string\n results_for?: string\n id_list?:number[]\n personalized_products?: number[] // Product IDs affected by RPI personalization\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n analytics?: {\n badges?: {\n winners?: Record<string, string> // Product ID -> tooltip message\n overexposed?: Record<string, string> // Product ID -> tooltip message\n new_arrivals?: string[]\n }\n key_hidden_winners?: string\n key_overexposed?: string\n }\n matching_sets?: Record<string, string> // Product ID -> set name\n group_strategy?: Record<string, string> // Product ID -> group name\n variety_strategy?: number[] // Product IDs affected by variety strategy\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n rpi_methodology_assigned?: string | null\n rpi_methodology_preview_requested?: boolean\n rpi_methodology_effective?: string | null\n rpi_methodology_fallback_reason?: string | null\n rpi_snapshot_computed_at?: string | null\n rpi_snapshot_covered_until?: string | null\n rpi_candidate_available?: boolean\n rpi_candidate_unavailable_reason?: string | null\n rpi_candidate_readiness_status?: string | null\n\n}\nexport type inventoryCallback = ({}: {\n action: \"products inventory\"\n payload: {productsInventory: productsInventoryResults[]}\n}) => void\nexport interface productsInventoryResults {\n id: string;\n totalInventory: number;\n}\n"]}
@@ -151,11 +151,13 @@ export interface FullTextServerResponseAll extends fullTextSearchResponse {
151
151
  reformulation_source?: string;
152
152
  request_url?: string;
153
153
  rpi_methodology_assigned?: string | null;
154
+ rpi_methodology_preview_requested?: boolean;
154
155
  rpi_methodology_effective?: string | null;
155
156
  rpi_methodology_fallback_reason?: string | null;
156
157
  rpi_snapshot_computed_at?: string | null;
157
158
  rpi_snapshot_covered_until?: string | null;
158
159
  rpi_candidate_available?: boolean;
159
160
  rpi_candidate_unavailable_reason?: string | null;
161
+ rpi_candidate_readiness_status?: string | null;
160
162
  }
161
163
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/@types/results.ts"],"names":[],"mappings":"","sourcesContent":["import type {Narrow, ServerNarrow} from \"./narrow\";\nimport type {SortBy} from \"./sortBy\";\n\nimport type { Facet} from \"./facets\";\nimport {AncestorCategory, RelatedCategory} from \"./categories\";\n\nimport {Product, ServerProduct} from \"./product\";\n\nexport interface PopularSearch {\n label: string\n type: \"p\"\n}\ninterface Base {\n narrow: Narrow,\n sortBy: SortBy,\n totalResults: number,\n page: number,\n totalPages: number,\n products: Product[],\n facets: Facet[],\n avoidTagPrefixes: string[],\n sort: SortBy,\n productIDS: string[],\n bannerHTML: string,\n bannerHTMLMobile: string,\n // ancestors: Ancestor[]\n}\n\nexport interface SearchResults extends Base {\n query: string,\n totalCMS: number,\n didYouMean?: string[],\n personalizedProductIds: number[],\n sessionProductIds: number[],\n resultsFor?: string,\n isRelatedResults?: boolean\n relatedSearch?: string[]\n}\n\nexport interface CategoryResults extends Base {\n categoryID: string,\n categoryName: string,\n personalizationRecencyLimit?: number,\n personalizedProductIds: number[],\n sessionProductIds: number[],\n relatedCategories: RelatedCategory[],\n}\n\nexport interface LandingPageResults extends Base {\n lpSlug: string,\n title: string,\n indexPage: number\n}\n\nexport type FacetRequired = 0 | 1 | 2\nexport type CategoryAncestor = [string, string, string]\n\nexport interface fullTextSearchProps {\n term: string\n storeID: number\n siteKey?:string\n uuid: string\n cdn?: number\n userToken: string,\n sessionStart: number,\n page?: number\n productsPerPage?: number\n sortBy?: SortBy\n narrowBy?: ServerNarrow[]\n facetRequired?: FacetRequired\n withAttributes?: boolean\n recent?: string\n searchWithinSearch?: string,\n segments?: string[]\n hasShopifyMarkets?: boolean\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n custom_sort_settings?:{personalization_rank: number, creation_date: number, popularity: number, inventory: number}\n with_product_analytics?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n force_and?: boolean\n ps?: 'new' | 'returning' | null // Personalization segment for user segment coefficient preview (new/returning user)\n spv?: string | null // Session Product Views JSON for personalization preview (e.g., '{\"pid1\":ts,\"pid2\":ts}')\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n session_token?: string // Session token override for AI Explainer (sent as st param)\n}\nexport interface ContextFilteringData {\n hidden: number[] // Product IDs hidden by context filtering\n unpublished: Record<number, string> // product_id -> region_code (e.g., \"US\", \"CA\")\n unsellable: Record<number, string> // product_id -> region_code (e.g., \"UK\", \"FR\")\n}\n\nexport interface fullTextSearchResponse {\n products: ServerProduct[]\n facets: Facet[]\n total_results: number\n facets_completed?: boolean\n sortBy?: SortBy,\n narrow: [string, string, string][]\n pop_products_no_results?: boolean\n page: number\n total_p: number\n personalized_products?: number[],\n ancestor_categories?: CategoryAncestor[]\n avoid_tag_prefixes?: string\n merch?: any\n}\nexport interface QueryUnderstanding {\n query: string\n has_ai_enrichment: boolean\n structured_attributes: Record<string, string[]>\n social_attributes: Record<string, string[]>\n intent_mapping: string[]\n}\n\nexport interface FullTextServerResponseAll extends fullTextSearchResponse{\n alternatives?: string[]\n term: string\n cms_number_found?: number\n within_search_results_for?: string\n banner_html: string\n banner_html_mobile: string\n turbolink?: string\n personalized_products?: number[]\n session_products?: number[]\n results_for?: string\n related_results?: boolean,\n ancestor_categories?: AncestorCategory[],\n related_search?: string[],\n hidden?:any,\n isNoResults?:any,\n totalResults?:any,\n hiddenProducts?:ServerProduct[],\n pageCount?:number,\n sort_by?: SortBy,\n id_list?:number[]\n antonyms?: Record<string, number[]>\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n analytics?: {\n badges?: {\n winners?: Record<string, string> // Product ID -> tooltip message\n overexposed?: Record<string, string> // Product ID -> tooltip message\n new_arrivals?: string[]\n }\n key_hidden_winners?: string\n key_overexposed?: string\n }\n matching_sets?: Record<string, string> // Product ID -> set name\n group_strategy?: Record<string, string> // Product ID -> group name\n variety_strategy?: number[] // Product IDs affected by variety strategy\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n rpi_methodology_assigned?: string | null\n rpi_methodology_effective?: string | null\n rpi_methodology_fallback_reason?: string | null\n rpi_snapshot_computed_at?: string | null\n rpi_snapshot_covered_until?: string | null\n rpi_candidate_available?: boolean\n rpi_candidate_unavailable_reason?: string | null\n\n}\n"]}
1
+ {"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/@types/results.ts"],"names":[],"mappings":"","sourcesContent":["import type {Narrow, ServerNarrow} from \"./narrow\";\nimport type {SortBy} from \"./sortBy\";\n\nimport type { Facet} from \"./facets\";\nimport {AncestorCategory, RelatedCategory} from \"./categories\";\n\nimport {Product, ServerProduct} from \"./product\";\n\nexport interface PopularSearch {\n label: string\n type: \"p\"\n}\ninterface Base {\n narrow: Narrow,\n sortBy: SortBy,\n totalResults: number,\n page: number,\n totalPages: number,\n products: Product[],\n facets: Facet[],\n avoidTagPrefixes: string[],\n sort: SortBy,\n productIDS: string[],\n bannerHTML: string,\n bannerHTMLMobile: string,\n // ancestors: Ancestor[]\n}\n\nexport interface SearchResults extends Base {\n query: string,\n totalCMS: number,\n didYouMean?: string[],\n personalizedProductIds: number[],\n sessionProductIds: number[],\n resultsFor?: string,\n isRelatedResults?: boolean\n relatedSearch?: string[]\n}\n\nexport interface CategoryResults extends Base {\n categoryID: string,\n categoryName: string,\n personalizationRecencyLimit?: number,\n personalizedProductIds: number[],\n sessionProductIds: number[],\n relatedCategories: RelatedCategory[],\n}\n\nexport interface LandingPageResults extends Base {\n lpSlug: string,\n title: string,\n indexPage: number\n}\n\nexport type FacetRequired = 0 | 1 | 2\nexport type CategoryAncestor = [string, string, string]\n\nexport interface fullTextSearchProps {\n term: string\n storeID: number\n siteKey?:string\n uuid: string\n cdn?: number\n userToken: string,\n sessionStart: number,\n page?: number\n productsPerPage?: number\n sortBy?: SortBy\n narrowBy?: ServerNarrow[]\n facetRequired?: FacetRequired\n withAttributes?: boolean\n recent?: string\n searchWithinSearch?: string,\n segments?: string[]\n hasShopifyMarkets?: boolean\n disable_turbolinks?:boolean\n disable_strategies?:boolean\n disable_merchandising?:boolean\n disable_semantics?:boolean\n disable_merchandising_rules?:boolean\n disable_antonyms?:boolean\n custom_sort_settings?:{personalization_rank: number, creation_date: number, popularity: number, inventory: number}\n with_product_analytics?:boolean\n market_context?: string | null // Region code for market-specific filtering (e.g., \"US\", \"CA\", \"UK\")\n variation_id?: string | null // A/B test variation ID for reasoning/experiment tracking\n force_and?: boolean\n ps?: 'new' | 'returning' | null // Personalization segment for user segment coefficient preview (new/returning user)\n spv?: string | null // Session Product Views JSON for personalization preview (e.g., '{\"pid1\":ts,\"pid2\":ts}')\n personalization?: string | null // Comma-separated personalization types to enable (e.g., 'gender')\n session_token?: string // Session token override for AI Explainer (sent as st param)\n}\nexport interface ContextFilteringData {\n hidden: number[] // Product IDs hidden by context filtering\n unpublished: Record<number, string> // product_id -> region_code (e.g., \"US\", \"CA\")\n unsellable: Record<number, string> // product_id -> region_code (e.g., \"UK\", \"FR\")\n}\n\nexport interface fullTextSearchResponse {\n products: ServerProduct[]\n facets: Facet[]\n total_results: number\n facets_completed?: boolean\n sortBy?: SortBy,\n narrow: [string, string, string][]\n pop_products_no_results?: boolean\n page: number\n total_p: number\n personalized_products?: number[],\n ancestor_categories?: CategoryAncestor[]\n avoid_tag_prefixes?: string\n merch?: any\n}\nexport interface QueryUnderstanding {\n query: string\n has_ai_enrichment: boolean\n structured_attributes: Record<string, string[]>\n social_attributes: Record<string, string[]>\n intent_mapping: string[]\n}\n\nexport interface FullTextServerResponseAll extends fullTextSearchResponse{\n alternatives?: string[]\n term: string\n cms_number_found?: number\n within_search_results_for?: string\n banner_html: string\n banner_html_mobile: string\n turbolink?: string\n personalized_products?: number[]\n session_products?: number[]\n results_for?: string\n related_results?: boolean,\n ancestor_categories?: AncestorCategory[],\n related_search?: string[],\n hidden?:any,\n isNoResults?:any,\n totalResults?:any,\n hiddenProducts?:ServerProduct[],\n pageCount?:number,\n sort_by?: SortBy,\n id_list?:number[]\n antonyms?: Record<string, number[]>\n context?: ContextFilteringData // Context-based filtering data (market contexts)\n analytics?: {\n badges?: {\n winners?: Record<string, string> // Product ID -> tooltip message\n overexposed?: Record<string, string> // Product ID -> tooltip message\n new_arrivals?: string[]\n }\n key_hidden_winners?: string\n key_overexposed?: string\n }\n matching_sets?: Record<string, string> // Product ID -> set name\n group_strategy?: Record<string, string> // Product ID -> group name\n variety_strategy?: number[] // Product IDs affected by variety strategy\n query_understanding?: QueryUnderstanding\n reformulation_source?: string\n request_url?: string // Full URL with all params that was executed (for debugging)\n rpi_methodology_assigned?: string | null\n rpi_methodology_preview_requested?: boolean\n rpi_methodology_effective?: string | null\n rpi_methodology_fallback_reason?: string | null\n rpi_snapshot_computed_at?: string | null\n rpi_snapshot_covered_until?: string | null\n rpi_candidate_available?: boolean\n rpi_candidate_unavailable_reason?: string | null\n rpi_candidate_readiness_status?: string | null\n\n}\n"]}
@@ -152,12 +152,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
152
152
  reformulation_source?: string | undefined;
153
153
  request_url?: string | undefined;
154
154
  rpi_methodology_assigned?: string | null | undefined;
155
+ rpi_methodology_preview_requested?: boolean | undefined;
155
156
  rpi_methodology_effective?: string | null | undefined;
156
157
  rpi_methodology_fallback_reason?: string | null | undefined;
157
158
  rpi_snapshot_computed_at?: string | null | undefined;
158
159
  rpi_snapshot_covered_until?: string | null | undefined;
159
160
  rpi_candidate_available?: boolean | undefined;
160
161
  rpi_candidate_unavailable_reason?: string | null | undefined;
162
+ rpi_candidate_readiness_status?: string | null | undefined;
161
163
  facets: import("../../@types/facets").Facet[];
162
164
  total_results: number;
163
165
  facets_completed?: boolean | undefined;
@@ -208,12 +210,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
208
210
  reformulation_source?: string | undefined;
209
211
  request_url?: string | undefined;
210
212
  rpi_methodology_assigned?: string | null | undefined;
213
+ rpi_methodology_preview_requested?: boolean | undefined;
211
214
  rpi_methodology_effective?: string | null | undefined;
212
215
  rpi_methodology_fallback_reason?: string | null | undefined;
213
216
  rpi_snapshot_computed_at?: string | null | undefined;
214
217
  rpi_snapshot_covered_until?: string | null | undefined;
215
218
  rpi_candidate_available?: boolean | undefined;
216
219
  rpi_candidate_unavailable_reason?: string | null | undefined;
220
+ rpi_candidate_readiness_status?: string | null | undefined;
217
221
  } | {
218
222
  products: import("../../@types/product").ServerProduct[];
219
223
  alternatives?: string[] | undefined;
@@ -254,12 +258,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
254
258
  reformulation_source?: string | undefined;
255
259
  request_url?: string | undefined;
256
260
  rpi_methodology_assigned?: string | null | undefined;
261
+ rpi_methodology_preview_requested?: boolean | undefined;
257
262
  rpi_methodology_effective?: string | null | undefined;
258
263
  rpi_methodology_fallback_reason?: string | null | undefined;
259
264
  rpi_snapshot_computed_at?: string | null | undefined;
260
265
  rpi_snapshot_covered_until?: string | null | undefined;
261
266
  rpi_candidate_available?: boolean | undefined;
262
267
  rpi_candidate_unavailable_reason?: string | null | undefined;
268
+ rpi_candidate_readiness_status?: string | null | undefined;
263
269
  facets: import("../../@types/facets").Facet[];
264
270
  total_results: number;
265
271
  facets_completed?: boolean | undefined;
@@ -310,12 +316,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
310
316
  reformulation_source?: string | undefined;
311
317
  request_url?: string | undefined;
312
318
  rpi_methodology_assigned?: string | null | undefined;
319
+ rpi_methodology_preview_requested?: boolean | undefined;
313
320
  rpi_methodology_effective?: string | null | undefined;
314
321
  rpi_methodology_fallback_reason?: string | null | undefined;
315
322
  rpi_snapshot_computed_at?: string | null | undefined;
316
323
  rpi_snapshot_covered_until?: string | null | undefined;
317
324
  rpi_candidate_available?: boolean | undefined;
318
325
  rpi_candidate_unavailable_reason?: string | null | undefined;
326
+ rpi_candidate_readiness_status?: string | null | undefined;
319
327
  alternatives?: string[] | undefined;
320
328
  term: string;
321
329
  cms_number_found?: number | undefined;
@@ -372,12 +380,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
372
380
  reformulation_source?: string | undefined;
373
381
  request_url?: string | undefined;
374
382
  rpi_methodology_assigned?: string | null | undefined;
383
+ rpi_methodology_preview_requested?: boolean | undefined;
375
384
  rpi_methodology_effective?: string | null | undefined;
376
385
  rpi_methodology_fallback_reason?: string | null | undefined;
377
386
  rpi_snapshot_computed_at?: string | null | undefined;
378
387
  rpi_snapshot_covered_until?: string | null | undefined;
379
388
  rpi_candidate_available?: boolean | undefined;
380
389
  rpi_candidate_unavailable_reason?: string | null | undefined;
390
+ rpi_candidate_readiness_status?: string | null | undefined;
381
391
  facets: import("../../@types/facets").Facet[];
382
392
  total_results: number;
383
393
  facets_completed?: boolean | undefined;
@@ -434,12 +444,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
434
444
  reformulation_source?: string | undefined;
435
445
  request_url?: string | undefined;
436
446
  rpi_methodology_assigned?: string | null | undefined;
447
+ rpi_methodology_preview_requested?: boolean | undefined;
437
448
  rpi_methodology_effective?: string | null | undefined;
438
449
  rpi_methodology_fallback_reason?: string | null | undefined;
439
450
  rpi_snapshot_computed_at?: string | null | undefined;
440
451
  rpi_snapshot_covered_until?: string | null | undefined;
441
452
  rpi_candidate_available?: boolean | undefined;
442
453
  rpi_candidate_unavailable_reason?: string | null | undefined;
454
+ rpi_candidate_readiness_status?: string | null | undefined;
443
455
  } | {
444
456
  products: import("../../@types/product").ServerProduct[];
445
457
  alternatives?: string[] | undefined;
@@ -480,12 +492,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
480
492
  reformulation_source?: string | undefined;
481
493
  request_url?: string | undefined;
482
494
  rpi_methodology_assigned?: string | null | undefined;
495
+ rpi_methodology_preview_requested?: boolean | undefined;
483
496
  rpi_methodology_effective?: string | null | undefined;
484
497
  rpi_methodology_fallback_reason?: string | null | undefined;
485
498
  rpi_snapshot_computed_at?: string | null | undefined;
486
499
  rpi_snapshot_covered_until?: string | null | undefined;
487
500
  rpi_candidate_available?: boolean | undefined;
488
501
  rpi_candidate_unavailable_reason?: string | null | undefined;
502
+ rpi_candidate_readiness_status?: string | null | undefined;
489
503
  facets: import("../../@types/facets").Facet[];
490
504
  total_results: number;
491
505
  facets_completed?: boolean | undefined;
@@ -539,12 +553,14 @@ export declare function fastStateReducer(state: FastStateProps, action: FastStat
539
553
  reformulation_source?: string | undefined;
540
554
  request_url?: string | undefined;
541
555
  rpi_methodology_assigned?: string | null | undefined;
556
+ rpi_methodology_preview_requested?: boolean | undefined;
542
557
  rpi_methodology_effective?: string | null | undefined;
543
558
  rpi_methodology_fallback_reason?: string | null | undefined;
544
559
  rpi_snapshot_computed_at?: string | null | undefined;
545
560
  rpi_snapshot_covered_until?: string | null | undefined;
546
561
  rpi_candidate_available?: boolean | undefined;
547
562
  rpi_candidate_unavailable_reason?: string | null | undefined;
563
+ rpi_candidate_readiness_status?: string | null | undefined;
548
564
  auto_facets?: boolean | undefined;
549
565
  isp_quick_view_mode?: number | undefined;
550
566
  term: string;
package/dist/index.d.ts CHANGED
@@ -42,6 +42,6 @@ import { getShowRequestUrlSetting, setShowRequestUrlSetting } from "./utils/getR
42
42
  import { getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, UserSegmentPreview, UserSegmentPreviewSettings } from "./utils/userSegmentPreview";
43
43
  import { getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, PersonalizationPreviewSettings, PersonalizationType } from "./utils/personalizationPreview";
44
44
  import { getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, SessionTokenPreviewSettings } from "./utils/sessionTokenPreview";
45
- import { LiveSessionData, SpvProductMeta, RequestType, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken } from "./utils/liveSessionPreview";
46
- export type { MenuItemName, CustomClasses, SideMenuType, SideMenuItem, ProductLabelType, Banner, EditorProduct, IconProps, ServerProduct, ProductAnalytics, ProductPersonalizationCoefficients, FilterCoefEntry, Pillar, PillarKind, PersonalizationConfidence, PersonalizationStory, FullTextServerResponseAll, QueryUnderstanding, smartCollectionsResults, AutocompleteReasoningResponse, FeaturedRanking, ComparisonData, UserSegmentPreview, UserSegmentPreviewSettings, PersonalizationPreviewSettings, PersonalizationType, SessionTokenPreviewSettings, LiveSessionData, SpvProductMeta, RequestType };
47
- export { BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible, useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon, RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct, useFastState, useFastStateFunctions, FastSimonApi, MainTheme, Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard, getLSVectorSearchOn, getLSVectorSearchDict, getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting, getShowRequestUrlSetting, setShowRequestUrlSetting, getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken };
45
+ import { LiveSessionData, SpvProductMeta, RequestType, SpvLimits, SPV_LIMITS, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken } from "./utils/liveSessionPreview";
46
+ export type { MenuItemName, CustomClasses, SideMenuType, SideMenuItem, ProductLabelType, Banner, EditorProduct, IconProps, ServerProduct, ProductAnalytics, ProductPersonalizationCoefficients, FilterCoefEntry, Pillar, PillarKind, PersonalizationConfidence, PersonalizationStory, FullTextServerResponseAll, QueryUnderstanding, smartCollectionsResults, AutocompleteReasoningResponse, FeaturedRanking, ComparisonData, UserSegmentPreview, UserSegmentPreviewSettings, PersonalizationPreviewSettings, PersonalizationType, SessionTokenPreviewSettings, LiveSessionData, SpvProductMeta, RequestType, SpvLimits };
47
+ export { BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible, useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon, RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct, useFastState, useFastStateFunctions, FastSimonApi, MainTheme, Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard, getLSVectorSearchOn, getLSVectorSearchDict, getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting, getShowRequestUrlSetting, setShowRequestUrlSetting, getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken, SPV_LIMITS };
package/dist/index.js CHANGED
@@ -34,6 +34,6 @@ import { getShowRequestUrlSetting, setShowRequestUrlSetting } from "./utils/getR
34
34
  import { getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings } from "./utils/userSegmentPreview";
35
35
  import { getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams } from "./utils/personalizationPreview";
36
36
  import { getSessionTokenPreviewSettings, setSessionTokenPreviewSettings } from "./utils/sessionTokenPreview";
37
- import { generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken } from "./utils/liveSessionPreview";
38
- export { BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible, useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon, RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct, useFastState, useFastStateFunctions, FastSimonApi, MainTheme, Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard, getLSVectorSearchOn, getLSVectorSearchDict, getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting, getShowRequestUrlSetting, setShowRequestUrlSetting, getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken };
37
+ import { SPV_LIMITS, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken } from "./utils/liveSessionPreview";
38
+ export { BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible, useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon, RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct, useFastState, useFastStateFunctions, FastSimonApi, MainTheme, Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard, getLSVectorSearchOn, getLSVectorSearchDict, getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting, getShowRequestUrlSetting, setShowRequestUrlSetting, getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken, SPV_LIMITS };
39
39
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {CustomClasses, SideMenuType, SideMenuItem, MenuItemName} from \"./@types/sideMenu\"\nimport {ProductLabelType} from \"./@types/productLabel\"\nimport {videoFormats, Banner} from \"./@types/banner\"\nimport {EditorProduct, isEditorProduct} from \"./@types/editorProduct\"\nimport {IconProps} from \"./@types/iconProps\"\n\nimport {BackOfficeFiltersIcon} from \"./icons/BackOfficeFiltersIcon\"\nimport {BannersIcon} from \"./icons/BannersIcon\"\nimport {HelpIcon} from \"./icons/HelpIcon\"\nimport {InStoreFiltersIcon} from \"./icons/InStoreFiltersIcon\"\nimport {PromoteIcon} from \"./icons/PromoteIcon\"\nimport {RemoveIcon} from \"./icons/RemoveIcon\"\nimport {RestoreIcon} from \"./icons/RestoreIcon\"\nimport {GeneralIcon} from \"./icons/GeneralIcon\"\nimport BackOfficeFiltersMenu from './components/BackOfficeFiltersMenu'\nimport InStoreFiltersMenu from './components/InStoreFiltersMenu'\nimport DndContextWrapper from './components/DragAndDrop/DndContextWrapper'\nimport Droppable from './components/DragAndDrop/Droppable'\nimport ProductLabel from './components/ProductLabel/ProductLabel'\nimport {SortableItem} from './components/DragAndDrop/SortableItem'\nimport ProductGrid from \"./components/productGrid/ProductGrid\";\nimport SelectCard from \"./components/SelectCard/SelectCard\";\nimport RenderIfVisible from \"./components/RenderIfVisible\";\nimport {useDragToSelect} from \"./utils/useDragToSelect\";\nimport {CustomTagsFilters} from \"./utils/customTagsFilters\";\nimport {useProductsPerRow} from \"./utils/useProductsPerRow\";\nimport { LandingPage } from \"./services/landingPage\";\nimport { FullTextSearch } from \"./services/search\";\nimport { getFormatProducts } from \"./services/getFormatProducts\";\nimport {useFastState, useFastStateFunctions, ComparisonData} from \"./components/FastSimonApi\"\nimport { ServerProduct, ProductAnalytics, ProductPersonalizationCoefficients, FilterCoefEntry, Pillar, PillarKind, PersonalizationConfidence, PersonalizationStory } from \"./@types/product\"\nimport { FullTextServerResponseAll, QueryUnderstanding } from \"./@types/results\"\nimport { smartCollectionsResults } from \"./@types/categories\"\nimport FastSimonApi from \"./components/FastSimonApi/FastSimonApi\"\nimport MainTheme from \"./components/FastSimonApi\"\nimport { FeaturedRanking } from \"@fast-simon/utilities/lib/@types/product\"\nimport { Products } from \"./services/products\"\nimport {getLSVectorSearchDict, getLSVectorSearchOn } from \"./utils/getLSVectorSearch\"\nimport { AutocompleteReasoningResponse } from \"./@types/autocomplete\"\nimport {getUseCacheSetting, setUseCacheSetting} from \"./utils/getCacheSettings\"\nimport {getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting} from \"./utils/getSalesAnalyticsSettings\"\nimport {getShowRequestUrlSetting, setShowRequestUrlSetting} from \"./utils/getRequestUrlSettings\"\nimport {getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, UserSegmentPreview, UserSegmentPreviewSettings} from \"./utils/userSegmentPreview\"\nimport {getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, PersonalizationPreviewSettings, PersonalizationType} from \"./utils/personalizationPreview\"\nimport {getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, SessionTokenPreviewSettings} from \"./utils/sessionTokenPreview\"\nimport {LiveSessionData, SpvProductMeta, RequestType, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken} from \"./utils/liveSessionPreview\"\n// export * from './components';\n\nexport type {\n MenuItemName,\n CustomClasses,\n SideMenuType,\n SideMenuItem,\n ProductLabelType,\n Banner,\n EditorProduct,\n IconProps,\n ServerProduct,\n ProductAnalytics,\n ProductPersonalizationCoefficients,\n FilterCoefEntry,\n Pillar,\n PillarKind,\n PersonalizationConfidence,\n PersonalizationStory,\n FullTextServerResponseAll,\n QueryUnderstanding,\n smartCollectionsResults,\n AutocompleteReasoningResponse,\n FeaturedRanking,\n ComparisonData,\n UserSegmentPreview,\n UserSegmentPreviewSettings,\n PersonalizationPreviewSettings,\n PersonalizationType,\n SessionTokenPreviewSettings,\n LiveSessionData,\n SpvProductMeta,\n RequestType\n};\n\nexport {\n BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible,\n useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon,\n RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct,useFastState, useFastStateFunctions,FastSimonApi,MainTheme,\n Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard ,getLSVectorSearchOn,getLSVectorSearchDict,\n getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting,\n getShowRequestUrlSetting, setShowRequestUrlSetting,\n getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings,\n getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams,\n getSessionTokenPreviewSettings, setSessionTokenPreviewSettings,\n generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart,\n buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken\n};"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {CustomClasses, SideMenuType, SideMenuItem, MenuItemName} from \"./@types/sideMenu\"\nimport {ProductLabelType} from \"./@types/productLabel\"\nimport {videoFormats, Banner} from \"./@types/banner\"\nimport {EditorProduct, isEditorProduct} from \"./@types/editorProduct\"\nimport {IconProps} from \"./@types/iconProps\"\n\nimport {BackOfficeFiltersIcon} from \"./icons/BackOfficeFiltersIcon\"\nimport {BannersIcon} from \"./icons/BannersIcon\"\nimport {HelpIcon} from \"./icons/HelpIcon\"\nimport {InStoreFiltersIcon} from \"./icons/InStoreFiltersIcon\"\nimport {PromoteIcon} from \"./icons/PromoteIcon\"\nimport {RemoveIcon} from \"./icons/RemoveIcon\"\nimport {RestoreIcon} from \"./icons/RestoreIcon\"\nimport {GeneralIcon} from \"./icons/GeneralIcon\"\nimport BackOfficeFiltersMenu from './components/BackOfficeFiltersMenu'\nimport InStoreFiltersMenu from './components/InStoreFiltersMenu'\nimport DndContextWrapper from './components/DragAndDrop/DndContextWrapper'\nimport Droppable from './components/DragAndDrop/Droppable'\nimport ProductLabel from './components/ProductLabel/ProductLabel'\nimport {SortableItem} from './components/DragAndDrop/SortableItem'\nimport ProductGrid from \"./components/productGrid/ProductGrid\";\nimport SelectCard from \"./components/SelectCard/SelectCard\";\nimport RenderIfVisible from \"./components/RenderIfVisible\";\nimport {useDragToSelect} from \"./utils/useDragToSelect\";\nimport {CustomTagsFilters} from \"./utils/customTagsFilters\";\nimport {useProductsPerRow} from \"./utils/useProductsPerRow\";\nimport { LandingPage } from \"./services/landingPage\";\nimport { FullTextSearch } from \"./services/search\";\nimport { getFormatProducts } from \"./services/getFormatProducts\";\nimport {useFastState, useFastStateFunctions, ComparisonData} from \"./components/FastSimonApi\"\nimport { ServerProduct, ProductAnalytics, ProductPersonalizationCoefficients, FilterCoefEntry, Pillar, PillarKind, PersonalizationConfidence, PersonalizationStory } from \"./@types/product\"\nimport { FullTextServerResponseAll, QueryUnderstanding } from \"./@types/results\"\nimport { smartCollectionsResults } from \"./@types/categories\"\nimport FastSimonApi from \"./components/FastSimonApi/FastSimonApi\"\nimport MainTheme from \"./components/FastSimonApi\"\nimport { FeaturedRanking } from \"@fast-simon/utilities/lib/@types/product\"\nimport { Products } from \"./services/products\"\nimport {getLSVectorSearchDict, getLSVectorSearchOn } from \"./utils/getLSVectorSearch\"\nimport { AutocompleteReasoningResponse } from \"./@types/autocomplete\"\nimport {getUseCacheSetting, setUseCacheSetting} from \"./utils/getCacheSettings\"\nimport {getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting} from \"./utils/getSalesAnalyticsSettings\"\nimport {getShowRequestUrlSetting, setShowRequestUrlSetting} from \"./utils/getRequestUrlSettings\"\nimport {getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings, UserSegmentPreview, UserSegmentPreviewSettings} from \"./utils/userSegmentPreview\"\nimport {getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams, PersonalizationPreviewSettings, PersonalizationType} from \"./utils/personalizationPreview\"\nimport {getSessionTokenPreviewSettings, setSessionTokenPreviewSettings, SessionTokenPreviewSettings} from \"./utils/sessionTokenPreview\"\nimport {LiveSessionData, SpvProductMeta, RequestType, SpvLimits, SPV_LIMITS, generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart, buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken} from \"./utils/liveSessionPreview\"\n// export * from './components';\n\nexport type {\n MenuItemName,\n CustomClasses,\n SideMenuType,\n SideMenuItem,\n ProductLabelType,\n Banner,\n EditorProduct,\n IconProps,\n ServerProduct,\n ProductAnalytics,\n ProductPersonalizationCoefficients,\n FilterCoefEntry,\n Pillar,\n PillarKind,\n PersonalizationConfidence,\n PersonalizationStory,\n FullTextServerResponseAll,\n QueryUnderstanding,\n smartCollectionsResults,\n AutocompleteReasoningResponse,\n FeaturedRanking,\n ComparisonData,\n UserSegmentPreview,\n UserSegmentPreviewSettings,\n PersonalizationPreviewSettings,\n PersonalizationType,\n SessionTokenPreviewSettings,\n LiveSessionData,\n SpvProductMeta,\n RequestType,\n SpvLimits\n};\n\nexport {\n BackOfficeFiltersMenu, InStoreFiltersMenu, Droppable, DndContextWrapper, SortableItem, ProductGrid, RenderIfVisible,\n useDragToSelect, LandingPage, FullTextSearch, BackOfficeFiltersIcon, BannersIcon, HelpIcon, InStoreFiltersIcon, PromoteIcon,\n RestoreIcon, RemoveIcon, GeneralIcon, videoFormats, isEditorProduct,useFastState, useFastStateFunctions,FastSimonApi,MainTheme,\n Products, getFormatProducts, useProductsPerRow, ProductLabel, CustomTagsFilters, SelectCard ,getLSVectorSearchOn,getLSVectorSearchDict,\n getUseCacheSetting, setUseCacheSetting, getShowSalesAnalyticsSetting, setShowSalesAnalyticsSetting,\n getShowRequestUrlSetting, setShowRequestUrlSetting,\n getPersonalizationSegment, getUserSegmentPreviewSettings, setUserSegmentPreviewSettings,\n getPersonalizationPreviewSettings, setPersonalizationPreviewSettings, buildPersonalizationParams,\n getSessionTokenPreviewSettings, setSessionTokenPreviewSettings,\n generateSessionToken, getLiveSessionData, setLiveSessionData, addProductView, addProductToCart,\n buildLiveSessionParams, resetLiveSession, startLiveSession, stopLiveSession, setSessionSegment, setLiveSessionSiteScope, formatTokenAge, getUserSegmentFromToken, SPV_LIMITS\n};"]}
@@ -15,6 +15,12 @@ export interface LiveSessionData {
15
15
  lastActive: number;
16
16
  }
17
17
  export type RequestType = 'collection' | 'search';
18
+ export interface SpvLimits {
19
+ min: number;
20
+ max: number;
21
+ expiryDays: number;
22
+ }
23
+ export declare const SPV_LIMITS: Record<RequestType, SpvLimits>;
18
24
  /**
19
25
  * Set the site scope for live session storage. Must be called when the site changes.
20
26
  * This ensures SPV/token are isolated per site.
@@ -1,4 +1,7 @@
1
- const SPV_LIMITS = {
1
+ // Exported because the Explainer's session UI has to say when a session carries enough views to
2
+ // personalize, and `min` differs per surface. Two components had each re-derived it as a literal
3
+ // and had already drifted apart -- one said 1, the other 3 -- so the number ships from here.
4
+ export const SPV_LIMITS = {
2
5
  collection: { min: 1, max: 10, expiryDays: 365 },
3
6
  search: { min: 3, max: 50, expiryDays: 5 },
4
7
  };
@@ -1 +1 @@
1
- {"version":3,"file":"liveSessionPreview.js","sourceRoot":"","sources":["../../src/utils/liveSessionPreview.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,GAAmC;IAC/C,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE;IAChD,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;CAC7C,CAAC;AAEF,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAC5D,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,qDAAqD;AAEhG,sEAAsE;AACtE,IAAI,UAAU,GAAG,EAAE,CAAC;AAEpB;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAChD,UAAU,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,SAAS,iBAAiB;IACtB,OAAO,UAAU,CAAC,CAAC,CAAC,GAAG,uBAAuB,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,uBAAuB,CAAC;AAC7F,CAAC;AAED,8CAA8C;AAC9C,MAAM,SAAS,GAAG,kCAAkC,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAoB;IACrD,MAAM,GAAG,GAAG,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACtC,IAAI,EAAE,GAAG,GAAG,CAAC;IACb,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QACzB,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QACnC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;KAC5B;IACD,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QACzB,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;KAC3D;IACD,OAAO,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,WAAW,GAAoB;IACjC,OAAO,EAAE,KAAK;IACd,YAAY,EAAE,EAAE;IAChB,GAAG,EAAE,EAAE;IACP,OAAO,EAAE,EAAE;IACX,GAAG,EAAE,EAAE;IACP,OAAO,EAAE,EAAE;IACX,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,UAAU,kBAAkB;IAC9B,IAAI;QACA,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACzD,IAAI,MAAM,EAAE;YACR,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC;YACzC,mFAAmF;YACnF,kFAAkF;YAClF,+EAA+E;YAC/E,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5G,IAAI,CAAC,MAAM,CAAC,UAAU;gBAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YAClE,OAAO,MAAM,CAAC;SACjB;KACJ;IAAC,OAAO,CAAC,EAAE;QACR,wBAAwB;KAC3B;IACD,yBAAY,WAAW,EAAG;AAC9B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAqB;IACpD,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACpE,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAAqB;IAC3C,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,kBAAkB,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAqB;IACvC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACtB,GAA2B,EAC3B,MAAiB;IAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;IACzE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,qCAAqC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,MAAM,MAAM,GAAG,OAAO;SACjB,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC;SACjC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAE1B,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IACjD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,IAAqB;IACnE,MAAM,IAAI,GAAG,YAAY,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACpD,IAAI,IAAI;QAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACzC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,iBAAyB,EAAE,IAAqB;IAC7E,2FAA2F;IAC3F,iDAAiD;IACjD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACnC,OAAO,kBAAkB,EAAE,CAAC;KAC/B;IACD,MAAM,IAAI,GAAG,YAAY,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC5D,IAAI,IAAI;QAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IACjD,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAqB,EAAE,cAA2B,YAAY,EAAE,uBAAiC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;IAK9J,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;QACf,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,EAAE,CAAC;KACpG;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxD,mEAAmE;IACnE,MAAM,WAAW,GAAG,WAAW,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,wFAAwF;IACxF,wFAAwF;IACxF,6FAA6F;IAC7F,+FAA+F;IAC/F,8FAA8F;IAC9F,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,GAAG,CAAC,CAAC;IACjD,OAAO;QACH,uBAAuB,EAAE,CAAC,WAAW,IAAI,WAAW,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;YAChE,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;YACvD,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;YACvD,KAAK,EAAE,oBAAoB;SAC9B,CAAC,CAAC,CAAC,IAAI;QACR,aAAa,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAC7C,oBAAoB,EAAE,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC;KAChE,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAA4B;IAC1D,MAAM,IAAI,GAAG,YAAY,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,OAAO,KAAK,WAAW;QAC1C,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAE,aAAa;QACrD,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACjB,IAAI,CAAC,YAAY,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;IACzD,IAAI,CAAC,SAAS,GAAG,cAAc,CAAC;IAChC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,IAAI,GAAoB;QAC1B,OAAO,EAAE,IAAI;QACb,YAAY,EAAE,oBAAoB,EAAE;QACpC,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,GAAG;QACd,UAAU,EAAE,GAAG;KAClB,CAAC;IACF,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC5B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,IAAI,QAAQ,CAAC,YAAY,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE;QACtD,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,QAAQ,CAAC;KACnB;IACD,uCAAuC;IACvC,OAAO,gBAAgB,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC3B,MAAM,IAAI,GAAG,kBAAkB,EAAE,CAAC;IAClC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACrB,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC5C,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC;IAC1C,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,GAAG,KAAK,KAAK,OAAO,GAAG,CAAC;IAC9C,OAAO,GAAG,OAAO,GAAG,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACrD,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;AACjF,CAAC","sourcesContent":["export interface SpvProductMeta {\n title: string;\n image?: string;\n url?: string;\n size?: string; // cart-adds only: the size that was added (a product can be carted in several sizes)\n}\n\nexport interface LiveSessionData {\n enabled: boolean;\n sessionToken: string;\n spv: Record<string, number>; // {product_id: unix_timestamp}\n spvMeta: Record<string, SpvProductMeta>; // {product_id: {title, image}} — display info only\n spc: Record<string, number>; // {\"product_id::variant_id\": unix_timestamp} — cart actions\n spcMeta: Record<string, SpvProductMeta>; // {\"product_id::variant_id\": {title, image, url, size}} — per cart-add, display info only\n createdAt: number; // unix timestamp ms when token was created\n lastActive: number; // unix timestamp ms of last activity (matches storefront 30-min timeout)\n}\n\nexport type RequestType = 'collection' | 'search';\n\ninterface SpvLimits {\n min: number;\n max: number;\n expiryDays: number;\n}\n\nconst SPV_LIMITS: Record<RequestType, SpvLimits> = {\n collection: { min: 1, max: 10, expiryDays: 365 },\n search: { min: 3, max: 50, expiryDays: 5 },\n};\n\nconst LIVE_SESSION_KEY_PREFIX = 'ai_explainer_live_session';\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes — matches storefront SESSION_TOKEN_LIFE\n\n// Current site scope — must be set before any live session operations\nlet _siteScope = '';\n\n/**\n * Set the site scope for live session storage. Must be called when the site changes.\n * This ensures SPV/token are isolated per site.\n */\nexport function setLiveSessionSiteScope(uuid: string): void {\n _siteScope = uuid;\n}\n\nfunction getLiveSessionKey(): string {\n return _siteScope ? `${LIVE_SESSION_KEY_PREFIX}_${_siteScope}` : LIVE_SESSION_KEY_PREFIX;\n}\n\n// Crockford Base32 alphabet for ULID encoding\nconst CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\n\n/**\n * Generate a ULID-based session token matching production format: 'g' + 26-char ULID.\n * @param timestampMs - Optional timestamp in ms to encode (defaults to Date.now()).\n * Use a past timestamp to simulate a \"returning\" user (e.g., Date.now() - 2 * 86400000).\n */\nexport function generateSessionToken(timestampMs?: number): string {\n const now = timestampMs ?? Date.now();\n let ts = now;\n const timePart = new Array(10);\n for (let i = 9; i >= 0; i--) {\n timePart[i] = CROCKFORD[ts & 0x1f];\n ts = Math.floor(ts / 32);\n }\n const randPart = new Array(16);\n for (let i = 0; i < 16; i++) {\n randPart[i] = CROCKFORD[Math.floor(Math.random() * 32)];\n }\n return 'g' + timePart.join('') + randPart.join('');\n}\n\nconst defaultData: LiveSessionData = {\n enabled: false,\n sessionToken: '',\n spv: {},\n spvMeta: {},\n spc: {},\n spcMeta: {},\n createdAt: 0,\n lastActive: 0,\n};\n\nexport function getLiveSessionData(): LiveSessionData {\n try {\n const stored = localStorage.getItem(getLiveSessionKey());\n if (stored) {\n const parsed = JSON.parse(stored);\n if (!parsed.spvMeta) parsed.spvMeta = {};\n // spc / spcMeta are keyed by \"product_id::variant_id\". Drop any key without \"::\" —\n // serving silently ignores such keys and they'd corrupt the product-id derivation\n // (cartedProductIds). Also covers data from an earlier bare-variant-id format.\n parsed.spc = Object.fromEntries(Object.entries(parsed.spc || {}).filter(([k]) => k.includes('::')));\n parsed.spcMeta = Object.fromEntries(Object.entries(parsed.spcMeta || {}).filter(([k]) => k.includes('::')));\n if (!parsed.lastActive) parsed.lastActive = parsed.createdAt || 0;\n return parsed;\n }\n } catch (e) {\n // Ignore parsing errors\n }\n return { ...defaultData };\n}\n\nexport function setLiveSessionData(data: LiveSessionData): void {\n localStorage.setItem(getLiveSessionKey(), JSON.stringify(data));\n}\n\n/**\n * Check if the session has timed out (30+ min of inactivity).\n */\nfunction isSessionExpired(data: LiveSessionData): boolean {\n if (!data.lastActive) return false;\n return (Date.now() - data.lastActive) >= SESSION_TIMEOUT_MS;\n}\n\n/**\n * Touch the session (update lastActive). Called on every meaningful action.\n */\nfunction touchSession(data: LiveSessionData): LiveSessionData {\n data.lastActive = Date.now();\n return data;\n}\n\n/**\n * Filter and cap an accumulator (SPV or SPC) for a specific request type.\n */\nfunction filterAccumulator(\n acc: Record<string, number>,\n limits: SpvLimits\n): Record<string, number> | undefined {\n const cutoff = Math.floor(Date.now() / 1000) - limits.expiryDays * 86400;\n const entries = Object.entries(acc);\n // Fast path: skip filtering if empty\n if (entries.length === 0) return undefined;\n\n const recent = entries\n .filter(([_, ts]) => ts >= cutoff)\n .sort((a, b) => b[1] - a[1])\n .slice(0, limits.max);\n\n if (recent.length < limits.min) return undefined;\n return Object.fromEntries(recent);\n}\n\n/**\n * Add a product view to SPV. Touches session. Persists to localStorage.\n * @param meta - Optional product title/image for display in the session panel.\n */\nexport function addProductView(productId: string, meta?: SpvProductMeta): LiveSessionData {\n const data = touchSession(getLiveSessionData());\n data.spv[productId] = Math.floor(Date.now() / 1000);\n if (meta) data.spvMeta[productId] = meta;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Add a cart-add to SPC. Touches session. Persists to localStorage.\n * @param productVariantKey - must be \"product_id::variant_id\" to match the serving parser\n * (a key without \"::\" is silently dropped by serving's _init_spc_cart; treated as a no-op here).\n * @param meta - optional product title/image/url/size for display in the session panel, keyed by\n * the full \"product_id::variant_id\" so each size of a product is its own cart-add.\n */\nexport function addProductToCart(productVariantKey: string, meta?: SpvProductMeta): LiveSessionData {\n // Validate before touching the session: serving drops keys without \"::\", so a bad key is a\n // no-op and shouldn't mutate the session at all.\n if (!productVariantKey.includes('::')) {\n return getLiveSessionData();\n }\n const data = touchSession(getLiveSessionData());\n data.spc[productVariantKey] = Math.floor(Date.now() / 1000);\n if (meta) data.spcMeta[productVariantKey] = meta;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Convert LiveSessionData into the params format that updateSettingsAndRefetch expects.\n * Also touches the session (updates lastActive).\n */\n/**\n * @param personalizationTypes - Available filter types from the backend (e.g., ['gender', 'size', 'type']).\n * Defaults to all known types if not provided.\n */\nexport function buildLiveSessionParams(data: LiveSessionData, requestType: RequestType = 'collection', personalizationTypes: string[] = ['gender', 'size', 'type']): {\n personalization_preview: { enabled: boolean; spvJson: string; spcJson: string; types: string[] } | null;\n session_token: string | undefined;\n user_segment_preview: 'none' | 'new' | 'returning';\n} {\n if (!data.enabled) {\n return { personalization_preview: null, session_token: undefined, user_segment_preview: 'none' };\n }\n\n const limits = SPV_LIMITS[requestType];\n const filteredSpv = filterAccumulator(data.spv, limits);\n // SPC only sent with collections (not supported in search serving)\n const filteredSpc = requestType === 'collection' ? filterAccumulator(data.spc, limits) : undefined;\n\n // spv OR spc enables the preview — a cart-only session (size personalization) is valid.\n // Active personalization types ALSO enable it with an empty session: sites that run the\n // size-availability correction from n=0 (empty cart -> population-prior baseline) still need\n // the request to carry `personalization`, otherwise serving's use_filter_personalization stays\n // off and the cold-start size down-ranking a real first-time shopper sees can't be previewed.\n const hasTypes = personalizationTypes.length > 0;\n return {\n personalization_preview: (filteredSpv || filteredSpc || hasTypes) ? {\n enabled: true,\n spvJson: filteredSpv ? JSON.stringify(filteredSpv) : '',\n spcJson: filteredSpc ? JSON.stringify(filteredSpc) : '',\n types: personalizationTypes,\n } : null,\n session_token: data.sessionToken || undefined,\n user_segment_preview: getUserSegmentFromToken(data.createdAt),\n };\n}\n\n/**\n * Regenerate session token with a specific age for new/returning segment testing.\n * \"new\" → token created now (age < 1 day). \"returning\" → token created 2 days ago.\n * Preserves SPV/SPC — only the token changes.\n */\nexport function setSessionSegment(segment: 'new' | 'returning'): LiveSessionData {\n const data = touchSession(getLiveSessionData());\n const tokenTimestamp = segment === 'returning'\n ? Date.now() - 2 * 24 * 60 * 60 * 1000 // 2 days ago\n : Date.now();\n data.sessionToken = generateSessionToken(tokenTimestamp);\n data.createdAt = tokenTimestamp;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Reset live session: fresh token, clear SPV + SPC.\n */\nexport function resetLiveSession(): LiveSessionData {\n const now = Date.now();\n const data: LiveSessionData = {\n enabled: true,\n sessionToken: generateSessionToken(),\n spv: {},\n spvMeta: {},\n spc: {},\n spcMeta: {},\n createdAt: now,\n lastActive: now,\n };\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Start a live session. Resumes if existing and not expired.\n * If expired (30+ min inactive), resets to a fresh session (matching storefront behavior).\n */\nexport function startLiveSession(): LiveSessionData {\n const existing = getLiveSessionData();\n if (existing.sessionToken && !isSessionExpired(existing)) {\n existing.enabled = true;\n existing.lastActive = Date.now();\n setLiveSessionData(existing);\n return existing;\n }\n // No session or expired — create fresh\n return resetLiveSession();\n}\n\n/**\n * Stop live session (pause). Token, SPV and SPC remain for potential resume.\n */\nexport function stopLiveSession(): LiveSessionData {\n const data = getLiveSessionData();\n data.enabled = false;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Format token age as human-readable string.\n */\nexport function formatTokenAge(createdAt: number): string {\n if (!createdAt) return 'N/A';\n const totalMinutes = Math.floor((Date.now() - createdAt) / 60000);\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n if (days > 0) return `${days}d ${hours}h`;\n if (hours > 0) return `${hours}h ${minutes}m`;\n return `${minutes}m`;\n}\n\n/**\n * Get user segment derived from token age.\n */\nexport function getUserSegmentFromToken(createdAt: number): 'new' | 'returning' {\n if (!createdAt) return 'new';\n return (Date.now() - createdAt) >= 24 * 60 * 60 * 1000 ? 'returning' : 'new';\n}\n"]}
1
+ {"version":3,"file":"liveSessionPreview.js","sourceRoot":"","sources":["../../src/utils/liveSessionPreview.ts"],"names":[],"mappings":"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","sourcesContent":["export interface SpvProductMeta {\n title: string;\n image?: string;\n url?: string;\n size?: string; // cart-adds only: the size that was added (a product can be carted in several sizes)\n}\n\nexport interface LiveSessionData {\n enabled: boolean;\n sessionToken: string;\n spv: Record<string, number>; // {product_id: unix_timestamp}\n spvMeta: Record<string, SpvProductMeta>; // {product_id: {title, image}} — display info only\n spc: Record<string, number>; // {\"product_id::variant_id\": unix_timestamp} — cart actions\n spcMeta: Record<string, SpvProductMeta>; // {\"product_id::variant_id\": {title, image, url, size}} — per cart-add, display info only\n createdAt: number; // unix timestamp ms when token was created\n lastActive: number; // unix timestamp ms of last activity (matches storefront 30-min timeout)\n}\n\nexport type RequestType = 'collection' | 'search';\n\nexport interface SpvLimits {\n min: number;\n max: number;\n expiryDays: number;\n}\n\n// Exported because the Explainer's session UI has to say when a session carries enough views to\n// personalize, and `min` differs per surface. Two components had each re-derived it as a literal\n// and had already drifted apart -- one said 1, the other 3 -- so the number ships from here.\nexport const SPV_LIMITS: Record<RequestType, SpvLimits> = {\n collection: { min: 1, max: 10, expiryDays: 365 },\n search: { min: 3, max: 50, expiryDays: 5 },\n};\n\nconst LIVE_SESSION_KEY_PREFIX = 'ai_explainer_live_session';\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes — matches storefront SESSION_TOKEN_LIFE\n\n// Current site scope — must be set before any live session operations\nlet _siteScope = '';\n\n/**\n * Set the site scope for live session storage. Must be called when the site changes.\n * This ensures SPV/token are isolated per site.\n */\nexport function setLiveSessionSiteScope(uuid: string): void {\n _siteScope = uuid;\n}\n\nfunction getLiveSessionKey(): string {\n return _siteScope ? `${LIVE_SESSION_KEY_PREFIX}_${_siteScope}` : LIVE_SESSION_KEY_PREFIX;\n}\n\n// Crockford Base32 alphabet for ULID encoding\nconst CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\n\n/**\n * Generate a ULID-based session token matching production format: 'g' + 26-char ULID.\n * @param timestampMs - Optional timestamp in ms to encode (defaults to Date.now()).\n * Use a past timestamp to simulate a \"returning\" user (e.g., Date.now() - 2 * 86400000).\n */\nexport function generateSessionToken(timestampMs?: number): string {\n const now = timestampMs ?? Date.now();\n let ts = now;\n const timePart = new Array(10);\n for (let i = 9; i >= 0; i--) {\n timePart[i] = CROCKFORD[ts & 0x1f];\n ts = Math.floor(ts / 32);\n }\n const randPart = new Array(16);\n for (let i = 0; i < 16; i++) {\n randPart[i] = CROCKFORD[Math.floor(Math.random() * 32)];\n }\n return 'g' + timePart.join('') + randPart.join('');\n}\n\nconst defaultData: LiveSessionData = {\n enabled: false,\n sessionToken: '',\n spv: {},\n spvMeta: {},\n spc: {},\n spcMeta: {},\n createdAt: 0,\n lastActive: 0,\n};\n\nexport function getLiveSessionData(): LiveSessionData {\n try {\n const stored = localStorage.getItem(getLiveSessionKey());\n if (stored) {\n const parsed = JSON.parse(stored);\n if (!parsed.spvMeta) parsed.spvMeta = {};\n // spc / spcMeta are keyed by \"product_id::variant_id\". Drop any key without \"::\" —\n // serving silently ignores such keys and they'd corrupt the product-id derivation\n // (cartedProductIds). Also covers data from an earlier bare-variant-id format.\n parsed.spc = Object.fromEntries(Object.entries(parsed.spc || {}).filter(([k]) => k.includes('::')));\n parsed.spcMeta = Object.fromEntries(Object.entries(parsed.spcMeta || {}).filter(([k]) => k.includes('::')));\n if (!parsed.lastActive) parsed.lastActive = parsed.createdAt || 0;\n return parsed;\n }\n } catch (e) {\n // Ignore parsing errors\n }\n return { ...defaultData };\n}\n\nexport function setLiveSessionData(data: LiveSessionData): void {\n localStorage.setItem(getLiveSessionKey(), JSON.stringify(data));\n}\n\n/**\n * Check if the session has timed out (30+ min of inactivity).\n */\nfunction isSessionExpired(data: LiveSessionData): boolean {\n if (!data.lastActive) return false;\n return (Date.now() - data.lastActive) >= SESSION_TIMEOUT_MS;\n}\n\n/**\n * Touch the session (update lastActive). Called on every meaningful action.\n */\nfunction touchSession(data: LiveSessionData): LiveSessionData {\n data.lastActive = Date.now();\n return data;\n}\n\n/**\n * Filter and cap an accumulator (SPV or SPC) for a specific request type.\n */\nfunction filterAccumulator(\n acc: Record<string, number>,\n limits: SpvLimits\n): Record<string, number> | undefined {\n const cutoff = Math.floor(Date.now() / 1000) - limits.expiryDays * 86400;\n const entries = Object.entries(acc);\n // Fast path: skip filtering if empty\n if (entries.length === 0) return undefined;\n\n const recent = entries\n .filter(([_, ts]) => ts >= cutoff)\n .sort((a, b) => b[1] - a[1])\n .slice(0, limits.max);\n\n if (recent.length < limits.min) return undefined;\n return Object.fromEntries(recent);\n}\n\n/**\n * Add a product view to SPV. Touches session. Persists to localStorage.\n * @param meta - Optional product title/image for display in the session panel.\n */\nexport function addProductView(productId: string, meta?: SpvProductMeta): LiveSessionData {\n const data = touchSession(getLiveSessionData());\n data.spv[productId] = Math.floor(Date.now() / 1000);\n if (meta) data.spvMeta[productId] = meta;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Add a cart-add to SPC. Touches session. Persists to localStorage.\n * @param productVariantKey - must be \"product_id::variant_id\" to match the serving parser\n * (a key without \"::\" is silently dropped by serving's _init_spc_cart; treated as a no-op here).\n * @param meta - optional product title/image/url/size for display in the session panel, keyed by\n * the full \"product_id::variant_id\" so each size of a product is its own cart-add.\n */\nexport function addProductToCart(productVariantKey: string, meta?: SpvProductMeta): LiveSessionData {\n // Validate before touching the session: serving drops keys without \"::\", so a bad key is a\n // no-op and shouldn't mutate the session at all.\n if (!productVariantKey.includes('::')) {\n return getLiveSessionData();\n }\n const data = touchSession(getLiveSessionData());\n data.spc[productVariantKey] = Math.floor(Date.now() / 1000);\n if (meta) data.spcMeta[productVariantKey] = meta;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Convert LiveSessionData into the params format that updateSettingsAndRefetch expects.\n * Also touches the session (updates lastActive).\n */\n/**\n * @param personalizationTypes - Available filter types from the backend (e.g., ['gender', 'size', 'type']).\n * Defaults to all known types if not provided.\n */\nexport function buildLiveSessionParams(data: LiveSessionData, requestType: RequestType = 'collection', personalizationTypes: string[] = ['gender', 'size', 'type']): {\n personalization_preview: { enabled: boolean; spvJson: string; spcJson: string; types: string[] } | null;\n session_token: string | undefined;\n user_segment_preview: 'none' | 'new' | 'returning';\n} {\n if (!data.enabled) {\n return { personalization_preview: null, session_token: undefined, user_segment_preview: 'none' };\n }\n\n const limits = SPV_LIMITS[requestType];\n const filteredSpv = filterAccumulator(data.spv, limits);\n // SPC only sent with collections (not supported in search serving)\n const filteredSpc = requestType === 'collection' ? filterAccumulator(data.spc, limits) : undefined;\n\n // spv OR spc enables the preview — a cart-only session (size personalization) is valid.\n // Active personalization types ALSO enable it with an empty session: sites that run the\n // size-availability correction from n=0 (empty cart -> population-prior baseline) still need\n // the request to carry `personalization`, otherwise serving's use_filter_personalization stays\n // off and the cold-start size down-ranking a real first-time shopper sees can't be previewed.\n const hasTypes = personalizationTypes.length > 0;\n return {\n personalization_preview: (filteredSpv || filteredSpc || hasTypes) ? {\n enabled: true,\n spvJson: filteredSpv ? JSON.stringify(filteredSpv) : '',\n spcJson: filteredSpc ? JSON.stringify(filteredSpc) : '',\n types: personalizationTypes,\n } : null,\n session_token: data.sessionToken || undefined,\n user_segment_preview: getUserSegmentFromToken(data.createdAt),\n };\n}\n\n/**\n * Regenerate session token with a specific age for new/returning segment testing.\n * \"new\" → token created now (age < 1 day). \"returning\" → token created 2 days ago.\n * Preserves SPV/SPC — only the token changes.\n */\nexport function setSessionSegment(segment: 'new' | 'returning'): LiveSessionData {\n const data = touchSession(getLiveSessionData());\n const tokenTimestamp = segment === 'returning'\n ? Date.now() - 2 * 24 * 60 * 60 * 1000 // 2 days ago\n : Date.now();\n data.sessionToken = generateSessionToken(tokenTimestamp);\n data.createdAt = tokenTimestamp;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Reset live session: fresh token, clear SPV + SPC.\n */\nexport function resetLiveSession(): LiveSessionData {\n const now = Date.now();\n const data: LiveSessionData = {\n enabled: true,\n sessionToken: generateSessionToken(),\n spv: {},\n spvMeta: {},\n spc: {},\n spcMeta: {},\n createdAt: now,\n lastActive: now,\n };\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Start a live session. Resumes if existing and not expired.\n * If expired (30+ min inactive), resets to a fresh session (matching storefront behavior).\n */\nexport function startLiveSession(): LiveSessionData {\n const existing = getLiveSessionData();\n if (existing.sessionToken && !isSessionExpired(existing)) {\n existing.enabled = true;\n existing.lastActive = Date.now();\n setLiveSessionData(existing);\n return existing;\n }\n // No session or expired — create fresh\n return resetLiveSession();\n}\n\n/**\n * Stop live session (pause). Token, SPV and SPC remain for potential resume.\n */\nexport function stopLiveSession(): LiveSessionData {\n const data = getLiveSessionData();\n data.enabled = false;\n setLiveSessionData(data);\n return data;\n}\n\n/**\n * Format token age as human-readable string.\n */\nexport function formatTokenAge(createdAt: number): string {\n if (!createdAt) return 'N/A';\n const totalMinutes = Math.floor((Date.now() - createdAt) / 60000);\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n if (days > 0) return `${days}d ${hours}h`;\n if (hours > 0) return `${hours}h ${minutes}m`;\n return `${minutes}m`;\n}\n\n/**\n * Get user segment derived from token age.\n */\nexport function getUserSegmentFromToken(createdAt: number): 'new' | 'returning' {\n if (!createdAt) return 'new';\n return (Date.now() - createdAt) >= 24 * 60 * 60 * 1000 ? 'returning' : 'new';\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fast-simon/dashboard-utilities",
3
- "version": "1.0.167-beta.1",
3
+ "version": "1.0.168-beta.0",
4
4
  "scripts": {
5
5
  "dev": "vite",
6
6
  "preview": "vite preview",