@shopify/shop-minis-react 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mocks.js CHANGED
@@ -392,7 +392,8 @@ function I() {
392
392
  requestPermission: {
393
393
  granted: !0
394
394
  },
395
- reportError: void 0
395
+ reportError: void 0,
396
+ reportFetch: void 0
396
397
  }, r = {};
397
398
  for (const e in o)
398
399
  Object.prototype.hasOwnProperty.call(o, e) && (r[e] = v(
package/dist/mocks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mocks.js","sources":["../src/mocks.ts"],"sourcesContent":["import {\n Product,\n Gender,\n UserState,\n MinisContentStatus,\n} from '@shopify/shop-minis-platform'\nimport {ShopActions} from '@shopify/shop-minis-platform/actions'\n\n// Helper functions for common data structures\nexport const createProduct = (\n id: string,\n title: string,\n price = '99.99',\n compareAtPrice?: string\n): Product => ({\n id,\n title,\n price: {amount: price, currencyCode: 'USD'},\n ...(compareAtPrice && {\n compareAtPrice: {amount: compareAtPrice, currencyCode: 'USD'},\n }),\n reviewAnalytics: {averageRating: 4.5, reviewCount: 10},\n shop: createShop('shop1', 'Mock Shop'),\n defaultVariantId: `variant-${id}`,\n isFavorited: false,\n featuredImage: {\n url: `https://cdn.shopify.com/static/sample-images/teapot.jpg`,\n altText: title,\n },\n})\n\nexport const createShop = (\n id: string,\n name: string,\n options?: {\n themeType?: 'coverImage' | 'brandColor' | 'logoColor' | 'none'\n withBrandSettings?: boolean\n primaryColor?: string\n logoDominantColor?: string\n logoAverageColor?: string\n coverDominantColor?: string\n wordmarkUrl?: string\n coverImageUrl?: string\n featuredImagesLimit?: number\n }\n) => {\n // Determine theme configuration\n const themeType = options?.themeType || 'none'\n const shouldHaveBrandSettings =\n options?.withBrandSettings || themeType !== 'none'\n\n // Generate featured images\n const featuredImagesCount = options?.featuredImagesLimit || 3\n const featuredImages = Array.from({length: featuredImagesCount}, (_, i) => ({\n url: `https://picsum.photos/400/400?random=${id}-${i}`,\n sensitive: false,\n altText: `${name} featured image ${i + 1}`,\n }))\n\n // Configure colors based on theme type\n const getThemeColors = () => {\n switch (themeType) {\n case 'coverImage':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor || '#FF6B35',\n }\n case 'brandColor':\n return {\n primary: options?.primaryColor || '#27AE60',\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n case 'logoColor':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor || '#E74C3C',\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n default:\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n }\n }\n\n // Configure header theme\n const createHeaderTheme = () => {\n if (themeType === 'coverImage' || options?.coverImageUrl) {\n return {\n id: `header-theme-${id}`,\n coverImage: {\n url:\n options?.coverImageUrl ||\n 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=800&h=400&fit=crop',\n altText: `${name} cover image`,\n sensitive: false,\n thumbhash: 'k9oGHQRnh493V4dIeHeXh4h3iIeI',\n },\n wordmark:\n options?.wordmarkUrl || themeType === 'coverImage'\n ? {\n url:\n options?.wordmarkUrl ||\n 'https://merrypeople.com/cdn/shop/files/Transparent_Background_1.png?v=1696465429&width=1024',\n altText: `${name} wordmark`,\n sensitive: false,\n }\n : undefined,\n }\n }\n\n if (options?.wordmarkUrl) {\n return {\n id: `header-theme-${id}`,\n wordmark: {\n url: options.wordmarkUrl,\n altText: `${name} wordmark`,\n sensitive: false,\n },\n }\n }\n\n return undefined\n }\n\n return {\n id,\n name,\n primaryDomain: {\n url: `https://${name.toLowerCase().replace(/\\s+/g, '-')}.com`,\n },\n reviewAnalytics: {averageRating: 4.3, reviewCount: 50},\n visualTheme: {\n id: `visual-theme-${id}`,\n featuredImages,\n logoImage: {\n url: `https://picsum.photos/100/100?random=${id}`,\n sensitive: false,\n },\n brandSettings: shouldHaveBrandSettings\n ? {\n id: `brand-settings-${id}`,\n colors: {\n id: `colors-${id}`,\n ...getThemeColors(),\n },\n headerTheme: createHeaderTheme(),\n }\n : undefined,\n },\n }\n}\n\nconst createPagination = (hasNext = false) => ({\n hasNextPage: hasNext,\n endCursor: hasNext ? 'cursor123' : null,\n})\n\nconst createProductList = (id: string, name: string, products: any[] = []) => ({\n id,\n publicId: `public-${id}`,\n name,\n products,\n})\n\n// Helper type to extract the data type from a ShopAction\ntype ShopActionDataType<T> = T extends (\n ...args: any[]\n) => Promise<{ok: true; data: infer R} | {ok: false; error: any}>\n ? R\n : never\n\nfunction makeMockMethod<K extends keyof ShopActions>(\n key: K,\n result: ShopActionDataType<ShopActions[K]>\n): ShopActions[K] {\n return ((params: Parameters<ShopActions[K]>[0]) => {\n console.log(`[Mock Action] ${String(key)}`, params)\n return Promise.resolve({ok: true as const, data: result})\n }) as ShopActions[K]\n}\n\nexport function makeMockActions(): ShopActions {\n const results: {\n [K in keyof ShopActions]: ShopActionDataType<ShopActions[K]>\n } = {\n translateContentUp: undefined,\n translateContentDown: undefined,\n followShop: true,\n unfollowShop: false,\n favorite: undefined,\n unfavorite: undefined,\n getShopAppInformation: {\n appVersion: '1.0.0',\n buildNumber: '12345',\n buildId: 'dev-build-123',\n },\n productRecommendationImpression: undefined,\n productRecommendationClick: undefined,\n hideEntryPoint: undefined,\n closeMini: undefined,\n getAccountInformation: {\n status: 'available',\n value: 'user@example.com',\n },\n getCurrentUser: {\n data: {\n displayName: 'John Doe',\n avatarImage: {url: 'https://example.com/avatar.jpg'},\n },\n },\n createOrderAttribution: undefined,\n addToCart: undefined,\n buyProduct: undefined,\n buyProducts: undefined,\n showErrorScreen: undefined,\n showErrorToast: undefined,\n getDeeplinkPaths: {\n matchers: ['/products', '/collections', '/cart'],\n },\n navigateToDeeplink: undefined,\n navigateToShop: undefined,\n navigateToProduct: undefined,\n navigateToOrder: undefined,\n navigateToCheckout: undefined,\n createImageUploadLink: {\n targets: [\n {\n url: 'https://example.com/upload',\n resourceUrl: 'https://example.com/resource',\n parameters: [{name: 'key', value: 'upload-123'}],\n },\n ],\n },\n completeImageUpload: {\n files: [\n {\n id: 'file-123',\n fileStatus: 'READY',\n image: {url: 'https://example.com/image.jpg'},\n },\n ],\n },\n getPersistedItem: null,\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: null,\n setInternalPersistedItem: undefined,\n removeInternalPersistedItem: undefined,\n getAllInternalPersistedKeys: ['internal-key1', 'internal-key2'],\n clearInternalPersistedItems: undefined,\n getSecret: 'secret-value',\n setSecret: undefined,\n removeSecret: undefined,\n reportInteraction: undefined,\n reportImpression: undefined,\n reportContentImpression: undefined,\n getProductLists: {\n data: [\n createProductList('list-1', 'Wishlist'),\n createProductList('list-2', 'Favorites'),\n ],\n pageInfo: createPagination(),\n },\n getProductList: {\n data: createProductList('list-1', 'Wishlist', [\n createProduct('prod-1', 'Sample Product'),\n ]),\n pageInfo: createPagination(),\n },\n addProductList: createProductList('list-3', 'New List'),\n removeProductList: undefined,\n renameProductList: createProductList('list-1', 'Updated Wishlist'),\n addProductListItem: undefined,\n removeProductListItem: undefined,\n getRecommendedProducts: {\n data: [\n createProduct('rec-1', 'Recommended Product 1', '79.99'),\n createProduct('rec-2', 'Recommended Product 2', '129.99'),\n createProduct('rec-3', 'Recommended Product 3', '129.99'),\n createProduct('rec-4', 'Recommended Product 4', '29.99'),\n createProduct('rec-5', 'Recommended Product 5', '39.99'),\n createProduct('rec-6', 'Recommended Product 6', '49.99'),\n createProduct('rec-7', 'Recommended Product 7', '59.99'),\n createProduct('rec-8', 'Recommended Product 8', '69.99'),\n createProduct('rec-9', 'Recommended Product 9', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getRecommendedShops: {\n data: [\n createShop('shop-1', 'Amazing Store'),\n createShop('shop-2', 'Best Deals Shop'),\n ],\n pageInfo: createPagination(),\n },\n searchProductsByShop: {\n data: [\n createProduct('search-1', 'Search Result 1', '59.99'),\n createProduct('search-2', 'Search Result 2', '89.99'),\n ],\n pageInfo: createPagination(),\n },\n getOrders: {\n data: [\n {\n id: 'order-1',\n name: '#1001',\n lineItems: [\n {\n productTitle: 'Sample Product',\n variantTitle: 'Medium',\n quantity: 2,\n product: null,\n },\n ],\n shop: createShop('shop-1', 'Sample Shop'),\n },\n ],\n pageInfo: createPagination(),\n },\n getBuyerAttributes: {\n data: {\n genderAffinity: 'NEUTRAL' as Gender,\n categoryAffinities: [\n {id: 'cat1', name: 'Electronics'},\n {id: 'cat2', name: 'Clothing'},\n ],\n },\n },\n showFeedbackSheet: undefined,\n getPopularProducts: {\n data: [\n createProduct('pop-1', 'The Hero Snowboard', '702.95'),\n createProduct('pop-2', 'Snow Jacket', '605.95', '702.00'),\n createProduct('pop-3', 'Winter Gloves', '89.95'),\n createProduct('pop-4', 'Summer Gloves', '89.95'),\n createProduct('pop-5', 'Spring Gloves', '89.95'),\n createProduct('pop-6', 'Playstation 5', '499.95'),\n createProduct('pop-7', 'Xbox Series X', '499.95'),\n createProduct('pop-8', 'Nintendo Switch', '299.95'),\n createProduct('pop-9', 'Playstation 4', '299.95'),\n createProduct('pop-10', 'Nintendo 3DS', '89.95'),\n ],\n pageInfo: createPagination(),\n },\n share: {\n message: 'Shared!',\n success: true,\n },\n shareSingle: {\n message: 'Shared!',\n success: true,\n },\n getCuratedProducts: {\n data: [\n createProduct('cur-1', 'Curated Product 1', '79.99'),\n createProduct('cur-2', 'Curated Product 2', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getSavedProducts: {\n data: [createProduct('saved-1', 'Saved Product 1', '49.99')],\n pageInfo: createPagination(),\n },\n getRecentProducts: {\n data: [createProduct('recent-1', 'Recent Product 1', '59.99')],\n pageInfo: createPagination(),\n },\n getProductSearch: {\n data: [\n createProduct('search-1', 'Search Product 1', '39.99'),\n createProduct('search-2', 'Search Product 2', '19.99'),\n createProduct('search-3', 'Search Product 3', '29.99'),\n createProduct('search-4', 'Search Product 4', '49.99'),\n createProduct('search-5', 'Search Product 5', '9.99'),\n ],\n pageInfo: createPagination(),\n },\n getProducts: {data: [createProduct('prod-2', 'Product 2', '19.99')]},\n getProduct: {data: createProduct('prod-1', 'Sample Product')},\n getProductVariants: {\n data: [\n {\n id: 'variant-1',\n title: 'Variant 1',\n isFavorited: false,\n image: {url: 'https://example.com/variant-1.jpg'},\n price: {amount: '19.99', currencyCode: 'USD'},\n compareAtPrice: {amount: '29.99', currencyCode: 'USD'},\n },\n ],\n pageInfo: createPagination(),\n },\n getProductMedia: {\n data: [\n {\n id: 'media-1',\n image: {url: 'https://example.com/media-1.jpg'},\n mediaContentType: 'IMAGE',\n alt: 'Sample product image',\n },\n ],\n pageInfo: createPagination(),\n },\n getShop: {\n data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),\n },\n getRecentShops: {\n data: [createShop('recent-shop-1', 'Recent Shop 1')],\n pageInfo: createPagination(),\n },\n getFollowedShops: {\n data: [createShop('followed-shop-1', 'Followed Shop 1')],\n pageInfo: createPagination(),\n },\n previewProductInAr: undefined,\n createContent: {\n data: {\n publicId: 'content-123',\n externalId: null,\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n description: 'This is a mock content item',\n visibility: ['DISCOVERABLE'],\n shareableUrl: 'https://example.com/content/123',\n products: null,\n },\n },\n getContent: {\n data: [\n {\n publicId: 'content-123',\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n visibility: ['DISCOVERABLE'],\n status: MinisContentStatus.READY,\n },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\n navigateToCart: undefined,\n requestPermission: {\n granted: true,\n },\n reportError: undefined,\n } as const\n\n const mock: Partial<ShopActions> = {}\n for (const key in results) {\n if (Object.prototype.hasOwnProperty.call(results, key)) {\n // @ts-expect-error: dynamic assignment is safe due to exhaustive mapping\n mock[key] = makeMockMethod(\n key as keyof ShopActions,\n results[key as keyof typeof results]\n )\n }\n }\n return mock as ShopActions\n}\n\n// Detect if running on a mobile device\nconst isMobile = (): boolean => {\n const userAgent = navigator.userAgent.toLowerCase()\n const isIOS = /iphone|ipad|ipod/.test(userAgent)\n const isAndroid = /android/.test(userAgent)\n\n return isIOS || isAndroid\n}\n\nexport const injectMocks = ({force}: {force?: boolean} = {}) => {\n // Only inject mocks if we aren't on a mobile device or we force it\n if (isMobile() && !force) {\n return\n }\n\n if (!window.minisSDK) {\n window.minisSDK = makeMockActions()\n window.minisParams = {\n handle: 'mock-handle',\n initialUrl: '/mock-initial-url',\n platform: 'web',\n }\n }\n}\n"],"names":["createProduct","id","title","price","compareAtPrice","createShop","name","options","themeType","shouldHaveBrandSettings","featuredImagesCount","featuredImages","_","i","getThemeColors","createHeaderTheme","createPagination","hasNext","createProductList","products","makeMockMethod","key","result","params","makeMockActions","results","MinisContentStatus","UserState","mock","isMobile","userAgent","isIOS","isAndroid","injectMocks","force"],"mappings":";;AASO,MAAMA,IAAgB,CAC3BC,GACAC,GACAC,IAAQ,SACRC,OACa;AAAA,EACb,IAAAH;AAAA,EACA,OAAAC;AAAA,EACA,OAAO,EAAC,QAAQC,GAAO,cAAc,MAAK;AAAA,EAC1C,GAAIC,KAAkB;AAAA,IACpB,gBAAgB,EAAC,QAAQA,GAAgB,cAAc,MAAK;AAAA,EAC9D;AAAA,EACA,iBAAiB,EAAC,eAAe,KAAK,aAAa,GAAE;AAAA,EACrD,MAAMC,EAAW,SAAS,WAAW;AAAA,EACrC,kBAAkB,WAAWJ,CAAE;AAAA,EAC/B,aAAa;AAAA,EACb,eAAe;AAAA,IACb,KAAK;AAAA,IACL,SAASC;AAAA,EAAA;AAEb,IAEaG,IAAa,CACxBJ,GACAK,GACAC,MAWG;AAEG,QAAAC,IAAYD,GAAS,aAAa,QAClCE,IACJF,GAAS,qBAAqBC,MAAc,QAGxCE,IAAsBH,GAAS,uBAAuB,GACtDI,IAAiB,MAAM,KAAK,EAAC,QAAQD,EAAmB,GAAG,CAACE,GAAGC,OAAO;AAAA,IAC1E,KAAK,wCAAwCZ,CAAE,IAAIY,CAAC;AAAA,IACpD,WAAW;AAAA,IACX,SAAS,GAAGP,CAAI,mBAAmBO,IAAI,CAAC;AAAA,EAAA,EACxC,GAGIC,IAAiB,MAAM;AAC3B,YAAQN,GAAW;AAAA,MACjB,KAAK;AACI,eAAA;AAAA,UACL,SAASD,GAAS;AAAA,UAClB,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS,sBAAsB;AAAA,QAChD;AAAA,MACF,KAAK;AACI,eAAA;AAAA,UACL,SAASA,GAAS,gBAAgB;AAAA,UAClC,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,MACF,KAAK;AACI,eAAA;AAAA,UACL,SAASA,GAAS;AAAA,UAClB,cAAcA,GAAS,qBAAqB;AAAA,UAC5C,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,MACF;AACS,eAAA;AAAA,UACL,SAASA,GAAS;AAAA,UAClB,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,IAAA;AAAA,EAEN,GAGMQ,IAAoB,MAAM;AAC1B,QAAAP,MAAc,gBAAgBD,GAAS;AAClC,aAAA;AAAA,QACL,IAAI,gBAAgBN,CAAE;AAAA,QACtB,YAAY;AAAA,UACV,KACEM,GAAS,iBACT;AAAA,UACF,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,UACX,WAAW;AAAA,QACb;AAAA,QACA,UACEC,GAAS,eAAeC,MAAc,eAClC;AAAA,UACE,KACED,GAAS,eACT;AAAA,UACF,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,QAAA,IAEb;AAAA,MACR;AAGF,QAAIC,GAAS;AACJ,aAAA;AAAA,QACL,IAAI,gBAAgBN,CAAE;AAAA,QACtB,UAAU;AAAA,UACR,KAAKM,EAAQ;AAAA,UACb,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,QAAA;AAAA,MAEf;AAAA,EAIJ;AAEO,SAAA;AAAA,IACL,IAAAL;AAAA,IACA,MAAAK;AAAA,IACA,eAAe;AAAA,MACb,KAAK,WAAWA,EAAK,cAAc,QAAQ,QAAQ,GAAG,CAAC;AAAA,IACzD;AAAA,IACA,iBAAiB,EAAC,eAAe,KAAK,aAAa,GAAE;AAAA,IACrD,aAAa;AAAA,MACX,IAAI,gBAAgBL,CAAE;AAAA,MACtB,gBAAAU;AAAA,MACA,WAAW;AAAA,QACT,KAAK,wCAAwCV,CAAE;AAAA,QAC/C,WAAW;AAAA,MACb;AAAA,MACA,eAAeQ,IACX;AAAA,QACE,IAAI,kBAAkBR,CAAE;AAAA,QACxB,QAAQ;AAAA,UACN,IAAI,UAAUA,CAAE;AAAA,UAChB,GAAGa,EAAe;AAAA,QACpB;AAAA,QACA,aAAaC,EAAkB;AAAA,MAAA,IAEjC;AAAA,IAAA;AAAA,EAER;AACF,GAEMC,IAAmB,CAACC,IAAU,QAAW;AAAA,EAC7C,aAAaA;AAAA,EACb,WAAWA,IAAU,cAAc;AACrC,IAEMC,IAAoB,CAACjB,GAAYK,GAAca,IAAkB,CAAA,OAAQ;AAAA,EAC7E,IAAAlB;AAAA,EACA,UAAU,UAAUA,CAAE;AAAA,EACtB,MAAAK;AAAA,EACA,UAAAa;AACF;AASA,SAASC,EACPC,GACAC,GACgB;AAChB,SAAQ,CAACC,OACP,QAAQ,IAAI,iBAAiB,OAAOF,CAAG,CAAC,IAAIE,CAAM,GAC3C,QAAQ,QAAQ,EAAC,IAAI,IAAe,MAAMD,GAAO;AAE5D;AAEO,SAASE,IAA+B;AAC7C,QAAMC,IAEF;AAAA,IACF,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,uBAAuB;AAAA,MACrB,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,IACA,iCAAiC;AAAA,IACjC,4BAA4B;AAAA,IAC5B,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,uBAAuB;AAAA,MACrB,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM;AAAA,QACJ,aAAa;AAAA,QACb,aAAa,EAAC,KAAK,iCAAgC;AAAA,MAAA;AAAA,IAEvD;AAAA,IACA,wBAAwB;AAAA,IACxB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,MAChB,UAAU,CAAC,aAAa,gBAAgB,OAAO;AAAA,IACjD;AAAA,IACA,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,MACrB,SAAS;AAAA,QACP;AAAA,UACE,KAAK;AAAA,UACL,aAAa;AAAA,UACb,YAAY,CAAC,EAAC,MAAM,OAAO,OAAO,aAAa,CAAA;AAAA,QAAA;AAAA,MACjD;AAAA,IAEJ;AAAA,IACA,qBAAqB;AAAA,MACnB,OAAO;AAAA,QACL;AAAA,UACE,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,OAAO,EAAC,KAAK,gCAA+B;AAAA,QAAA;AAAA,MAC9C;AAAA,IAEJ;AAAA,IACA,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,qBAAqB,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAC5C,qBAAqB;AAAA,IACrB,0BAA0B;AAAA,IAC1B,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,6BAA6B,CAAC,iBAAiB,eAAe;AAAA,IAC9D,6BAA6B;AAAA,IAC7B,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,yBAAyB;AAAA,IACzB,iBAAiB;AAAA,MACf,MAAM;AAAA,QACJP,EAAkB,UAAU,UAAU;AAAA,QACtCA,EAAkB,UAAU,WAAW;AAAA,MACzC;AAAA,MACA,UAAUF,EAAiB;AAAA,IAC7B;AAAA,IACA,gBAAgB;AAAA,MACd,MAAME,EAAkB,UAAU,YAAY;AAAA,QAC5ClB,EAAc,UAAU,gBAAgB;AAAA,MAAA,CACzC;AAAA,MACD,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,gBAAgBE,EAAkB,UAAU,UAAU;AAAA,IACtD,mBAAmB;AAAA,IACnB,mBAAmBA,EAAkB,UAAU,kBAAkB;AAAA,IACjE,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,MACtB,MAAM;AAAA,QACJlB,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,QACxDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,QACxDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,MAC1D;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,qBAAqB;AAAA,MACnB,MAAM;AAAA,QACJX,EAAW,UAAU,eAAe;AAAA,QACpCA,EAAW,UAAU,iBAAiB;AAAA,MACxC;AAAA,MACA,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,sBAAsB;AAAA,MACpB,MAAM;AAAA,QACJhB,EAAc,YAAY,mBAAmB,OAAO;AAAA,QACpDA,EAAc,YAAY,mBAAmB,OAAO;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,WAAW;AAAA,YACT;AAAA,cACE,cAAc;AAAA,cACd,cAAc;AAAA,cACd,UAAU;AAAA,cACV,SAAS;AAAA,YAAA;AAAA,UAEb;AAAA,UACA,MAAMX,EAAW,UAAU,aAAa;AAAA,QAAA;AAAA,MAE5C;AAAA,MACA,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,UAClB,EAAC,IAAI,QAAQ,MAAM,cAAa;AAAA,UAChC,EAAC,IAAI,QAAQ,MAAM,WAAU;AAAA,QAAA;AAAA,MAC/B;AAAA,IAEJ;AAAA,IACA,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJhB,EAAc,SAAS,sBAAsB,QAAQ;AAAA,QACrDA,EAAc,SAAS,eAAe,UAAU,QAAQ;AAAA,QACxDA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,SAAS,mBAAmB,QAAQ;AAAA,QAClDA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,UAAU,gBAAgB,OAAO;AAAA,MACjD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,OAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJhB,EAAc,SAAS,qBAAqB,OAAO;AAAA,QACnDA,EAAc,SAAS,qBAAqB,QAAQ;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM,CAAChB,EAAc,WAAW,mBAAmB,OAAO,CAAC;AAAA,MAC3D,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,mBAAmB;AAAA,MACjB,MAAM,CAAChB,EAAc,YAAY,oBAAoB,OAAO,CAAC;AAAA,MAC7D,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM;AAAA,QACJhB,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,MAAM;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,aAAa,EAAC,MAAM,CAAChB,EAAc,UAAU,aAAa,OAAO,CAAC,EAAC;AAAA,IACnE,YAAY,EAAC,MAAMA,EAAc,UAAU,gBAAgB,EAAC;AAAA,IAC5D,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO,EAAC,KAAK,oCAAmC;AAAA,UAChD,OAAO,EAAC,QAAQ,SAAS,cAAc,MAAK;AAAA,UAC5C,gBAAgB,EAAC,QAAQ,SAAS,cAAc,MAAK;AAAA,QAAA;AAAA,MAEzD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,OAAO,EAAC,KAAK,kCAAiC;AAAA,UAC9C,kBAAkB;AAAA,UAClB,KAAK;AAAA,QAAA;AAAA,MAET;AAAA,MACA,UAAUA,EAAiB;AAAA,IAC7B;AAAA,IACA,SAAS;AAAA,MACP,MAAMX,EAAW,UAAU,eAAe,EAAC,qBAAqB,EAAE,CAAA;AAAA,IACpE;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM,CAACA,EAAW,iBAAiB,eAAe,CAAC;AAAA,MACnD,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM,CAACX,EAAW,mBAAmB,iBAAiB,CAAC;AAAA,MACvD,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,oBAAoB;AAAA,IACpB,eAAe;AAAA,MACb,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,OAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,UACL,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,QACP,aAAa;AAAA,QACb,YAAY,CAAC,cAAc;AAAA,QAC3B,cAAc;AAAA,QACd,UAAU;AAAA,MAAA;AAAA,IAEd;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,QACJ;AAAA,UACE,UAAU;AAAA,UACV,OAAO;AAAA,YACL,IAAI;AAAA,YACJ,KAAK;AAAA,YACL,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AAAA,UACA,OAAO;AAAA,UACP,YAAY,CAAC,cAAc;AAAA,UAC3B,QAAQU,EAAmB;AAAA,QAAA;AAAA,MAC7B;AAAA,IAEJ;AAAA,IACA,mBAAmB;AAAA,MACjB,MAAM;AAAA,QACJ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,WAAWC,EAAU;AAAA,MAAA;AAAA,IAEzB;AAAA,IACA,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,MACjB,SAAS;AAAA,IACX;AAAA,IACA,aAAa;AAAA,EACf,GAEMC,IAA6B,CAAC;AACpC,aAAWP,KAAOI;AAChB,IAAI,OAAO,UAAU,eAAe,KAAKA,GAASJ,CAAG,MAEnDO,EAAKP,CAAG,IAAID;AAAA,MACVC;AAAA,MACAI,EAAQJ,CAA2B;AAAA,IACrC;AAGG,SAAAO;AACT;AAGA,MAAMC,IAAW,MAAe;AACxB,QAAAC,IAAY,UAAU,UAAU,YAAY,GAC5CC,IAAQ,mBAAmB,KAAKD,CAAS,GACzCE,IAAY,UAAU,KAAKF,CAAS;AAE1C,SAAOC,KAASC;AAClB,GAEaC,IAAc,CAAC,EAAC,OAAAC,EAAK,IAAuB,OAAO;AAE1D,EAAAL,EAAA,KAAc,CAACK,KAId,OAAO,aACV,OAAO,WAAWV,EAAgB,GAClC,OAAO,cAAc;AAAA,IACnB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AAEJ;"}
1
+ {"version":3,"file":"mocks.js","sources":["../src/mocks.ts"],"sourcesContent":["import {\n Product,\n Gender,\n UserState,\n MinisContentStatus,\n} from '@shopify/shop-minis-platform'\nimport {ShopActions} from '@shopify/shop-minis-platform/actions'\n\n// Helper functions for common data structures\nexport const createProduct = (\n id: string,\n title: string,\n price = '99.99',\n compareAtPrice?: string\n): Product => ({\n id,\n title,\n price: {amount: price, currencyCode: 'USD'},\n ...(compareAtPrice && {\n compareAtPrice: {amount: compareAtPrice, currencyCode: 'USD'},\n }),\n reviewAnalytics: {averageRating: 4.5, reviewCount: 10},\n shop: createShop('shop1', 'Mock Shop'),\n defaultVariantId: `variant-${id}`,\n isFavorited: false,\n featuredImage: {\n url: `https://cdn.shopify.com/static/sample-images/teapot.jpg`,\n altText: title,\n },\n})\n\nexport const createShop = (\n id: string,\n name: string,\n options?: {\n themeType?: 'coverImage' | 'brandColor' | 'logoColor' | 'none'\n withBrandSettings?: boolean\n primaryColor?: string\n logoDominantColor?: string\n logoAverageColor?: string\n coverDominantColor?: string\n wordmarkUrl?: string\n coverImageUrl?: string\n featuredImagesLimit?: number\n }\n) => {\n // Determine theme configuration\n const themeType = options?.themeType || 'none'\n const shouldHaveBrandSettings =\n options?.withBrandSettings || themeType !== 'none'\n\n // Generate featured images\n const featuredImagesCount = options?.featuredImagesLimit || 3\n const featuredImages = Array.from({length: featuredImagesCount}, (_, i) => ({\n url: `https://picsum.photos/400/400?random=${id}-${i}`,\n sensitive: false,\n altText: `${name} featured image ${i + 1}`,\n }))\n\n // Configure colors based on theme type\n const getThemeColors = () => {\n switch (themeType) {\n case 'coverImage':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor || '#FF6B35',\n }\n case 'brandColor':\n return {\n primary: options?.primaryColor || '#27AE60',\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n case 'logoColor':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor || '#E74C3C',\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n default:\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n }\n }\n\n // Configure header theme\n const createHeaderTheme = () => {\n if (themeType === 'coverImage' || options?.coverImageUrl) {\n return {\n id: `header-theme-${id}`,\n coverImage: {\n url:\n options?.coverImageUrl ||\n 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=800&h=400&fit=crop',\n altText: `${name} cover image`,\n sensitive: false,\n thumbhash: 'k9oGHQRnh493V4dIeHeXh4h3iIeI',\n },\n wordmark:\n options?.wordmarkUrl || themeType === 'coverImage'\n ? {\n url:\n options?.wordmarkUrl ||\n 'https://merrypeople.com/cdn/shop/files/Transparent_Background_1.png?v=1696465429&width=1024',\n altText: `${name} wordmark`,\n sensitive: false,\n }\n : undefined,\n }\n }\n\n if (options?.wordmarkUrl) {\n return {\n id: `header-theme-${id}`,\n wordmark: {\n url: options.wordmarkUrl,\n altText: `${name} wordmark`,\n sensitive: false,\n },\n }\n }\n\n return undefined\n }\n\n return {\n id,\n name,\n primaryDomain: {\n url: `https://${name.toLowerCase().replace(/\\s+/g, '-')}.com`,\n },\n reviewAnalytics: {averageRating: 4.3, reviewCount: 50},\n visualTheme: {\n id: `visual-theme-${id}`,\n featuredImages,\n logoImage: {\n url: `https://picsum.photos/100/100?random=${id}`,\n sensitive: false,\n },\n brandSettings: shouldHaveBrandSettings\n ? {\n id: `brand-settings-${id}`,\n colors: {\n id: `colors-${id}`,\n ...getThemeColors(),\n },\n headerTheme: createHeaderTheme(),\n }\n : undefined,\n },\n }\n}\n\nconst createPagination = (hasNext = false) => ({\n hasNextPage: hasNext,\n endCursor: hasNext ? 'cursor123' : null,\n})\n\nconst createProductList = (id: string, name: string, products: any[] = []) => ({\n id,\n publicId: `public-${id}`,\n name,\n products,\n})\n\n// Helper type to extract the data type from a ShopAction\ntype ShopActionDataType<T> = T extends (\n ...args: any[]\n) => Promise<{ok: true; data: infer R} | {ok: false; error: any}>\n ? R\n : never\n\nfunction makeMockMethod<K extends keyof ShopActions>(\n key: K,\n result: ShopActionDataType<ShopActions[K]>\n): ShopActions[K] {\n return ((params: Parameters<ShopActions[K]>[0]) => {\n console.log(`[Mock Action] ${String(key)}`, params)\n return Promise.resolve({ok: true as const, data: result})\n }) as ShopActions[K]\n}\n\nexport function makeMockActions(): ShopActions {\n const results: {\n [K in keyof ShopActions]: ShopActionDataType<ShopActions[K]>\n } = {\n translateContentUp: undefined,\n translateContentDown: undefined,\n followShop: true,\n unfollowShop: false,\n favorite: undefined,\n unfavorite: undefined,\n getShopAppInformation: {\n appVersion: '1.0.0',\n buildNumber: '12345',\n buildId: 'dev-build-123',\n },\n productRecommendationImpression: undefined,\n productRecommendationClick: undefined,\n hideEntryPoint: undefined,\n closeMini: undefined,\n getAccountInformation: {\n status: 'available',\n value: 'user@example.com',\n },\n getCurrentUser: {\n data: {\n displayName: 'John Doe',\n avatarImage: {url: 'https://example.com/avatar.jpg'},\n },\n },\n createOrderAttribution: undefined,\n addToCart: undefined,\n buyProduct: undefined,\n buyProducts: undefined,\n showErrorScreen: undefined,\n showErrorToast: undefined,\n getDeeplinkPaths: {\n matchers: ['/products', '/collections', '/cart'],\n },\n navigateToDeeplink: undefined,\n navigateToShop: undefined,\n navigateToProduct: undefined,\n navigateToOrder: undefined,\n navigateToCheckout: undefined,\n createImageUploadLink: {\n targets: [\n {\n url: 'https://example.com/upload',\n resourceUrl: 'https://example.com/resource',\n parameters: [{name: 'key', value: 'upload-123'}],\n },\n ],\n },\n completeImageUpload: {\n files: [\n {\n id: 'file-123',\n fileStatus: 'READY',\n image: {url: 'https://example.com/image.jpg'},\n },\n ],\n },\n getPersistedItem: null,\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: null,\n setInternalPersistedItem: undefined,\n removeInternalPersistedItem: undefined,\n getAllInternalPersistedKeys: ['internal-key1', 'internal-key2'],\n clearInternalPersistedItems: undefined,\n getSecret: 'secret-value',\n setSecret: undefined,\n removeSecret: undefined,\n reportInteraction: undefined,\n reportImpression: undefined,\n reportContentImpression: undefined,\n getProductLists: {\n data: [\n createProductList('list-1', 'Wishlist'),\n createProductList('list-2', 'Favorites'),\n ],\n pageInfo: createPagination(),\n },\n getProductList: {\n data: createProductList('list-1', 'Wishlist', [\n createProduct('prod-1', 'Sample Product'),\n ]),\n pageInfo: createPagination(),\n },\n addProductList: createProductList('list-3', 'New List'),\n removeProductList: undefined,\n renameProductList: createProductList('list-1', 'Updated Wishlist'),\n addProductListItem: undefined,\n removeProductListItem: undefined,\n getRecommendedProducts: {\n data: [\n createProduct('rec-1', 'Recommended Product 1', '79.99'),\n createProduct('rec-2', 'Recommended Product 2', '129.99'),\n createProduct('rec-3', 'Recommended Product 3', '129.99'),\n createProduct('rec-4', 'Recommended Product 4', '29.99'),\n createProduct('rec-5', 'Recommended Product 5', '39.99'),\n createProduct('rec-6', 'Recommended Product 6', '49.99'),\n createProduct('rec-7', 'Recommended Product 7', '59.99'),\n createProduct('rec-8', 'Recommended Product 8', '69.99'),\n createProduct('rec-9', 'Recommended Product 9', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getRecommendedShops: {\n data: [\n createShop('shop-1', 'Amazing Store'),\n createShop('shop-2', 'Best Deals Shop'),\n ],\n pageInfo: createPagination(),\n },\n searchProductsByShop: {\n data: [\n createProduct('search-1', 'Search Result 1', '59.99'),\n createProduct('search-2', 'Search Result 2', '89.99'),\n ],\n pageInfo: createPagination(),\n },\n getOrders: {\n data: [\n {\n id: 'order-1',\n name: '#1001',\n lineItems: [\n {\n productTitle: 'Sample Product',\n variantTitle: 'Medium',\n quantity: 2,\n product: null,\n },\n ],\n shop: createShop('shop-1', 'Sample Shop'),\n },\n ],\n pageInfo: createPagination(),\n },\n getBuyerAttributes: {\n data: {\n genderAffinity: 'NEUTRAL' as Gender,\n categoryAffinities: [\n {id: 'cat1', name: 'Electronics'},\n {id: 'cat2', name: 'Clothing'},\n ],\n },\n },\n showFeedbackSheet: undefined,\n getPopularProducts: {\n data: [\n createProduct('pop-1', 'The Hero Snowboard', '702.95'),\n createProduct('pop-2', 'Snow Jacket', '605.95', '702.00'),\n createProduct('pop-3', 'Winter Gloves', '89.95'),\n createProduct('pop-4', 'Summer Gloves', '89.95'),\n createProduct('pop-5', 'Spring Gloves', '89.95'),\n createProduct('pop-6', 'Playstation 5', '499.95'),\n createProduct('pop-7', 'Xbox Series X', '499.95'),\n createProduct('pop-8', 'Nintendo Switch', '299.95'),\n createProduct('pop-9', 'Playstation 4', '299.95'),\n createProduct('pop-10', 'Nintendo 3DS', '89.95'),\n ],\n pageInfo: createPagination(),\n },\n share: {\n message: 'Shared!',\n success: true,\n },\n shareSingle: {\n message: 'Shared!',\n success: true,\n },\n getCuratedProducts: {\n data: [\n createProduct('cur-1', 'Curated Product 1', '79.99'),\n createProduct('cur-2', 'Curated Product 2', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getSavedProducts: {\n data: [createProduct('saved-1', 'Saved Product 1', '49.99')],\n pageInfo: createPagination(),\n },\n getRecentProducts: {\n data: [createProduct('recent-1', 'Recent Product 1', '59.99')],\n pageInfo: createPagination(),\n },\n getProductSearch: {\n data: [\n createProduct('search-1', 'Search Product 1', '39.99'),\n createProduct('search-2', 'Search Product 2', '19.99'),\n createProduct('search-3', 'Search Product 3', '29.99'),\n createProduct('search-4', 'Search Product 4', '49.99'),\n createProduct('search-5', 'Search Product 5', '9.99'),\n ],\n pageInfo: createPagination(),\n },\n getProducts: {data: [createProduct('prod-2', 'Product 2', '19.99')]},\n getProduct: {data: createProduct('prod-1', 'Sample Product')},\n getProductVariants: {\n data: [\n {\n id: 'variant-1',\n title: 'Variant 1',\n isFavorited: false,\n image: {url: 'https://example.com/variant-1.jpg'},\n price: {amount: '19.99', currencyCode: 'USD'},\n compareAtPrice: {amount: '29.99', currencyCode: 'USD'},\n },\n ],\n pageInfo: createPagination(),\n },\n getProductMedia: {\n data: [\n {\n id: 'media-1',\n image: {url: 'https://example.com/media-1.jpg'},\n mediaContentType: 'IMAGE',\n alt: 'Sample product image',\n },\n ],\n pageInfo: createPagination(),\n },\n getShop: {\n data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),\n },\n getRecentShops: {\n data: [createShop('recent-shop-1', 'Recent Shop 1')],\n pageInfo: createPagination(),\n },\n getFollowedShops: {\n data: [createShop('followed-shop-1', 'Followed Shop 1')],\n pageInfo: createPagination(),\n },\n previewProductInAr: undefined,\n createContent: {\n data: {\n publicId: 'content-123',\n externalId: null,\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n description: 'This is a mock content item',\n visibility: ['DISCOVERABLE'],\n shareableUrl: 'https://example.com/content/123',\n products: null,\n },\n },\n getContent: {\n data: [\n {\n publicId: 'content-123',\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n visibility: ['DISCOVERABLE'],\n status: MinisContentStatus.READY,\n },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\n navigateToCart: undefined,\n requestPermission: {\n granted: true,\n },\n reportError: undefined,\n reportFetch: undefined,\n } as const\n\n const mock: Partial<ShopActions> = {}\n for (const key in results) {\n if (Object.prototype.hasOwnProperty.call(results, key)) {\n // @ts-expect-error: dynamic assignment is safe due to exhaustive mapping\n mock[key] = makeMockMethod(\n key as keyof ShopActions,\n results[key as keyof typeof results]\n )\n }\n }\n return mock as ShopActions\n}\n\n// Detect if running on a mobile device\nconst isMobile = (): boolean => {\n const userAgent = navigator.userAgent.toLowerCase()\n const isIOS = /iphone|ipad|ipod/.test(userAgent)\n const isAndroid = /android/.test(userAgent)\n\n return isIOS || isAndroid\n}\n\nexport const injectMocks = ({force}: {force?: boolean} = {}) => {\n // Only inject mocks if we aren't on a mobile device or we force it\n if (isMobile() && !force) {\n return\n }\n\n if (!window.minisSDK) {\n window.minisSDK = makeMockActions()\n window.minisParams = {\n handle: 'mock-handle',\n initialUrl: '/mock-initial-url',\n platform: 'web',\n }\n }\n}\n"],"names":["createProduct","id","title","price","compareAtPrice","createShop","name","options","themeType","shouldHaveBrandSettings","featuredImagesCount","featuredImages","_","i","getThemeColors","createHeaderTheme","createPagination","hasNext","createProductList","products","makeMockMethod","key","result","params","makeMockActions","results","MinisContentStatus","UserState","mock","isMobile","userAgent","isIOS","isAndroid","injectMocks","force"],"mappings":";;AASO,MAAMA,IAAgB,CAC3BC,GACAC,GACAC,IAAQ,SACRC,OACa;AAAA,EACb,IAAAH;AAAA,EACA,OAAAC;AAAA,EACA,OAAO,EAAC,QAAQC,GAAO,cAAc,MAAK;AAAA,EAC1C,GAAIC,KAAkB;AAAA,IACpB,gBAAgB,EAAC,QAAQA,GAAgB,cAAc,MAAK;AAAA,EAC9D;AAAA,EACA,iBAAiB,EAAC,eAAe,KAAK,aAAa,GAAE;AAAA,EACrD,MAAMC,EAAW,SAAS,WAAW;AAAA,EACrC,kBAAkB,WAAWJ,CAAE;AAAA,EAC/B,aAAa;AAAA,EACb,eAAe;AAAA,IACb,KAAK;AAAA,IACL,SAASC;AAAA,EAAA;AAEb,IAEaG,IAAa,CACxBJ,GACAK,GACAC,MAWG;AAEG,QAAAC,IAAYD,GAAS,aAAa,QAClCE,IACJF,GAAS,qBAAqBC,MAAc,QAGxCE,IAAsBH,GAAS,uBAAuB,GACtDI,IAAiB,MAAM,KAAK,EAAC,QAAQD,EAAmB,GAAG,CAACE,GAAGC,OAAO;AAAA,IAC1E,KAAK,wCAAwCZ,CAAE,IAAIY,CAAC;AAAA,IACpD,WAAW;AAAA,IACX,SAAS,GAAGP,CAAI,mBAAmBO,IAAI,CAAC;AAAA,EAAA,EACxC,GAGIC,IAAiB,MAAM;AAC3B,YAAQN,GAAW;AAAA,MACjB,KAAK;AACI,eAAA;AAAA,UACL,SAASD,GAAS;AAAA,UAClB,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS,sBAAsB;AAAA,QAChD;AAAA,MACF,KAAK;AACI,eAAA;AAAA,UACL,SAASA,GAAS,gBAAgB;AAAA,UAClC,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,MACF,KAAK;AACI,eAAA;AAAA,UACL,SAASA,GAAS;AAAA,UAClB,cAAcA,GAAS,qBAAqB;AAAA,UAC5C,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,MACF;AACS,eAAA;AAAA,UACL,SAASA,GAAS;AAAA,UAClB,cAAcA,GAAS;AAAA,UACvB,aAAaA,GAAS;AAAA,UACtB,eAAeA,GAAS;AAAA,QAC1B;AAAA,IAAA;AAAA,EAEN,GAGMQ,IAAoB,MAAM;AAC1B,QAAAP,MAAc,gBAAgBD,GAAS;AAClC,aAAA;AAAA,QACL,IAAI,gBAAgBN,CAAE;AAAA,QACtB,YAAY;AAAA,UACV,KACEM,GAAS,iBACT;AAAA,UACF,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,UACX,WAAW;AAAA,QACb;AAAA,QACA,UACEC,GAAS,eAAeC,MAAc,eAClC;AAAA,UACE,KACED,GAAS,eACT;AAAA,UACF,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,QAAA,IAEb;AAAA,MACR;AAGF,QAAIC,GAAS;AACJ,aAAA;AAAA,QACL,IAAI,gBAAgBN,CAAE;AAAA,QACtB,UAAU;AAAA,UACR,KAAKM,EAAQ;AAAA,UACb,SAAS,GAAGD,CAAI;AAAA,UAChB,WAAW;AAAA,QAAA;AAAA,MAEf;AAAA,EAIJ;AAEO,SAAA;AAAA,IACL,IAAAL;AAAA,IACA,MAAAK;AAAA,IACA,eAAe;AAAA,MACb,KAAK,WAAWA,EAAK,cAAc,QAAQ,QAAQ,GAAG,CAAC;AAAA,IACzD;AAAA,IACA,iBAAiB,EAAC,eAAe,KAAK,aAAa,GAAE;AAAA,IACrD,aAAa;AAAA,MACX,IAAI,gBAAgBL,CAAE;AAAA,MACtB,gBAAAU;AAAA,MACA,WAAW;AAAA,QACT,KAAK,wCAAwCV,CAAE;AAAA,QAC/C,WAAW;AAAA,MACb;AAAA,MACA,eAAeQ,IACX;AAAA,QACE,IAAI,kBAAkBR,CAAE;AAAA,QACxB,QAAQ;AAAA,UACN,IAAI,UAAUA,CAAE;AAAA,UAChB,GAAGa,EAAe;AAAA,QACpB;AAAA,QACA,aAAaC,EAAkB;AAAA,MAAA,IAEjC;AAAA,IAAA;AAAA,EAER;AACF,GAEMC,IAAmB,CAACC,IAAU,QAAW;AAAA,EAC7C,aAAaA;AAAA,EACb,WAAWA,IAAU,cAAc;AACrC,IAEMC,IAAoB,CAACjB,GAAYK,GAAca,IAAkB,CAAA,OAAQ;AAAA,EAC7E,IAAAlB;AAAA,EACA,UAAU,UAAUA,CAAE;AAAA,EACtB,MAAAK;AAAA,EACA,UAAAa;AACF;AASA,SAASC,EACPC,GACAC,GACgB;AAChB,SAAQ,CAACC,OACP,QAAQ,IAAI,iBAAiB,OAAOF,CAAG,CAAC,IAAIE,CAAM,GAC3C,QAAQ,QAAQ,EAAC,IAAI,IAAe,MAAMD,GAAO;AAE5D;AAEO,SAASE,IAA+B;AAC7C,QAAMC,IAEF;AAAA,IACF,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,uBAAuB;AAAA,MACrB,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,IACA,iCAAiC;AAAA,IACjC,4BAA4B;AAAA,IAC5B,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,uBAAuB;AAAA,MACrB,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM;AAAA,QACJ,aAAa;AAAA,QACb,aAAa,EAAC,KAAK,iCAAgC;AAAA,MAAA;AAAA,IAEvD;AAAA,IACA,wBAAwB;AAAA,IACxB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,MAChB,UAAU,CAAC,aAAa,gBAAgB,OAAO;AAAA,IACjD;AAAA,IACA,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,MACrB,SAAS;AAAA,QACP;AAAA,UACE,KAAK;AAAA,UACL,aAAa;AAAA,UACb,YAAY,CAAC,EAAC,MAAM,OAAO,OAAO,aAAa,CAAA;AAAA,QAAA;AAAA,MACjD;AAAA,IAEJ;AAAA,IACA,qBAAqB;AAAA,MACnB,OAAO;AAAA,QACL;AAAA,UACE,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,OAAO,EAAC,KAAK,gCAA+B;AAAA,QAAA;AAAA,MAC9C;AAAA,IAEJ;AAAA,IACA,kBAAkB;AAAA,IAClB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,qBAAqB,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAC5C,qBAAqB;AAAA,IACrB,0BAA0B;AAAA,IAC1B,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,6BAA6B,CAAC,iBAAiB,eAAe;AAAA,IAC9D,6BAA6B;AAAA,IAC7B,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,IAClB,yBAAyB;AAAA,IACzB,iBAAiB;AAAA,MACf,MAAM;AAAA,QACJP,EAAkB,UAAU,UAAU;AAAA,QACtCA,EAAkB,UAAU,WAAW;AAAA,MACzC;AAAA,MACA,UAAUF,EAAiB;AAAA,IAC7B;AAAA,IACA,gBAAgB;AAAA,MACd,MAAME,EAAkB,UAAU,YAAY;AAAA,QAC5ClB,EAAc,UAAU,gBAAgB;AAAA,MAAA,CACzC;AAAA,MACD,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,gBAAgBE,EAAkB,UAAU,UAAU;AAAA,IACtD,mBAAmB;AAAA,IACnB,mBAAmBA,EAAkB,UAAU,kBAAkB;AAAA,IACjE,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,MACtB,MAAM;AAAA,QACJlB,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,QACxDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,QACxDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,OAAO;AAAA,QACvDA,EAAc,SAAS,yBAAyB,QAAQ;AAAA,MAC1D;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,qBAAqB;AAAA,MACnB,MAAM;AAAA,QACJX,EAAW,UAAU,eAAe;AAAA,QACpCA,EAAW,UAAU,iBAAiB;AAAA,MACxC;AAAA,MACA,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,sBAAsB;AAAA,MACpB,MAAM;AAAA,QACJhB,EAAc,YAAY,mBAAmB,OAAO;AAAA,QACpDA,EAAc,YAAY,mBAAmB,OAAO;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,WAAW;AAAA,YACT;AAAA,cACE,cAAc;AAAA,cACd,cAAc;AAAA,cACd,UAAU;AAAA,cACV,SAAS;AAAA,YAAA;AAAA,UAEb;AAAA,UACA,MAAMX,EAAW,UAAU,aAAa;AAAA,QAAA;AAAA,MAE5C;AAAA,MACA,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB,oBAAoB;AAAA,UAClB,EAAC,IAAI,QAAQ,MAAM,cAAa;AAAA,UAChC,EAAC,IAAI,QAAQ,MAAM,WAAU;AAAA,QAAA;AAAA,MAC/B;AAAA,IAEJ;AAAA,IACA,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJhB,EAAc,SAAS,sBAAsB,QAAQ;AAAA,QACrDA,EAAc,SAAS,eAAe,UAAU,QAAQ;AAAA,QACxDA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,OAAO;AAAA,QAC/CA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,SAAS,mBAAmB,QAAQ;AAAA,QAClDA,EAAc,SAAS,iBAAiB,QAAQ;AAAA,QAChDA,EAAc,UAAU,gBAAgB,OAAO;AAAA,MACjD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,OAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA,aAAa;AAAA,MACX,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJhB,EAAc,SAAS,qBAAqB,OAAO;AAAA,QACnDA,EAAc,SAAS,qBAAqB,QAAQ;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM,CAAChB,EAAc,WAAW,mBAAmB,OAAO,CAAC;AAAA,MAC3D,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,mBAAmB;AAAA,MACjB,MAAM,CAAChB,EAAc,YAAY,oBAAoB,OAAO,CAAC;AAAA,MAC7D,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM;AAAA,QACJhB,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,OAAO;AAAA,QACrDA,EAAc,YAAY,oBAAoB,MAAM;AAAA,MACtD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,aAAa,EAAC,MAAM,CAAChB,EAAc,UAAU,aAAa,OAAO,CAAC,EAAC;AAAA,IACnE,YAAY,EAAC,MAAMA,EAAc,UAAU,gBAAgB,EAAC;AAAA,IAC5D,oBAAoB;AAAA,MAClB,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,aAAa;AAAA,UACb,OAAO,EAAC,KAAK,oCAAmC;AAAA,UAChD,OAAO,EAAC,QAAQ,SAAS,cAAc,MAAK;AAAA,UAC5C,gBAAgB,EAAC,QAAQ,SAAS,cAAc,MAAK;AAAA,QAAA;AAAA,MAEzD;AAAA,MACA,UAAUgB,EAAiB;AAAA,IAC7B;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,QACJ;AAAA,UACE,IAAI;AAAA,UACJ,OAAO,EAAC,KAAK,kCAAiC;AAAA,UAC9C,kBAAkB;AAAA,UAClB,KAAK;AAAA,QAAA;AAAA,MAET;AAAA,MACA,UAAUA,EAAiB;AAAA,IAC7B;AAAA,IACA,SAAS;AAAA,MACP,MAAMX,EAAW,UAAU,eAAe,EAAC,qBAAqB,EAAE,CAAA;AAAA,IACpE;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM,CAACA,EAAW,iBAAiB,eAAe,CAAC;AAAA,MACnD,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,kBAAkB;AAAA,MAChB,MAAM,CAACX,EAAW,mBAAmB,iBAAiB,CAAC;AAAA,MACvD,UAAUW,EAAiB;AAAA,IAC7B;AAAA,IACA,oBAAoB;AAAA,IACpB,eAAe;AAAA,MACb,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,OAAO;AAAA,UACL,IAAI;AAAA,UACJ,KAAK;AAAA,UACL,OAAO;AAAA,UACP,QAAQ;AAAA,QACV;AAAA,QACA,OAAO;AAAA,QACP,aAAa;AAAA,QACb,YAAY,CAAC,cAAc;AAAA,QAC3B,cAAc;AAAA,QACd,UAAU;AAAA,MAAA;AAAA,IAEd;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,QACJ;AAAA,UACE,UAAU;AAAA,UACV,OAAO;AAAA,YACL,IAAI;AAAA,YACJ,KAAK;AAAA,YACL,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AAAA,UACA,OAAO;AAAA,UACP,YAAY,CAAC,cAAc;AAAA,UAC3B,QAAQU,EAAmB;AAAA,QAAA;AAAA,MAC7B;AAAA,IAEJ;AAAA,IACA,mBAAmB;AAAA,MACjB,MAAM;AAAA,QACJ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,WAAWC,EAAU;AAAA,MAAA;AAAA,IAEzB;AAAA,IACA,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,MACjB,SAAS;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,EACf,GAEMC,IAA6B,CAAC;AACpC,aAAWP,KAAOI;AAChB,IAAI,OAAO,UAAU,eAAe,KAAKA,GAASJ,CAAG,MAEnDO,EAAKP,CAAG,IAAID;AAAA,MACVC;AAAA,MACAI,EAAQJ,CAA2B;AAAA,IACrC;AAGG,SAAAO;AACT;AAGA,MAAMC,IAAW,MAAe;AACxB,QAAAC,IAAY,UAAU,UAAU,YAAY,GAC5CC,IAAQ,mBAAmB,KAAKD,CAAS,GACzCE,IAAY,UAAU,KAAKF,CAAS;AAE1C,SAAOC,KAASC;AAClB,GAEaC,IAAc,CAAC,EAAC,OAAAC,EAAK,IAAuB,OAAO;AAE1D,EAAAL,EAAA,KAAc,CAACK,KAId,OAAO,aACV,OAAO,WAAWV,EAAgB,GAClC,OAAO,cAAc;AAAA,IACnB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ;AAEJ;"}
package/eslint/README.md CHANGED
@@ -31,10 +31,15 @@ npx eslint . --fix
31
31
 
32
32
  ## What You Get
33
33
 
34
+ ### Custom Shop Minis Rules
34
35
  - ✅ No internal imports allowed
35
36
  - ✅ Warnings for `<img>`, `<button>`, `<label>` tags (auto-fixes with imports)
36
37
  - ✅ Manifest scope validation - ensures manifest.json has scopes for hooks you use (auto-fixes manifest)
37
38
 
39
+ ### Security Rules (Using Built-in ESLint Rules)
40
+ - ✅ WebAssembly usage blocked - prevents WASM in Shop Minis environment
41
+ - ✅ Unsafe code execution blocked - prevents `eval()`, Function constructor, and dynamic code execution
42
+
38
43
  ## Rules
39
44
 
40
45
  ### `no-internal-imports`
@@ -184,6 +189,51 @@ fetch() call loads from "api.example.com" which is not in trusted_domains.
184
189
  <img> src attribute loads from "cdn.shopify.com" which is not in trusted_domains.
185
190
  ```
186
191
 
192
+ ## Security Rules
193
+
194
+ The following security restrictions are enforced using standard ESLint plugins and built-in rules:
195
+
196
+ ### WebAssembly Restrictions
197
+
198
+ **Rules:** `no-restricted-globals`, `no-restricted-syntax`
199
+
200
+ ```tsx
201
+ // ❌ Error
202
+ const module = new WebAssembly.Module(bytes)
203
+ WebAssembly.instantiate(bytes)
204
+ WebAssembly.compile(bytes)
205
+
206
+ // ✅ Correct
207
+ // Use JavaScript implementations instead of WebAssembly
208
+ ```
209
+
210
+ **Why:** WebAssembly is not supported in the Shop Minis security sandbox.
211
+
212
+ ### Unsafe Code Execution
213
+
214
+ **Rules:** `no-eval`, `no-implied-eval`, `no-new-func`, `no-script-url` (built-in ESLint rules)
215
+
216
+ ```tsx
217
+ // ❌ Error
218
+ eval('alert(1)')
219
+ new Function('a', 'b', 'return a + b')
220
+ setTimeout('alert(1)', 1000)
221
+ window.location = 'javascript:void(0)'
222
+
223
+ // ✅ Correct
224
+ const add = (a, b) => a + b
225
+ setTimeout(() => alert(1), 1000)
226
+ window.location = 'https://example.com'
227
+ ```
228
+
229
+ **Why:** Dynamic code execution can bypass security restrictions.
230
+
231
+ **Built-in rules used:**
232
+ - `no-eval` - blocks `eval()` and `window.eval()`
233
+ - `no-new-func` - blocks `new Function()` constructor
234
+ - `no-implied-eval` - blocks `setTimeout()` / `setInterval()` with string arguments
235
+ - `no-script-url` - blocks `javascript:` URLs
236
+
187
237
  ## Extending Rules
188
238
 
189
239
  To add more component mappings to `prefer-sdk-components`, edit `eslint/rules/prefer-sdk-components.cjs`:
package/eslint/config.cjs CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  // Import the plugin directly so consumers don't need to install it separately
10
+ const compatPlugin = require('eslint-plugin-compat')
10
11
  const reactPlugin = require('eslint-plugin-react')
11
12
 
12
13
  const shopMinisPlugin = require('./index.cjs')
@@ -26,13 +27,51 @@ module.exports = {
26
27
  plugins: {
27
28
  'shop-minis': shopMinisPlugin,
28
29
  react: reactPlugin,
30
+ compat: compatPlugin,
29
31
  },
30
32
  rules: {
33
+ // Console usage
31
34
  'no-console': 'warn',
35
+
36
+ // React security
32
37
  'react/no-danger': 'error',
38
+
39
+ // Shop Minis custom rules
40
+ 'shop-minis/no-env-without-fallback': 'error',
33
41
  'shop-minis/no-internal-imports': 'error',
42
+ 'shop-minis/no-secrets': 'error',
34
43
  'shop-minis/prefer-sdk-components': 'warn',
35
44
  'shop-minis/prefer-sdk-hooks': 'warn',
36
45
  'shop-minis/validate-manifest': 'error',
46
+
47
+ // Unsafe code execution (built-in ESLint rules)
48
+ 'no-eval': 'error',
49
+ 'no-implied-eval': 'error',
50
+ 'no-new-func': 'error',
51
+ 'no-script-url': 'error',
52
+
53
+ // WebAssembly restrictions (built-in ESLint rules)
54
+ 'no-restricted-globals': [
55
+ 'error',
56
+ {
57
+ name: 'WebAssembly',
58
+ message:
59
+ 'WebAssembly is not supported in the Shop Minis environment. Consider using alternative JavaScript implementations.',
60
+ },
61
+ ],
62
+ 'no-restricted-syntax': [
63
+ 'error',
64
+ {
65
+ selector: "MemberExpression[object.name='WebAssembly']",
66
+ message:
67
+ 'WebAssembly is not supported in the Shop Minis environment. Consider using alternative JavaScript implementations.',
68
+ },
69
+ {
70
+ selector: "NewExpression[callee.object.name='WebAssembly']",
71
+ message:
72
+ 'WebAssembly is not supported in the Shop Minis environment. Consider using alternative JavaScript implementations.',
73
+ },
74
+ ],
75
+ 'compat/compat': 'error',
37
76
  },
38
77
  }
package/eslint/index.cjs CHANGED
@@ -4,14 +4,18 @@
4
4
  * @fileoverview Custom ESLint rules for Shop Minis React SDK
5
5
  */
6
6
 
7
+ const noEnvWithoutFallback = require('./rules/no-env-without-fallback.cjs')
7
8
  const noInternalImports = require('./rules/no-internal-imports.cjs')
9
+ const noSecrets = require('./rules/no-secrets.cjs')
8
10
  const preferSdkComponents = require('./rules/prefer-sdk-components.cjs')
9
11
  const preferSdkHooks = require('./rules/prefer-sdk-hooks.cjs')
10
12
  const validateManifest = require('./rules/validate-manifest.cjs')
11
13
 
12
14
  module.exports = {
13
15
  rules: {
16
+ 'no-env-without-fallback': noEnvWithoutFallback,
14
17
  'no-internal-imports': noInternalImports,
18
+ 'no-secrets': noSecrets,
15
19
  'prefer-sdk-components': preferSdkComponents,
16
20
  'prefer-sdk-hooks': preferSdkHooks,
17
21
  'validate-manifest': validateManifest,
@@ -0,0 +1,148 @@
1
+ /**
2
+ * ESLint rule to require fallbacks for environment variables
3
+ * @fileoverview Disallow using import.meta.env without a fallback value
4
+ *
5
+ * In Shop Minis, environment variables work in development but are not available
6
+ * in production. This rule ensures developers always provide fallback values.
7
+ */
8
+
9
+ module.exports = {
10
+ meta: {
11
+ type: 'problem',
12
+ docs: {
13
+ description:
14
+ 'Require fallback values when using import.meta.env variables',
15
+ category: 'Best Practices',
16
+ recommended: true,
17
+ },
18
+ messages: {
19
+ noFallback:
20
+ 'Environment variable "{{ name }}" must have a fallback value. Environment variables are only available in development. Use || or ?? to provide a production fallback.',
21
+ },
22
+ schema: [],
23
+ },
24
+
25
+ create(context) {
26
+ return {
27
+ MemberExpression(node) {
28
+ // Check if this is import.meta.env.SOMETHING
29
+ if (!isImportMetaEnv(node)) {
30
+ return
31
+ }
32
+
33
+ // Get the env variable name for the error message
34
+ const envName = node.property.name || node.property.value || 'unknown'
35
+
36
+ // Check if it has a fallback (|| or ??)
37
+ if (hasFallback(node)) {
38
+ return
39
+ }
40
+
41
+ context.report({
42
+ node,
43
+ messageId: 'noFallback',
44
+ data: {
45
+ name: `import.meta.env.${envName}`,
46
+ },
47
+ })
48
+ },
49
+ }
50
+ },
51
+ }
52
+
53
+ /**
54
+ * Check if node is import.meta.env.SOMETHING
55
+ */
56
+ function isImportMetaEnv(node) {
57
+ // Must be a member expression with a property
58
+ if (node.type !== 'MemberExpression' || !node.property) {
59
+ return false
60
+ }
61
+
62
+ // The object should be import.meta.env
63
+ const obj = node.object
64
+ if (
65
+ obj.type === 'MemberExpression' &&
66
+ obj.object.type === 'MetaProperty' &&
67
+ obj.object.meta.name === 'import' &&
68
+ obj.object.property.name === 'meta' &&
69
+ obj.property.name === 'env'
70
+ ) {
71
+ return true
72
+ }
73
+
74
+ return false
75
+ }
76
+
77
+ /**
78
+ * Check if the node has a fallback via || or ?? operator
79
+ */
80
+ function hasFallback(node) {
81
+ let current = node.parent
82
+
83
+ // Walk up the tree to find if we're in a logical expression with fallback
84
+ while (current) {
85
+ // Direct fallback: import.meta.env.FOO || 'default' or import.meta.env.FOO ?? 'default'
86
+ // Also handles chains: import.meta.env.A || import.meta.env.B || 'default'
87
+ if (
88
+ current.type === 'LogicalExpression' &&
89
+ (current.operator === '||' || current.operator === '??')
90
+ ) {
91
+ // Check if this logical expression chain eventually has a non-env fallback
92
+ if (logicalChainHasFallback(current)) {
93
+ return true
94
+ }
95
+ }
96
+
97
+ // Conditional expression: import.meta.env.FOO ? import.meta.env.FOO : 'default'
98
+ if (current.type === 'ConditionalExpression') {
99
+ // If used in the test part, it has a fallback (the alternate)
100
+ if (node === current.test) {
101
+ return true
102
+ }
103
+ // If used in the consequent and test is an env var check, it's valid
104
+ if (node === current.consequent && isImportMetaEnv(current.test)) {
105
+ return true
106
+ }
107
+ }
108
+
109
+ current = current.parent
110
+ }
111
+
112
+ return false
113
+ }
114
+
115
+ /**
116
+ * Check if a logical expression chain has a fallback at the end
117
+ * For: A || B || C, we walk up to find the topmost || or ?? and check its right side
118
+ */
119
+ function logicalChainHasFallback(node) {
120
+ // Walk up to the topmost logical expression in the chain
121
+ let topmost = node
122
+ while (
123
+ topmost.parent &&
124
+ topmost.parent.type === 'LogicalExpression' &&
125
+ (topmost.parent.operator === '||' || topmost.parent.operator === '??') &&
126
+ topmost.parent.left === topmost
127
+ ) {
128
+ topmost = topmost.parent
129
+ }
130
+
131
+ // The rightmost value in the chain is the fallback
132
+ // Check that it's not also an import.meta.env (that would mean no real fallback)
133
+ const rightmost = getRightmostValue(topmost)
134
+ return !isImportMetaEnv(rightmost)
135
+ }
136
+
137
+ /**
138
+ * Get the rightmost value in a logical expression chain
139
+ */
140
+ function getRightmostValue(node) {
141
+ if (
142
+ node.type === 'LogicalExpression' &&
143
+ (node.operator === '||' || node.operator === '??')
144
+ ) {
145
+ return getRightmostValue(node.right)
146
+ }
147
+ return node
148
+ }
@@ -0,0 +1,27 @@
1
+ /* eslint-disable import/extensions */
2
+ /**
3
+ * ESLint rule wrapper for eslint-plugin-no-secrets with custom messages
4
+ * @fileoverview Customized messages for detecting hardcoded secrets in Shop Minis
5
+ */
6
+
7
+ const noSecretsPlugin = require('eslint-plugin-no-secrets')
8
+
9
+ // Get the original rule
10
+ const originalRule = noSecretsPlugin.rules['no-secrets']
11
+
12
+ module.exports = {
13
+ meta: {
14
+ ...originalRule.meta,
15
+ messages: {
16
+ HIGH_ENTROPY:
17
+ 'This string may be a hardcoded credential, which should never be committed. Please check the Shop Minis documentation learn how to handle these cases. You can disable this rule if it is not a credential.',
18
+ PATTERN_MATCH:
19
+ 'Potential {{ name }} detected. Hardcoded credentials should never be committed. Please check the Shop Minis documentation learn how to handle these cases. You can disable this rule if it is not a credential.',
20
+ },
21
+ },
22
+ create(context) {
23
+ // Create the original rule's visitor
24
+ const originalVisitor = originalRule.create(context)
25
+ return originalVisitor
26
+ },
27
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shopify/shop-minis-react",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "0.4.3",
4
+ "version": "0.4.5",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -43,21 +43,23 @@
43
43
  "typescript": ">=5.0.0"
44
44
  },
45
45
  "dependencies": {
46
- "@shopify/shop-minis-platform": "0.8.0",
46
+ "@shopify/shop-minis-platform": "0.8.1",
47
47
  "@tailwindcss/vite": "4.1.8",
48
48
  "@tanstack/react-query": "5.86.0",
49
49
  "@types/color": "3.0.6",
50
50
  "@types/lodash": "4.17.20",
51
51
  "@types/react-window": "1.8.8",
52
52
  "@types/url-parse": "1.4.9",
53
- "@typescript-eslint/parser": "^7.0.0",
53
+ "@typescript-eslint/parser": "7.0.0",
54
54
  "@vitejs/plugin-react": "4.5.1",
55
55
  "class-variance-authority": "0.7.1",
56
56
  "clsx": "2.1.1",
57
57
  "color": "4.2.3",
58
58
  "embla-carousel-react": "8.6.0",
59
- "eslint": "^8.57.0",
60
- "eslint-plugin-react": "^7.37.5",
59
+ "eslint": "8.57.0",
60
+ "eslint-plugin-compat": "6.0.2",
61
+ "eslint-plugin-no-secrets": "2.2.1",
62
+ "eslint-plugin-react": "7.37.5",
61
63
  "js-base64": "3.7.7",
62
64
  "lodash": "4.17.21",
63
65
  "lucide-react": "0.513.0",
package/src/mocks.ts CHANGED
@@ -470,6 +470,7 @@ export function makeMockActions(): ShopActions {
470
470
  granted: true,
471
471
  },
472
472
  reportError: undefined,
473
+ reportFetch: undefined,
473
474
  } as const
474
475
 
475
476
  const mock: Partial<ShopActions> = {}