├── src ├── index.ts ├── types │ ├── index.ts │ ├── Target │ │ ├── index.ts │ │ ├── Class.ts │ │ ├── Reviews.ts │ │ └── Product.ts │ └── EScraper.ts ├── constant │ └── index.ts ├── core │ ├── index.ts │ └── Target.ts ├── entry.ts └── helpers │ └── index.ts ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── tsconfig.json ├── .eslintrc.js ├── package.json └── Readme.md /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './entry'; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | build/ 4 | 5 | .DS_Store 6 | test.ts -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Target'; 2 | export * from './EScraper'; 3 | -------------------------------------------------------------------------------- /src/constant/index.ts: -------------------------------------------------------------------------------- 1 | export const config = { 2 | supportedStores: ['Target'], 3 | }; 4 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | import { Target } from './Target'; 2 | 3 | export const eStores = { 4 | Target, 5 | }; 6 | -------------------------------------------------------------------------------- /src/types/Target/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Class'; 2 | export * from './Product'; 3 | export * from './Reviews'; 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | src/ 4 | 5 | 6 | .DS_Store 7 | .eslintrc.js 8 | tsconfig.json 9 | .gitignore 10 | .prettierrc.js 11 | test.ts -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 200, 6 | tabWidth: 4, 7 | }; 8 | -------------------------------------------------------------------------------- /src/types/Target/Class.ts: -------------------------------------------------------------------------------- 1 | export type TargetTypes = 'product' | 'location' | 'reviews'; 2 | 3 | export interface TargetConstructor { 4 | type?: TargetTypes; 5 | number: number; 6 | offset: number; 7 | keyword?: string; 8 | productId?: string; 9 | storeId?: number; 10 | sponsoredProducts?: number; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/EScraper.ts: -------------------------------------------------------------------------------- 1 | import { ItemEntity, ResultEntity } from '.'; 2 | 3 | export type Stores = 'Target'; 4 | 5 | export type ScrapingType = 'product' | 'reviews' | 'location'; 6 | 7 | export interface EntryOptions { 8 | store: Stores; 9 | productId?: string; 10 | keyword?: string; 11 | number?: number; 12 | offset?: number; 13 | storeId?: number; 14 | sponsoredProducts?: number; 15 | } 16 | 17 | export interface Output { 18 | total: number; 19 | has_more: boolean; 20 | next: number; 21 | output: ItemEntity[] | ResultEntity[]; 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "emitDecoratorMetadata": true, 5 | "lib": ["es7"], 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2017", 10 | "removeComments": true, 11 | "noImplicitAny": false, 12 | "moduleResolution": "node", 13 | "declaration": true, 14 | "noUnusedLocals": true, 15 | "outDir": "./build", 16 | "baseUrl": "src", 17 | "strict": true 18 | }, 19 | "include": ["src/**/*.*"], 20 | "exclude": ["node_modules", "src/**/*.test.ts", "**/__mocks__/*", "**/__mockData__/"] 21 | } 22 | -------------------------------------------------------------------------------- /src/entry.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-throw-literal */ 2 | import { eStores } from './core'; 3 | 4 | import { config } from './constant'; 5 | 6 | import { EntryOptions, ScrapingType, Output } from './types'; 7 | 8 | const eScraper = async ({ number = 30, offset = 0, keyword, storeId, sponsoredProducts, store, productId }: EntryOptions, type: ScrapingType): Promise => { 9 | if (!eStores[store]) { 10 | throw `Unsupported store. Supported stores: ${config.supportedStores}`; 11 | } 12 | const scraper = new eStores[store]({ number, offset, keyword, storeId, sponsoredProducts, productId }); 13 | 14 | const result = await scraper[type](); 15 | 16 | return result; 17 | }; 18 | 19 | export const product = async (options: EntryOptions) => eScraper(options, 'product'); 20 | export const reviews = async (options: EntryOptions) => eScraper(options, 'reviews'); 21 | -------------------------------------------------------------------------------- /src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-throw-literal */ 2 | /* eslint-disable no-restricted-syntax */ 3 | /* eslint-disable no-console */ 4 | /* eslint-disable guard-for-in */ 5 | /** 6 | * Validate input values 7 | * @param entry 8 | */ 9 | export const entryValidator = (entry: { [key: string]: { value: any; condition: any; required: boolean } }) => { 10 | for (const item in entry) { 11 | if (entry[item].required && !entry[item].value) { 12 | throw `Missing ${item}`; 13 | } 14 | if (typeof entry[item].condition === 'number') { 15 | if (typeof entry[item].value !== 'number') { 16 | throw `${item} can only be a number`; 17 | } 18 | 19 | if (entry[item].condition && entry[item].value > entry[item].condition) { 20 | throw `Max ${item} value is: ${entry[item].condition}`; 21 | } 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | }, 6 | extends: ['airbnb-base', 'prettier'], 7 | globals: { 8 | Atomics: 'readonly', 9 | SharedArrayBuffer: 'readonly', 10 | }, 11 | parser: '@typescript-eslint/parser', 12 | parserOptions: { 13 | ecmaVersion: 2018, 14 | sourceType: 'module', 15 | }, 16 | plugins: ['@typescript-eslint', 'prettier'], 17 | rules: { 18 | 'prettier/prettier': ['error'], 19 | 'import/no-unresolved': 'off', 20 | 'import/extensions': 'off', 21 | 'no-bitwise': 'off', 22 | camelcase: 'off', 23 | 'import/prefer-default-export': 'off', 24 | }, 25 | overrides: [ 26 | { 27 | files: ['*.ts'], 28 | rules: { 29 | '@typescript-eslint/no-unused-vars': [2, { args: 'none' }], 30 | }, 31 | }, 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-scraper", 3 | "description": "Collect products, reviews, locations from a different E-Commerce stores. Currently supporting Target.Com and soon more.", 4 | "main": "./build/index.js", 5 | "types": "./build/index.d.ts", 6 | "scripts": { 7 | "build": "rimraf build && tsc", 8 | "docker:build": "tsc", 9 | "format": "prettier --config ./.prettierrc.js --write './src/**/*.ts'", 10 | "lint": "eslint ./src/**/*.ts" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+ssh://git@github.com/drawrowfly/e-scraper.git" 15 | }, 16 | "keywords": [ 17 | "target", 18 | "store", 19 | "scraper", 20 | "product", 21 | "reviews", 22 | "location", 23 | "API" 24 | ], 25 | "author": "drawRowFly", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/drawrowfly/e-scraper/issues" 29 | }, 30 | "homepage": "https://github.com/drawrowfly/e-scraper#readme", 31 | "dependencies": { 32 | "async": "^3.2.0", 33 | "bluebird": "^3.7.2", 34 | "node-fetch": "^2.6.0", 35 | "yargs": "^15.3.1" 36 | }, 37 | "devDependencies": { 38 | "@types/async": "^3.2.3", 39 | "@types/bluebird": "^3.5.32", 40 | "@types/json2csv": "^5.0.1", 41 | "@types/node-fetch": "^2.5.7", 42 | "@types/ora": "^3.2.0", 43 | "@types/request-promise": "^4.1.46", 44 | "@types/yargs": "^15.0.5", 45 | "@typescript-eslint/eslint-plugin": "^3.0.2", 46 | "@typescript-eslint/parser": "^3.0.2", 47 | "eslint": "^7.1.0", 48 | "eslint-config-airbnb-base": "^14.1.0", 49 | "eslint-config-prettier": "^6.11.0", 50 | "eslint-plugin-import": "^2.20.2", 51 | "eslint-plugin-prettier": "^3.1.3", 52 | "jest": "^26.0.1", 53 | "prettier": "^2.0.5", 54 | "ts-jest": "^26.0.0", 55 | "ts-node": "^8.10.1", 56 | "typescript": "^3.9.3" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/types/Target/Reviews.ts: -------------------------------------------------------------------------------- 1 | export interface TargetProductReviews { 2 | hasErrors: boolean; 3 | offset: number; 4 | totalResults: number; 5 | limit: number; 6 | duration: number; 7 | result: ResultEntity[]; 8 | } 9 | export interface ResultEntity { 10 | Id: string; 11 | ProductId: string; 12 | Rating: number; 13 | RatingRange: number; 14 | Title?: string | null; 15 | ReviewText: string; 16 | IsRecommended?: boolean | null; 17 | SecondaryRatingsOrder?: (string | null)[] | null; 18 | SecondaryRatings: SecondaryRatings; 19 | BadgesOrder?: (string | null)[] | null; 20 | ClientResponses?: (ClientResponsesEntity | null)[] | null; 21 | Photos?: (PhotosEntity | null)[] | null; 22 | AuthorId: string; 23 | UserNickname: string; 24 | UserLocation?: null; 25 | Badges: Badges; 26 | IsSyndicated: boolean; 27 | SourceClient: string; 28 | IsRatingsOnly: boolean; 29 | TotalFeedbackCount: number; 30 | TotalPositiveFeedbackCount: number; 31 | TotalNegativeFeedbackCount: number; 32 | TotalClientResponseCount: number; 33 | SubmissionTime: string; 34 | } 35 | 36 | interface SecondaryRatings { 37 | Quality?: QualityOrValue | null; 38 | Value?: QualityOrValue1 | null; 39 | } 40 | interface QualityOrValue { 41 | Id: string; 42 | Value: number; 43 | Label: string; 44 | ValueRange: number; 45 | DisplayType: string; 46 | } 47 | interface QualityOrValue1 { 48 | Id: string; 49 | Value: number; 50 | Label: string; 51 | ValueRange: number; 52 | DisplayType: string; 53 | } 54 | interface ClientResponsesEntity { 55 | Department: string; 56 | Response: string; 57 | ResponseSource: string; 58 | Date: string; 59 | } 60 | interface PhotosEntity { 61 | Sizes: Sizes; 62 | } 63 | interface Sizes { 64 | normal: NormalOrThumbnail; 65 | thumbnail: NormalOrThumbnail; 66 | } 67 | interface NormalOrThumbnail { 68 | Id: string; 69 | Url: string; 70 | } 71 | interface Badges { 72 | verifiedPurchaser?: VerifiedPurchaser | null; 73 | } 74 | interface VerifiedPurchaser { 75 | Id: string; 76 | ContentType: string; 77 | BadgeType: string; 78 | } 79 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # E-Commerce Scraper 2 | 3 | Collect product and reviews from a different e-commerce stores. 4 | 5 | Note that current repo is in its early stages 6 | 7 | ## If you like this tool then please Star it 8 | 9 | ## Supported Stores 10 | - https://www.target.com/ 11 | 12 | ## Features 13 | - **Scrape products** 14 | - **Scrape reviews by product Id** 15 | 16 | ## To Do 17 | - [ ] Add proxy support 18 | - [ ] Run from the CLI 19 | - [ ] Save output to the CSV/JSON files 20 | - [ ] Add more stores 21 | - [ ] https://www.walmart.com/ 22 | - [ ] https://www.bol.com/ 23 | - [ ] I'm open to add more stores. Not Amazon and preferably with Alexa rank starting from 10k 24 | 25 | **Possible errors** 26 | 27 | - If there will be let me know 28 | 29 | ## Installation 30 | 31 | **Install from NPM** 32 | 33 | ```sh 34 | $ npm i e-scraper 35 | ``` 36 | 37 | **Install from YARN** 38 | 39 | ```sh 40 | $ yarn add e-scraper 41 | ``` 42 | 43 | ## USAGE 44 | 45 | # Module 46 | 47 | 48 | **Options** 49 | 50 | ```javascript 51 | const options = { 52 | // Store name 53 | // Supported stores: Target 54 | store: "Target", 55 | 56 | // When you search for a product, you need to specify keyword value 57 | keyword: 'xbox', 58 | 59 | // When you need to collect reviews, you need to specific productId value 60 | productId: '334343', 61 | 62 | // Number of products or reviews to collect 63 | number: 50, 64 | 65 | // Number of products or reviews to skip 66 | offset: 50, 67 | 68 | // Set store ID 69 | // Some stores can have different store Id values 70 | // Default {storeId} value for a Target is 3991 71 | storeId: 3991, 72 | 73 | // Include in search result promoted values 74 | // Some stores can include promoted products in to the response 75 | // Target does support this option 76 | // Default {sponsoredProducts} is 1 77 | sponsoredProducts: 1, 78 | }; 79 | ``` 80 | 81 | ```javascript 82 | const { product, reviews } = require('e-scraper'); 83 | 84 | (async () => { 85 | try { 86 | // Collect products 87 | const products = await product({ store: 'Target' keyword: 'Xbox ', number: 50 }); 88 | // Collect reviews 89 | const reviews = await reviews({ store: 'Target', productId: '232323', number: 50 }); 90 | 91 | } catch (error) { 92 | console.log(error); 93 | } 94 | })(); 95 | ``` 96 | 97 | **JSON output:** 98 | 99 | ```javascript 100 | { 101 | total: '829', 102 | has_more: true, 103 | next: 91, 104 | output: [{Reviews, Products...}] 105 | } 106 | ``` 107 | 108 | 109 | --- 110 | 111 | ## License 112 | 113 | **MIT** 114 | 115 | **Free Software** 116 | -------------------------------------------------------------------------------- /src/core/Target.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | import { URLSearchParams } from 'url'; 3 | 4 | import { entryValidator } from '../helpers'; 5 | 6 | import { TargetConstructor, TargetProductSearch, TargetProductReviews, Output } from '../types'; 7 | 8 | export class Target { 9 | private number: number; 10 | 11 | private tcin: string; 12 | 13 | private offset: number; 14 | 15 | private keyword: string; 16 | 17 | private storeId: number; 18 | 19 | private sponsoredProducts: number; 20 | 21 | constructor({ number, offset = 0, keyword = '', storeId = 3991, sponsoredProducts = 1, productId = '' }: TargetConstructor) { 22 | this.number = number; 23 | this.offset = offset; 24 | this.keyword = keyword; 25 | this.storeId = storeId; 26 | this.sponsoredProducts = sponsoredProducts; 27 | this.tcin = productId; 28 | } 29 | 30 | /** 31 | * Target product scraper 32 | */ 33 | public async product(): Promise { 34 | /** 35 | * Validate input values 36 | */ 37 | entryValidator({ 38 | keyword: { value: this.keyword, condition: '', required: true }, 39 | storeId: { value: this.storeId, condition: 0, required: true }, 40 | number: { value: this.number, condition: 90, required: false }, 41 | offset: { value: this.offset, condition: 0, required: false }, 42 | sponsoredProducts: { value: this.sponsoredProducts, condition: 0, required: false }, 43 | }); 44 | 45 | const query = new URLSearchParams({ 46 | channel: 'web', 47 | count: `${this.number}`, 48 | default_purchasability_filter: 'false', 49 | facet_recovery: 'false', 50 | fulfillment_test_mode: 'grocery_opu_team_member_test', 51 | isDLP: 'false', 52 | keyword: this.keyword, 53 | offset: `${this.offset}`, 54 | pageId: `/s/${this.keyword}`, 55 | pricing_store_id: `${this.storeId}`, 56 | include_sponsored_search_v2: `${this.sponsoredProducts}`, 57 | platform: 'desktop', 58 | key: 'eb2551e4accc14f38cc42d32fbc2b2ea', 59 | }); 60 | 61 | const options = { 62 | method: 'GET', 63 | headers: { 64 | accept: 'application/json', 65 | }, 66 | }; 67 | 68 | const result = await fetch(`https://redsky.target.com/v2/plp/search/?${query}`, options); 69 | const json: TargetProductSearch = await result.json(); 70 | 71 | try { 72 | return { 73 | total: json.search_response.metaData[5].value, 74 | has_more: !(this.offset + this.number > json.search_response.metaData[5].value), 75 | next: this.offset + this.number > json.search_response.metaData[5].value ? 0 : this.offset + this.number, 76 | output: json.search_response.items.Item, 77 | }; 78 | } catch (error) { 79 | return { 80 | total: 0, 81 | has_more: false, 82 | next: 0, 83 | output: [], 84 | }; 85 | } 86 | } 87 | 88 | /** 89 | * Target product review scraper 90 | */ 91 | public async reviews(): Promise { 92 | /** 93 | * Validate input values 94 | */ 95 | entryValidator({ 96 | tcin: { value: this.tcin, condition: '', required: true }, 97 | number: { value: this.number, condition: 50, required: false }, 98 | offset: { value: this.offset, condition: 0, required: false }, 99 | }); 100 | 101 | const query = new URLSearchParams({ 102 | sort: 'time_desc', 103 | filter: '', 104 | count: `${this.number}`, 105 | offset: `${this.offset}`, 106 | key: 'eb2551e4accc14f38cc42d32fbc2b2ea', 107 | }); 108 | 109 | const options = { 110 | method: 'GET', 111 | headers: { 112 | accept: 'application/json', 113 | }, 114 | }; 115 | 116 | const result = await fetch(`https://redsky.target.com/groot-domain-api/v1/reviews/${this.tcin}/?${query}`, options); 117 | const json: TargetProductReviews = await result.json(); 118 | 119 | try { 120 | return { 121 | total: json.totalResults, 122 | has_more: !(this.offset + this.number > json.totalResults), 123 | next: this.offset + this.number > json.totalResults ? 0 : this.offset + this.number, 124 | output: json.result, 125 | }; 126 | } catch (error) { 127 | return { 128 | total: 0, 129 | has_more: false, 130 | next: 0, 131 | output: [], 132 | }; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/types/Target/Product.ts: -------------------------------------------------------------------------------- 1 | export interface SearchResponse { 2 | items: Items; 3 | metaData: MetaDataEntity[]; 4 | facet_list?: FacetListEntity[] | null; 5 | sort_options: SortOptions; 6 | buckets?: BucketsEntity[] | null; 7 | experiments_viewed: ExperimentsViewedOrSponsored; 8 | suggestions?: string[] | null; 9 | sponsored: ExperimentsViewedOrSponsored; 10 | } 11 | export interface Items { 12 | Item: ItemEntity[]; 13 | } 14 | export interface ItemEntity { 15 | esrb_rating?: string | null; 16 | subscribable: boolean; 17 | package_dimensions?: PackageDimensions | null; 18 | title: string; 19 | tcin: string; 20 | type: string; 21 | dpci?: string | null; 22 | upc?: string | null; 23 | url: string; 24 | description?: string | null; 25 | representative_child_part_number: string; 26 | merch_sub_class: string; 27 | merch_class: string; 28 | merch_class_id: string; 29 | brand: string; 30 | product_brand: ProductBrand; 31 | images?: ImagesEntityOrImages[] | null; 32 | item_street_date?: string | null; 33 | average_rating: number; 34 | total_reviews: number; 35 | top_reviews?: TopReviewsEntity[] | null; 36 | total_review_count: number; 37 | secondary_ratings_averages_order?: string[] | null; 38 | secondary_ratings_averages?: SecondaryRatingsAverages | null; 39 | ratings_only_review_count: number; 40 | rating_distribution?: RatingDistributionEntity[] | null; 41 | overall_rating_range: number; 42 | bullet_description?: string[] | null; 43 | soft_bullets?: SoftBullets | null; 44 | eligibility_rules?: EligibilityRules | null; 45 | price: Price; 46 | promotions?: PromotionsEntity[] | null; 47 | esp_item?: EspItem | null; 48 | packaging?: Packaging | null; 49 | sales_classification_nodes?: SalesClassificationNodesEntity[] | null; 50 | avail_ratio?: string | null; 51 | variation_attributes?: VariationAttributes | null; 52 | child_items?: ChildItemsEntity[] | null; 53 | product_delivery_method?: string | null; 54 | } 55 | 56 | export interface TargetProductSearch { 57 | search_recommendations: SearchRecommendations; 58 | search_response: SearchResponse; 59 | } 60 | interface SearchRecommendations { 61 | query: string; 62 | } 63 | interface PackageDimensions { 64 | weight: string; 65 | weight_unit_of_measure: string; 66 | width: string; 67 | depth: string; 68 | height: string; 69 | dimension_unit_of_measure: string; 70 | } 71 | interface ProductBrand { 72 | facet_id: string; 73 | brand: string; 74 | } 75 | interface ImagesEntityOrImages { 76 | base_url: string; 77 | primary: string; 78 | alternate_urls?: string[] | null; 79 | content_labels?: ContentLabelsEntity[] | null; 80 | } 81 | interface ContentLabelsEntity { 82 | image_url: string; 83 | } 84 | interface TopReviewsEntity { 85 | user_nickname: string; 86 | rating: number; 87 | rating_range: number; 88 | review_text: string; 89 | title?: string | null; 90 | } 91 | interface SecondaryRatingsAverages { 92 | Value?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics | null; 93 | performance_1?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics1 | null; 94 | functionality?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics2 | null; 95 | Quality?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics3 | null; 96 | SoundQuality?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics4 | null; 97 | gameplay?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics5 | null; 98 | lastingAppeal?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics6 | null; 99 | Graphics?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics7 | null; 100 | } 101 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics { 102 | Id: string; 103 | AverageRating: number; 104 | Label: string; 105 | ValueRange: number; 106 | DisplayType: string; 107 | } 108 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics1 { 109 | Id: string; 110 | AverageRating: number; 111 | Label: string; 112 | ValueRange: number; 113 | DisplayType: string; 114 | } 115 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics2 { 116 | Id: string; 117 | AverageRating: number; 118 | Label: string; 119 | ValueRange: number; 120 | DisplayType: string; 121 | } 122 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics3 { 123 | Id: string; 124 | AverageRating: number; 125 | Label: string; 126 | ValueRange: number; 127 | DisplayType: string; 128 | } 129 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics4 { 130 | Id: string; 131 | AverageRating: number; 132 | Label: string; 133 | ValueRange: number; 134 | DisplayType: string; 135 | } 136 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics5 { 137 | Id: string; 138 | AverageRating: number; 139 | Label: string; 140 | ValueRange: number; 141 | DisplayType: string; 142 | } 143 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics6 { 144 | Id: string; 145 | AverageRating: number; 146 | Label: string; 147 | ValueRange: number; 148 | DisplayType: string; 149 | } 150 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics7 { 151 | Id: string; 152 | AverageRating: number; 153 | Label: string; 154 | ValueRange: number; 155 | DisplayType: string; 156 | } 157 | interface RatingDistributionEntity { 158 | Count: number; 159 | RatingValue: number; 160 | } 161 | interface SoftBullets { 162 | title: string; 163 | bullets?: string[] | null; 164 | } 165 | interface EligibilityRules { 166 | inventory_notification_to_guest_excluded: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 167 | hold: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 168 | rush?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery1 | null; 169 | ship_to_guest: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 170 | ship_to_store?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery2 | null; 171 | scheduled_delivery?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery3 | null; 172 | } 173 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery { 174 | is_active: boolean; 175 | } 176 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery1 { 177 | is_active: boolean; 178 | } 179 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery2 { 180 | is_active: boolean; 181 | } 182 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery3 { 183 | is_active: boolean; 184 | } 185 | interface Price { 186 | tcin: string; 187 | formatted_current_price: string; 188 | formatted_current_price_type: string; 189 | is_current_price_range: boolean; 190 | current_retail?: number | null; 191 | current_retail_min?: number | null; 192 | current_retail_max?: number | null; 193 | reg_retail_min?: number | null; 194 | reg_retail_max?: number | null; 195 | } 196 | interface PromotionsEntity { 197 | promotion_id: string; 198 | applied_location_id: number; 199 | start_date: string; 200 | end_date: string; 201 | visibility: number; 202 | time_based: boolean; 203 | channel: string; 204 | plp_message?: string | null; 205 | pdp_message: string; 206 | subscription_type: string; 207 | legal_disclaimer_text: string; 208 | site_wide: boolean; 209 | global_subscription_flag: boolean; 210 | threshold_type: string; 211 | threshold_value: number; 212 | promotion_class: string; 213 | promotion_url?: string | null; 214 | } 215 | interface EspItem { 216 | tcin: string; 217 | esp_group_id: string; 218 | maximum_qualifying_price: number; 219 | minimum_qualifying_price: number; 220 | product_description: ProductDescription; 221 | enrichment: Enrichment; 222 | price: Price1; 223 | } 224 | interface ProductDescription { 225 | title: string; 226 | bullet_descriptions?: string[] | null; 227 | } 228 | interface Enrichment { 229 | images: Images; 230 | } 231 | interface Images { 232 | primary_image_url: string; 233 | } 234 | interface Price1 { 235 | tcin: string; 236 | formatted_current_price: string; 237 | formatted_current_price_type: string; 238 | is_current_price_range: boolean; 239 | current_retail: number; 240 | } 241 | interface Packaging { 242 | is_retail_ticketed: boolean; 243 | } 244 | interface SalesClassificationNodesEntity { 245 | node_id: string; 246 | } 247 | interface VariationAttributes { 248 | giftcard_denominations?: string[] | null; 249 | giftcard_delivery?: string[] | null; 250 | } 251 | interface ChildItemsEntity { 252 | tcin: string; 253 | images: ImagesEntityOrImages1; 254 | package_dimensions: PackageDimensions1; 255 | eligibility_rules?: EligibilityRules1 | null; 256 | prepaid_segment: PrepaidSegment; 257 | } 258 | interface ImagesEntityOrImages1 { 259 | base_url: string; 260 | primary: string; 261 | content_labels?: ContentLabelsEntity[] | null; 262 | } 263 | interface PackageDimensions1 { 264 | weight: string; 265 | weight_unit_of_measure: string; 266 | width: string; 267 | depth: string; 268 | height: string; 269 | dimension_unit_of_measure: string; 270 | } 271 | interface EligibilityRules1 { 272 | ship_to_store: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 273 | } 274 | interface PrepaidSegment { 275 | denomination_option_code: string; 276 | } 277 | interface MetaDataEntity { 278 | name: string; 279 | value: number; 280 | } 281 | interface FacetListEntity { 282 | displayName: string; 283 | name: string; 284 | id: number; 285 | type: string; 286 | expand: boolean; 287 | details?: DetailsEntity[] | null; 288 | } 289 | interface DetailsEntity { 290 | display_name: string; 291 | value: string; 292 | descriptor: boolean; 293 | url: string; 294 | facetId?: string | null; 295 | facetCanonical?: string | null; 296 | } 297 | interface SortOptions { 298 | relevance: string; 299 | Featured: string; 300 | PriceLow: string; 301 | PriceHigh: string; 302 | RatingHigh: string; 303 | bestselling: string; 304 | newest: string; 305 | } 306 | interface BucketsEntity { 307 | items: Items1; 308 | metaData: MetaDataEntity1[]; 309 | } 310 | interface Items1 { 311 | Item: ItemEntity1[]; 312 | } 313 | interface ItemEntity1 { 314 | subscribable: boolean; 315 | package_dimensions?: PackageDimensions2 | null; 316 | title: string; 317 | tcin: string; 318 | type: string; 319 | url: string; 320 | description?: string | null; 321 | representative_child_part_number: string; 322 | merch_sub_class: string; 323 | merch_class: string; 324 | merch_class_id: string; 325 | images?: ImagesEntityOrImages[] | null; 326 | average_rating: number; 327 | total_reviews: number; 328 | total_review_count: number; 329 | ratings_only_review_count: number; 330 | rating_distribution?: RatingDistributionEntity[] | null; 331 | overall_rating_range: number; 332 | product_vendors?: ProductVendorsEntity[] | null; 333 | fulfillment?: Fulfillment | null; 334 | soft_bullets?: SoftBullets1 | null; 335 | price: Price2; 336 | sales_classification_nodes?: SalesClassificationNodesEntity[] | null; 337 | esrb_rating?: string | null; 338 | dpci?: string | null; 339 | upc?: string | null; 340 | brand?: string | null; 341 | product_brand?: ProductBrand1 | null; 342 | item_street_date?: string | null; 343 | top_reviews?: TopReviewsEntity[] | null; 344 | secondary_ratings_averages_order?: string[] | null; 345 | secondary_ratings_averages?: SecondaryRatingsAverages1 | null; 346 | bullet_description?: string[] | null; 347 | eligibility_rules?: EligibilityRules2 | null; 348 | promotions?: PromotionsEntity[] | null; 349 | esp_item?: EspItem1 | null; 350 | packaging?: Packaging1 | null; 351 | prepaid_segment?: PrepaidSegment1 | null; 352 | product_delivery_method?: string | null; 353 | avail_ratio?: string | null; 354 | variation_attributes?: VariationAttributes1 | null; 355 | child_items?: ChildItemsEntity1[] | null; 356 | ribbons?: string[] | null; 357 | swatches?: Swatches | null; 358 | promotion_call_out_message?: string | null; 359 | } 360 | interface PackageDimensions2 { 361 | weight: string; 362 | weight_unit_of_measure: string; 363 | width: string; 364 | depth: string; 365 | height: string; 366 | dimension_unit_of_measure: string; 367 | } 368 | interface ProductVendorsEntity { 369 | id: string; 370 | vendor_name: string; 371 | uri: string; 372 | } 373 | interface Fulfillment { 374 | is_marketplace: boolean; 375 | } 376 | interface SoftBullets1 { 377 | title: string; 378 | bullets?: string[] | null; 379 | } 380 | interface Price2 { 381 | tcin: string; 382 | formatted_current_price: string; 383 | formatted_current_price_type: string; 384 | is_current_price_range: boolean; 385 | current_retail?: number | null; 386 | current_retail_min?: number | null; 387 | current_retail_max?: number | null; 388 | reg_retail_min?: number | null; 389 | reg_retail_max?: number | null; 390 | formatted_comparison_price?: string | null; 391 | formatted_comparison_price_type?: string | null; 392 | } 393 | interface ProductBrand1 { 394 | facet_id: string; 395 | brand: string; 396 | } 397 | interface SecondaryRatingsAverages1 { 398 | Value?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics8 | null; 399 | Quality?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics9 | null; 400 | SoundQuality?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics10 | null; 401 | gameplay?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics11 | null; 402 | lastingAppeal?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics12 | null; 403 | Graphics?: ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics13 | null; 404 | } 405 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics8 { 406 | Id: string; 407 | AverageRating: number; 408 | Label: string; 409 | ValueRange: number; 410 | DisplayType: string; 411 | } 412 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics9 { 413 | Id: string; 414 | AverageRating: number; 415 | Label: string; 416 | ValueRange: number; 417 | DisplayType: string; 418 | } 419 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics10 { 420 | Id: string; 421 | AverageRating: number; 422 | Label: string; 423 | ValueRange: number; 424 | DisplayType: string; 425 | } 426 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics11 { 427 | Id: string; 428 | AverageRating: number; 429 | Label: string; 430 | ValueRange: number; 431 | DisplayType: string; 432 | } 433 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics12 { 434 | Id: string; 435 | AverageRating: number; 436 | Label: string; 437 | ValueRange: number; 438 | DisplayType: string; 439 | } 440 | interface ValueOrPerformance1OrFunctionalityOrQualityOrSoundQualityOrGameplayOrLastingAppealOrGraphics13 { 441 | Id: string; 442 | AverageRating: number; 443 | Label: string; 444 | ValueRange: number; 445 | DisplayType: string; 446 | } 447 | interface EligibilityRules2 { 448 | hold?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery4 | null; 449 | ship_to_store?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery5 | null; 450 | } 451 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery4 { 452 | is_active: boolean; 453 | } 454 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery5 { 455 | is_active: boolean; 456 | } 457 | interface EspItem1 { 458 | tcin: string; 459 | esp_group_id: string; 460 | maximum_qualifying_price: number; 461 | minimum_qualifying_price: number; 462 | product_description: ProductDescription; 463 | enrichment: Enrichment; 464 | price: Price1; 465 | } 466 | interface Packaging1 { 467 | is_retail_ticketed: boolean; 468 | } 469 | interface PrepaidSegment1 { 470 | denomination_option_code: string; 471 | } 472 | interface VariationAttributes1 { 473 | color?: string[] | null; 474 | } 475 | interface ChildItemsEntity1 { 476 | tcin: string; 477 | images: ImagesEntityOrImages; 478 | package_dimensions: PackageDimensions1; 479 | eligibility_rules?: EligibilityRules3 | null; 480 | } 481 | interface EligibilityRules3 { 482 | inventory_notification_to_guest_excluded: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 483 | hold: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 484 | rush?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery6 | null; 485 | ship_to_guest: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 486 | ship_to_store: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery; 487 | scheduled_delivery?: InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery3 | null; 488 | } 489 | interface InventoryNotificationToGuestExcludedOrHoldOrRushOrShipToGuestOrShipToStoreOrScheduledDelivery6 { 490 | is_active: boolean; 491 | } 492 | interface Swatches { 493 | color?: ColorEntity[] | null; 494 | } 495 | interface ColorEntity { 496 | color: string; 497 | partNumber: string; 498 | img_url: string; 499 | } 500 | interface MetaDataEntity1 { 501 | name: string; 502 | value: string; 503 | } 504 | interface ExperimentsViewedOrSponsored {} 505 | --------------------------------------------------------------------------------