From 45578752b36316b6f72940de13f5fa5a80a230a5 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Fri, 12 Apr 2024 14:07:04 -0500 Subject: [PATCH 01/32] Make scopes optional for mangaged install token exchange apps --- .../shopify-api/lib/__tests__/config.test.ts | 19 +++--- packages/apps/shopify-api/lib/auth/index.ts | 2 + .../oauth/__tests__/create-session.test.ts | 5 +- .../lib/auth/oauth/__tests__/oauth.test.ts | 22 +++++-- .../oauth/__tests__/token-exchange.test.ts | 6 +- .../apps/shopify-api/lib/auth/oauth/oauth.ts | 3 + packages/apps/shopify-api/lib/base-types.ts | 6 +- packages/apps/shopify-api/lib/config.ts | 2 - .../lib/session/__tests__/session.test.ts | 60 ++++++++++++++----- .../apps/shopify-api/lib/session/session.ts | 24 +++++--- .../src/__tests__/index.test.ts | 5 +- .../src/__tests__/integration/oauth.test.ts | 7 ++- .../middlewares/__tests__/csp-headers.test.ts | 6 +- .../ensure-installed-on-shop.test.ts | 5 +- .../validate-authenticated-session.test.ts | 17 ++++-- .../src/server/shopify-app.ts | 1 + 16 files changed, 135 insertions(+), 55 deletions(-) diff --git a/packages/apps/shopify-api/lib/__tests__/config.test.ts b/packages/apps/shopify-api/lib/__tests__/config.test.ts index 93a48536f9..04eb00ad2d 100644 --- a/packages/apps/shopify-api/lib/__tests__/config.test.ts +++ b/packages/apps/shopify-api/lib/__tests__/config.test.ts @@ -29,7 +29,6 @@ describe('Config object', () => { expect(config.apiKey).toEqual(validParams.apiKey); expect(config.apiSecretKey).toEqual(validParams.apiSecretKey); - expect(config.scopes.equals(validParams.scopes)).toBeTruthy(); expect(config.hostName).toEqual(validParams.hostName); }); @@ -54,16 +53,6 @@ describe('Config object', () => { expect(error.message).toContain('Missing values for: apiSecretKey'); } - invalid = {...validParams}; - invalid.scopes = []; - try { - validateConfig(invalid); - fail('Initializing without scopes did not throw an exception'); - } catch (error) { - expect(error).toBeInstanceOf(ShopifyErrors.ShopifyError); - expect(error.message).toContain('Missing values for: scopes'); - } - invalid = {...validParams}; invalid.hostName = ''; try { @@ -97,6 +86,14 @@ describe('Config object', () => { validParams.isCustomStoreApp = false; }); + it('scopes can be not defined', () => { + delete (validParams as any).scopes; + + expect(() => validateConfig(validParams)).not.toThrow( + ShopifyErrors.ShopifyError, + ); + }); + it("ignores a missing 'apiKey' when isCustomStoreApp is true", () => { validParams.isCustomStoreApp = true; validParams.adminApiAccessToken = 'token'; diff --git a/packages/apps/shopify-api/lib/auth/index.ts b/packages/apps/shopify-api/lib/auth/index.ts index 37cc7b00f4..1954b28928 100644 --- a/packages/apps/shopify-api/lib/auth/index.ts +++ b/packages/apps/shopify-api/lib/auth/index.ts @@ -11,6 +11,8 @@ import { } from './get-embedded-app-url'; import {TokenExchange, tokenExchange} from './oauth/token-exchange'; +export {AuthScopes} from './scopes'; + export function shopifyAuth( config: Config, ): ShopifyAuth { diff --git a/packages/apps/shopify-api/lib/auth/oauth/__tests__/create-session.test.ts b/packages/apps/shopify-api/lib/auth/oauth/__tests__/create-session.test.ts index 25cf6be945..635526346c 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/__tests__/create-session.test.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/__tests__/create-session.test.ts @@ -20,10 +20,13 @@ describe('createSession', () => { `creates a new offline session when embedded is %s`, (isEmbeddedApp) => { const shopify = shopifyApi(testConfig({isEmbeddedApp})); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const accessTokenResponse = { access_token: 'some access token string', - scope: shopify.config.scopes.toString(), + scope: scopes, }; const session = createSession({ diff --git a/packages/apps/shopify-api/lib/auth/oauth/__tests__/oauth.test.ts b/packages/apps/shopify-api/lib/auth/oauth/__tests__/oauth.test.ts index 855381f0df..f349adfdff 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/__tests__/oauth.test.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/__tests__/oauth.test.ts @@ -78,9 +78,13 @@ describe('beginAuth', () => { rawRequest: request, }); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; + const query = { client_id: shopify.config.apiKey, - scope: shopify.config.scopes.toString(), + scope: scopes, redirect_uri: `${shopify.config.hostScheme}://${shopify.config.hostName}/some-callback`, state: VALID_NONCE, 'grant_options[]': '', @@ -102,10 +106,13 @@ describe('beginAuth', () => { callbackPath: '/some-callback', rawRequest: request, }); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const query = { client_id: shopify.config.apiKey, - scope: shopify.config.scopes.toString(), + scope: scopes, redirect_uri: `http://${shopify.config.hostName}/some-callback`, state: VALID_NONCE, 'grant_options[]': '', @@ -127,10 +134,13 @@ describe('beginAuth', () => { callbackPath: '/some-callback', rawRequest: request, }); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const query = { client_id: shopify.config.apiKey, - scope: shopify.config.scopes.toString(), + scope: scopes, redirect_uri: `${shopify.config.hostScheme}://${shopify.config.hostName}/some-callback`, state: VALID_NONCE, 'grant_options[]': 'per-user', @@ -293,9 +303,13 @@ describe('callback', () => { testCallbackQuery.hmac = expectedHmac; request.url += `?${new URLSearchParams(testCallbackQuery).toString()}`; + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; + const successResponse = { access_token: 'some access token string', - scope: shopify.config.scopes.toString(), + scope: scopes, }; queueMockResponse(JSON.stringify(successResponse)); diff --git a/packages/apps/shopify-api/lib/auth/oauth/__tests__/token-exchange.test.ts b/packages/apps/shopify-api/lib/auth/oauth/__tests__/token-exchange.test.ts index 72814cd4f3..809f996d44 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/__tests__/token-exchange.test.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/__tests__/token-exchange.test.ts @@ -103,10 +103,12 @@ describe('tokenExchange', () => { shopify.config.apiSecretKey, sessionTokenPayload, ); - + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const successResponse = { access_token: 'some access token string', - scope: shopify.config.scopes.toString(), + scope: scopes, }; queueMockResponse(JSON.stringify(successResponse)); diff --git a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts index 5366016309..2692654b73 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts @@ -68,6 +68,9 @@ export function begin(config: ConfigInterface): OAuthBegin { config.isCustomStoreApp, 'Cannot perform OAuth for private apps', ); + if (!config.scopes) { + throw new ShopifyErrors.MissingRequiredArgument('Apps that use OAuth must define the required scopes in the config'); + } const log = logger(config); log.info('Beginning OAuth', {shop, isOnline, callbackPath}); diff --git a/packages/apps/shopify-api/lib/base-types.ts b/packages/apps/shopify-api/lib/base-types.ts index 2a41e139c2..7ffb22b4c5 100644 --- a/packages/apps/shopify-api/lib/base-types.ts +++ b/packages/apps/shopify-api/lib/base-types.ts @@ -27,7 +27,7 @@ export interface ConfigParams< */ apiSecretKey: string; /** - * The scopes your app needs to access the API. + * The scopes your app needs to access the API. Not required if using Shopify managed installation. */ scopes?: string[] | AuthScopes; /** @@ -117,11 +117,11 @@ export interface ConfigParams< export type ConfigInterface = Omit< Params, - 'restResources' + 'restResources' | 'scopes' > & { apiKey: string; hostScheme: 'http' | 'https'; - scopes: AuthScopes; + scopes?: AuthScopes; isCustomStoreApp: boolean; billing?: BillingConfig; logger: { diff --git a/packages/apps/shopify-api/lib/config.ts b/packages/apps/shopify-api/lib/config.ts index d06fa8fe4b..24aecd9fd1 100644 --- a/packages/apps/shopify-api/lib/config.ts +++ b/packages/apps/shopify-api/lib/config.ts @@ -10,7 +10,6 @@ export function validateConfig( const config = { apiKey: '', apiSecretKey: '', - scopes: new AuthScopes([]), hostName: '', hostScheme: 'https', apiVersion: LATEST_API_VERSION, @@ -30,7 +29,6 @@ export function validateConfig( const mandatory: (keyof Params)[] = ['apiSecretKey', 'hostName']; if (!('isCustomStoreApp' in params) || !params.isCustomStoreApp) { mandatory.push('apiKey'); - mandatory.push('scopes'); } if ('isCustomStoreApp' in params && params.isCustomStoreApp) { if ( diff --git a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts index 8ad036eec0..b539960853 100644 --- a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts +++ b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts @@ -58,20 +58,46 @@ describe('isActive', () => { expect(session.isActive(shopify.config.scopes)).toBeTruthy(); }); +}); - it('returns false if session is not active', () => { - const shopify = shopifyApi(testConfig()); +it('returns true when scopes that passed in empty and scopes are not equal', () => { + const session = new Session({ + id: 'active', + shop: 'active-shop', + state: 'test_state', + isOnline: true, + scope: 'test_scope', + accessToken: 'indeed', + expires: new Date(Date.now() + 86400), + }); - const session = new Session({ - id: 'not_active', - shop: 'inactive-shop', - state: 'not_same', - isOnline: true, - scope: 'test_scope', - expires: new Date(Date.now() - 1), - }); - expect(session.isActive(shopify.config.scopes)).toBeFalsy(); + expect(session.isActive('')).toBeTruthy(); +}); + +it('returns false if session is not active', () => { + const shopify = shopifyApi(testConfig()); + + const session = new Session({ + id: 'not_active', + shop: 'inactive-shop', + state: 'not_same', + isOnline: true, + scope: 'test_scope', + expires: new Date(Date.now() - 1), + }); + expect(session.isActive(shopify.config.scopes)).toBeFalsy(); +}); + +it('returns false if checking scopes and scopes are not equal', () => { + const session = new Session({ + id: 'not_active', + shop: 'inactive-shop', + state: 'not_same', + isOnline: true, + scope: 'test_scope', + expires: new Date(Date.now() - 1), }); + expect(session.isActive('fake_scope')).toBeFalsy(); }); describe('isExpired', () => { @@ -142,13 +168,16 @@ describe('isExpired', () => { describe('isScopeChanged', () => { it('returns true if scopes requested have changed', () => { const shopify = shopifyApi(testConfig()); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const session = new Session({ id: 'not_active', shop: 'inactive-shop', state: 'not_same', isOnline: true, - scope: shopify.config.scopes.toString(), + scope: scopes, expires: new Date(Date.now() - 1), }); expect( @@ -158,16 +187,19 @@ describe('isScopeChanged', () => { it('returns false if scopes requested are unchanged', () => { const shopify = shopifyApi(testConfig()); + const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; const session = new Session({ id: 'not_active', shop: 'inactive-shop', state: 'not_same', isOnline: true, - scope: shopify.config.scopes.toString(), + scope: scopes, expires: new Date(Date.now() - 1), }); - expect(session.isScopeChanged(shopify.config.scopes)).toBeFalsy(); + expect(session.isScopeChanged(scopes)).toBeFalsy(); }); }); diff --git a/packages/apps/shopify-api/lib/session/session.ts b/packages/apps/shopify-api/lib/session/session.ts index 4652171160..8709c7b205 100644 --- a/packages/apps/shopify-api/lib/session/session.ts +++ b/packages/apps/shopify-api/lib/session/session.ts @@ -172,14 +172,24 @@ export class Session { } /** - * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes. + * Whether the session is active. Active sessions have an access token that is not expired, and has has the given + * scopes if scopes is equal to a truthy value. */ - public isActive(scopes: AuthScopes | string | string[]): boolean { - return ( - !this.isScopeChanged(scopes) && - Boolean(this.accessToken) && - !this.isExpired() - ); + public isActive(scopes: AuthScopes | string | string[] | undefined): boolean { + const usingScopes = + scopes instanceof AuthScopes + ? scopes.toArray().length > 0 + : scopes !== '' && typeof scopes !== undefined && scopes !== null; + const hasAccessToken = Boolean(this.accessToken); + const isTokenNotExpired = !this.isExpired(); + if (usingScopes) { + const isScopeChanged = this.isScopeChanged( + scopes as AuthScopes | string | string[], + ); + return !isScopeChanged && hasAccessToken && isTokenNotExpired; + } else { + return hasAccessToken && isTokenNotExpired; + } } /** diff --git a/packages/apps/shopify-app-express/src/__tests__/index.test.ts b/packages/apps/shopify-app-express/src/__tests__/index.test.ts index 1c220e2993..51cf1a3f49 100644 --- a/packages/apps/shopify-app-express/src/__tests__/index.test.ts +++ b/packages/apps/shopify-app-express/src/__tests__/index.test.ts @@ -50,11 +50,14 @@ describe('shopifyApp', () => { logger: testConfig.api.logger, }, }); + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; expect(shopify).toBeDefined(); expect(shopify.api.config.apiKey).toEqual('envKey'); expect(shopify.api.config.apiSecretKey).toEqual('envSecret'); - expect(shopify.api.config.scopes.toString()).toEqual('envScope1,envScope2'); + expect(scopes).toEqual('envScope1,envScope2'); expect(shopify.api.config.hostName).toEqual('envHost'); expect(shopify.api.config.hostScheme).toEqual('https'); expect(shopify.api.config.customShopDomains).toEqual(['*.envCustomDomain']); diff --git a/packages/apps/shopify-app-express/src/__tests__/integration/oauth.test.ts b/packages/apps/shopify-app-express/src/__tests__/integration/oauth.test.ts index 3a2e423b1b..ef91334ab6 100644 --- a/packages/apps/shopify-app-express/src/__tests__/integration/oauth.test.ts +++ b/packages/apps/shopify-app-express/src/__tests__/integration/oauth.test.ts @@ -240,9 +240,10 @@ function assertOAuthBeginRedirectUrl( expect(redirecUri.pathname).toBe('/test/auth/callback'); expect(url.searchParams.get('client_id')).toEqual(shopify.api.config.apiKey); - expect(url.searchParams.get('scope')).toEqual( - shopify.api.config.scopes.toString(), - ); + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; + expect(url.searchParams.get('scope')).toEqual(scopes); expect(url.searchParams.get('state')).toEqual(expect.stringMatching(/.{15}/)); if (isOnline) { diff --git a/packages/apps/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts b/packages/apps/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts index b2e1a457f5..02a3f38e95 100644 --- a/packages/apps/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts +++ b/packages/apps/shopify-app-express/src/middlewares/__tests__/csp-headers.test.ts @@ -34,12 +34,16 @@ describe('cspHeaders', () => { res.send(htmlPage); }); + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; + session = new Session({ id: '123-this-is-a-session-id', shop: TEST_SHOP, state: '123-this-is-a-state', isOnline: shopify.config.useOnlineTokens, - scope: shopify.api.config.scopes.toString(), + scope: scopes, expires: undefined, accessToken: 'totally-real-access-token', }); diff --git a/packages/apps/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts b/packages/apps/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts index da6c3c4a32..3da36075c9 100644 --- a/packages/apps/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts +++ b/packages/apps/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts @@ -20,12 +20,15 @@ describe('ensureInstalledOnShop', () => { app.get('/test/shop', async (req, res) => { res.json({data: {shop: {name: req.query.shop}}}); }); + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; session = new Session({ id: `offline_${TEST_SHOP}`, shop: TEST_SHOP, state: '123-this-is-a-state', isOnline: false, - scope: shopify.api.config.scopes.toString(), + scope: scopes, expires: undefined, accessToken: 'totally-real-access-token', }); diff --git a/packages/apps/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts b/packages/apps/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts index d32c68f2ea..e7a6972810 100644 --- a/packages/apps/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts +++ b/packages/apps/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts @@ -41,13 +41,16 @@ describe('validateAuthenticatedSession', () => { algorithm: 'HS256', }, ); + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; session = new Session({ id: sessionId, shop, state: '123-this-is-a-state', isOnline: shopify.config.useOnlineTokens, - scope: shopify.api.config.scopes.toString(), + scope: scopes, expires: undefined, accessToken: 'totally-real-access-token', }); @@ -164,13 +167,14 @@ describe('validateAuthenticatedSession', () => { ); const location = new URL(response.header.location); const locationParams = location.searchParams; + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; expect(location.hostname).toBe('other-shop.myshopify.io'); expect(location.pathname).toBe('/admin/oauth/authorize'); expect(locationParams.get('client_id')).toBe(shopify.api.config.apiKey); - expect(locationParams.get('scope')).toBe( - shopify.api.config.scopes.toString(), - ); + expect(locationParams.get('scope')).toBe(scopes); expect(locationParams.get('redirect_uri')).toEqual( expect.stringMatching(expectedRedirectUriStart.href), ); @@ -246,13 +250,16 @@ describe('validateAuthenticatedSession', () => { sessionId, )}`, ]; + const scopes = shopify.api.config.scopes + ? shopify.api.config.scopes.toString() + : ''; session = new Session({ id: sessionId, shop: 'my-shop.myshopify.io', state: '123-this-is-a-state', isOnline: shopify.config.useOnlineTokens, - scope: shopify.api.config.scopes.toString(), + scope: scopes, expires: undefined, accessToken: 'totally-real-access-token', }); diff --git a/packages/apps/shopify-app-remix/src/server/shopify-app.ts b/packages/apps/shopify-app-remix/src/server/shopify-app.ts index e0dd568d92..6f0f0d4703 100644 --- a/packages/apps/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/apps/shopify-app-remix/src/server/shopify-app.ts @@ -184,6 +184,7 @@ function deriveConfig( return { ...appConfig, ...apiConfig, + scopes: apiConfig.scopes, idempotentPromiseHandler: new IdempotentPromiseHandler(), canUseLoginForm: appConfig.distribution !== AppDistribution.ShopifyAdmin, useOnlineTokens: appConfig.useOnlineTokens ?? false, From 8a904653d24a2e25ebba3bfd5ed27e5bf207049c Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Fri, 12 Apr 2024 15:25:33 -0500 Subject: [PATCH 02/32] lint --- packages/apps/shopify-api/lib/auth/oauth/oauth.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts index 2692654b73..dd0b1b8072 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts @@ -69,7 +69,9 @@ export function begin(config: ConfigInterface): OAuthBegin { 'Cannot perform OAuth for private apps', ); if (!config.scopes) { - throw new ShopifyErrors.MissingRequiredArgument('Apps that use OAuth must define the required scopes in the config'); + throw new ShopifyErrors.MissingRequiredArgument( + 'Apps that use OAuth must define the required scopes in the config', + ); } const log = logger(config); From 1229cfe7b990062abad46e367f130c2a7fff5ecc Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Fri, 12 Apr 2024 17:36:14 -0500 Subject: [PATCH 03/32] Remove exception --- packages/apps/shopify-api/lib/auth/oauth/oauth.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts index dd0b1b8072..77c0d68869 100644 --- a/packages/apps/shopify-api/lib/auth/oauth/oauth.ts +++ b/packages/apps/shopify-api/lib/auth/oauth/oauth.ts @@ -68,11 +68,6 @@ export function begin(config: ConfigInterface): OAuthBegin { config.isCustomStoreApp, 'Cannot perform OAuth for private apps', ); - if (!config.scopes) { - throw new ShopifyErrors.MissingRequiredArgument( - 'Apps that use OAuth must define the required scopes in the config', - ); - } const log = logger(config); log.info('Beginning OAuth', {shop, isOnline, callbackPath}); @@ -104,9 +99,10 @@ export function begin(config: ConfigInterface): OAuthBegin { path: callbackPath, }); + const scopes = config.scopes ? config.scopes.toString() : ''; const query = { client_id: config.apiKey, - scope: config.scopes.toString(), + scope: scopes, redirect_uri: `${config.hostScheme}://${config.hostName}${callbackPath}`, state, 'grant_options[]': isOnline ? 'per-user' : '', From 637c6c39a323ef50fcdbaccbb3253479be17a216 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Tue, 16 Apr 2024 14:00:47 -0500 Subject: [PATCH 04/32] Add migration guide --- .changeset/lovely-balloons-punch.md | 7 +++++++ .../apps/shopify-api/docs/migrating-to-v10.md | 9 ++++++++ .../apps/shopify-api/lib/session/session.ts | 21 +++++++------------ 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 .changeset/lovely-balloons-punch.md create mode 100644 packages/apps/shopify-api/docs/migrating-to-v10.md diff --git a/.changeset/lovely-balloons-punch.md b/.changeset/lovely-balloons-punch.md new file mode 100644 index 0000000000..156a3dc1e1 --- /dev/null +++ b/.changeset/lovely-balloons-punch.md @@ -0,0 +1,7 @@ +--- +"@shopify/shopify-api": major +"@shopify/shopify-app-express": patch +"@shopify/shopify-app-remix": patch +--- + +This `scopes` field on the API config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. diff --git a/packages/apps/shopify-api/docs/migrating-to-v10.md b/packages/apps/shopify-api/docs/migrating-to-v10.md new file mode 100644 index 0000000000..03302cf6b6 --- /dev/null +++ b/packages/apps/shopify-api/docs/migrating-to-v10.md @@ -0,0 +1,9 @@ +# Migrating to v10 + +This document outlines the changes you need to make to your app to migrate from v9 to v10 of this package. + +## `Scopes` on config object are now optional + +The `scopes` property on the config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. + +Using both the `scopes` property and managed install can lead to unexpected behavior if these values are not kept in sync. diff --git a/packages/apps/shopify-api/lib/session/session.ts b/packages/apps/shopify-api/lib/session/session.ts index 8709c7b205..69cc283899 100644 --- a/packages/apps/shopify-api/lib/session/session.ts +++ b/packages/apps/shopify-api/lib/session/session.ts @@ -176,28 +176,23 @@ export class Session { * scopes if scopes is equal to a truthy value. */ public isActive(scopes: AuthScopes | string | string[] | undefined): boolean { - const usingScopes = - scopes instanceof AuthScopes - ? scopes.toArray().length > 0 - : scopes !== '' && typeof scopes !== undefined && scopes !== null; const hasAccessToken = Boolean(this.accessToken); const isTokenNotExpired = !this.isExpired(); - if (usingScopes) { - const isScopeChanged = this.isScopeChanged( - scopes as AuthScopes | string | string[], - ); - return !isScopeChanged && hasAccessToken && isTokenNotExpired; - } else { - return hasAccessToken && isTokenNotExpired; - } + const isScopeChanged = this.isScopeChanged( + scopes as AuthScopes | string | string[], + ); + return !isScopeChanged && hasAccessToken && isTokenNotExpired; } /** * Whether the access token has the given scopes. */ - public isScopeChanged(scopes: AuthScopes | string | string[]): boolean { + public isScopeChanged( + scopes: AuthScopes | string | string[] | undefined, + ): boolean { const scopesObject = scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes); + if (scopesObject.toArray().length === 0) return false; return !scopesObject.equals(this.scope); } From a86df65df9b7ea230f345d907b495aada5eb7e8f Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Tue, 16 Apr 2024 16:37:43 -0500 Subject: [PATCH 05/32] update isScopeChanged --- packages/apps/shopify-api/lib/session/session.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apps/shopify-api/lib/session/session.ts b/packages/apps/shopify-api/lib/session/session.ts index 69cc283899..9aeba2e053 100644 --- a/packages/apps/shopify-api/lib/session/session.ts +++ b/packages/apps/shopify-api/lib/session/session.ts @@ -178,9 +178,7 @@ export class Session { public isActive(scopes: AuthScopes | string | string[] | undefined): boolean { const hasAccessToken = Boolean(this.accessToken); const isTokenNotExpired = !this.isExpired(); - const isScopeChanged = this.isScopeChanged( - scopes as AuthScopes | string | string[], - ); + const isScopeChanged = this.isScopeChanged(scopes); return !isScopeChanged && hasAccessToken && isTokenNotExpired; } @@ -192,7 +190,9 @@ export class Session { ): boolean { const scopesObject = scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes); - if (scopesObject.toArray().length === 0) return false; + if (typeof scopes === undefined || scopesObject.toArray().length === 0) { + return false; + } return !scopesObject.equals(this.scope); } From 933e356364a9f053073c195ed6746d7e3c2eb67c Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Wed, 17 Apr 2024 10:26:18 -0500 Subject: [PATCH 06/32] Dont make empty authScopes object if scopes is undefined --- packages/apps/shopify-api/lib/config.ts | 14 +++++++++---- .../lib/session/__tests__/session.test.ts | 20 +++++++++++++++++-- .../apps/shopify-api/lib/session/session.ts | 6 +++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/apps/shopify-api/lib/config.ts b/packages/apps/shopify-api/lib/config.ts index 24aecd9fd1..d6dba13dec 100644 --- a/packages/apps/shopify-api/lib/config.ts +++ b/packages/apps/shopify-api/lib/config.ts @@ -66,12 +66,18 @@ export function validateConfig( ...mandatoryParams } = params; + let scopes; + if (params.scopes === undefined) { + scopes = undefined; + } else if (params.scopes instanceof AuthScopes) { + scopes = params.scopes; + } else { + scopes = new AuthScopes(params.scopes); + } + Object.assign(config, mandatoryParams, { hostName: params.hostName.replace(/\/$/, ''), - scopes: - params.scopes instanceof AuthScopes - ? params.scopes - : new AuthScopes(params.scopes), + scopes, hostScheme: hostScheme ?? config.hostScheme, isCustomStoreApp: isCustomStoreApp ?? config.isCustomStoreApp, adminApiAccessToken: adminApiAccessToken ?? config.adminApiAccessToken, diff --git a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts index b539960853..2f2401f68b 100644 --- a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts +++ b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts @@ -1,6 +1,7 @@ import {Session} from '../session'; import {testConfig} from '../../__tests__/test-config'; import {shopifyApi} from '../..'; +import {AuthScopes} from '../../auth'; describe('session', () => { it('can create a session from another session', () => { @@ -60,7 +61,7 @@ describe('isActive', () => { }); }); -it('returns true when scopes that passed in empty and scopes are not equal', () => { +it('returns true when scopes that passed in undefined and scopes are not equal', () => { const session = new Session({ id: 'active', shop: 'active-shop', @@ -71,7 +72,22 @@ it('returns true when scopes that passed in empty and scopes are not equal', () expires: new Date(Date.now() + 86400), }); - expect(session.isActive('')).toBeTruthy(); + expect(session.isActive(undefined)).toBeTruthy(); +}); + +it('returns false when scopes that passed in empty and scopes are not equal', () => { + const session = new Session({ + id: 'active', + shop: 'active-shop', + state: 'test_state', + isOnline: true, + scope: 'test_scope', + accessToken: 'indeed', + expires: new Date(Date.now() + 86400), + }); + + const scopes = new AuthScopes([]); + expect(session.isActive(scopes)).toBeFalsy(); }); it('returns false if session is not active', () => { diff --git a/packages/apps/shopify-api/lib/session/session.ts b/packages/apps/shopify-api/lib/session/session.ts index 9aeba2e053..4f0f8c3a14 100644 --- a/packages/apps/shopify-api/lib/session/session.ts +++ b/packages/apps/shopify-api/lib/session/session.ts @@ -188,11 +188,11 @@ export class Session { public isScopeChanged( scopes: AuthScopes | string | string[] | undefined, ): boolean { - const scopesObject = - scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes); - if (typeof scopes === undefined || scopesObject.toArray().length === 0) { + if (typeof scopes === 'undefined') { return false; } + const scopesObject = + scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes); return !scopesObject.equals(this.scope); } From 598729ef26cec43a0c8a3f73f9f9b6e5a51e38a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 20:48:29 +0000 Subject: [PATCH 07/32] Bump @types/node from 20.12.6 to 20.12.7 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.12.6 to 20.12.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- packages/apps/shopify-api/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/apps/shopify-api/package.json b/packages/apps/shopify-api/package.json index ca813d5cbe..0913a348e2 100644 --- a/packages/apps/shopify-api/package.json +++ b/packages/apps/shopify-api/package.json @@ -83,7 +83,7 @@ "@cloudflare/workers-types": "^4.20240320.1", "@swc/core": "^1.4.8", "@types/express": "^4.17.13", - "@types/node": "^20.11.30", + "@types/node": "^20.12.7", "@types/node-fetch": "^2.6.11", "@types/uuid": "^9.0.8", "express": "^4.19.2", diff --git a/yarn.lock b/yarn.lock index c9e59537d1..b8f20e0346 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4712,10 +4712,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^20.11.30": - version "20.12.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.6.tgz#72d068870518d7da1d97b49db401e2d6a1805294" - integrity sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ== +"@types/node@*", "@types/node@^20.12.7": + version "20.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== dependencies: undici-types "~5.26.4" From 6f1a98e967045d3e555cd197848072c211874774 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:30:32 -0400 Subject: [PATCH 08/32] Add support for mandatory ids in find() --- .changeset/short-pillows-own.md | 5 +++++ .../clients/admin/__tests__/resources/base.test.ts | 10 +++++++++- .../admin/__tests__/resources/fake-resource.ts | 1 + packages/apps/shopify-api/rest/base.ts | 12 ++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-pillows-own.md diff --git a/.changeset/short-pillows-own.md b/.changeset/short-pillows-own.md new file mode 100644 index 0000000000..99566792f5 --- /dev/null +++ b/.changeset/short-pillows-own.md @@ -0,0 +1,5 @@ +--- +"@shopify/shopify-api": patch +--- + +Fixing REST resource `find()` methods to fail when missing all ids, instead of defaulting to the same behaviour as `all()`. diff --git a/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/base.test.ts b/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/base.test.ts index d156762ca7..d2006b29c1 100644 --- a/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/base.test.ts +++ b/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/base.test.ts @@ -4,7 +4,7 @@ import { } from '../../../../__tests__/test-helper'; import {testConfig} from '../../../../__tests__/test-config'; import {Session} from '../../../../session/session'; -import {HttpResponseError} from '../../../../error'; +import {HttpResponseError, RestResourceError} from '../../../../error'; import {PageInfo} from '../../../types'; import {ApiVersion, LATEST_API_VERSION} from '../../../../types'; import {shopifyApi} from '../../../../index'; @@ -45,6 +45,14 @@ describe('Base REST resource', () => { }).toMatchMadeHttpRequest(); }); + it.each([NaN, null, undefined])('throws an error if id is %s', async (id) => { + const shopify = shopifyApi(testConfig({restResources})); + + expect(async () => + shopify.rest.FakeResource.find({id: id as number, session}), + ).rejects.toThrow(RestResourceError); + }); + it('finds resource with param', async () => { const shopify = shopifyApi(testConfig({restResources})); diff --git a/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/fake-resource.ts b/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/fake-resource.ts index 62995877ee..090bfad00f 100644 --- a/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/fake-resource.ts +++ b/packages/apps/shopify-api/lib/clients/admin/__tests__/resources/fake-resource.ts @@ -103,6 +103,7 @@ export class FakeResource extends Base { session, urlIds: {id, other_resource_id}, params: {param, ...otherArgs}, + requireIds: true, }); return result.data ? result.data[0] : null; } diff --git a/packages/apps/shopify-api/rest/base.ts b/packages/apps/shopify-api/rest/base.ts index 9e94b2d13f..7d99590a6f 100644 --- a/packages/apps/shopify-api/rest/base.ts +++ b/packages/apps/shopify-api/rest/base.ts @@ -13,6 +13,7 @@ interface BaseFindArgs { session: Session; params?: ParamSet; urlIds: IdSet; + requireIds?: boolean; } interface BaseConstructorArgs { @@ -77,7 +78,18 @@ export class Base { session, urlIds, params, + requireIds = false, }: BaseFindArgs): Promise> { + if (requireIds) { + const hasIds = Object.entries(urlIds).some(([_key, value]) => value); + + if (!hasIds) { + throw new RestResourceError( + 'No IDs given for request, cannot find path', + ); + } + } + const response = await this.request({ http_method: 'get', operation: 'get', From 9100f150f402ce4b52ac082d02920e605d65d52c Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:37:14 -0400 Subject: [PATCH 09/32] Require ids in all find() methods --- packages/apps/shopify-api/rest/admin/2022-10/android_pay_key.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/article.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/collection.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/country.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/customer_address.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/event.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2022-10/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/image.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/location.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/metafield.ts | 1 + .../rest/admin/2022-10/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/order.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/page.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/product.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/province.ts | 1 + .../rest/admin/2022-10/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/report.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/user.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2022-10/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/article.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/collection.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/country.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/customer_address.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/customer_saved_search.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2023-01/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/image.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/location.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/metafield.ts | 1 + .../rest/admin/2023-01/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/page.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/product.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/province.ts | 1 + .../rest/admin/2023-01/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/report.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/user.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2023-01/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/article.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/collection.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/country.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/customer_address.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/customer_saved_search.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2023-04/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/image.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/location.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/metafield.ts | 1 + .../rest/admin/2023-04/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/page.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/product.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/province.ts | 1 + .../rest/admin/2023-04/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/report.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/user.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2023-04/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/article.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/collection.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/country.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/customer_address.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/customer_saved_search.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2023-07/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/image.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/location.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/metafield.ts | 1 + .../rest/admin/2023-07/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/page.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/product.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/province.ts | 1 + .../rest/admin/2023-07/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/report.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/user.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2023-07/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/article.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/collection.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/country.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/customer_address.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2023-10/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/image.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/location.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/metafield.ts | 1 + .../rest/admin/2023-10/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/order.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/page.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/product.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/province.ts | 1 + .../rest/admin/2023-10/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/report.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/user.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2023-10/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/article.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/collection.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/country.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/customer_address.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/event.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2024-01/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/image.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/location.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/metafield.ts | 1 + .../rest/admin/2024-01/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/order.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/page.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/product.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/province.ts | 1 + .../rest/admin/2024-01/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/report.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/user.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2024-01/webhook.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/apple_pay_certificate.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/application_charge.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/application_credit.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/article.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/blog.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/carrier_service.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/checkout.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/collect.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/collection.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/collection_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/comment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/country.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/custom_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/customer.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/customer_address.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/discount_code.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/dispute.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/dispute_evidence.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/draft_order.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/event.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/fulfillment.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/fulfillment_event.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/fulfillment_order.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/fulfillment_service.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/gift_card.ts | 1 + .../apps/shopify-api/rest/admin/2024-04/gift_card_adjustment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/image.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/inventory_item.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/location.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/marketing_event.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/metafield.ts | 1 + .../rest/admin/2024-04/mobile_platform_application.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/order.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/order_risk.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/page.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/payment.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/payment_gateway.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/payout.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/price_rule.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/product.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/product_listing.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/province.ts | 1 + .../rest/admin/2024-04/recurring_application_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/redirect.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/refund.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/script_tag.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/smart_collection.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/theme.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/transaction.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/usage_charge.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/user.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/variant.ts | 1 + packages/apps/shopify-api/rest/admin/2024-04/webhook.ts | 1 + 381 files changed, 381 insertions(+) diff --git a/packages/apps/shopify-api/rest/admin/2022-10/android_pay_key.ts b/packages/apps/shopify-api/rest/admin/2022-10/android_pay_key.ts index 67f66afb4e..6ea752e9c8 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/android_pay_key.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/android_pay_key.ts @@ -41,6 +41,7 @@ export class AndroidPayKey extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2022-10/apple_pay_certificate.ts index 3d4e308a2b..1cefdff07c 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/application_charge.ts b/packages/apps/shopify-api/rest/admin/2022-10/application_charge.ts index 63f34bc5e2..a0bf75bd82 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/application_credit.ts b/packages/apps/shopify-api/rest/admin/2022-10/application_credit.ts index 272586b224..2761d03959 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/application_credit.ts @@ -48,6 +48,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/article.ts b/packages/apps/shopify-api/rest/admin/2022-10/article.ts index 3ee03c86e1..765e256d68 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/article.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/blog.ts b/packages/apps/shopify-api/rest/admin/2022-10/blog.ts index 814f4c33ea..1f421ec1d7 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2022-10/carrier_service.ts index bfe7b9bc4a..19b4a7ed22 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/checkout.ts b/packages/apps/shopify-api/rest/admin/2022-10/checkout.ts index 329322ebda..fb06cb6548 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/collect.ts b/packages/apps/shopify-api/rest/admin/2022-10/collect.ts index ed9a27788d..53ad9c1113 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/collection.ts b/packages/apps/shopify-api/rest/admin/2022-10/collection.ts index 6c863f0625..37503e7eec 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2022-10/collection_listing.ts index 66b9c156c0..38ffc4cd0b 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/comment.ts b/packages/apps/shopify-api/rest/admin/2022-10/comment.ts index 6bdca42021..7cd0b629cf 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/country.ts b/packages/apps/shopify-api/rest/admin/2022-10/country.ts index 724c85a24e..c802cbf468 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/country.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2022-10/custom_collection.ts index c19864ce99..850ccc2adb 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/customer.ts b/packages/apps/shopify-api/rest/admin/2022-10/customer.ts index a4690b79c4..d1c3ff89c6 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/customer_address.ts b/packages/apps/shopify-api/rest/admin/2022-10/customer_address.ts index f04dc95ba4..14af8feff4 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/discount_code.ts b/packages/apps/shopify-api/rest/admin/2022-10/discount_code.ts index 46d8cc2add..6234d2bcd7 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/dispute.ts b/packages/apps/shopify-api/rest/admin/2022-10/dispute.ts index 6591de9be1..3e34933c4a 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2022-10/dispute_evidence.ts index 36dbea5ce1..1f7da8ecfd 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/draft_order.ts b/packages/apps/shopify-api/rest/admin/2022-10/draft_order.ts index b0fe977e7e..7239629e57 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/event.ts b/packages/apps/shopify-api/rest/admin/2022-10/event.ts index b9247b0de5..bbae60d561 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/event.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment.ts index d70b7e47cb..eccac157db 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_event.ts index 6732441b22..7fc3429110 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_order.ts index 558489a1b7..5db18cdfb1 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_order.ts @@ -86,6 +86,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_service.ts index c3804ae0e8..c840023409 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/gift_card.ts b/packages/apps/shopify-api/rest/admin/2022-10/gift_card.ts index ddc26cf6ce..e7eace7524 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2022-10/gift_card_adjustment.ts index bc0d026fe8..f69e79c33e 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/image.ts b/packages/apps/shopify-api/rest/admin/2022-10/image.ts index fdb9e56e1c..535b5af779 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/image.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2022-10/inventory_item.ts index 8bc8d015f1..83720e03b5 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/location.ts b/packages/apps/shopify-api/rest/admin/2022-10/location.ts index cf4837a58f..29a3c6ffb4 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/location.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2022-10/marketing_event.ts index 9025bd03df..7df6529141 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/metafield.ts b/packages/apps/shopify-api/rest/admin/2022-10/metafield.ts index a8626ee9f2..4ba01f04a3 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2022-10/mobile_platform_application.ts index 2fef090aa7..9141d5588a 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/order.ts b/packages/apps/shopify-api/rest/admin/2022-10/order.ts index bed682d422..e0e23da2d1 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/order.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/order_risk.ts b/packages/apps/shopify-api/rest/admin/2022-10/order_risk.ts index 31112a1fb9..0c1a1d7d9f 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/page.ts b/packages/apps/shopify-api/rest/admin/2022-10/page.ts index f6c1187aae..112c6edad9 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/page.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/payment.ts b/packages/apps/shopify-api/rest/admin/2022-10/payment.ts index 830379f689..cd6f6de799 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2022-10/payment_gateway.ts index a6a6c31592..bdf6c437cf 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/payout.ts b/packages/apps/shopify-api/rest/admin/2022-10/payout.ts index c233a0d437..1cf651a3a6 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/price_rule.ts b/packages/apps/shopify-api/rest/admin/2022-10/price_rule.ts index 9e1a18b4aa..aa50ddf29f 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/product.ts b/packages/apps/shopify-api/rest/admin/2022-10/product.ts index 0c05e4ab46..d902c36926 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/product.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/product_listing.ts b/packages/apps/shopify-api/rest/admin/2022-10/product_listing.ts index 18b25eb1f0..0bd43d68e1 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/province.ts b/packages/apps/shopify-api/rest/admin/2022-10/province.ts index b4fa464c4b..58e3358f2a 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/province.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2022-10/recurring_application_charge.ts index 257b89133b..f3186dba69 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/redirect.ts b/packages/apps/shopify-api/rest/admin/2022-10/redirect.ts index 7fdda93491..9b862a2e0e 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/refund.ts b/packages/apps/shopify-api/rest/admin/2022-10/refund.ts index 9658032bce..a8690bf54d 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/report.ts b/packages/apps/shopify-api/rest/admin/2022-10/report.ts index f4ceab19d3..87eece9510 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/report.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/script_tag.ts b/packages/apps/shopify-api/rest/admin/2022-10/script_tag.ts index 2b0c8d2d83..897b8d82ba 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2022-10/smart_collection.ts index 8f0a7ae439..76f3fd4845 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/theme.ts b/packages/apps/shopify-api/rest/admin/2022-10/theme.ts index 3e873e01f0..2333225edf 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/transaction.ts b/packages/apps/shopify-api/rest/admin/2022-10/transaction.ts index 7b34228067..e66e8c6dc9 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2022-10/usage_charge.ts index bfda612afe..42dcfd9d1a 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/user.ts b/packages/apps/shopify-api/rest/admin/2022-10/user.ts index 91bc0758e7..0e56fbfdc1 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/user.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/variant.ts b/packages/apps/shopify-api/rest/admin/2022-10/variant.ts index 4494b8d3ec..e79e532459 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2022-10/webhook.ts b/packages/apps/shopify-api/rest/admin/2022-10/webhook.ts index bea51fcd9c..6ccc22426b 100644 --- a/packages/apps/shopify-api/rest/admin/2022-10/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2022-10/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2023-01/apple_pay_certificate.ts index 02c0dc520f..e26a98d2e3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-01/application_charge.ts index 1b797f5b25..3b816c1ff3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/application_credit.ts b/packages/apps/shopify-api/rest/admin/2023-01/application_credit.ts index 8dfdbbdbd2..523d902f2e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/application_credit.ts @@ -48,6 +48,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/article.ts b/packages/apps/shopify-api/rest/admin/2023-01/article.ts index 1d37f4aaf1..2ee5039fd4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/article.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/blog.ts b/packages/apps/shopify-api/rest/admin/2023-01/blog.ts index c61831e388..38943c8119 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2023-01/carrier_service.ts index 59999f6981..8bd1de9f94 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/checkout.ts b/packages/apps/shopify-api/rest/admin/2023-01/checkout.ts index d647fdc74f..d08fdefc7f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/collect.ts b/packages/apps/shopify-api/rest/admin/2023-01/collect.ts index 583560b964..e1f91e253b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/collection.ts b/packages/apps/shopify-api/rest/admin/2023-01/collection.ts index 5a12cd43fd..4037dfbaea 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2023-01/collection_listing.ts index 7321aa1ef7..723ced9df9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/comment.ts b/packages/apps/shopify-api/rest/admin/2023-01/comment.ts index 85edf3aed6..72a55aab68 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/country.ts b/packages/apps/shopify-api/rest/admin/2023-01/country.ts index a720a3e166..7d7d2ac1ba 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/country.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2023-01/custom_collection.ts index cb131858d3..08173ed754 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/customer.ts b/packages/apps/shopify-api/rest/admin/2023-01/customer.ts index a6297025f8..03a5bbec2c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/customer_address.ts b/packages/apps/shopify-api/rest/admin/2023-01/customer_address.ts index ed5aadfaa8..9b9356d810 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/customer_saved_search.ts b/packages/apps/shopify-api/rest/admin/2023-01/customer_saved_search.ts index b95ecee4de..a234271fc3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/customer_saved_search.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/customer_saved_search.ts @@ -67,6 +67,7 @@ export class CustomerSavedSearch extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/discount_code.ts b/packages/apps/shopify-api/rest/admin/2023-01/discount_code.ts index 64d432e626..d51fde5634 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/dispute.ts b/packages/apps/shopify-api/rest/admin/2023-01/dispute.ts index fb8517fd80..fb62a0e56b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2023-01/dispute_evidence.ts index 33a436ecba..03b5cb8275 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/draft_order.ts b/packages/apps/shopify-api/rest/admin/2023-01/draft_order.ts index d6561d5118..63e31b87a7 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/event.ts b/packages/apps/shopify-api/rest/admin/2023-01/event.ts index 37eeac7a64..a16d94ea56 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment.ts index ccd278ca79..6962431ca0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_event.ts index 106fe41b75..51aed53c4a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_order.ts index 465a419f71..0a6008f594 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_order.ts @@ -87,6 +87,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_service.ts index 2337a88b2c..72cdb40d13 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/gift_card.ts b/packages/apps/shopify-api/rest/admin/2023-01/gift_card.ts index c4b73c68bd..2b63177986 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2023-01/gift_card_adjustment.ts index 6b64b100de..fbf8c32bc8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/image.ts b/packages/apps/shopify-api/rest/admin/2023-01/image.ts index 43a34d4ccd..9999d254ca 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/image.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2023-01/inventory_item.ts index 9b6c1d1f77..ff7122fcfb 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/location.ts b/packages/apps/shopify-api/rest/admin/2023-01/location.ts index 1163750107..b93016373e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/location.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2023-01/marketing_event.ts index 9a3fcb3401..000483df75 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/metafield.ts b/packages/apps/shopify-api/rest/admin/2023-01/metafield.ts index cd9e809c34..71dd406a0f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2023-01/mobile_platform_application.ts index 8281a72503..545c2d0da1 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/order.ts b/packages/apps/shopify-api/rest/admin/2023-01/order.ts index 979d3f69af..ee1edf631b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/order_risk.ts b/packages/apps/shopify-api/rest/admin/2023-01/order_risk.ts index d1326c65fb..8e99395e76 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/page.ts b/packages/apps/shopify-api/rest/admin/2023-01/page.ts index a171be5d2d..21a701384b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/page.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/payment.ts b/packages/apps/shopify-api/rest/admin/2023-01/payment.ts index 8ea2607084..fb21f45014 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2023-01/payment_gateway.ts index c6a530d44d..03d958fee2 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/payout.ts b/packages/apps/shopify-api/rest/admin/2023-01/payout.ts index fbbc2309c5..f8da465f6c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/price_rule.ts b/packages/apps/shopify-api/rest/admin/2023-01/price_rule.ts index 6ee94d7b91..5dd5eb033d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/product.ts b/packages/apps/shopify-api/rest/admin/2023-01/product.ts index 021b6fe758..b97f713eba 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/product.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/product_listing.ts b/packages/apps/shopify-api/rest/admin/2023-01/product_listing.ts index b248520b46..c2912043b5 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/province.ts b/packages/apps/shopify-api/rest/admin/2023-01/province.ts index 03df088d80..720162b7dc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/province.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-01/recurring_application_charge.ts index 75e66a7aca..03dac7940a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/redirect.ts b/packages/apps/shopify-api/rest/admin/2023-01/redirect.ts index 4c937284a4..02a23167d8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/refund.ts b/packages/apps/shopify-api/rest/admin/2023-01/refund.ts index f1ddc63ca8..aad4c11302 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/report.ts b/packages/apps/shopify-api/rest/admin/2023-01/report.ts index bf9efe3952..ba63e1cfcc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/report.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/script_tag.ts b/packages/apps/shopify-api/rest/admin/2023-01/script_tag.ts index 2f52c0862a..eb68078588 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2023-01/smart_collection.ts index 13b7310428..907b1ceb43 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/theme.ts b/packages/apps/shopify-api/rest/admin/2023-01/theme.ts index 179295b581..74771b06bc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/transaction.ts b/packages/apps/shopify-api/rest/admin/2023-01/transaction.ts index 3388d49ecc..c73366172c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2023-01/usage_charge.ts index 200add8977..c80d040bd4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/user.ts b/packages/apps/shopify-api/rest/admin/2023-01/user.ts index d8f82ba4c5..843db957e6 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/user.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/variant.ts b/packages/apps/shopify-api/rest/admin/2023-01/variant.ts index 2d77fbc3a5..7974863b7f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-01/webhook.ts b/packages/apps/shopify-api/rest/admin/2023-01/webhook.ts index 487ec63e7e..ee9abdfa7d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-01/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2023-01/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2023-04/apple_pay_certificate.ts index e9ad6f0cd4..a781cbfda9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-04/application_charge.ts index 81a32b820c..7fc3aca760 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/application_credit.ts b/packages/apps/shopify-api/rest/admin/2023-04/application_credit.ts index 59f1d056ba..778ee7ca10 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/application_credit.ts @@ -48,6 +48,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/article.ts b/packages/apps/shopify-api/rest/admin/2023-04/article.ts index 0a359d93bd..a86fd38f2a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/article.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/blog.ts b/packages/apps/shopify-api/rest/admin/2023-04/blog.ts index 674401e1e9..a8a1b471b6 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2023-04/carrier_service.ts index 666dd54718..9f8cde9daf 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/checkout.ts b/packages/apps/shopify-api/rest/admin/2023-04/checkout.ts index f139d59f98..b025d418b4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/collect.ts b/packages/apps/shopify-api/rest/admin/2023-04/collect.ts index 050b67e2b5..be4002f3a1 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/collection.ts b/packages/apps/shopify-api/rest/admin/2023-04/collection.ts index 0cf4528cdb..e2c9a2737d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2023-04/collection_listing.ts index 1e6c6c068d..bf488c7d8d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/comment.ts b/packages/apps/shopify-api/rest/admin/2023-04/comment.ts index b8dcd7ae47..1f6ca4add5 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/country.ts b/packages/apps/shopify-api/rest/admin/2023-04/country.ts index 2b0efb54ec..b271ecfb4d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/country.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2023-04/custom_collection.ts index fe66e88d72..b9463f4b3c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/customer.ts b/packages/apps/shopify-api/rest/admin/2023-04/customer.ts index f121621126..926f867821 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/customer_address.ts b/packages/apps/shopify-api/rest/admin/2023-04/customer_address.ts index 511fb7009e..74c4fcb650 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/customer_saved_search.ts b/packages/apps/shopify-api/rest/admin/2023-04/customer_saved_search.ts index 4f6d7698d6..44628d96ba 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/customer_saved_search.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/customer_saved_search.ts @@ -67,6 +67,7 @@ export class CustomerSavedSearch extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/discount_code.ts b/packages/apps/shopify-api/rest/admin/2023-04/discount_code.ts index eca66286d7..ec58d51ce5 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/dispute.ts b/packages/apps/shopify-api/rest/admin/2023-04/dispute.ts index 84a87b4cea..45c789db91 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2023-04/dispute_evidence.ts index d9a0306f5a..e67adc9a5b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/draft_order.ts b/packages/apps/shopify-api/rest/admin/2023-04/draft_order.ts index 30fb5ff35a..b3b5b61e33 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/event.ts b/packages/apps/shopify-api/rest/admin/2023-04/event.ts index 53ef430b7c..31a6999cf9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment.ts index fad4e3abf6..e507571055 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_event.ts index a9764c4eb4..9efa585485 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_order.ts index 03647f1871..c16d2106bb 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_order.ts @@ -87,6 +87,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_service.ts index 6bc87f8157..fcf6be3bfe 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/gift_card.ts b/packages/apps/shopify-api/rest/admin/2023-04/gift_card.ts index 556edaf60f..7183d2efe2 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2023-04/gift_card_adjustment.ts index 101b2e596e..9acdf641b0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/image.ts b/packages/apps/shopify-api/rest/admin/2023-04/image.ts index ac8c7f5e98..8933743868 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/image.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2023-04/inventory_item.ts index 047f3c1c0b..e1a9e67b33 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/location.ts b/packages/apps/shopify-api/rest/admin/2023-04/location.ts index fb58970ad1..84d3adfbdb 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/location.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2023-04/marketing_event.ts index 68f39fca75..4fe8ba3efc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/metafield.ts b/packages/apps/shopify-api/rest/admin/2023-04/metafield.ts index efcf795b0b..d2446163ae 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2023-04/mobile_platform_application.ts index 06e488b734..91b061497f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/order.ts b/packages/apps/shopify-api/rest/admin/2023-04/order.ts index f744f4be86..6c879e573f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/order_risk.ts b/packages/apps/shopify-api/rest/admin/2023-04/order_risk.ts index 2e05163aa6..148f0ee284 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/page.ts b/packages/apps/shopify-api/rest/admin/2023-04/page.ts index b0ba7b883e..c1e7530785 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/page.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/payment.ts b/packages/apps/shopify-api/rest/admin/2023-04/payment.ts index fb7720f85c..06dc86ba73 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2023-04/payment_gateway.ts index 2c3166acb9..f519ca5f19 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/payout.ts b/packages/apps/shopify-api/rest/admin/2023-04/payout.ts index 81aae46324..a9edb7ddf6 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/price_rule.ts b/packages/apps/shopify-api/rest/admin/2023-04/price_rule.ts index f2e6b73dee..8fe37f1fb0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/product.ts b/packages/apps/shopify-api/rest/admin/2023-04/product.ts index c41d5315f4..f90363175e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/product.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/product_listing.ts b/packages/apps/shopify-api/rest/admin/2023-04/product_listing.ts index e02e7bb7e0..f3d733eaa5 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/province.ts b/packages/apps/shopify-api/rest/admin/2023-04/province.ts index 835cf6e600..d06a930186 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/province.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-04/recurring_application_charge.ts index 1eadaa1059..3a877500d4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/redirect.ts b/packages/apps/shopify-api/rest/admin/2023-04/redirect.ts index cdc9ba67b8..00c97aee33 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/refund.ts b/packages/apps/shopify-api/rest/admin/2023-04/refund.ts index 5ae4060324..61b27acd43 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/report.ts b/packages/apps/shopify-api/rest/admin/2023-04/report.ts index e4808807e1..4d6408a12c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/report.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/script_tag.ts b/packages/apps/shopify-api/rest/admin/2023-04/script_tag.ts index 9b04cb43dd..a29e34a518 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2023-04/smart_collection.ts index b807bd7794..d7079492c8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/theme.ts b/packages/apps/shopify-api/rest/admin/2023-04/theme.ts index dbee15fe4b..877feebc1b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/transaction.ts b/packages/apps/shopify-api/rest/admin/2023-04/transaction.ts index 4402992cfe..775d0d64a8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2023-04/usage_charge.ts index 9d1fbb5e0d..b7e8805993 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/user.ts b/packages/apps/shopify-api/rest/admin/2023-04/user.ts index 3378630fab..3dab9f4b0d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/user.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/variant.ts b/packages/apps/shopify-api/rest/admin/2023-04/variant.ts index dfb91f579e..ded1cc5cdf 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-04/webhook.ts b/packages/apps/shopify-api/rest/admin/2023-04/webhook.ts index a69b1721c9..ae61562f40 100644 --- a/packages/apps/shopify-api/rest/admin/2023-04/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2023-04/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2023-07/apple_pay_certificate.ts index 03a35d99f4..52343e6f8e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-07/application_charge.ts index dd9608170f..17c8e932fc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/application_credit.ts b/packages/apps/shopify-api/rest/admin/2023-07/application_credit.ts index f4034ea14a..f1d16edbb9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/application_credit.ts @@ -47,6 +47,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/article.ts b/packages/apps/shopify-api/rest/admin/2023-07/article.ts index cf085f1c8d..d570ba337b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/article.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/blog.ts b/packages/apps/shopify-api/rest/admin/2023-07/blog.ts index 41aa651633..52d5dfbadd 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2023-07/carrier_service.ts index fc143555a9..50ab9428f3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/checkout.ts b/packages/apps/shopify-api/rest/admin/2023-07/checkout.ts index a3ce1d0d75..9f0d40377d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/collect.ts b/packages/apps/shopify-api/rest/admin/2023-07/collect.ts index 63719946ef..9a9319811f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/collection.ts b/packages/apps/shopify-api/rest/admin/2023-07/collection.ts index b243f2b076..b6e598a62d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2023-07/collection_listing.ts index 05212d7a84..b41234e717 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/comment.ts b/packages/apps/shopify-api/rest/admin/2023-07/comment.ts index 1a00a8dc1a..1915bedaef 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/country.ts b/packages/apps/shopify-api/rest/admin/2023-07/country.ts index 1a873761b8..815f28916e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/country.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2023-07/custom_collection.ts index a262179736..5f6ef27a40 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/customer.ts b/packages/apps/shopify-api/rest/admin/2023-07/customer.ts index 374215f5d1..0d3996bfa7 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/customer_address.ts b/packages/apps/shopify-api/rest/admin/2023-07/customer_address.ts index 8575a3fcae..839e09f009 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/customer_saved_search.ts b/packages/apps/shopify-api/rest/admin/2023-07/customer_saved_search.ts index 7a7f0a34ba..9c4fe0033f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/customer_saved_search.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/customer_saved_search.ts @@ -67,6 +67,7 @@ export class CustomerSavedSearch extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/discount_code.ts b/packages/apps/shopify-api/rest/admin/2023-07/discount_code.ts index 5421a78732..6e1eb617ac 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/dispute.ts b/packages/apps/shopify-api/rest/admin/2023-07/dispute.ts index 51322df737..8da3392bab 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2023-07/dispute_evidence.ts index fbf4d29d24..59f87b0b1e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/draft_order.ts b/packages/apps/shopify-api/rest/admin/2023-07/draft_order.ts index 894323a359..51df7b73ec 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/event.ts b/packages/apps/shopify-api/rest/admin/2023-07/event.ts index b2bb9b84d9..10c3a3ab84 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment.ts index d48500528f..6ef95dc911 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_event.ts index b42bee7c73..ceb634e3f1 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_order.ts index 439be7b66e..b0eb721c89 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_order.ts @@ -87,6 +87,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_service.ts index afcd091b0d..afbdbdd955 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/gift_card.ts b/packages/apps/shopify-api/rest/admin/2023-07/gift_card.ts index 0fe9998d77..f6be6f0437 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2023-07/gift_card_adjustment.ts index fdbd84ce8b..2d15732211 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/image.ts b/packages/apps/shopify-api/rest/admin/2023-07/image.ts index 31e9660849..7dc9b3fad2 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/image.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2023-07/inventory_item.ts index 88e8fed8d3..954ca132c4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/location.ts b/packages/apps/shopify-api/rest/admin/2023-07/location.ts index cfcab51d58..e616157455 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/location.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2023-07/marketing_event.ts index 6222106e92..163de249cc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/metafield.ts b/packages/apps/shopify-api/rest/admin/2023-07/metafield.ts index f25404e26c..423e303832 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2023-07/mobile_platform_application.ts index befb988fbd..ff6d046970 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/order.ts b/packages/apps/shopify-api/rest/admin/2023-07/order.ts index 0ef5c9a570..7b9e1ab547 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/order_risk.ts b/packages/apps/shopify-api/rest/admin/2023-07/order_risk.ts index 81c78fdfb1..af2e337dc9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/page.ts b/packages/apps/shopify-api/rest/admin/2023-07/page.ts index f4cf61af4b..e365092861 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/page.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/payment.ts b/packages/apps/shopify-api/rest/admin/2023-07/payment.ts index 90c4d569ec..bc8d24d230 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2023-07/payment_gateway.ts index 5b828ac980..3de35c8b38 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/payout.ts b/packages/apps/shopify-api/rest/admin/2023-07/payout.ts index bf9ab7426a..4667dc67d3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/price_rule.ts b/packages/apps/shopify-api/rest/admin/2023-07/price_rule.ts index ce6fc786aa..34ce6596ad 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/product.ts b/packages/apps/shopify-api/rest/admin/2023-07/product.ts index 4ca60cab55..91e1e756be 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/product.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/product_listing.ts b/packages/apps/shopify-api/rest/admin/2023-07/product_listing.ts index d22aac98ae..9aa0bf4db6 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/province.ts b/packages/apps/shopify-api/rest/admin/2023-07/province.ts index 227833e732..6b9880c4dd 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/province.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-07/recurring_application_charge.ts index 4acac3056c..7a8f8d1b40 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/redirect.ts b/packages/apps/shopify-api/rest/admin/2023-07/redirect.ts index 1e74af0d6b..6c2c0597d0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/refund.ts b/packages/apps/shopify-api/rest/admin/2023-07/refund.ts index 85c2de76f9..47f9f84163 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/report.ts b/packages/apps/shopify-api/rest/admin/2023-07/report.ts index 9d062db375..1644596137 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/report.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/script_tag.ts b/packages/apps/shopify-api/rest/admin/2023-07/script_tag.ts index 502f289f6d..d495c7d01a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2023-07/smart_collection.ts index 3851223ad9..6c5ada41c0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/theme.ts b/packages/apps/shopify-api/rest/admin/2023-07/theme.ts index c40a107be0..0dc8243376 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/transaction.ts b/packages/apps/shopify-api/rest/admin/2023-07/transaction.ts index 66a89c8cda..fc5cd5e00a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2023-07/usage_charge.ts index b993cdda49..015ceed91d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/user.ts b/packages/apps/shopify-api/rest/admin/2023-07/user.ts index b9ff92a5cf..18494391f9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/user.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/variant.ts b/packages/apps/shopify-api/rest/admin/2023-07/variant.ts index d2346da687..103005742f 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-07/webhook.ts b/packages/apps/shopify-api/rest/admin/2023-07/webhook.ts index 7a01f98a9c..fbe893a025 100644 --- a/packages/apps/shopify-api/rest/admin/2023-07/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2023-07/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2023-10/apple_pay_certificate.ts index dc40c196f2..f822c3b6ea 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-10/application_charge.ts index 763e1b37d5..9bcd8f7eb9 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/application_credit.ts b/packages/apps/shopify-api/rest/admin/2023-10/application_credit.ts index 65adf66dfe..ddf20ca293 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/application_credit.ts @@ -47,6 +47,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/article.ts b/packages/apps/shopify-api/rest/admin/2023-10/article.ts index 9962fb0ef6..01aea506ae 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/article.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/blog.ts b/packages/apps/shopify-api/rest/admin/2023-10/blog.ts index 6d2a5f09a4..730a81ff3b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2023-10/carrier_service.ts index 0a8072c886..ec484249ed 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/checkout.ts b/packages/apps/shopify-api/rest/admin/2023-10/checkout.ts index 4d79294dee..7bf12f4535 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/collect.ts b/packages/apps/shopify-api/rest/admin/2023-10/collect.ts index e07268d565..4c3bc83bcd 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/collection.ts b/packages/apps/shopify-api/rest/admin/2023-10/collection.ts index b8f47c51a3..409f1e59e7 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2023-10/collection_listing.ts index 66149e29d9..ac2f10df2c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/comment.ts b/packages/apps/shopify-api/rest/admin/2023-10/comment.ts index 08fd088f09..bb690bea05 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/country.ts b/packages/apps/shopify-api/rest/admin/2023-10/country.ts index 56a97435ed..831962ded0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/country.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2023-10/custom_collection.ts index 31d77d215d..145ad0f8dc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/customer.ts b/packages/apps/shopify-api/rest/admin/2023-10/customer.ts index da4bba9f05..9179b8310a 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/customer_address.ts b/packages/apps/shopify-api/rest/admin/2023-10/customer_address.ts index e5f77b5906..a882e795ac 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/discount_code.ts b/packages/apps/shopify-api/rest/admin/2023-10/discount_code.ts index 4ed3c8d61e..c855b6cf79 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/dispute.ts b/packages/apps/shopify-api/rest/admin/2023-10/dispute.ts index b560009866..0f3becdcf0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2023-10/dispute_evidence.ts index 3730e065f7..37c7ddcb6e 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/draft_order.ts b/packages/apps/shopify-api/rest/admin/2023-10/draft_order.ts index 881c6ee5a0..f3ef7f7f7c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/event.ts b/packages/apps/shopify-api/rest/admin/2023-10/event.ts index e1942df542..13b4d9b97b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment.ts index 985e63d748..72da8330ac 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_event.ts index b06812e654..e0c0d75df7 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_order.ts index cca11a0666..3db9d182df 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_order.ts @@ -87,6 +87,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_service.ts index 0ed6edd978..e39416f249 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/gift_card.ts b/packages/apps/shopify-api/rest/admin/2023-10/gift_card.ts index cbe8ff3efa..1116138316 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2023-10/gift_card_adjustment.ts index 2c533fe291..eb6eef0472 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/image.ts b/packages/apps/shopify-api/rest/admin/2023-10/image.ts index 733f1e6a82..1b3cac85a0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/image.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2023-10/inventory_item.ts index 70e0bf2280..21a3faf93d 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/location.ts b/packages/apps/shopify-api/rest/admin/2023-10/location.ts index e02d24a07a..7c8d78a4af 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/location.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2023-10/marketing_event.ts index 46ec11a3ef..f40b166f61 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/metafield.ts b/packages/apps/shopify-api/rest/admin/2023-10/metafield.ts index a7850a1f80..0091015b55 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2023-10/mobile_platform_application.ts index eff6b37b1b..745d627cb7 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/order.ts b/packages/apps/shopify-api/rest/admin/2023-10/order.ts index 4e8036974b..ab50359216 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/order.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/order_risk.ts b/packages/apps/shopify-api/rest/admin/2023-10/order_risk.ts index e7113a8feb..e5c25d263b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/page.ts b/packages/apps/shopify-api/rest/admin/2023-10/page.ts index 4a485a0fc1..5fb360b399 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/page.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/payment.ts b/packages/apps/shopify-api/rest/admin/2023-10/payment.ts index a327472cae..79347ded50 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2023-10/payment_gateway.ts index 046ee62950..b214daa7d8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/payout.ts b/packages/apps/shopify-api/rest/admin/2023-10/payout.ts index 2492f5c803..30c20868d3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/price_rule.ts b/packages/apps/shopify-api/rest/admin/2023-10/price_rule.ts index 1b76ce8734..bb00c3af2c 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/product.ts b/packages/apps/shopify-api/rest/admin/2023-10/product.ts index a95e4af404..a1d09728c3 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/product.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/product_listing.ts b/packages/apps/shopify-api/rest/admin/2023-10/product_listing.ts index 4bceb35859..fd5917cbbc 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/province.ts b/packages/apps/shopify-api/rest/admin/2023-10/province.ts index e60a8c6654..c9035e32c4 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/province.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2023-10/recurring_application_charge.ts index efc893aa29..9445973ac0 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/redirect.ts b/packages/apps/shopify-api/rest/admin/2023-10/redirect.ts index 05dacb46b7..702b8b85be 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/refund.ts b/packages/apps/shopify-api/rest/admin/2023-10/refund.ts index 41c3600d82..c500bfb573 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/report.ts b/packages/apps/shopify-api/rest/admin/2023-10/report.ts index 4048bde780..e5d5c934be 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/report.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/script_tag.ts b/packages/apps/shopify-api/rest/admin/2023-10/script_tag.ts index 2980b0f37f..eb1ddb3c0b 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2023-10/smart_collection.ts index c2b53db482..9f832f5f72 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/theme.ts b/packages/apps/shopify-api/rest/admin/2023-10/theme.ts index 6c814de5ce..dc95ea5dcf 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/transaction.ts b/packages/apps/shopify-api/rest/admin/2023-10/transaction.ts index fe42c3a71e..41ff9d90d8 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2023-10/usage_charge.ts index 07c30b3637..89ba29b100 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/user.ts b/packages/apps/shopify-api/rest/admin/2023-10/user.ts index a8d0e76558..cc2215c361 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/user.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/variant.ts b/packages/apps/shopify-api/rest/admin/2023-10/variant.ts index 8e1f43148e..0c4cfc7716 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2023-10/webhook.ts b/packages/apps/shopify-api/rest/admin/2023-10/webhook.ts index 2ea6591692..d7e92d1e85 100644 --- a/packages/apps/shopify-api/rest/admin/2023-10/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2023-10/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2024-01/apple_pay_certificate.ts index 57c89fa650..f2ebbeb03e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/application_charge.ts b/packages/apps/shopify-api/rest/admin/2024-01/application_charge.ts index b5a5e920e2..f3088363ac 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/application_credit.ts b/packages/apps/shopify-api/rest/admin/2024-01/application_credit.ts index b48c79a762..316093cc7d 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/application_credit.ts @@ -47,6 +47,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/article.ts b/packages/apps/shopify-api/rest/admin/2024-01/article.ts index 7797545af2..abb87187a7 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/article.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/blog.ts b/packages/apps/shopify-api/rest/admin/2024-01/blog.ts index 715e4375fb..f0176f42cf 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2024-01/carrier_service.ts index d9ecbbaf7c..f76fee1862 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/checkout.ts b/packages/apps/shopify-api/rest/admin/2024-01/checkout.ts index 96b796f279..830ffd7e8b 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/collect.ts b/packages/apps/shopify-api/rest/admin/2024-01/collect.ts index 6827967693..46a6706947 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/collection.ts b/packages/apps/shopify-api/rest/admin/2024-01/collection.ts index 9b348efe25..ac5e48a588 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2024-01/collection_listing.ts index 590a11d60d..5f279f7d4e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/comment.ts b/packages/apps/shopify-api/rest/admin/2024-01/comment.ts index d297151c7a..5ca7052058 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/country.ts b/packages/apps/shopify-api/rest/admin/2024-01/country.ts index 3fa1a1ca83..808a65746e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/country.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2024-01/custom_collection.ts index 1e0a0f399e..f0036a1ecc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/customer.ts b/packages/apps/shopify-api/rest/admin/2024-01/customer.ts index 7dc9d36dcb..d5a3cff37e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/customer_address.ts b/packages/apps/shopify-api/rest/admin/2024-01/customer_address.ts index 9c2705a45b..21a49efe5c 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/discount_code.ts b/packages/apps/shopify-api/rest/admin/2024-01/discount_code.ts index f0ff236af2..fdf241a327 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/dispute.ts b/packages/apps/shopify-api/rest/admin/2024-01/dispute.ts index 42d390bef1..a07feb45b5 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2024-01/dispute_evidence.ts index ef2144f22f..8af969a220 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/draft_order.ts b/packages/apps/shopify-api/rest/admin/2024-01/draft_order.ts index b73d9e6666..feb9bf6764 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/event.ts b/packages/apps/shopify-api/rest/admin/2024-01/event.ts index 7f90dae102..c88d161a6b 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment.ts index b2b277d4cd..1a9376f413 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_event.ts index c43f6fafab..bbfec2f2d5 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_order.ts index 8ccc083655..fa1566ae31 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_order.ts @@ -93,6 +93,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"include_financial_summaries": include_financial_summaries, "include_order_reference_fields": include_order_reference_fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_service.ts index 52ac30391f..14ea1c4e17 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/gift_card.ts b/packages/apps/shopify-api/rest/admin/2024-01/gift_card.ts index ea9e871152..fbead8ebb2 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2024-01/gift_card_adjustment.ts index ed1e574246..803aa5405e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/image.ts b/packages/apps/shopify-api/rest/admin/2024-01/image.ts index 0c8e39debd..713d55b91b 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/image.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2024-01/inventory_item.ts index 2bd0ab312c..9607bc85ee 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/location.ts b/packages/apps/shopify-api/rest/admin/2024-01/location.ts index e26f6a48be..e80e4fda75 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/location.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2024-01/marketing_event.ts index ed7ce8d638..ece8fcc03a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/metafield.ts b/packages/apps/shopify-api/rest/admin/2024-01/metafield.ts index cfc896506b..6ff77ab0f7 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2024-01/mobile_platform_application.ts index 0795dbf3c0..d64c66c8c9 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/order.ts b/packages/apps/shopify-api/rest/admin/2024-01/order.ts index 7481ae738d..c26db30c1a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/order_risk.ts b/packages/apps/shopify-api/rest/admin/2024-01/order_risk.ts index 7cc6c17f8e..57d29496e3 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/page.ts b/packages/apps/shopify-api/rest/admin/2024-01/page.ts index 217ae250c1..bf4d29dbad 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/page.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/payment.ts b/packages/apps/shopify-api/rest/admin/2024-01/payment.ts index 27d881e93b..fb8d6bdcb2 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2024-01/payment_gateway.ts index c4b4adf52a..21c768cd55 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/payout.ts b/packages/apps/shopify-api/rest/admin/2024-01/payout.ts index b024fd9da4..6a512f4ea6 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/price_rule.ts b/packages/apps/shopify-api/rest/admin/2024-01/price_rule.ts index 81c8eca2e8..15375e6703 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/product.ts b/packages/apps/shopify-api/rest/admin/2024-01/product.ts index d5a0e7f1f4..fed32592bc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/product.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/product_listing.ts b/packages/apps/shopify-api/rest/admin/2024-01/product_listing.ts index 61a6743fe3..1d777d037f 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/province.ts b/packages/apps/shopify-api/rest/admin/2024-01/province.ts index ea1b6bdeeb..f6a672c0a3 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/province.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2024-01/recurring_application_charge.ts index cb4e75fa2e..c87d94d2a8 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/redirect.ts b/packages/apps/shopify-api/rest/admin/2024-01/redirect.ts index 578af7cfe5..ef4a704898 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/refund.ts b/packages/apps/shopify-api/rest/admin/2024-01/refund.ts index 24163e5586..6fcf4ffec0 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/report.ts b/packages/apps/shopify-api/rest/admin/2024-01/report.ts index 74a77142ba..f92c0a00f5 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/report.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/report.ts @@ -55,6 +55,7 @@ export class Report extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/script_tag.ts b/packages/apps/shopify-api/rest/admin/2024-01/script_tag.ts index 538a41b549..809ebe59e2 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2024-01/smart_collection.ts index a13e3267ef..e6a1ec58e0 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/theme.ts b/packages/apps/shopify-api/rest/admin/2024-01/theme.ts index beb4031984..6a9fb13c70 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/transaction.ts b/packages/apps/shopify-api/rest/admin/2024-01/transaction.ts index 3ffc274a62..ffccbe07d4 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2024-01/usage_charge.ts index 460697c7e1..87c2984cf1 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/user.ts b/packages/apps/shopify-api/rest/admin/2024-01/user.ts index 05b77d9108..a6292a5d5e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/user.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/variant.ts b/packages/apps/shopify-api/rest/admin/2024-01/variant.ts index 1c2bb1b6e0..4112369a6d 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-01/webhook.ts b/packages/apps/shopify-api/rest/admin/2024-01/webhook.ts index 3ee245ffab..196ea791bb 100644 --- a/packages/apps/shopify-api/rest/admin/2024-01/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2024-01/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/apple_pay_certificate.ts b/packages/apps/shopify-api/rest/admin/2024-04/apple_pay_certificate.ts index 4b304db1a7..afd9b5dfee 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/apple_pay_certificate.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/apple_pay_certificate.ts @@ -48,6 +48,7 @@ export class ApplePayCertificate extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/application_charge.ts b/packages/apps/shopify-api/rest/admin/2024-04/application_charge.ts index a3c66d8916..9813125fe9 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/application_charge.ts @@ -49,6 +49,7 @@ export class ApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/application_credit.ts b/packages/apps/shopify-api/rest/admin/2024-04/application_credit.ts index 766438ae12..a0e252d8c5 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/application_credit.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/application_credit.ts @@ -47,6 +47,7 @@ export class ApplicationCredit extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/article.ts b/packages/apps/shopify-api/rest/admin/2024-04/article.ts index 653bd5146c..69c07fbedc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/article.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/article.ts @@ -97,6 +97,7 @@ export class Article extends Base { ): Promise
{ const result = await this.baseFind
({ session: session, + requireIds: true, urlIds: {"id": id, "blog_id": blog_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/blog.ts b/packages/apps/shopify-api/rest/admin/2024-04/blog.ts index 6cf61f23b1..bc24c15988 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/blog.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/blog.ts @@ -62,6 +62,7 @@ export class Blog extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/carrier_service.ts b/packages/apps/shopify-api/rest/admin/2024-04/carrier_service.ts index f56e81770e..b8d4a0d05a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/carrier_service.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/carrier_service.ts @@ -47,6 +47,7 @@ export class CarrierService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/checkout.ts b/packages/apps/shopify-api/rest/admin/2024-04/checkout.ts index 2b2727a645..6931bc07ef 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/checkout.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/checkout.ts @@ -58,6 +58,7 @@ export class Checkout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"token": token}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/collect.ts b/packages/apps/shopify-api/rest/admin/2024-04/collect.ts index 002026e70c..64b3ea6621 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/collect.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/collect.ts @@ -56,6 +56,7 @@ export class Collect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/collection.ts b/packages/apps/shopify-api/rest/admin/2024-04/collection.ts index b155f762bc..a352957edc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/collection.ts @@ -48,6 +48,7 @@ export class Collection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/collection_listing.ts b/packages/apps/shopify-api/rest/admin/2024-04/collection_listing.ts index 1165e4977b..72daef91dc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/collection_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/collection_listing.ts @@ -59,6 +59,7 @@ export class CollectionListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"collection_id": collection_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/comment.ts b/packages/apps/shopify-api/rest/admin/2024-04/comment.ts index c4cca9ae4d..d7919aaf1b 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/comment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/comment.ts @@ -93,6 +93,7 @@ export class Comment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/country.ts b/packages/apps/shopify-api/rest/admin/2024-04/country.ts index d39a78e436..5c46336e91 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/country.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/country.ts @@ -60,6 +60,7 @@ export class Country extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/custom_collection.ts b/packages/apps/shopify-api/rest/admin/2024-04/custom_collection.ts index 537aff6070..35a15eb4f7 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/custom_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/custom_collection.ts @@ -73,6 +73,7 @@ export class CustomCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/customer.ts b/packages/apps/shopify-api/rest/admin/2024-04/customer.ts index 773a9a507a..bdbe6977dd 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/customer.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/customer.ts @@ -97,6 +97,7 @@ export class Customer extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/customer_address.ts b/packages/apps/shopify-api/rest/admin/2024-04/customer_address.ts index 22dc5b7228..dd77bf36dd 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/customer_address.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/customer_address.ts @@ -72,6 +72,7 @@ export class CustomerAddress extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "customer_id": customer_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/discount_code.ts b/packages/apps/shopify-api/rest/admin/2024-04/discount_code.ts index 54a3816f0b..dafa0ea259 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/discount_code.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/discount_code.ts @@ -79,6 +79,7 @@ export class DiscountCode extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "price_rule_id": price_rule_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/dispute.ts b/packages/apps/shopify-api/rest/admin/2024-04/dispute.ts index ab903a6394..543a30d09f 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/dispute.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/dispute.ts @@ -44,6 +44,7 @@ export class Dispute extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/dispute_evidence.ts b/packages/apps/shopify-api/rest/admin/2024-04/dispute_evidence.ts index 096827dd3b..a04b1409e8 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/dispute_evidence.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/dispute_evidence.ts @@ -41,6 +41,7 @@ export class DisputeEvidence extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"dispute_id": dispute_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/draft_order.ts b/packages/apps/shopify-api/rest/admin/2024-04/draft_order.ts index 0cf6f7f4f5..a257aecddc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/draft_order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/draft_order.ts @@ -81,6 +81,7 @@ export class DraftOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/event.ts b/packages/apps/shopify-api/rest/admin/2024-04/event.ts index 886bb0c5ea..84cf90fc1d 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/event.ts @@ -60,6 +60,7 @@ export class Event extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment.ts b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment.ts index 0bf3591736..c2ea75df85 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment.ts @@ -77,6 +77,7 @@ export class Fulfillment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_event.ts b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_event.ts index c04d8ec816..23b20d357e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_event.ts @@ -67,6 +67,7 @@ export class FulfillmentEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id, "fulfillment_id": fulfillment_id}, params: {"event_id": event_id}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_order.ts b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_order.ts index 4274fec62d..9423ca4735 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_order.ts @@ -93,6 +93,7 @@ export class FulfillmentOrder extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"include_financial_summaries": include_financial_summaries, "include_order_reference_fields": include_order_reference_fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_service.ts b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_service.ts index d8ded5679d..e4861b764a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_service.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/fulfillment_service.ts @@ -48,6 +48,7 @@ export class FulfillmentService extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/gift_card.ts b/packages/apps/shopify-api/rest/admin/2024-04/gift_card.ts index 1828ac23a1..c6feb124fc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/gift_card.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/gift_card.ts @@ -71,6 +71,7 @@ export class GiftCard extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/gift_card_adjustment.ts b/packages/apps/shopify-api/rest/admin/2024-04/gift_card_adjustment.ts index af79130a6c..a75fe4249c 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/gift_card_adjustment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/gift_card_adjustment.ts @@ -49,6 +49,7 @@ export class GiftCardAdjustment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "gift_card_id": gift_card_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/image.ts b/packages/apps/shopify-api/rest/admin/2024-04/image.ts index 980d96c4d6..fa8b9014bb 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/image.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/image.ts @@ -62,6 +62,7 @@ export class Image extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "product_id": product_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/inventory_item.ts b/packages/apps/shopify-api/rest/admin/2024-04/inventory_item.ts index 47519bb6d3..881e2cca49 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/inventory_item.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/inventory_item.ts @@ -43,6 +43,7 @@ export class InventoryItem extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/location.ts b/packages/apps/shopify-api/rest/admin/2024-04/location.ts index 38ca3076ae..8a8ed6fd78 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/location.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/location.ts @@ -51,6 +51,7 @@ export class Location extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/marketing_event.ts b/packages/apps/shopify-api/rest/admin/2024-04/marketing_event.ts index 162757b588..52a2797a85 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/marketing_event.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/marketing_event.ts @@ -68,6 +68,7 @@ export class MarketingEvent extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/metafield.ts b/packages/apps/shopify-api/rest/admin/2024-04/metafield.ts index 430d6f9d0b..bdb841e1e9 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/metafield.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/metafield.ts @@ -181,6 +181,7 @@ export class Metafield extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "article_id": article_id, "blog_id": blog_id, "collection_id": collection_id, "customer_id": customer_id, "draft_order_id": draft_order_id, "order_id": order_id, "page_id": page_id, "product_image_id": product_image_id, "product_id": product_id, "variant_id": variant_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/mobile_platform_application.ts b/packages/apps/shopify-api/rest/admin/2024-04/mobile_platform_application.ts index 3ba88379b7..59e7386dbc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/mobile_platform_application.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/mobile_platform_application.ts @@ -47,6 +47,7 @@ export class MobilePlatformApplication extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/order.ts b/packages/apps/shopify-api/rest/admin/2024-04/order.ts index 2fc0dc6273..58c2b2e389 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/order.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/order.ts @@ -107,6 +107,7 @@ export class Order extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/order_risk.ts b/packages/apps/shopify-api/rest/admin/2024-04/order_risk.ts index 0b1169083d..718da0d804 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/order_risk.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/order_risk.ts @@ -56,6 +56,7 @@ export class OrderRisk extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/page.ts b/packages/apps/shopify-api/rest/admin/2024-04/page.ts index 456a2b782c..63ba11a60a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/page.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/page.ts @@ -78,6 +78,7 @@ export class Page extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/payment.ts b/packages/apps/shopify-api/rest/admin/2024-04/payment.ts index 5328f662f5..275ebb53b4 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/payment.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/payment.ts @@ -56,6 +56,7 @@ export class Payment extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "checkout_id": checkout_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/payment_gateway.ts b/packages/apps/shopify-api/rest/admin/2024-04/payment_gateway.ts index 8f0fa89f41..fb7d6ccf79 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/payment_gateway.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/payment_gateway.ts @@ -47,6 +47,7 @@ export class PaymentGateway extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/payout.ts b/packages/apps/shopify-api/rest/admin/2024-04/payout.ts index 7b51a22c62..1e5d8e678a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/payout.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/payout.ts @@ -46,6 +46,7 @@ export class Payout extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/price_rule.ts b/packages/apps/shopify-api/rest/admin/2024-04/price_rule.ts index f99605db78..9aa9669e17 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/price_rule.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/price_rule.ts @@ -63,6 +63,7 @@ export class PriceRule extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/product.ts b/packages/apps/shopify-api/rest/admin/2024-04/product.ts index 8195955f48..ab896444d3 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/product.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/product.ts @@ -88,6 +88,7 @@ export class Product extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/product_listing.ts b/packages/apps/shopify-api/rest/admin/2024-04/product_listing.ts index 0aa2ac5dbe..4bbb96a9d1 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/product_listing.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/product_listing.ts @@ -69,6 +69,7 @@ export class ProductListing extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"product_id": product_id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/province.ts b/packages/apps/shopify-api/rest/admin/2024-04/province.ts index 4ed48f8d39..d0fb6c8d2a 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/province.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/province.ts @@ -54,6 +54,7 @@ export class Province extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "country_id": country_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/recurring_application_charge.ts b/packages/apps/shopify-api/rest/admin/2024-04/recurring_application_charge.ts index 4feef25360..8a53170b1e 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/recurring_application_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/recurring_application_charge.ts @@ -55,6 +55,7 @@ export class RecurringApplicationCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/redirect.ts b/packages/apps/shopify-api/rest/admin/2024-04/redirect.ts index 33d34b1840..0429407201 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/redirect.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/redirect.ts @@ -61,6 +61,7 @@ export class Redirect extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/refund.ts b/packages/apps/shopify-api/rest/admin/2024-04/refund.ts index a58b6b173a..e8b83afaf4 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/refund.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/refund.ts @@ -63,6 +63,7 @@ export class Refund extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/script_tag.ts b/packages/apps/shopify-api/rest/admin/2024-04/script_tag.ts index 11357f4f74..be307609d0 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/script_tag.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/script_tag.ts @@ -63,6 +63,7 @@ export class ScriptTag extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/smart_collection.ts b/packages/apps/shopify-api/rest/admin/2024-04/smart_collection.ts index 933a71d475..5f7b23e7fc 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/smart_collection.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/smart_collection.ts @@ -80,6 +80,7 @@ export class SmartCollection extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/theme.ts b/packages/apps/shopify-api/rest/admin/2024-04/theme.ts index e820c07c1d..faf6dce115 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/theme.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/theme.ts @@ -50,6 +50,7 @@ export class Theme extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/transaction.ts b/packages/apps/shopify-api/rest/admin/2024-04/transaction.ts index 57d6f539b7..b53e5bb259 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/transaction.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/transaction.ts @@ -57,6 +57,7 @@ export class Transaction extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "order_id": order_id}, params: {"fields": fields, "in_shop_currency": in_shop_currency}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/usage_charge.ts b/packages/apps/shopify-api/rest/admin/2024-04/usage_charge.ts index 4aea27b758..74f70f2a56 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/usage_charge.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/usage_charge.ts @@ -51,6 +51,7 @@ export class UsageCharge extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id, "recurring_application_charge_id": recurring_application_charge_id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/user.ts b/packages/apps/shopify-api/rest/admin/2024-04/user.ts index 09bab899cb..37db901377 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/user.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/user.ts @@ -47,6 +47,7 @@ export class User extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/variant.ts b/packages/apps/shopify-api/rest/admin/2024-04/variant.ts index 2ce14e99eb..92fb88b16f 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/variant.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/variant.ts @@ -64,6 +64,7 @@ export class Variant extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); diff --git a/packages/apps/shopify-api/rest/admin/2024-04/webhook.ts b/packages/apps/shopify-api/rest/admin/2024-04/webhook.ts index 459c11cf38..7466699623 100644 --- a/packages/apps/shopify-api/rest/admin/2024-04/webhook.ts +++ b/packages/apps/shopify-api/rest/admin/2024-04/webhook.ts @@ -65,6 +65,7 @@ export class Webhook extends Base { ): Promise { const result = await this.baseFind({ session: session, + requireIds: true, urlIds: {"id": id}, params: {"fields": fields}, }); From 5305e55bbbc14288b7c5d3f3ee85113fa7211a22 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:54:43 -0400 Subject: [PATCH 10/32] Build admin-api-client with centralized file --- config/rollup/rollup-utils.js | 36 +++++++--- .../api-clients/admin-api-client/package.json | 2 +- .../admin-api-client/rollup.config.cjs | 68 ++++--------------- .../admin-api-client/tsconfig.build.json | 7 ++ .../admin-api-client/tsconfig.json | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../rollup.config.js | 2 +- .../apps/shopify-app-express/rollup.config.js | 2 +- .../apps/shopify-app-remix/rollup.config.js | 9 ++- 19 files changed, 65 insertions(+), 85 deletions(-) diff --git a/config/rollup/rollup-utils.js b/config/rollup/rollup-utils.js index 70e46ade72..ca0366faf8 100644 --- a/config/rollup/rollup-utils.js +++ b/config/rollup/rollup-utils.js @@ -1,12 +1,11 @@ -import path from 'path'; - import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import replace from '@rollup/plugin-replace'; +import terser from '@rollup/plugin-terser'; import excludeDependenciesFromBundle from 'rollup-plugin-exclude-dependencies-from-bundle'; -export function getPlugins(outDir) { +export function getPlugins({outDir, tsconfig, minify, replacements}) { return [ excludeDependenciesFromBundle({dependencies: true, peerDependencies: true}), resolve({ @@ -14,16 +13,18 @@ export function getPlugins(outDir) { }), replace({ preventAssignment: true, + ...(replacements ?? {}), }), commonjs(), typescript({ - tsconfig: path.resolve('./tsconfig.build.json'), + tsconfig: tsconfig ? tsconfig : './tsconfig.build.json', outDir, outputToFilesystem: false, noEmit: true, declaration: false, moduleResolution: 'Bundler', }), + ...(minify === true ? [terser({keep_fnames: new RegExp('fetch')})] : []), ]; } @@ -45,19 +46,36 @@ export const cjsConfigs = { exports: 'named', }; -export function getConfig(pkg, input = 'src/index.ts') { +export function getConfig({ + pkg, + minify, + tsconfig, + replacements, + input = 'src/index.ts', + flatOutput = false, +}) { return [ { input, - plugins: getPlugins('./dist/esm'), + plugins: getPlugins({ + outDir: flatOutput ? './dist' : './dist/esm', + minify, + tsconfig, + replacements, + }), external: Object.keys(pkg.dependencies), - output: [esmConfigs], + output: [{...esmConfigs, dir: flatOutput ? './dist' : './dist/esm'}], }, { input, - plugins: getPlugins('./dist/cjs'), + plugins: getPlugins({ + outDir: flatOutput ? './dist' : './dist/cjs', + minify, + tsconfig, + replacements, + }), external: Object.keys(pkg.dependencies), - output: [cjsConfigs], + output: [{...cjsConfigs, dir: flatOutput ? './dist' : './dist/cjs'}], }, ]; } diff --git a/packages/api-clients/admin-api-client/package.json b/packages/api-clients/admin-api-client/package.json index c0501f5803..1474f255c9 100644 --- a/packages/api-clients/admin-api-client/package.json +++ b/packages/api-clients/admin-api-client/package.json @@ -30,7 +30,7 @@ }, "scripts": { "lint": "eslint . --ext .js,.ts", - "build": "yarn run rollup", + "build": "yarn tsc -p ./tsconfig.build.json && yarn run rollup", "test": "jest", "test:ci": "yarn test", "rollup": "rollup -c --bundleConfigAsCjs", diff --git a/packages/api-clients/admin-api-client/rollup.config.cjs b/packages/api-clients/admin-api-client/rollup.config.cjs index e9cf0405f3..a79a079c32 100644 --- a/packages/api-clients/admin-api-client/rollup.config.cjs +++ b/packages/api-clients/admin-api-client/rollup.config.cjs @@ -1,64 +1,20 @@ -import dts from "rollup-plugin-dts"; -import typescript from "@rollup/plugin-typescript"; -import resolve from "@rollup/plugin-node-resolve"; -import commonjs from "@rollup/plugin-commonjs"; -import terser from "@rollup/plugin-terser"; -import replace from "@rollup/plugin-replace"; +import dts from 'rollup-plugin-dts'; -import * as pkg from "./package.json"; +import * as pkg from './package.json'; +import {getConfig} from '../../../config/rollup/rollup-utils'; -export const mainSrcInput = "src/index.ts"; +export const mainSrcInput = 'src/index.ts'; -export function getPlugins({ tsconfig, minify } = {}) { - return [ - replace({ - preventAssignment: true, - ROLLUP_REPLACE_CLIENT_VERSION: pkg.version, - }), - resolve(), - commonjs(), - typescript({ - tsconfig: tsconfig ? tsconfig : "./tsconfig.build.json", - outDir: "./dist/ts", - }), - ...(minify === true ? [terser({ keep_fnames: new RegExp("fetch") })] : []), - ]; -} - -const config = [ - { +export default [ + ...getConfig({ + pkg, input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: "./dist", - format: "es", - sourcemap: true, - preserveModules: true, - preserveModulesRoot: "src", - entryFileNames: "[name].mjs", - }, - ], - }, + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + flatOutput: true, + }), { - input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: "./dist", - format: "cjs", - sourcemap: true, - exports: "named", - preserveModules: true, - preserveModulesRoot: "src", - }, - ], - }, - { - input: "./dist/ts/index.d.ts", - output: [{ file: "dist/admin-api-client.d.ts", format: "es" }], + input: './dist/ts/index.d.ts', + output: [{file: 'dist/admin-api-client.d.ts', format: 'es'}], plugins: [dts.default()], }, ]; - -export default config; diff --git a/packages/api-clients/admin-api-client/tsconfig.build.json b/packages/api-clients/admin-api-client/tsconfig.build.json index 763a468288..766eebed58 100644 --- a/packages/api-clients/admin-api-client/tsconfig.build.json +++ b/packages/api-clients/admin-api-client/tsconfig.build.json @@ -1,4 +1,11 @@ { + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "rootDir": "src", + "baseUrl": "src", + "outDir": "dist/ts", + }, "extends": "./tsconfig.json", "exclude": ["./node_modules", "./src/**/tests/*.ts"] } diff --git a/packages/api-clients/admin-api-client/tsconfig.json b/packages/api-clients/admin-api-client/tsconfig.json index 3390793465..8e95047dc6 100644 --- a/packages/api-clients/admin-api-client/tsconfig.json +++ b/packages/api-clients/admin-api-client/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "declarationMap": true, "target": "ES2022", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "rootDir": "src", "baseUrl": "src", "strict": true, diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-drizzle/rollup.config.js index ff6de0654a..d5373536bd 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/drizzle.ts'); +const config = getConfig({pkg, input: 'src/drizzle.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/rollup.config.js index d1e82ec6ca..3d09e9bebd 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/dynamodb.ts'); +const config = getConfig({pkg, input: 'src/dynamodb.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-kv/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-kv/rollup.config.js index e352da6717..55179e61e3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-kv/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-kv/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/kv.ts'); +const config = getConfig({pkg, input: 'src/kv.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-memory/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-memory/rollup.config.js index c59d0dcb5f..8a1c0b01c3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-memory/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-memory/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/memory.ts'); +const config = getConfig({pkg, input: 'src/memory.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-mongodb/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-mongodb/rollup.config.js index 5cb96f8cda..64d9e820d9 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mongodb/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-mongodb/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/mongodb.ts'); +const config = getConfig({pkg, input: 'src/mongodb.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-mysql/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-mysql/rollup.config.js index e7c7cce20c..5921299400 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mysql/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-mysql/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/mysql.ts'); +const config = getConfig({pkg, input: 'src/mysql.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-postgresql/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-postgresql/rollup.config.js index 20173f39bc..4f1b22d8f3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-postgresql/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-postgresql/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/postgresql.ts'); +const config = getConfig({pkg, input: 'src/postgresql.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-prisma/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-prisma/rollup.config.js index 513eb6b469..da81b04f5b 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-prisma/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-prisma/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/prisma.ts'); +const config = getConfig({pkg, input: 'src/prisma.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-redis/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-redis/rollup.config.js index 659674d73e..0b03cef08c 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-redis/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-redis/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/redis.ts'); +const config = getConfig({pkg, input: 'src/redis.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-sqlite/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-sqlite/rollup.config.js index 66e833fbb9..22063396ab 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-sqlite/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-sqlite/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg, 'src/sqlite.ts'); +const config = getConfig({pkg, input: 'src/sqlite.ts'}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage-test-utils/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage-test-utils/rollup.config.js index ac55229481..40cfcb00a3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-test-utils/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage-test-utils/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg); +const config = getConfig({pkg}); export default config; diff --git a/packages/apps/session-storage/shopify-app-session-storage/rollup.config.js b/packages/apps/session-storage/shopify-app-session-storage/rollup.config.js index ac55229481..40cfcb00a3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage/rollup.config.js +++ b/packages/apps/session-storage/shopify-app-session-storage/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg); +const config = getConfig({pkg}); export default config; diff --git a/packages/apps/shopify-app-express/rollup.config.js b/packages/apps/shopify-app-express/rollup.config.js index e5ac4e176c..24dca1a2b2 100644 --- a/packages/apps/shopify-app-express/rollup.config.js +++ b/packages/apps/shopify-app-express/rollup.config.js @@ -2,6 +2,6 @@ import {getConfig} from '../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; -const config = getConfig(pkg); +const config = getConfig({pkg}); export default config; diff --git a/packages/apps/shopify-app-remix/rollup.config.js b/packages/apps/shopify-app-remix/rollup.config.js index 9048a940c0..4b88ed6b47 100644 --- a/packages/apps/shopify-app-remix/rollup.config.js +++ b/packages/apps/shopify-app-remix/rollup.config.js @@ -11,8 +11,7 @@ const adapterInputs = fs .filter((dirent) => dirent.isDirectory() && dirent.name !== '__tests__') .map(({name}) => `src/server/adapters/${name}/index.ts`); -export default getConfig(pkg, [ - 'src/server/index.ts', - 'src/react/index.ts', - ...adapterInputs, -]); +export default getConfig({ + pkg, + input: ['src/server/index.ts', 'src/react/index.ts', ...adapterInputs], +}); From 163f571cf5120aa2dddaaacce0a201bdaf03e04b Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:16:18 -0400 Subject: [PATCH 11/32] Build all packages with centralized configs --- .changeset/fuzzy-months-hug.md | 2 + config/rollup/rollup-utils.js | 17 ++++- .../admin-api-client/rollup.config.cjs | 4 +- .../admin-api-client/tsconfig.build.json | 2 - .../api-codegen-preset/package.json | 2 +- .../api-codegen-preset/rollup.config.js | 62 +++---------------- .../api-codegen-preset/tsconfig.build.json | 7 +++ .../api-clients/graphql-client/package.json | 2 +- .../graphql-client/rollup.config.cjs | 61 ++++-------------- .../graphql-client/tsconfig.build.json | 9 +++ .../api-clients/graphql-client/tsconfig.json | 2 +- .../storefront-api-client/package.json | 2 +- .../storefront-api-client/rollup.config.cjs | 61 ++++-------------- .../storefront-api-client/tsconfig.build.json | 10 +++ .../storefront-api-client/tsconfig.json | 2 +- 15 files changed, 82 insertions(+), 163 deletions(-) create mode 100644 .changeset/fuzzy-months-hug.md create mode 100644 packages/api-clients/graphql-client/tsconfig.build.json create mode 100644 packages/api-clients/storefront-api-client/tsconfig.build.json diff --git a/.changeset/fuzzy-months-hug.md b/.changeset/fuzzy-months-hug.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/fuzzy-months-hug.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/config/rollup/rollup-utils.js b/config/rollup/rollup-utils.js index ca0366faf8..59e76422ac 100644 --- a/config/rollup/rollup-utils.js +++ b/config/rollup/rollup-utils.js @@ -5,9 +5,22 @@ import replace from '@rollup/plugin-replace'; import terser from '@rollup/plugin-terser'; import excludeDependenciesFromBundle from 'rollup-plugin-exclude-dependencies-from-bundle'; -export function getPlugins({outDir, tsconfig, minify, replacements}) { +export function getPlugins({ + outDir, + tsconfig, + minify, + replacements, + bundleDependencies = false, +}) { return [ - excludeDependenciesFromBundle({dependencies: true, peerDependencies: true}), + ...(bundleDependencies + ? [] + : [ + excludeDependenciesFromBundle({ + dependencies: true, + peerDependencies: true, + }), + ]), resolve({ extensions: ['.js', '.jsx', '.ts', '.tsx'], }), diff --git a/packages/api-clients/admin-api-client/rollup.config.cjs b/packages/api-clients/admin-api-client/rollup.config.cjs index a79a079c32..0d2b65d2b1 100644 --- a/packages/api-clients/admin-api-client/rollup.config.cjs +++ b/packages/api-clients/admin-api-client/rollup.config.cjs @@ -3,12 +3,10 @@ import dts from 'rollup-plugin-dts'; import * as pkg from './package.json'; import {getConfig} from '../../../config/rollup/rollup-utils'; -export const mainSrcInput = 'src/index.ts'; - export default [ ...getConfig({ pkg, - input: mainSrcInput, + input: 'src/index.ts', replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, flatOutput: true, }), diff --git a/packages/api-clients/admin-api-client/tsconfig.build.json b/packages/api-clients/admin-api-client/tsconfig.build.json index 766eebed58..3c450aebff 100644 --- a/packages/api-clients/admin-api-client/tsconfig.build.json +++ b/packages/api-clients/admin-api-client/tsconfig.build.json @@ -2,8 +2,6 @@ "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, - "rootDir": "src", - "baseUrl": "src", "outDir": "dist/ts", }, "extends": "./tsconfig.json", diff --git a/packages/api-clients/api-codegen-preset/package.json b/packages/api-clients/api-codegen-preset/package.json index 45cbb03763..ff82cd31ed 100644 --- a/packages/api-clients/api-codegen-preset/package.json +++ b/packages/api-clients/api-codegen-preset/package.json @@ -34,7 +34,7 @@ }, "scripts": { "lint": "eslint . --ext .js,.ts", - "build": "yarn run rollup", + "build": "yarn tsc -p ./tsconfig.build.json && yarn run rollup", "test": "jest", "test:ci": "yarn test", "rollup": "rollup -c --bundleConfigAsCjs", diff --git a/packages/api-clients/api-codegen-preset/rollup.config.js b/packages/api-clients/api-codegen-preset/rollup.config.js index 0622567e4d..d301e718aa 100644 --- a/packages/api-clients/api-codegen-preset/rollup.config.js +++ b/packages/api-clients/api-codegen-preset/rollup.config.js @@ -1,65 +1,17 @@ import dts from 'rollup-plugin-dts'; -import typescript from '@rollup/plugin-typescript'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import terser from '@rollup/plugin-terser'; -import replace from '@rollup/plugin-replace'; import * as pkg from './package.json'; +import {getConfig} from '../../../config/rollup/rollup-utils'; -export const mainSrcInput = 'src/index.ts'; - -export function getPlugins({tsconfig, minify} = {}) { - return [ - replace({ - preventAssignment: true, - }), - resolve(), - commonjs(), - typescript({ - tsconfig: tsconfig ? tsconfig : './tsconfig.build.json', - outDir: './dist/ts', - }), - ...(minify === true ? [terser({keep_fnames: new RegExp('fetch')})] : []), - ]; -} - -const config = [ - { - input: mainSrcInput, - plugins: getPlugins(), - external: Object.keys(pkg.dependencies), - output: [ - { - dir: './dist', - format: 'es', - sourcemap: true, - preserveModules: true, - preserveModulesRoot: 'src', - entryFileNames: '[name].mjs', - }, - ], - }, - { - input: mainSrcInput, - plugins: getPlugins(), - external: Object.keys(pkg.dependencies), - output: [ - { - dir: './dist', - format: 'cjs', - sourcemap: true, - exports: 'named', - preserveModules: true, - preserveModulesRoot: 'src', - }, - ], - }, +export default [ + ...getConfig({ + pkg, + input: 'src/index.ts', + flatOutput: true, + }), { input: './dist/ts/index.d.ts', output: [{file: 'dist/index.d.ts', format: 'es'}], plugins: [dts.default()], }, ]; - -export default config; diff --git a/packages/api-clients/api-codegen-preset/tsconfig.build.json b/packages/api-clients/api-codegen-preset/tsconfig.build.json index 763a468288..90de35f322 100644 --- a/packages/api-clients/api-codegen-preset/tsconfig.build.json +++ b/packages/api-clients/api-codegen-preset/tsconfig.build.json @@ -1,4 +1,11 @@ { + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "moduleResolution": "Bundler", + "module": "Preserve", + "outDir": "dist/ts", + }, "extends": "./tsconfig.json", "exclude": ["./node_modules", "./src/**/tests/*.ts"] } diff --git a/packages/api-clients/graphql-client/package.json b/packages/api-clients/graphql-client/package.json index 1eb8dc9d4e..3d20a6f0e4 100644 --- a/packages/api-clients/graphql-client/package.json +++ b/packages/api-clients/graphql-client/package.json @@ -31,7 +31,7 @@ }, "scripts": { "lint": "eslint . --ext .js,.ts", - "build": "yarn run rollup", + "build": "yarn tsc -p ./tsconfig.build.json && yarn run rollup", "test": "jest", "test:ci": "yarn test", "rollup": "rollup -c --bundleConfigAsCjs", diff --git a/packages/api-clients/graphql-client/rollup.config.cjs b/packages/api-clients/graphql-client/rollup.config.cjs index 1016d45e8e..7926910aa1 100644 --- a/packages/api-clients/graphql-client/rollup.config.cjs +++ b/packages/api-clients/graphql-client/rollup.config.cjs @@ -1,31 +1,12 @@ import dts from 'rollup-plugin-dts'; -import typescript from '@rollup/plugin-typescript'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import terser from '@rollup/plugin-terser'; -import replace from '@rollup/plugin-replace'; + +import {getConfig, getPlugins} from '../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; const mainSrcInput = 'src/index.ts'; const clientSrcInput = 'src/graphql-client/index.ts'; -export function getPlugins({tsconfig, minify} = {}) { - return [ - replace({ - preventAssignment: true, - ROLLUP_REPLACE_CLIENT_VERSION: pkg.version, - }), - resolve(), - commonjs(), - typescript({ - tsconfig: tsconfig ? tsconfig : './tsconfig.json', - outDir: './dist/ts', - }), - ...(minify === true ? [terser({keep_fnames: new RegExp('fetch')})] : []), - ]; -} - const packageName = pkg.name.substring(1); export const bannerConfig = { banner: `/*! ${packageName}@${pkg.version} -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-app-js/blob/main/LICENSE.md */`, @@ -35,8 +16,11 @@ const config = [ { input: clientSrcInput, plugins: getPlugins({ + outDir: './dist/ts', minify: true, tsconfig: './tsconfig.umd.json', + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + bundleDependencies: true, }), output: [ { @@ -51,7 +35,10 @@ const config = [ { input: clientSrcInput, plugins: getPlugins({ + outDir: './dist/ts', tsconfig: './tsconfig.umd.json', + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + bundleDependencies: true, }), output: [ { @@ -63,34 +50,12 @@ const config = [ }, ], }, - { - input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: './dist', - format: 'es', - sourcemap: true, - preserveModules: true, - preserveModulesRoot: 'src', - entryFileNames: '[name].mjs', - }, - ], - }, - { + ...getConfig({ + pkg, input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: './dist', - format: 'cjs', - sourcemap: true, - exports: 'named', - preserveModules: true, - preserveModulesRoot: 'src', - }, - ], - }, + flatOutput: true, + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + }), { input: './dist/ts/index.d.ts', output: [{file: 'dist/graphql-client.d.ts', format: 'es'}], diff --git a/packages/api-clients/graphql-client/tsconfig.build.json b/packages/api-clients/graphql-client/tsconfig.build.json new file mode 100644 index 0000000000..3c450aebff --- /dev/null +++ b/packages/api-clients/graphql-client/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist/ts", + }, + "extends": "./tsconfig.json", + "exclude": ["./node_modules", "./src/**/tests/*.ts"] +} diff --git a/packages/api-clients/graphql-client/tsconfig.json b/packages/api-clients/graphql-client/tsconfig.json index c66104deb6..f310f989f2 100644 --- a/packages/api-clients/graphql-client/tsconfig.json +++ b/packages/api-clients/graphql-client/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "declarationMap": true, "target": "ES2022", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "rootDir": "src", "baseUrl": "src", "strict": true, diff --git a/packages/api-clients/storefront-api-client/package.json b/packages/api-clients/storefront-api-client/package.json index e47e002434..049392b757 100644 --- a/packages/api-clients/storefront-api-client/package.json +++ b/packages/api-clients/storefront-api-client/package.json @@ -31,7 +31,7 @@ }, "scripts": { "lint": "eslint . --ext .js,.ts", - "build": "yarn run rollup", + "build": "yarn tsc -p ./tsconfig.build.json && yarn run rollup", "test": "jest", "test:ci": "yarn test", "rollup": "rollup -c --bundleConfigAsCjs", diff --git a/packages/api-clients/storefront-api-client/rollup.config.cjs b/packages/api-clients/storefront-api-client/rollup.config.cjs index 0752f2e982..54ac7c4186 100644 --- a/packages/api-clients/storefront-api-client/rollup.config.cjs +++ b/packages/api-clients/storefront-api-client/rollup.config.cjs @@ -1,30 +1,11 @@ import dts from 'rollup-plugin-dts'; -import typescript from '@rollup/plugin-typescript'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import terser from '@rollup/plugin-terser'; -import replace from '@rollup/plugin-replace'; + +import {getConfig, getPlugins} from '../../../config/rollup/rollup-utils'; import * as pkg from './package.json'; export const mainSrcInput = 'src/index.ts'; -export function getPlugins({tsconfig, minify} = {}) { - return [ - replace({ - preventAssignment: true, - ROLLUP_REPLACE_CLIENT_VERSION: pkg.version, - }), - resolve(), - commonjs(), - typescript({ - tsconfig: tsconfig ? tsconfig : './tsconfig.json', - outDir: './dist/ts', - }), - ...(minify === true ? [terser({keep_fnames: new RegExp('fetch')})] : []), - ]; -} - const packageName = pkg.name.substring(1); export const bannerConfig = { banner: `/*! ${packageName}@${pkg.version} -- Copyright (c) 2023-present, Shopify Inc. -- license (MIT): https://github.com/Shopify/shopify-app-js/blob/main/LICENSE.md */`, @@ -34,8 +15,11 @@ const config = [ { input: mainSrcInput, plugins: getPlugins({ + outDir: './dist/ts', minify: true, tsconfig: './tsconfig.umd.json', + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + bundleDependencies: true, }), output: [ { @@ -50,7 +34,10 @@ const config = [ { input: mainSrcInput, plugins: getPlugins({ + outDir: './dist/ts', tsconfig: './tsconfig.umd.json', + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + bundleDependencies: true, }), output: [ { @@ -62,34 +49,12 @@ const config = [ }, ], }, - { - input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: './dist', - format: 'es', - sourcemap: true, - preserveModules: true, - preserveModulesRoot: 'src', - entryFileNames: '[name].mjs', - }, - ], - }, - { + ...getConfig({ + pkg, input: mainSrcInput, - plugins: getPlugins(), - output: [ - { - dir: './dist', - format: 'cjs', - sourcemap: true, - exports: 'named', - preserveModules: true, - preserveModulesRoot: 'src', - }, - ], - }, + flatOutput: true, + replacements: {ROLLUP_REPLACE_CLIENT_VERSION: pkg.version}, + }), { input: './dist/ts/index.d.ts', output: [{file: 'dist/storefront-api-client.d.ts', format: 'es'}], diff --git a/packages/api-clients/storefront-api-client/tsconfig.build.json b/packages/api-clients/storefront-api-client/tsconfig.build.json new file mode 100644 index 0000000000..ec75ef93ff --- /dev/null +++ b/packages/api-clients/storefront-api-client/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist/ts", + "noImplicitAny": false + }, + "extends": "./tsconfig.json", + "exclude": ["./node_modules", "./src/**/tests/*.ts"] +} diff --git a/packages/api-clients/storefront-api-client/tsconfig.json b/packages/api-clients/storefront-api-client/tsconfig.json index c66104deb6..f310f989f2 100644 --- a/packages/api-clients/storefront-api-client/tsconfig.json +++ b/packages/api-clients/storefront-api-client/tsconfig.json @@ -5,7 +5,7 @@ "declaration": true, "declarationMap": true, "target": "ES2022", - "lib": ["ESNext"], + "lib": ["ESNext", "DOM"], "rootDir": "src", "baseUrl": "src", "strict": true, From 1b5d80ed55a0e02d3ef7582bc53930492ee8d3c4 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:59:16 -0400 Subject: [PATCH 12/32] Removed direct dependency on remix/node --- .changeset/tall-brooms-reflect.md | 5 +++++ packages/apps/shopify-app-remix/package.json | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .changeset/tall-brooms-reflect.md diff --git a/.changeset/tall-brooms-reflect.md b/.changeset/tall-brooms-reflect.md new file mode 100644 index 0000000000..70748e8472 --- /dev/null +++ b/.changeset/tall-brooms-reflect.md @@ -0,0 +1,5 @@ +--- +"@shopify/shopify-app-remix": patch +--- + +Removed `@remix-run/node` as a direct dependency. Any app using the Vercel adapter already needs `@remix-run/node`, so this shouldn't affect any apps. diff --git a/packages/apps/shopify-app-remix/package.json b/packages/apps/shopify-app-remix/package.json index fefb364509..368f39b964 100644 --- a/packages/apps/shopify-app-remix/package.json +++ b/packages/apps/shopify-app-remix/package.json @@ -76,6 +76,7 @@ "Storefront API" ], "devDependencies": { + "@remix-run/node": "^2.6.0", "@remix-run/react": "^2.8.1", "@shopify/generate-docs": "^0.15.2", "@shopify/polaris": "^12.18.0", @@ -91,7 +92,6 @@ "react-router-dom": "^6.22.3" }, "dependencies": { - "@remix-run/node": "^2.6.0", "@remix-run/server-runtime": "^2.5.1", "@shopify/admin-api-client": "^0.2.8", "@shopify/shopify-api": "^9.7.2", @@ -102,12 +102,16 @@ }, "peerDependencies": { "@remix-run/react": "*", + "@remix-run/node": "*", "@shopify/polaris": "*", "react": "*" }, "peerDependenciesMeta": { "@shopify/polaris": { "optional": true + }, + "@remix-run/node": { + "optional": true } }, "files": [ From 61576be9745b2893f67275d6e3f31da3f5617207 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:33:29 -0400 Subject: [PATCH 13/32] Build shopify-api package with rollup --- .changeset/sweet-drinks-film.md | 24 ++++++++++ .eslintignore | 1 + packages/apps/shopify-api/.gitignore | 4 -- .../adapters/__e2etests__/e2e-runner.test.ts | 19 ++++---- .../__e2etests__/rollup.test-apps.config.js | 41 +++++++++++++++++ .../rollup.test-web-api-app.config.ts | 27 ----------- .../cf-worker/__tests__/cf-worker.test.ts | 2 +- .../adapters/node/__tests__/node.test.ts | 20 ++++----- .../web-api/__tests__/web-api.test.ts | 2 +- packages/apps/shopify-api/package.json | 45 ++++++++++--------- packages/apps/shopify-api/rollup.config.js | 31 +++++++++++++ packages/apps/shopify-api/tsconfig.build.json | 22 +++++++++ 12 files changed, 160 insertions(+), 78 deletions(-) create mode 100644 .changeset/sweet-drinks-film.md create mode 100644 packages/apps/shopify-api/adapters/__e2etests__/rollup.test-apps.config.js delete mode 100644 packages/apps/shopify-api/adapters/__e2etests__/rollup.test-web-api-app.config.ts create mode 100644 packages/apps/shopify-api/rollup.config.js create mode 100644 packages/apps/shopify-api/tsconfig.build.json diff --git a/.changeset/sweet-drinks-film.md b/.changeset/sweet-drinks-film.md new file mode 100644 index 0000000000..518182f76e --- /dev/null +++ b/.changeset/sweet-drinks-film.md @@ -0,0 +1,24 @@ +--- +"@shopify/shopify-api": major +--- + +Changed the package's build process to produce both ESM and CJS outputs. + +While this should have no effect on most apps, if you're directly importing a file from the package, its path will have changed. +Regular imports for package files remain unchanged. + +Before: + +```ts +import 'node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client' +import '@shopify/shopify-api/adapters/node' +``` + +After: + +```ts +// Add `dist/esm|cjs/` before the file +import 'node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client' +// Unchanged +import '@shopify/shopify-api/adapters/node' +``` diff --git a/.eslintignore b/.eslintignore index 609a4f942e..84dc146aa3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ rollup.config.js +rollup.config.*.js rollup.config.cjs .eslintrc.cjs node_modules/ diff --git a/packages/apps/shopify-api/.gitignore b/packages/apps/shopify-api/.gitignore index d8da94e235..e71378008b 100644 --- a/packages/apps/shopify-api/.gitignore +++ b/packages/apps/shopify-api/.gitignore @@ -1,5 +1 @@ .wrangler -*.js -*.js.map -*.d.ts -*.d.ts.map diff --git a/packages/apps/shopify-api/adapters/__e2etests__/e2e-runner.test.ts b/packages/apps/shopify-api/adapters/__e2etests__/e2e-runner.test.ts index f3abaaa02f..43d31d8de4 100644 --- a/packages/apps/shopify-api/adapters/__e2etests__/e2e-runner.test.ts +++ b/packages/apps/shopify-api/adapters/__e2etests__/e2e-runner.test.ts @@ -13,18 +13,15 @@ export function runTests(env: E2eTestEnvironment) { name: 'Dummy Shopify server', domain: `http://localhost:${env.dummyServerPort}`, dummyServerPort: 'not actually used', - process: spawn( - 'yarn', - ['node', 'adapters/__e2etests__/test_apps/test-dummy-shopify-server.js'], - { - env: { - ...process.env, // eslint-disable-line no-process-env - HTTP_SERVER_PORT: env.dummyServerPort, - }, - detached: true, - // stdio: 'inherit', + + process: spawn('yarn', ['node', 'bundle/test-dummy-shopify-server.mjs'], { + env: { + ...process.env, // eslint-disable-line no-process-env + HTTP_SERVER_PORT: env.dummyServerPort, }, - ), + detached: true, + // stdio: 'inherit', + }), testable: false, ready: false, }; diff --git a/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-apps.config.js b/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-apps.config.js new file mode 100644 index 0000000000..5096756e1a --- /dev/null +++ b/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-apps.config.js @@ -0,0 +1,41 @@ +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import typescript from '@rollup/plugin-typescript'; +import replace from '@rollup/plugin-replace'; +import excludeDependenciesFromBundle from 'rollup-plugin-exclude-dependencies-from-bundle'; + +import * as pkg from '../../package.json'; + +const config = { + output: [{dir: 'bundle', format: 'esm', entryFileNames: '[name].mjs'}], + external: Object.keys(pkg.dependencies), + plugins: [ + excludeDependenciesFromBundle({dependencies: true, peerDependencies: true}), + resolve({extensions: ['.ts', '.js'], browser: true}), + replace({preventAssignment: true}), + commonjs(), + typescript({ + include: [`./**/*.ts`], + outDir: 'bundle', + noEmit: false, + emitDeclarationOnly: false, + }), + ], +}; + +const configArray = [ + { + ...config, + input: ['adapters/__e2etests__/test_apps/test-dummy-shopify-server.ts'], + }, + { + ...config, + input: ['adapters/__e2etests__/test_apps/test-node-app.ts'], + }, + { + ...config, + input: ['adapters/__e2etests__/test_apps/test-web-api-app.ts'], + }, +]; + +export default configArray; diff --git a/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-web-api-app.config.ts b/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-web-api-app.config.ts deleted file mode 100644 index 9362a352cf..0000000000 --- a/packages/apps/shopify-api/adapters/__e2etests__/rollup.test-web-api-app.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import swc from 'rollup-plugin-swc'; -import nodeResolve from '@rollup/plugin-node-resolve'; -import cjs from '@rollup/plugin-commonjs'; - -/* eslint-disable-next-line import/no-anonymous-default-export */ -export default { - input: 'adapters/__e2etests__/test_apps/test-web-api-app.js', - output: { - dir: 'bundle', - format: 'esm', - }, - plugins: [ - nodeResolve({ - extensions: ['.ts', '.js'], - browser: true, - }), - cjs(), - swc({ - jsc: { - parser: { - syntax: 'typescript', - }, - target: 'es2022', - }, - }), - ], -}; diff --git a/packages/apps/shopify-api/adapters/cf-worker/__tests__/cf-worker.test.ts b/packages/apps/shopify-api/adapters/cf-worker/__tests__/cf-worker.test.ts index dcd1e08386..d7a65050d5 100644 --- a/packages/apps/shopify-api/adapters/cf-worker/__tests__/cf-worker.test.ts +++ b/packages/apps/shopify-api/adapters/cf-worker/__tests__/cf-worker.test.ts @@ -25,7 +25,7 @@ const workerEnvironment: E2eTestEnvironment = { `${cfWorkerAppPort}`, '--inspector-port', '9249', - 'bundle/test-web-api-app.js', + 'bundle/test-web-api-app.mjs', ], { detached: true, diff --git a/packages/apps/shopify-api/adapters/node/__tests__/node.test.ts b/packages/apps/shopify-api/adapters/node/__tests__/node.test.ts index 6bc3e620b1..9f1fa8f1b3 100644 --- a/packages/apps/shopify-api/adapters/node/__tests__/node.test.ts +++ b/packages/apps/shopify-api/adapters/node/__tests__/node.test.ts @@ -12,19 +12,15 @@ const nodeEnvironment: E2eTestEnvironment = { name: 'NodeJS', domain: `http://localhost:${nodeAppPort}`, dummyServerPort, - process: spawn( - 'yarn', - ['node', 'adapters/__e2etests__/test_apps/test-node-app.js'], - { - env: { - ...process.env, // eslint-disable-line no-process-env - PORT: nodeAppPort, - HTTP_SERVER_PORT: dummyServerPort, - }, - detached: true, - // stdio: 'inherit', + process: spawn('yarn', ['node', 'bundle/test-node-app.mjs'], { + env: { + ...process.env, // eslint-disable-line no-process-env + PORT: nodeAppPort, + HTTP_SERVER_PORT: dummyServerPort, }, - ), + detached: true, + // stdio: 'inherit', + }), testable: true, ready: false, }; diff --git a/packages/apps/shopify-api/adapters/web-api/__tests__/web-api.test.ts b/packages/apps/shopify-api/adapters/web-api/__tests__/web-api.test.ts index 169abf7915..0f1a0b55e0 100644 --- a/packages/apps/shopify-api/adapters/web-api/__tests__/web-api.test.ts +++ b/packages/apps/shopify-api/adapters/web-api/__tests__/web-api.test.ts @@ -26,7 +26,7 @@ const webApiEnvironment: E2eTestEnvironment = { `${webApiAppPort}`, '--inspector-port', '9259', - 'bundle/test-web-api-app.js', + 'bundle/test-web-api-app.mjs', ], { detached: true, diff --git a/packages/apps/shopify-api/package.json b/packages/apps/shopify-api/package.json index ca813d5cbe..9daa99ca72 100644 --- a/packages/apps/shopify-api/package.json +++ b/packages/apps/shopify-api/package.json @@ -2,28 +2,33 @@ "name": "@shopify/shopify-api", "version": "9.7.2", "description": "Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "types": "./dist/ts/lib/index.d.ts", + "module": "./dist/esm/lib/index.mjs", + "main": "./dist/cjs/lib/index.js", "exports": { ".": { - "require": "./lib/index.js", - "types": "./lib/index.d.ts", - "default": "./lib/index.js" + "types": "./dist/ts/lib/index.d.ts", + "import": "./dist/esm/lib/index.mjs", + "require": "./dist/cjs/lib/index.js", + "default": "./dist/cjs/lib/index.js" }, "./rest/admin/*": { - "import": "./rest/admin/*/index.js", - "require": "./rest/admin/*/index.js", - "types": "./rest/admin/*/index.d.ts" + "types": "./dist/ts/rest/admin/*/index.d.ts", + "import": "./dist/esm/rest/admin/*/index.mjs", + "require": "./dist/cjs/rest/admin/*/index.js", + "default": "./dist/cjs/rest/admin/*/index.js" }, "./runtime": { - "import": "./runtime/index.js", - "require": "./runtime/index.js", - "types": "./runtime/index.d.ts" + "types": "./dist/ts/runtime/index.d.ts", + "import": "./dist/esm/runtime/index.mjs", + "require": "./dist/cjs/runtime/index.js", + "default": "./dist/cjs/runtime/index.js" }, "./adapters/*": { - "import": "./adapters/*/index.js", - "require": "./adapters/*/index.js", - "types": "./adapters/*/index.d.ts" + "types": "./dist/ts/adapters/*/index.d.ts", + "import": "./dist/esm/adapters/*/index.mjs", + "require": "./dist/cjs/adapters/*/index.js", + "default": "./dist/cjs/adapters/*/index.js" } }, "prettier": "@shopify/prettier-config", @@ -31,14 +36,13 @@ "test:all": "yarn pretest:adapters && jest && yarn posttest:adapters", "test:ci": "yarn test && yarn test:rest_resources && yarn test:adapters", "test": "jest --selectProjects library", - "pretest:adapters": "yarn build && yarn rollup -c adapters/__e2etests__/rollup.test-web-api-app.config.js", + "pretest:adapters": "yarn rollup --bundleConfigAsCjs -c adapters/__e2etests__/rollup.test-apps.config.js", "test:adapters": "jest --selectProjects adapters:mock adapters:node adapters:cf-worker adapters:web-api", "test:rest_resources": "jest --selectProjects rest_resources", "lint": "jest --selectProjects lint", "test:dev": "jest --selectProjects library lint", - "clean": "yarn tsc --build --clean", - "prebuild": "yarn tsc --build --clean", - "build": "tsc", + "build": "yarn run rollup -c rollup.config.js --bundleConfigAsCjs && yarn tsc -p ./tsconfig.build.json", + "clean": "rimraf .rollup.cache dist bundle", "release": "yarn build && changeset publish" }, "publishConfig": { @@ -94,10 +98,7 @@ "wrangler": "^3.33.0" }, "files": [ - "**/*.d.ts", - "**/*.d.ts.map", - "**/*.js", - "**/*.js.map", + "dist/*", "!bundle/*", "!node_modules", "!adapters/__e2etests__/*" diff --git a/packages/apps/shopify-api/rollup.config.js b/packages/apps/shopify-api/rollup.config.js new file mode 100644 index 0000000000..8acf75b231 --- /dev/null +++ b/packages/apps/shopify-api/rollup.config.js @@ -0,0 +1,31 @@ +import fs from 'fs'; + +import {getConfig} from '../../../config/rollup/rollup-utils'; + +import * as pkg from './package.json'; + +const basePath = `${__dirname}/adapters`; + +const adapterInputs = fs + .readdirSync(basePath, {withFileTypes: true}) + .filter( + (dirent) => + dirent.isDirectory() && + !['__tests__', '__e2etests__'].includes(dirent.name), + ) + .map(({name}) => `adapters/${name}/index.ts`); + +const restPath = `${__dirname}/rest/admin`; +const restInputs = fs + .readdirSync(restPath, {withFileTypes: true}) + .filter( + (dirent) => + dirent.isDirectory() && + !['__tests__', '__e2etests__'].includes(dirent.name), + ) + .map(({name}) => `rest/admin/${name}/index.ts`); + +export default getConfig({ + pkg, + input: ['lib/index.ts', 'runtime/index.ts', ...adapterInputs, ...restInputs], +}); diff --git a/packages/apps/shopify-api/tsconfig.build.json b/packages/apps/shopify-api/tsconfig.build.json new file mode 100644 index 0000000000..d036fd2530 --- /dev/null +++ b/packages/apps/shopify-api/tsconfig.build.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.build.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./dist/ts", + "rootDir": "." + }, + "include": [ + "lib/**/*.ts", + "future/**/*.ts", + "adapters/**/*.ts", + "rest/**/*.ts", + "runtime/**/*.ts", + ], + "exclude": [ + "**/*.test.ts", + "**/test/*", + "**/__tests__/*", + "**/__e2etests__/*", + "**/__test-helpers" + ] +} From 36cc3899f00be6ea9dddfc18b6e45010e03a6ba0 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:37:01 -0400 Subject: [PATCH 14/32] Fix config tests to not mock api --- .../src/server/__tests__/shopify-app.test.ts | 25 ++++--------------- .../src/server/shopify-app.ts | 4 ++- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts index 187fc7229a..1e1e9ad486 100644 --- a/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts +++ b/packages/apps/shopify-app-remix/src/server/__tests__/shopify-app.test.ts @@ -1,5 +1,4 @@ import {ShopifyError} from '@shopify/shopify-api'; -import * as shopifyApiPackage from '@shopify/shopify-api'; import { shopifyApp, @@ -11,13 +10,13 @@ import { ApiVersion, } from '../index'; import {testConfig} from '../__test-helpers'; +import {deriveApi} from '../shopify-app'; describe('shopifyApp', () => { /* eslint-disable no-process-env */ const oldEnv = process.env; beforeEach(() => { - jest.resetModules(); process.env = {...oldEnv}; }); @@ -61,36 +60,22 @@ describe('shopifyApp', () => { it("fixes the port if it's not set", () => { // GIVEN - jest.spyOn(shopifyApiPackage, 'shopifyApi'); // eslint-disable-next-line no-process-env process.env.PORT = '1234'; // WHEN - shopifyApp(testConfig({appUrl: 'http://localhost'})); + const apiObject = deriveApi(testConfig({appUrl: 'http://localhost'})); // THEN - expect(shopifyApiPackage.shopifyApi).toHaveBeenCalledWith( - expect.objectContaining({ - appUrl: 'http://localhost:1234', - }), - ); + expect(apiObject.config.hostName).toBe('localhost:1234'); }); it('applies user agent prefix', () => { - // GIVEN - jest.spyOn(shopifyApiPackage, 'shopifyApi'); - const config = testConfig({ - userAgentPrefix: 'test', - }); - // WHEN - shopifyApp(config); + const apiObject = deriveApi(testConfig({userAgentPrefix: 'test'})); // THEN - const {userAgentPrefix} = (shopifyApiPackage.shopifyApi as any).mock - .calls[1][0]; - - expect(userAgentPrefix).toMatch( + expect(apiObject.config.userAgentPrefix).toMatch( /^test \| Shopify Remix Library v[0-9]+\.[0-9]+\.[0-9]+(-rc.[0-9]+)?$/, ); }); diff --git a/packages/apps/shopify-app-remix/src/server/shopify-app.ts b/packages/apps/shopify-app-remix/src/server/shopify-app.ts index e0dd568d92..cccb2d2b63 100644 --- a/packages/apps/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/apps/shopify-app-remix/src/server/shopify-app.ts @@ -131,7 +131,9 @@ function isSingleMerchantApp( return config.distribution === AppDistribution.SingleMerchant; } -function deriveApi(appConfig: AppConfigArg) { +// This function is only exported so we can unit test it without having to mock the underlying module. +// It's not available to consumers of the library because it is not exported in the index module, and never should be. +export function deriveApi(appConfig: AppConfigArg): BasicParams['api'] { let appUrl: URL; try { appUrl = new URL(appConfig.appUrl); From 20408eb8e64829280d32d74694be0cfcee9908c2 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Mon, 22 Apr 2024 17:21:32 -0500 Subject: [PATCH 15/32] Alias v10_lineItemBilling to v11_lineItemBilling We want to defer the default enabling of this feature until the next release Alias flag in config --- packages/apps/shopify-api/future/flags.ts | 17 +++++++++++--- .../shopify-api/lib/__tests__/test-config.ts | 1 + .../billing/__tests__/legacy-request.test.ts | 22 +++++++++---------- .../lib/billing/__tests__/request.test.ts | 2 +- .../apps/shopify-api/lib/billing/types.ts | 2 +- .../admin/__tests__/rest_client.test.ts | 12 +++++----- packages/apps/shopify-api/lib/config.ts | 9 ++++++++ .../lib/logger/__tests__/logger.test.ts | 6 ++--- .../src/server/future/flags.ts | 5 ++++- .../src/server/shopify-app.ts | 2 +- 10 files changed, 51 insertions(+), 27 deletions(-) diff --git a/packages/apps/shopify-api/future/flags.ts b/packages/apps/shopify-api/future/flags.ts index 1cb5e04bb5..9d41d99bbe 100644 --- a/packages/apps/shopify-api/future/flags.ts +++ b/packages/apps/shopify-api/future/flags.ts @@ -6,9 +6,15 @@ import {type ConfigInterface} from '../lib/base-types'; */ export interface FutureFlags { /** - * Enable line item billing, to make billing configuration more similar to the GraphQL API. + * Enable line item billing, to make billing configuration more similar to the GraphQL API. Default enabling of this + * feature has been moved to v11. Use v11_lineItemBilling instead. */ v10_lineItemBilling?: boolean; + + /** + * Enable line item billing, to make billing configuration more similar to the GraphQL API. + */ + v11_lineItemBilling?: boolean; } /** @@ -36,10 +42,15 @@ export function logDisabledFutureFlags( const logFlag = (flag: string, message: string) => logger.info(`Future flag ${flag} is disabled.\n\n ${message}\n`); - if (!config.future?.v10_lineItemBilling) { + if (!config.future?.v11_lineItemBilling) { logFlag( - 'v10_lineItemBilling', + 'v11_lineItemBilling', 'Enable this flag to use the new billing API, that supports multiple line items per plan.', ); } + if (config.future?.v10_lineItemBilling) { + logger.info( + 'This feature will become enabled in v11. Use flag v11_lineItemBilling instead', + ); + } } diff --git a/packages/apps/shopify-api/lib/__tests__/test-config.ts b/packages/apps/shopify-api/lib/__tests__/test-config.ts index 20e2178f15..ab5850e3ba 100644 --- a/packages/apps/shopify-api/lib/__tests__/test-config.ts +++ b/packages/apps/shopify-api/lib/__tests__/test-config.ts @@ -56,6 +56,7 @@ const TEST_FUTURE_FLAGS: Required<{ [key in keyof FutureFlags]: true; }> = { v10_lineItemBilling: true, + v11_lineItemBilling: true, } as const; const TEST_CONFIG = { diff --git a/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts b/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts index 9422f03ef4..74cd7f4889 100644 --- a/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts +++ b/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts @@ -23,7 +23,7 @@ const GRAPHQL_BASE_REQUEST = { interface TestConfigInterface { name: string; - billingConfig: BillingConfig | BillingConfig<{v10_lineItemBilling: false}>; + billingConfig: BillingConfig | BillingConfig<{v11_lineItemBilling: false}>; paymentResponse: string; responseObject: any; errorResponse: string; @@ -195,7 +195,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: undefined, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -222,7 +222,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -259,7 +259,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -296,7 +296,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -328,7 +328,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -386,7 +386,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -417,7 +417,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -497,7 +497,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -530,7 +530,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); @@ -568,7 +568,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v10_lineItemBilling: false, + v11_lineItemBilling: false, }, }), ); diff --git a/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts b/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts index 0b886aa4f1..d3d3117829 100644 --- a/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts +++ b/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts @@ -23,7 +23,7 @@ const GRAPHQL_BASE_REQUEST = { interface TestConfigInterface { name: string; - billingConfig: BillingConfig<{v10_lineItemBilling: true}>; + billingConfig: BillingConfig<{v11_lineItemBilling: true}>; paymentResponse: string; responseObject: any; errorResponse: string; diff --git a/packages/apps/shopify-api/lib/billing/types.ts b/packages/apps/shopify-api/lib/billing/types.ts index ac6e5c77aa..b9d9cd5254 100644 --- a/packages/apps/shopify-api/lib/billing/types.ts +++ b/packages/apps/shopify-api/lib/billing/types.ts @@ -119,7 +119,7 @@ export type BillingConfigLegacyItem = export type BillingConfigItem< Future extends FutureFlagOptions = FutureFlagOptions, > = - FeatureEnabled extends true + FeatureEnabled extends true ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan : BillingConfigLegacyItem; diff --git a/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts b/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts index 05083d1003..d129ddee2a 100644 --- a/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts +++ b/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts @@ -594,7 +594,7 @@ describe('REST client', () => { await client.get({path: '/url/path'}); // first call to .log is .debug with package and runtime info during initialization - expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.stringContaining('API Deprecation Notice'), @@ -615,7 +615,7 @@ describe('REST client', () => { data: postBody, }); - expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(4); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.stringContaining( @@ -678,7 +678,7 @@ describe('REST client', () => { // this one should skip it await client.get({path: '/url/path'}); // first call to .log is .debug with package and runtime info during initialization - expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.anything(), @@ -691,7 +691,7 @@ describe('REST client', () => { // should warn a second time since 5 mins have passed await client.get({path: '/url/path'}); - expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(4); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.anything(), @@ -893,7 +893,7 @@ describe('REST client', () => { await client.post({path: '/url/path', data}); // The first log call is the runtime info - expect(shopify.config.logger.log).toHaveBeenCalledTimes(1); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); }); it('logs HTTP requests when the setting is on', async () => { @@ -915,7 +915,7 @@ describe('REST client', () => { expect.anything(), ); const logMessage = (shopify.config.logger.log as jest.Mock).mock - .calls[1][1]; + .calls[2][1]; expect(logMessage).toContain('Received response for HTTP'); expect(logMessage).toContain( `https://test-shop.myshopify.io/admin/api/${shopify.config.apiVersion}/url/path`, diff --git a/packages/apps/shopify-api/lib/config.ts b/packages/apps/shopify-api/lib/config.ts index d06fa8fe4b..0ce4d3b47f 100644 --- a/packages/apps/shopify-api/lib/config.ts +++ b/packages/apps/shopify-api/lib/config.ts @@ -56,6 +56,14 @@ export function validateConfig( ); } + // Alias the v10_lineItemBilling flag to v11_lineItemBilling because we aren't releasing in v10 + const future = params.future?.v10_lineItemBilling + ? { + v11_lineItemBilling: params.future?.v10_lineItemBilling, + ...params.future, + } + : params.future; + const { hostScheme, isCustomStoreApp, @@ -83,6 +91,7 @@ export function validateConfig( privateAppStorefrontAccessToken ?? config.privateAppStorefrontAccessToken, customShopDomains: customShopDomains ?? config.customShopDomains, billing: billing ?? config.billing, + future: future ?? config.future, }); if ( diff --git a/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts b/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts index 69d459ba49..f2a4cc3f71 100644 --- a/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts +++ b/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts @@ -194,7 +194,7 @@ describe('shopify.logger', () => { // We always log an INFO line with the runtime when starting up const expectedCallCount = config.expectedLevels.length + - (config.logLevel >= LogSeverity.Info ? 1 : 0); + (config.logLevel >= LogSeverity.Info ? 2 : 0); expect(shopify.config.logger.log).toHaveBeenCalledTimes( expectedCallCount, @@ -277,8 +277,8 @@ describe('shopify.logger', () => { await logPromise; // We always log the runtime before the actual message - expect(loggedMessages.length).toEqual(2); - expect(loggedMessages[1]).toEqual( + expect(loggedMessages.length).toEqual(3); + expect(loggedMessages[2]).toEqual( `${LogSeverity.Debug}: [shopify-api/DEBUG] debug message`, ); }); diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index abe2833623..eac9c1486f 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -43,7 +43,10 @@ export interface FutureFlags { // When adding new flags, use this format: // vX_myFutureFlag: Future extends FutureFlags ? Future['vX_myFutureFlag'] : false; export interface ApiFutureFlags { - v10_lineItemBilling: Future extends FutureFlags + v10_lineItemBilling?: Future extends FutureFlags + ? Future['v3_lineItemBilling'] + : false; + v11_lineItemBilling: Future extends FutureFlags ? Future['v3_lineItemBilling'] : false; } diff --git a/packages/apps/shopify-app-remix/src/server/shopify-app.ts b/packages/apps/shopify-app-remix/src/server/shopify-app.ts index e0dd568d92..8b81042b96 100644 --- a/packages/apps/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/apps/shopify-app-remix/src/server/shopify-app.ts @@ -162,7 +162,7 @@ function deriveApi(appConfig: AppConfigArg) { apiVersion: appConfig.apiVersion ?? LATEST_API_VERSION, isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin, future: { - v10_lineItemBilling: appConfig.future?.v3_lineItemBilling, + v11_lineItemBilling: appConfig.future?.v3_lineItemBilling, }, _logDisabledFutureFlags: false, }); From 97ba3a15182cf370572323dba607c5ba528324d5 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Tue, 23 Apr 2024 09:58:26 -0500 Subject: [PATCH 16/32] generate docs --- .../docs/generated/generated_docs_data.json | 12020 ++++++++-------- .../src/server/future/flags.ts | 3 - 2 files changed, 6010 insertions(+), 6013 deletions(-) diff --git a/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json b/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json index 734517277d..96d021c5c1 100644 --- a/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json @@ -19,57 +19,57 @@ { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "apiKey", + "name": "__APP_BRIDGE_URL", "value": "string", - "description": "The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.\n\nWhen using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment variable, then you need to pass it from the loader to the component." + "description": "Used internally by Shopify. You don't need to set this.", + "isOptional": true, + "isPrivate": true }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "isEmbeddedApp", - "value": "boolean", - "description": "Whether the app is loaded inside the Shopify Admin. Default is `true`.\n\n\n\n\n", - "isOptional": true + "name": "apiKey", + "value": "string", + "description": "The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.\n\nWhen using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment variable, then you need to pass it from the loader to the component." }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "i18n", - "value": "TranslationDictionary | TranslationDictionary[]", - "description": "The internationalization (i18n) configuration for your Polaris provider.\n\n\n\n\n", + "name": "children", + "value": "React.ReactNode", + "description": "Inner content of the application", "isOptional": true }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "__APP_BRIDGE_URL", - "value": "string", - "description": "Used internally by Shopify. You don't need to set this.", - "isOptional": true, - "isPrivate": true + "name": "features", + "value": "FeaturesConfig", + "description": "For toggling features", + "isOptional": true }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "theme", - "value": "ThemeName", - "description": "", + "name": "i18n", + "value": "TranslationDictionary | TranslationDictionary[]", + "description": "The internationalization (i18n) configuration for your Polaris provider.\n\n\n\n\n", "isOptional": true }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "features", - "value": "FeaturesConfig", - "description": "For toggling features", + "name": "isEmbeddedApp", + "value": "boolean", + "description": "Whether the app is loaded inside the Shopify Admin. Default is `true`.\n\n\n\n\n", "isOptional": true }, { "filePath": "src/react/components/AppProvider/AppProvider.tsx", "syntaxKind": "PropertySignature", - "name": "children", - "value": "React.ReactNode", - "description": "Inner content of the application", + "name": "theme", + "value": "ThemeName", + "description": "", "isOptional": true } ], @@ -322,6 +322,39 @@ "name": "NonEmbeddedAdminContext", "description": "", "members": [ + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", + "examples": [ + { + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" + } + ] + } + ] + }, { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", @@ -358,82 +391,310 @@ ] } ] - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, + } + ], + "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + }, + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", + "members": [ { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" } ] } ] } ], - "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", - "members": [ + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "scope", - "value": "string", - "description": "The desired scopes for the access token, at the time the session was created." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "The unique identifier for the session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain, such as `example.myshopify.com`." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "The desired scopes for the access token, at the time the session was created." }, { "filePath": "../shopify-api/lib/session/session.ts", @@ -509,9 +770,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -523,9 +784,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -538,23 +799,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -573,23 +827,30 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", "value": "string", - "description": "The user's locale." + "description": "The user's last name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "locale", + "value": "string", + "description": "The user's locale." } ], "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" @@ -643,23 +904,25 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "id", + "name": "accessToken", "value": "string", - "description": "The unique identifier for the session." + "description": "The access token for the session.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "state", + "name": "id", "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "description": "The unique identifier for the session." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -671,34 +934,32 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "accessToken", + "name": "shop", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The Shopify shop domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" @@ -710,179 +971,33 @@ "value": "Omit & {\n associated_user: Partial;\n}", "description": "" }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - } - ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." - } - ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" - }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "path", - "value": "string", - "description": "The path to the resource, relative to the API version root." + "name": "data", + "value": "string | Record", + "description": "The request body.", + "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." }, { "filePath": "../shopify-api/lib/clients/types.ts", @@ -895,22 +1010,30 @@ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "tries", + "value": "number", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, "DataType": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", @@ -934,14 +1057,6 @@ } ] }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, "PostRequestParams": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "TypeAliasDeclaration", @@ -949,139 +1064,47 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "BillingContext": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "BillingContext", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", + "description": "Cancels an ongoing subscription, given its ID.", + "examples": [ + { + "title": "Cancelling a subscription", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] }, { - "filePath": "src/server/clients/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The total number of times to try the request if it fails.", - "isOptional": true - } - ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" - }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" - } - ] - }, - "BillingContext": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "BillingContext", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "require", - "value": "(options: RequireBillingOptions) => Promise", + "name": "check", + "value": "(options: CheckBillingOptions) => Promise", "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", "examples": [ { - "title": "Requesting billing right away", - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", + "title": "Check what billing plans a merchant is subscribed to", + "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -1089,13 +1112,22 @@ "title": "shopify.server.ts" } ] - }, + } + ] + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "request", + "value": "(options: RequestBillingOptions) => Promise", + "description": "Requests payment for the plan.", + "examples": [ { - "title": "Redirect to a plan selection page", - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "title": "Using a custom return URL", + "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", "tabs": [ { - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -1103,36 +1135,22 @@ "title": "shopify.server.ts" } ] - }, - { - "title": "Requesting billing with line items", - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] } ] }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "check", - "value": "(options: CheckBillingOptions) => Promise", + "name": "require", + "value": "(options: RequireBillingOptions) => Promise", "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", "examples": [ { - "title": "Check what billing plans a merchant is subscribed to", - "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", + "title": "Requesting billing right away", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -1140,22 +1158,13 @@ "title": "shopify.server.ts" } ] - } - ] - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "request", - "value": "(options: RequestBillingOptions) => Promise", - "description": "Requests payment for the plan.", - "examples": [ + }, { - "title": "Using a custom return URL", - "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "title": "Redirect to a plan selection page", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -1163,26 +1172,17 @@ "title": "shopify.server.ts" } ] - } - ] - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", - "examples": [ + }, { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Requesting billing with line items", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", - "title": "/app/routes/cancel-subscription.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] @@ -1192,76 +1192,56 @@ ], "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, - "RequireBillingOptions": { + "CancelBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", + "name": "CancelBillingOptions", "description": "", "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "onFailure", - "value": "(error: any) => Promise", - "description": "How to handle the request if the shop doesn't have an active payment for any plan." + "name": "prorate", + "value": "boolean", + "description": "Whether to prorate the cancellation.\n\n\n\n\n", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true + "name": "subscriptionId", + "value": "string", + "description": "The ID of the subscription to cancel." } ], - "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" + "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" }, - "BillingCheckResponseObject": { + "AppSubscription": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "BillingCheckResponseObject", + "name": "AppSubscription", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "hasActivePayment", - "value": "boolean", - "description": "Whether the user has an active payment method." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "oneTimePurchases", - "value": "OneTimePurchase[]", - "description": "The one-time purchases the shop has." + "name": "id", + "value": "string", + "description": "The ID of the app subscription." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "appSubscriptions", - "value": "AppSubscription[]", - "description": "The active subscriptions the shop has." - } - ], - "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" - }, - "OneTimePurchase": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "OneTimePurchase", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the one-time purchase." + "name": "lineItems", + "value": "ActiveSubscriptionLineItem[]", + "description": "", + "isOptional": true }, { "filePath": "../shopify-api/lib/billing/types.ts", @@ -1275,21 +1255,14 @@ "syntaxKind": "PropertySignature", "name": "test", "value": "boolean", - "description": "Whether this is a test purchase." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "status", - "value": "string", - "description": "The status of the one-time purchase." + "description": "Whether this is a test subscription." } ], - "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" }, - "AppSubscription": { + "ActiveSubscriptionLineItem": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppSubscription", + "name": "ActiveSubscriptionLineItem", "description": "", "members": [ { @@ -1297,44 +1270,7 @@ "syntaxKind": "PropertySignature", "name": "id", "value": "string", - "description": "The ID of the app subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "test", - "value": "boolean", - "description": "Whether this is a test subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "lineItems", - "value": "ActiveSubscriptionLineItem[]", - "description": "", - "isOptional": true - } - ], - "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" - }, - "ActiveSubscriptionLineItem": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "ActiveSubscriptionLineItem", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "" + "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", @@ -1369,112 +1305,84 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "name": "discount", + "value": "AppPlanDiscount", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "price", - "value": "Money", + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "AppPlanDiscount", + "name": "price", + "value": "Money", "description": "" } ], "value": "export interface RecurringAppPlan {\n /*\n * The interval for this plan is charged on.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /*\n * The price of the plan.\n */\n price: Money;\n /*\n * The discount applied to the plan.\n */\n discount: AppPlanDiscount;\n}" }, - "BillingInterval": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingInterval", - "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "OneTime", - "value": "ONE_TIME" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Every30Days", - "value": "EVERY_30_DAYS" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Annual", - "value": "ANNUAL" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Usage", - "value": "USAGE" - } - ] - }, - "Money": { + "AppPlanDiscount": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "Money", + "name": "AppPlanDiscount", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", + "name": "durationLimitInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", + "name": "priceAfterDiscount", + "value": "Money", "description": "" - } - ], - "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" - }, - "AppPlanDiscount": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppPlanDiscount", - "description": "", - "members": [ + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "durationLimitInIntervals", + "name": "remainingDurationInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "remainingDurationInIntervals", - "value": "number", + "name": "value", + "value": "AppPlanDiscountAmount", "description": "" - }, + } + ], + "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + }, + "Money": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "Money", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "priceAfterDiscount", - "value": "Money", + "name": "amount", + "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "value", - "value": "AppPlanDiscountAmount", + "name": "currencyCode", + "value": "string", "description": "" } ], - "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" }, "AppPlanDiscountAmount": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -1529,6 +1437,34 @@ ], "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" }, + "BillingInterval": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingInterval", + "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "OneTime", + "value": "ONE_TIME" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Annual", + "value": "ANNUAL" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, "UsageAppPlan": { "filePath": "../shopify-api/lib/billing/types.ts", "name": "UsageAppPlan", @@ -1563,36 +1499,94 @@ "name": "CheckBillingOptions", "description": "", "members": [ + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true + }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "plans", "value": "(keyof Config[\"billing\"])[]", "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + } + ], + "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + }, + "BillingCheckResponseObject": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "The active subscriptions the shop has." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", + "name": "hasActivePayment", "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true + "description": "Whether the user has an active payment method." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "The one-time purchases the shop has." } ], - "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" }, - "RequestBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + "OneTimePurchase": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "OneTimePurchase", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plan", - "value": "keyof Config[\"billing\"]", - "description": "The plan to request. Must be one of the values defined in the `billing` config option." + "name": "id", + "value": "string", + "description": "The ID of the one-time purchase." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "The name of the purchased plan." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "The status of the one-time purchase." }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "Whether this is a test purchase." + } + ], + "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -1601,6 +1595,13 @@ "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", "isOptional": true }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plan", + "value": "keyof Config[\"billing\"]", + "description": "The plan to request. Must be one of the values defined in the `billing` config option." + }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -1612,36 +1613,35 @@ ], "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" }, - "CancelBillingOptions": { + "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "name": "RequireBillingOptions", "description": "", "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", - "value": "string", - "description": "The ID of the subscription to cancel." + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", - "isOptional": true + "name": "onFailure", + "value": "(error: any) => Promise", + "description": "How to handle the request if the shop doesn't have an active payment for any plan." }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true - } + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + } ], - "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" + "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" }, "EnsureCORSFunction": { "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", @@ -1658,21 +1658,31 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Using the decoded session token", - "description": "Get user-specific data using the `sessionToken` object.", + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" } ] } @@ -1747,31 +1757,21 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Using the decoded session token", + "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -1780,25 +1780,52 @@ ], "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, - "JwtPayload": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "JwtPayload", + "RedirectFunction": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "name": "RedirectFunction", "description": "", - "members": [ + "params": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "iss", + "name": "url", + "description": "", "value": "string", - "description": "The shop's admin domain." + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "dest", - "value": "string", - "description": "The shop's domain." - }, + "name": "init", + "description": "", + "value": "RedirectInit", + "isOptional": true, + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "description": "", + "name": "TypedResponse", + "value": "TypedResponse" + }, + "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" + }, + "RedirectInit": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectInit", + "value": "number | (ResponseInit & {target?: RedirectTarget})", + "description": "" + }, + "RedirectTarget": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectTarget", + "value": "'_self' | '_parent' | '_top'", + "description": "" + }, + "JwtPayload": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "JwtPayload", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", @@ -1809,9 +1836,9 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sub", + "name": "dest", "value": "string", - "description": "The User that the session token is intended for." + "description": "The shop's domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -1823,16 +1850,16 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "nbf", + "name": "iat", "value": "number", - "description": "When the session token activates." + "description": "When the session token was issued." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "iat", - "value": "number", - "description": "When the session token was issued." + "name": "iss", + "value": "string", + "description": "The shop's admin domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -1841,56 +1868,29 @@ "value": "string", "description": "A secure random UUID." }, + { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "When the session token activates." + }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", "name": "sid", "value": "string", "description": "A unique session ID per user and app." - } - ], - "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" - }, - "RedirectFunction": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", - "description": "", - "params": [ - { - "name": "url", - "description": "", - "value": "string", - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" }, { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "The User that the session token is intended for." } ], - "returns": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "description": "", - "name": "TypedResponse", - "value": "TypedResponse" - }, - "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" - }, - "RedirectInit": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", - "description": "" + "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" } } } @@ -1929,21 +1929,16 @@ "description": "", "exampleGroups": [ { - "title": "sessionToken", + "title": "cors", "examples": [ { - "description": "Get user-specific data using the `sessionToken` object.", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "codeblock": { - "title": "Using the decoded session token", + "title": "Setting CORS headers for a admin request", "tabs": [ { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "language": "typescript" - }, - { - "title": "shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/routes/admin/my-route.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", "language": "typescript" } ] @@ -2024,16 +2019,21 @@ ] }, { - "title": "cors", + "title": "sessionToken", "examples": [ { - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "description": "Get user-specific data using the `sessionToken` object.", "codeblock": { - "title": "Setting CORS headers for a admin request", + "title": "Using the decoded session token", "tabs": [ { - "title": "/app/routes/admin/my-route.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "language": "typescript" + }, + { + "title": "shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2042,98 +2042,98 @@ ] }, { - "title": "rest", + "title": "graphql", "examples": [ { - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "description": "Use `admin.graphql` to make query / mutation requests.", "codeblock": { - "title": "Using REST resources", + "title": "Querying the GraphQL API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } }, { - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", "codeblock": { - "title": "Performing a GET request to the REST API", + "title": "Handling GraphQL errors", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } - }, + } + ] + }, + { + "title": "rest", + "examples": [ { - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "codeblock": { - "title": "Performing a POST request to the REST API", + "title": "Using REST resources", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } - } - ] - }, - { - "title": "graphql", - "examples": [ + }, { - "description": "Use `admin.graphql` to make query / mutation requests.", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "codeblock": { - "title": "Querying the GraphQL API", + "title": "Performing a GET request to the REST API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } }, { - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "codeblock": { - "title": "Handling GraphQL errors", + "title": "Performing a POST request to the REST API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2142,16 +2142,16 @@ ] }, { - "title": "require", + "title": "cancel", "examples": [ { - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", "codeblock": { - "title": "Requesting billing right away", + "title": "Cancelling a subscription", "tabs": [ { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", "language": "typescript" }, { @@ -2161,15 +2161,20 @@ } ] } - }, + } + ] + }, + { + "title": "check", + "examples": [ { - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", "codeblock": { - "title": "Redirect to a plan selection page", + "title": "Check what billing plans a merchant is subscribed to", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", "language": "typescript" }, { @@ -2179,20 +2184,25 @@ } ] } - }, + } + ] + }, + { + "title": "request", + "examples": [ { - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", "codeblock": { - "title": "Requesting billing with line items", + "title": "Using a custom return URL", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", "language": "typescript" }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2201,16 +2211,16 @@ ] }, { - "title": "check", + "title": "require", "examples": [ { - "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "codeblock": { - "title": "Check what billing plans a merchant is subscribed to", + "title": "Requesting billing right away", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", "language": "typescript" }, { @@ -2220,20 +2230,15 @@ } ] } - } - ] - }, - { - "title": "request", - "examples": [ + }, { - "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", "codeblock": { - "title": "Using a custom return URL", + "title": "Redirect to a plan selection page", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", "language": "typescript" }, { @@ -2243,25 +2248,20 @@ } ] } - } - ] - }, - { - "title": "cancel", - "examples": [ + }, { - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", "codeblock": { - "title": "Cancelling a subscription", + "title": "Requesting billing with line items", "tabs": [ { - "title": "/app/routes/cancel-subscription.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", "language": "typescript" }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2292,51 +2292,23 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "require", - "value": "(options: RequireBillingOptions) => Promise", - "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", + "description": "Cancels an ongoing subscription, given its ID.", "examples": [ { - "title": "Requesting billing right away", - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Redirect to a plan selection page", - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "title": "Cancelling a subscription", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", "tabs": [ { - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] - }, - { - "title": "Requesting billing with line items", - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] } ] }, @@ -2389,91 +2361,128 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", + "name": "require", + "value": "(options: RequireBillingOptions) => Promise", + "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", "examples": [ { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Requesting billing right away", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", - "title": "/app/routes/cancel-subscription.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Redirect to a plan selection page", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] + }, + { + "title": "Requesting billing with line items", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] } ] } ], "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, - "RequireBillingOptions": { + "CancelBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", + "name": "CancelBillingOptions", "description": "", "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "onFailure", - "value": "(error: any) => Promise", - "description": "How to handle the request if the shop doesn't have an active payment for any plan." + "name": "prorate", + "value": "boolean", + "description": "Whether to prorate the cancellation.\n\n\n\n\n", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true + "name": "subscriptionId", + "value": "string", + "description": "The ID of the subscription to cancel." } ], - "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" + "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" }, - "BillingCheckResponseObject": { + "AppSubscription": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "BillingCheckResponseObject", + "name": "AppSubscription", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "hasActivePayment", - "value": "boolean", - "description": "Whether the user has an active payment method." + "name": "id", + "value": "string", + "description": "The ID of the app subscription." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "oneTimePurchases", - "value": "OneTimePurchase[]", - "description": "The one-time purchases the shop has." + "name": "lineItems", + "value": "ActiveSubscriptionLineItem[]", + "description": "", + "isOptional": true }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "appSubscriptions", - "value": "AppSubscription[]", - "description": "The active subscriptions the shop has." + "name": "name", + "value": "string", + "description": "The name of the purchased plan." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "Whether this is a test subscription." } ], - "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" + "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" }, - "OneTimePurchase": { + "ActiveSubscriptionLineItem": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "OneTimePurchase", + "name": "ActiveSubscriptionLineItem", "description": "", "members": [ { @@ -2481,80 +2490,7 @@ "syntaxKind": "PropertySignature", "name": "id", "value": "string", - "description": "The ID of the one-time purchase." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "test", - "value": "boolean", - "description": "Whether this is a test purchase." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "status", - "value": "string", - "description": "The status of the one-time purchase." - } - ], - "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" - }, - "AppSubscription": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppSubscription", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the app subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "test", - "value": "boolean", - "description": "Whether this is a test subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "lineItems", - "value": "ActiveSubscriptionLineItem[]", - "description": "", - "isOptional": true - } - ], - "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" - }, - "ActiveSubscriptionLineItem": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "ActiveSubscriptionLineItem", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "" + "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", @@ -2589,112 +2525,84 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "name": "discount", + "value": "AppPlanDiscount", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "price", - "value": "Money", + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "AppPlanDiscount", + "name": "price", + "value": "Money", "description": "" } ], "value": "export interface RecurringAppPlan {\n /*\n * The interval for this plan is charged on.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /*\n * The price of the plan.\n */\n price: Money;\n /*\n * The discount applied to the plan.\n */\n discount: AppPlanDiscount;\n}" }, - "BillingInterval": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingInterval", - "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "OneTime", - "value": "ONE_TIME" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Every30Days", - "value": "EVERY_30_DAYS" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Annual", - "value": "ANNUAL" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Usage", - "value": "USAGE" - } - ] - }, - "Money": { + "AppPlanDiscount": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "Money", + "name": "AppPlanDiscount", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", + "name": "durationLimitInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", + "name": "priceAfterDiscount", + "value": "Money", "description": "" - } - ], - "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" - }, - "AppPlanDiscount": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppPlanDiscount", - "description": "", - "members": [ + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "durationLimitInIntervals", + "name": "remainingDurationInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "remainingDurationInIntervals", - "value": "number", + "name": "value", + "value": "AppPlanDiscountAmount", "description": "" - }, + } + ], + "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + }, + "Money": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "Money", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "priceAfterDiscount", - "value": "Money", + "name": "amount", + "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "value", - "value": "AppPlanDiscountAmount", + "name": "currencyCode", + "value": "string", "description": "" } ], - "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" }, "AppPlanDiscountAmount": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -2749,6 +2657,34 @@ ], "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" }, + "BillingInterval": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingInterval", + "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "OneTime", + "value": "ONE_TIME" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Annual", + "value": "ANNUAL" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, "UsageAppPlan": { "filePath": "../shopify-api/lib/billing/types.ts", "name": "UsageAppPlan", @@ -2783,13 +2719,6 @@ "name": "CheckBillingOptions", "description": "", "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -2797,71 +2726,142 @@ "value": "boolean", "description": "Whether to consider test purchases.", "isOptional": true + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." } ], "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" }, - "RequestBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + "BillingCheckResponseObject": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "BillingCheckResponseObject", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plan", - "value": "keyof Config[\"billing\"]", - "description": "The plan to request. Must be one of the values defined in the `billing` config option." + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "The active subscriptions the shop has." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", + "name": "hasActivePayment", "value": "boolean", - "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", - "isOptional": true + "description": "Whether the user has an active payment method." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "returnUrl", - "value": "string", - "description": "The URL to return to after the merchant approves the payment.", - "isOptional": true + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "The one-time purchases the shop has." } ], - "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" + "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" }, - "CancelBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "OneTimePurchase": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "OneTimePurchase", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", + "name": "id", "value": "string", - "description": "The ID of the subscription to cancel." + "description": "The ID of the one-time purchase." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", - "isOptional": true + "name": "name", + "value": "string", + "description": "The name of the purchased plan." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", + "name": "status", + "value": "string", + "description": "The status of the one-time purchase." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "test", "value": "boolean", - "description": "", + "description": "Whether this is a test purchase." + } + ], + "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", + "isOptional": true + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plan", + "value": "keyof Config[\"billing\"]", + "description": "The plan to request. Must be one of the values defined in the `billing` config option." + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "returnUrl", + "value": "string", + "description": "The URL to return to after the merchant approves the payment.", "isOptional": true } ], - "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" + "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" + }, + "RequireBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequireBillingOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "onFailure", + "value": "(error: any) => Promise", + "description": "How to handle the request if the shop doesn't have an active payment for any plan." + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + } + ], + "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" } } } @@ -2880,16 +2880,16 @@ "description": "", "exampleGroups": [ { - "title": "require", + "title": "cancel", "examples": [ { - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", "codeblock": { - "title": "Requesting billing right away", + "title": "Cancelling a subscription", "tabs": [ { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", "language": "typescript" }, { @@ -2899,15 +2899,20 @@ } ] } - }, + } + ] + }, + { + "title": "check", + "examples": [ { - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", "codeblock": { - "title": "Redirect to a plan selection page", + "title": "Check what billing plans a merchant is subscribed to", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", "language": "typescript" }, { @@ -2917,20 +2922,25 @@ } ] } - }, + } + ] + }, + { + "title": "request", + "examples": [ { - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", "codeblock": { - "title": "Requesting billing with line items", + "title": "Using a custom return URL", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", "language": "typescript" }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2939,16 +2949,16 @@ ] }, { - "title": "check", + "title": "require", "examples": [ { - "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "codeblock": { - "title": "Check what billing plans a merchant is subscribed to", + "title": "Requesting billing right away", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", "language": "typescript" }, { @@ -2958,20 +2968,15 @@ } ] } - } - ] - }, - { - "title": "request", - "examples": [ + }, { - "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", "codeblock": { - "title": "Using a custom return URL", + "title": "Redirect to a plan selection page", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", "language": "typescript" }, { @@ -2981,25 +2986,20 @@ } ] } - } - ] - }, - { - "title": "cancel", - "examples": [ + }, { - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", "codeblock": { - "title": "Cancelling a subscription", + "title": "Requesting billing with line items", "tabs": [ { - "title": "/app/routes/cancel-subscription.ts", - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", "language": "typescript" }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -3050,16 +3050,16 @@ { "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", + "name": "admin", + "value": "AdminApiContext", + "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Shopify session for the Flow request", - "description": "Use the session associated with this request to use REST resources.", + "title": "Flow admin context", + "description": "Use the `admin` object in the context to interact with the Admin API.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "title": "/app/routes/flow.tsx" } ] @@ -3088,16 +3088,16 @@ { "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Flow admin context", - "description": "Use the `admin` object in the context to interact with the Admin API.", + "title": "Shopify session for the Flow request", + "description": "Use the session associated with this request to use REST resources.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "title": "/app/routes/flow.tsx" } ] @@ -3107,674 +3107,674 @@ ], "value": "export interface FlowContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Shopify session for the Flow request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { session, admin } = await authenticate.flow(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n */\n session: Session;\n\n /**\n * The payload from the Flow request.\n *\n * @example\n * Flow payload.\n * Get the request's POST payload.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { payload } = await authenticate.flow(request);\n * return new Response();\n * };\n * ```\n */\n payload: any;\n\n /**\n * An admin context for the Flow request.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Flow admin context.\n * Use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.flow(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n}" }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", "members": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "scope", - "value": "string", - "description": "The desired scopes for the access token, at the time the session was created." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "expires", - "value": "Date", - "description": "The date the access token expires." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "accessToken", - "value": "string", - "description": "The access token for the session." + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isActive", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isScopeChanged", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the access token has the given scopes." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isExpired", - "value": "(withinMillisecondsOfExpiry?: number) => boolean", - "description": "Whether the access token is expired." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toObject", - "value": "() => SessionParams", - "description": "Converts an object with data into a Session." - }, + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + } + ], + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(other: Session) => boolean", - "description": "Checks whether the given session is equal to this session." + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toPropertyArray", - "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", - "description": "Converts the session into an array of key-value pairs." + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" } ], - "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" }, - "OnlineAccessInfo": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessInfo", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", "description": "", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user_scope", - "value": "string", - "description": "The effective set of scopes for the session." + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true } ], - "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" }, - "OnlineAccessUser": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessUser", - "description": "", + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "email", - "value": "string", - "description": "The user's email address." + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "email_verified", - "value": "boolean", - "description": "Whether the user has verified their email address." + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "locale", - "value": "string", - "description": "The user's locale." + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" } - ], - "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" + ] }, - "AuthScopes": { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "name": "AuthScopes", - "description": "A class that represents a set of access token scopes.", + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", "members": [ { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "has", - "value": "(scope: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes includes the given one." + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(otherScopes: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes equals the given one." + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "toString", - "value": "() => string", - "description": "Returns a comma-separated string with the current set of scopes." + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "toArray", - "value": "() => any[]", - "description": "Returns an array with the current set of scopes." + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." } ], - "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" }, - "SessionParams": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "SessionParams", - "description": "", + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", "members": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "[key: string]", - "value": "any" - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "id", "value": "string", "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "shop", "value": "string", - "description": "The Shopify shop domain." + "description": "The Shopify shop domain, such as `example.myshopify.com`." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "state", "value": "string", "description": "The state of the session. Used for the OAuth authentication code flow." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "isOnline", "value": "boolean", "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "scope", "value": "string", - "description": "The scopes for the access token.", - "isOptional": true + "description": "The desired scopes for the access token, at the time the session was created." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "expires", "value": "Date", - "description": "The date the access token expires.", - "isOptional": true + "description": "The date the access token expires." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "accessToken", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The access token for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true - } - ], - "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" - }, - "StoredOnlineAccessInfo": { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StoredOnlineAccessInfo", - "value": "Omit & {\n associated_user: Partial;\n}", - "description": "" - }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "value": "OnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions." }, { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - } - ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/session/session.ts", "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the access token has the given scopes." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/session/session.ts", "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "Whether the access token is expired." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/session/session.ts", "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "toObject", + "value": "() => SessionParams", + "description": "Converts an object with data into a Session." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/session/session.ts", "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." + "name": "equals", + "value": "(other: Session) => boolean", + "description": "Checks whether the given session is equal to this session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", + "description": "Converts the session into an array of key-value pairs." } ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", + "OnlineAccessInfo": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessInfo", "description": "", "members": [ { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "path", + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", "value": "string", - "description": "The path to the resource, relative to the API version root." + "description": "The effective set of scopes for the session." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", - "isOptional": true + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." + } + ], + "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" + }, + "OnlineAccessUser": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessUser", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "query", - "value": "SearchParams", - "description": "Query parameters to be sent with the request.", - "isOptional": true + "name": "email", + "value": "string", + "description": "The user's email address." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", - "isOptional": true + "name": "email_verified", + "value": "boolean", + "description": "Whether the user has verified their email address." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", + "name": "first_name", + "value": "string", + "description": "The user's first name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "id", "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", - "isOptional": true + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", + "value": "string", + "description": "The user's last name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "locale", + "value": "string", + "description": "The user's locale." } ], - "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" + "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" }, - "DataType": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "DataType", - "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", + "AuthScopes": { + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "name": "AuthScopes", + "description": "A class that represents a set of access token scopes.", "members": [ { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "JSON", - "value": "application/json" + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes includes the given one." }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GraphQL", - "value": "application/graphql" + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes equals the given one." }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "URLEncoded", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, - "PostRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "PostRequestParams", - "value": "GetRequestParams & {\n data: Record | string;\n}", - "description": "" - }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "Returns a comma-separated string with the current set of scopes." }, { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => any[]", + "description": "Returns an array with the current set of scopes." } ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "SessionParams": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "SessionParams", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true + "filePath": "../shopify-api/lib/session/types.ts", + "name": "[key: string]", + "value": "any" }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", + "name": "accessToken", + "value": "string", + "description": "The access token for the session.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The total number of times to try the request if it fails.", - "isOptional": true - } - ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" - }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" + "name": "id", + "value": "string", + "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain." }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." + } + ], + "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" + }, + "StoredOnlineAccessInfo": { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StoredOnlineAccessInfo", + "value": "Omit & {\n associated_user: Partial;\n}", + "description": "" + }, + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "string | Record", + "description": "The request body.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "Query parameters to be sent with the request.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" + }, + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, + "DataType": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" } ] + }, + "PostRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "value": "GetRequestParams & {\n data: Record | string;\n}", + "description": "" } } } @@ -3812,16 +3812,16 @@ "description": "", "exampleGroups": [ { - "title": "session", + "title": "admin", "examples": [ { - "description": "Use the session associated with this request to use REST resources.", + "description": "Use the `admin` object in the context to interact with the Admin API.", "codeblock": { - "title": "Shopify session for the Flow request", + "title": "Flow admin context", "tabs": [ { "title": "/app/routes/flow.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "language": "typescript" } ] @@ -3848,16 +3848,16 @@ ] }, { - "title": "admin", + "title": "session", "examples": [ { - "description": "Use the `admin` object in the context to interact with the Admin API.", + "description": "Use the session associated with this request to use REST resources.", "codeblock": { - "title": "Flow admin context", + "title": "Shopify session for the Flow request", "tabs": [ { "title": "/app/routes/flow.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "language": "typescript" } ] @@ -3905,25 +3905,6 @@ "name": "FulfillmentServiceContext", "description": "", "members": [ - { - "filePath": "src/server/authenticate/fulfillment-service/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "Shopify session for the fulfillment service notification request", - "description": "Use the session associated with this request to use REST resources.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", - "title": "/app/routes/fulfillment_service_notification.tsx" - } - ] - } - ] - }, { "filePath": "src/server/authenticate/fulfillment-service/types.ts", "syntaxKind": "PropertySignature", @@ -3961,43 +3942,323 @@ ] } ] + }, + { + "filePath": "src/server/authenticate/fulfillment-service/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", + "examples": [ + { + "title": "Shopify session for the fulfillment service notification request", + "description": "Use the session associated with this request to use REST resources.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "title": "/app/routes/fulfillment_service_notification.tsx" + } + ] + } + ] } ], "value": "export interface FulfillmentServiceContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n * @example\n * Shopify session for the fulfillment service notification request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/fulfillment_service_notification.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { session, admin } = await authenticate.fulfillmentService(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n * */\n session: Session;\n /**\n *\n * An admin context for the fulfillment service request.\n *\n * Returned only if there is a session for the shop.\n * @example\n * Shopify session for the fulfillment service request.\n * Use the session associated with this request to use the Admin GraphQL API \n * ```ts\n * // /app/routes/fulfillment_order_notification.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.fulfillmentService(request);\n * const response = await admin?.graphql(\n * `#graphql\n * query {\n * shop {\n * assignedFulfillmentOrders(first: 10, assignmentStatus: FULFILLMENT_REQUESTED) {\n * edges {\n * node {\n * id\n * destination {\n * firstName\n * lastName\n * }\n * lineItems(first: 10) {\n * edges {\n * node {\n * id\n * productTitle\n * sku\n * remainingQuantity\n * }\n * }\n * }\n * merchantRequests(first: 10, kind: FULFILLMENT_REQUEST) {\n * edges {\n * node {\n * message\n * }\n * }\n * }\n * }\n * }\n * }\n * }\n * }`);\n *\n * const fulfillments = await response.json();\n * return json({ data: fulfillments.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * The payload from the fulfillment service request.\n *\n * @example\n * Fulfillment service request payload.\n * Get the request's POST payload.\n * ```ts\n * /app/routes/fulfillment_order_notification.ts\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { payload } = await authenticate.fulfillmentService(request);\n * if(payload.kind === 'FULFILLMENT_REQUEST') {\n * // handle fulfillment request\n * } else if (payload.kind === 'CANCELLATION_REQUEST') {\n * // handle cancellation request\n * };\n * return new Response();\n * ```\n */\n payload: Record & {\n kind: string;\n };\n}" }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", "members": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." - }, + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + } + ], + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "The unique identifier for the session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain, such as `example.myshopify.com`." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." + }, { "filePath": "../shopify-api/lib/session/session.ts", "syntaxKind": "PropertyDeclaration", @@ -4079,11 +4340,11 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." - }, - { + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." + }, + { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", "name": "associated_user_scope", @@ -4093,9 +4354,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -4108,23 +4369,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -4143,23 +4397,30 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", "value": "string", - "description": "The user's locale." + "description": "The user's last name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "locale", + "value": "string", + "description": "The user's locale." } ], "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" @@ -4213,23 +4474,25 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "id", + "name": "accessToken", "value": "string", - "description": "The unique identifier for the session." + "description": "The access token for the session.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "state", + "name": "id", "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "description": "The unique identifier for the session." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -4241,34 +4504,32 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "accessToken", + "name": "shop", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The Shopify shop domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" @@ -4280,157 +4541,27 @@ "value": "Omit & {\n associated_user: Partial;\n}", "description": "" }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "data", + "value": "string | Record", + "description": "The request body.", + "isOptional": true }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - } - ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", + "isOptional": true }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." - } - ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" - }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", - "description": "", - "members": [ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", @@ -4441,46 +4572,38 @@ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "query", + "value": "SearchParams", + "description": "Query parameters to be sent with the request.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "query", - "value": "SearchParams", - "description": "Query parameters to be sent with the request.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "tries", + "value": "number", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, "DataType": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", @@ -4504,135 +4627,12 @@ } ] }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, "PostRequestParams": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "PostRequestParams", "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" - }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", - "description": "", - "members": [ - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The total number of times to try the request if it fails.", - "isOptional": true - } - ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" - }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" - } - ] } } } @@ -4670,16 +4670,16 @@ "description": "", "exampleGroups": [ { - "title": "session", + "title": "admin", "examples": [ { - "description": "Use the session associated with this request to use REST resources.", + "description": "Use the session associated with this request to use the Admin GraphQL API", "codeblock": { - "title": "Shopify session for the fulfillment service notification request", + "title": "Shopify session for the fulfillment service request", "tabs": [ { - "title": "/app/routes/fulfillment_service_notification.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "title": "/app/routes/fulfillment_order_notification.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.fulfillmentService(request);\n const response = await admin?.graphql(\n `#graphql\n query {\n shop {\n assignedFulfillmentOrders(first: 10, assignmentStatus: FULFILLMENT_REQUESTED) {\n edges {\n node {\n id\n destination {\n firstName\n lastName\n }\n lineItems(first: 10) {\n edges {\n node {\n id\n productTitle\n sku\n remainingQuantity\n }\n }\n }\n merchantRequests(first: 10, kind: FULFILLMENT_REQUEST) {\n edges {\n node {\n message\n }\n }\n }\n }\n }\n }\n }\n}`);\n\n const fulfillments = await response.json();\n return json({ data: fulfillments.data });\n}", "language": "typescript" } ] @@ -4688,16 +4688,16 @@ ] }, { - "title": "admin", + "title": "payload", "examples": [ { - "description": "Use the session associated with this request to use the Admin GraphQL API", + "description": "Get the request's POST payload.", "codeblock": { - "title": "Shopify session for the fulfillment service request", + "title": "Fulfillment service request payload", "tabs": [ { - "title": "/app/routes/fulfillment_order_notification.ts", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.fulfillmentService(request);\n const response = await admin?.graphql(\n `#graphql\n query {\n shop {\n assignedFulfillmentOrders(first: 10, assignmentStatus: FULFILLMENT_REQUESTED) {\n edges {\n node {\n id\n destination {\n firstName\n lastName\n }\n lineItems(first: 10) {\n edges {\n node {\n id\n productTitle\n sku\n remainingQuantity\n }\n }\n }\n merchantRequests(first: 10, kind: FULFILLMENT_REQUEST) {\n edges {\n node {\n message\n }\n }\n }\n }\n }\n }\n }\n}`);\n\n const fulfillments = await response.json();\n return json({ data: fulfillments.data });\n}", + "title": "Example", + "code": "/app/routes/fulfillment_order_notification.ts\nimport { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.fulfillmentService(request);\n if(payload.kind === 'FULFILLMENT_REQUEST') {\n // handle fulfillment request\n } else if (payload.kind === 'CANCELLATION_REQUEST') {\n // handle cancellation request\n };\nreturn new Response();", "language": "typescript" } ] @@ -4706,16 +4706,16 @@ ] }, { - "title": "payload", + "title": "session", "examples": [ { - "description": "Get the request's POST payload.", + "description": "Use the session associated with this request to use REST resources.", "codeblock": { - "title": "Fulfillment service request payload", + "title": "Shopify session for the fulfillment service notification request", "tabs": [ { - "title": "Example", - "code": "/app/routes/fulfillment_order_notification.ts\nimport { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.fulfillmentService(request);\n if(payload.kind === 'FULFILLMENT_REQUEST') {\n // handle fulfillment request\n } else if (payload.kind === 'CANCELLATION_REQUEST') {\n // handle cancellation request\n };\nreturn new Response();", + "title": "/app/routes/fulfillment_service_notification.tsx", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "language": "typescript" } ] @@ -4764,13 +4764,6 @@ "name": "AppProxyContext", "description": "", "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -4778,13 +4771,6 @@ "value": "undefined", "description": "No session is available for the shop that made this request. Therefore no methods for interacting with the GraphQL / REST Admin APIs are available." }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "undefined", - "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -4823,6 +4809,20 @@ ] } ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "undefined", + "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "undefined", + "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." } ], "value": "export interface AppProxyContext extends Context {\n /**\n * No session is available for the shop that made this request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n */\n session: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.\n */\n admin: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no method for interacting with the Storefront API is available.\n */\n storefront: undefined;\n}" @@ -4875,25 +4875,6 @@ "name": "AppProxyContextWithSession", "description": "", "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", - "examples": [ - { - "title": "Using the session object", - "description": "Get the session for the shop that initiated the request to the app proxy.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -4913,25 +4894,6 @@ } ] }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "StorefrontContext", - "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", - "examples": [ - { - "title": "Interacting with the Storefront API", - "description": "Use the `storefront` object to interact with the GraphQL API.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -4970,77 +4932,376 @@ ] } ] - } - ], + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "examples": [ + { + "title": "Using the session object", + "description": "Get the session for the shop that initiated the request to the app proxy.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", + "title": "app/routes/**\\/.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "StorefrontContext", + "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", + "examples": [ + { + "title": "Interacting with the Storefront API", + "description": "Use the `storefront` object to interact with the GraphQL API.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", + "title": "app/routes/**\\/.ts" + } + ] + } + ] + } + ], "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } =\n * await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(\n * await getMyAppModelData({shop: session.shop})\n * );\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(\n * `#graphql\n * query blogIds {\n * blogs(first: 10) {\n * edges {\n * node {\n * id\n * }\n * }\n * }\n * }`\n * );\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", "members": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + } + ], + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "scope", - "value": "string", - "description": "The desired scopes for the access token, at the time the session was created." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "expires", - "value": "Date", - "description": "The date the access token expires." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "accessToken", - "value": "string", - "description": "The access token for the session." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions." + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isActive", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "The unique identifier for the session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain, such as `example.myshopify.com`." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "The desired scopes for the access token, at the time the session was created." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "The date the access token expires." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "The access token for the session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." }, { "filePath": "../shopify-api/lib/session/session.ts", @@ -5088,9 +5349,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -5102,9 +5363,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -5117,23 +5378,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -5152,23 +5406,30 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", "value": "string", - "description": "The user's locale." + "description": "The user's last name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "locale", + "value": "string", + "description": "The user's locale." } ], "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" @@ -5222,23 +5483,25 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "id", + "name": "accessToken", "value": "string", - "description": "The unique identifier for the session." + "description": "The access token for the session.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "state", + "name": "id", "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "description": "The unique identifier for the session." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -5250,34 +5513,32 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "accessToken", + "name": "shop", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The Shopify shop domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" @@ -5289,157 +5550,27 @@ "value": "Omit & {\n associated_user: Partial;\n}", "description": "" }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "data", + "value": "string | Record", + "description": "The request body.", + "isOptional": true }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - } - ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." - }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", + "isOptional": true }, - { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." - } - ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" - }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", - "description": "", - "members": [ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", @@ -5447,22 +5578,6 @@ "value": "string", "description": "The path to the resource, relative to the API version root." }, - { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true - }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", @@ -5474,22 +5589,30 @@ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "tries", + "value": "number", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, "DataType": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", @@ -5513,14 +5636,6 @@ } ] }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, "PostRequestParams": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "TypeAliasDeclaration", @@ -5528,128 +5643,13 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "StorefrontContext": { + "filePath": "src/server/clients/storefront/types.ts", + "name": "StorefrontContext", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The total number of times to try the request if it fails.", - "isOptional": true - } - ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" - }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" - } - ] - }, - "StorefrontContext": { - "filePath": "src/server/clients/storefront/types.ts", - "name": "StorefrontContext", - "description": "", - "members": [ - { - "filePath": "src/server/clients/storefront/types.ts", + "filePath": "src/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", "value": "GraphQLClient", @@ -5724,24 +5724,6 @@ "examples": { "description": "", "exampleGroups": [ - { - "title": "session", - "examples": [ - { - "description": "Get the session for the shop that initiated the request to the app proxy.", - "codeblock": { - "title": "Using the session object", - "tabs": [ - { - "title": "app/routes/**\\/.ts", - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", - "language": "typescript" - } - ] - } - } - ] - }, { "title": "admin", "examples": [ @@ -5760,24 +5742,6 @@ } ] }, - { - "title": "storefront", - "examples": [ - { - "description": "Use the `storefront` object to interact with the GraphQL API.", - "codeblock": { - "title": "Interacting with the Storefront API", - "tabs": [ - { - "title": "app/routes/**\\/.ts", - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", - "language": "typescript" - } - ] - } - } - ] - }, { "title": "liquid", "examples": [ @@ -5821,6 +5785,42 @@ } } ] + }, + { + "title": "session", + "examples": [ + { + "description": "Get the session for the shop that initiated the request to the app proxy.", + "codeblock": { + "title": "Using the session object", + "tabs": [ + { + "title": "app/routes/**\\/.ts", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "storefront", + "examples": [ + { + "description": "Use the `storefront` object to interact with the GraphQL API.", + "codeblock": { + "title": "Interacting with the Storefront API", + "tabs": [ + { + "title": "app/routes/**\\/.ts", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", + "language": "typescript" + } + ] + } + } + ] } ] } @@ -5889,16 +5889,16 @@ { "filePath": "src/server/authenticate/public/checkout/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Using the decoded session token", - "description": "Get store-specific data using the `sessionToken` object.", + "title": "Setting CORS headers for a public request", + "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", "title": "app/routes/public/my-route.ts" } ] @@ -5908,16 +5908,16 @@ { "filePath": "src/server/authenticate/public/checkout/types.ts", "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", "examples": [ { - "title": "Setting CORS headers for a public request", - "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", + "title": "Using the decoded session token", + "description": "Get store-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", "title": "app/routes/public/my-route.ts" } ] @@ -5927,6 +5927,13 @@ ], "value": "export interface CheckoutContext {\n /**\n * The decoded and validated session token for the request\n *\n * Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).\n *\n * @example\n * Using the decoded session token.\n * Get store-specific data using the `sessionToken` object.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({shop: sessionToken.dest}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that ensures the CORS headers are set correctly for the response.\n *\n * @example\n * Setting CORS headers for a public request.\n * Use the `cors` helper to ensure your app can respond to checkout extension requests.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * { corsHeaders: [\"X-My-Custom-Header\"] }\n * );\n * const data = await getMyAppData({shop: sessionToken.dest});\n * return cors(json(data));\n * };\n * ```\n */\n cors: EnsureCORSFunction;\n}" }, + "EnsureCORSFunction": { + "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", + "name": "EnsureCORSFunction", + "description": "", + "members": [], + "value": "export interface EnsureCORSFunction {\n (response: Response): Response;\n}" + }, "JwtPayload": { "filePath": "../shopify-api/lib/session/types.ts", "name": "JwtPayload", @@ -5935,9 +5942,9 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "iss", + "name": "aud", "value": "string", - "description": "The shop's admin domain." + "description": "The client ID of the receiving app." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -5949,61 +5956,54 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "aud", - "value": "string", - "description": "The client ID of the receiving app." + "name": "exp", + "value": "number", + "description": "When the session token expires." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sub", - "value": "string", - "description": "The User that the session token is intended for." + "name": "iat", + "value": "number", + "description": "When the session token was issued." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "exp", - "value": "number", - "description": "When the session token expires." + "name": "iss", + "value": "string", + "description": "The shop's admin domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "nbf", - "value": "number", - "description": "When the session token activates." + "name": "jti", + "value": "string", + "description": "A secure random UUID." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "iat", + "name": "nbf", "value": "number", - "description": "When the session token was issued." + "description": "When the session token activates." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "jti", + "name": "sid", "value": "string", - "description": "A secure random UUID." + "description": "A unique session ID per user and app." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sid", + "name": "sub", "value": "string", - "description": "A unique session ID per user and app." + "description": "The User that the session token is intended for." } ], "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" - }, - "EnsureCORSFunction": { - "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", - "name": "EnsureCORSFunction", - "description": "", - "members": [], - "value": "export interface EnsureCORSFunction {\n (response: Response): Response;\n}" } } } @@ -6034,16 +6034,16 @@ "description": "", "exampleGroups": [ { - "title": "sessionToken", + "title": "cors", "examples": [ { - "description": "Get store-specific data using the `sessionToken` object.", + "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", "codeblock": { - "title": "Using the decoded session token", + "title": "Setting CORS headers for a public request", "tabs": [ { "title": "app/routes/public/my-route.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", "language": "typescript" } ] @@ -6052,16 +6052,16 @@ ] }, { - "title": "cors", + "title": "sessionToken", "examples": [ { - "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", + "description": "Get store-specific data using the `sessionToken` object.", "codeblock": { - "title": "Setting CORS headers for a public request", + "title": "Using the decoded session token", "tabs": [ { "title": "app/routes/public/my-route.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", "language": "typescript" } ] @@ -6116,13 +6116,6 @@ "name": "WebhookContextWithoutSession", "description": "", "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "" - }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -6152,16 +6145,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The shop where the webhook was triggered.", + "name": "payload", + "value": "Record", + "description": "The payload from the webhook request.", "examples": [ { - "title": "Webhook shop", - "description": "Get the shop that triggered a webhook.", + "title": "Webhook payload", + "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -6171,16 +6164,23 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "topic", - "value": "Topics", - "description": "The topic of the webhook.", + "name": "session", + "value": "undefined", + "description": "" + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "examples": [ { - "title": "Webhook topic", - "description": "Get the event topic for the webhook.", + "title": "Webhook shop", + "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -6190,16 +6190,17 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "webhookId", + "name": "subTopic", "value": "string", - "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", + "description": "The sub-topic of the webhook. This is only available for certain webhooks.", + "isOptional": true, "examples": [ { - "title": "Webhook ID", - "description": "Get the webhook ID.", + "title": "Webhook sub-topic", + "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -6209,16 +6210,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "payload", - "value": "Record", - "description": "The payload from the webhook request.", + "name": "topic", + "value": "Topics", + "description": "The topic of the webhook.", "examples": [ { - "title": "Webhook payload", - "description": "Get the request's POST payload.", + "title": "Webhook topic", + "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -6228,17 +6229,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "subTopic", + "name": "webhookId", "value": "string", - "description": "The sub-topic of the webhook. This is only available for certain webhooks.", - "isOptional": true, + "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", "examples": [ { - "title": "Webhook sub-topic", - "description": "Get the webhook sub-topic.", + "title": "Webhook ID", + "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -6253,13 +6253,6 @@ "name": "WebhookContextWithSession", "description": "", "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop." - }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -6308,6 +6301,32 @@ } ] }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "payload", + "value": "Record", + "description": "The payload from the webhook request.", + "examples": [ + { + "title": "Webhook payload", + "description": "Get the request's POST payload.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop." + }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -6327,6 +6346,26 @@ } ] }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "subTopic", + "value": "string", + "description": "The sub-topic of the webhook. This is only available for certain webhooks.", + "isOptional": true, + "examples": [ + { + "title": "Webhook sub-topic", + "description": "Get the webhook sub-topic.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" + } + ] + } + ] + }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -6364,810 +6403,771 @@ ] } ] + } + ], + "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n */\n session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * variables: { input: { title: \"Product Name\" } },\n * },\n * });\n *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" + }, + "WebhookAdminContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookAdminContext", + "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", + "description": "" + }, + "AdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminContext", + "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", + "description": "" + }, + "NonEmbeddedAdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "NonEmbeddedAdminContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." }, { - "filePath": "src/server/authenticate/webhooks/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "payload", - "value": "Record", - "description": "The payload from the webhook request.", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Webhook payload", - "description": "Get the request's POST payload.", + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" } ] } ] }, { - "filePath": "src/server/authenticate/webhooks/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "subTopic", - "value": "string", - "description": "The sub-topic of the webhook. This is only available for certain webhooks.", - "isOptional": true, + "name": "session", + "value": "Session", + "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", "examples": [ { - "title": "Webhook sub-topic", - "description": "Get the webhook sub-topic.", + "title": "Using offline sessions", + "description": "Get your app's shop-specific data using an offline session.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Using online sessions", + "description": "Get your app's user-specific data using an online session.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } ] } ], - "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n */\n session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * variables: { input: { title: \"Product Name\" } },\n * },\n * });\n *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" + "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", "members": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "scope", - "value": "string", - "description": "The desired scopes for the access token, at the time the session was created." + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "expires", - "value": "Date", - "description": "The date the access token expires." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "accessToken", - "value": "string", - "description": "The access token for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isActive", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isScopeChanged", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the access token has the given scopes." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isExpired", - "value": "(withinMillisecondsOfExpiry?: number) => boolean", - "description": "Whether the access token is expired." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toObject", - "value": "() => SessionParams", - "description": "Converts an object with data into a Session." - }, + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + } + ], + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(other: Session) => boolean", - "description": "Checks whether the given session is equal to this session." + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toPropertyArray", - "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", - "description": "Converts the session into an array of key-value pairs." + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" } ], - "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" }, - "OnlineAccessInfo": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessInfo", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", "description": "", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user_scope", - "value": "string", - "description": "The effective set of scopes for the session." + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true } ], - "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" }, - "OnlineAccessUser": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessUser", - "description": "", + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "email", - "value": "string", - "description": "The user's email address." + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "email_verified", - "value": "boolean", - "description": "Whether the user has verified their email address." + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "locale", - "value": "string", - "description": "The user's locale." + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" } - ], - "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" + ] }, - "AuthScopes": { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "name": "AuthScopes", - "description": "A class that represents a set of access token scopes.", - "members": [ - { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "has", - "value": "(scope: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes includes the given one." + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(otherScopes: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes equals the given one." + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "toString", - "value": "() => string", - "description": "Returns a comma-separated string with the current set of scopes." + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", - "name": "toArray", - "value": "() => any[]", - "description": "Returns an array with the current set of scopes." + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." } ], - "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" }, - "SessionParams": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "SessionParams", - "description": "", + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", "members": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "[key: string]", - "value": "any" - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "id", "value": "string", "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "shop", "value": "string", - "description": "The Shopify shop domain." + "description": "The Shopify shop domain, such as `example.myshopify.com`." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "state", "value": "string", "description": "The state of the session. Used for the OAuth authentication code flow." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "isOnline", "value": "boolean", "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "scope", "value": "string", - "description": "The scopes for the access token.", - "isOptional": true + "description": "The desired scopes for the access token, at the time the session was created." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "expires", "value": "Date", - "description": "The date the access token expires.", - "isOptional": true + "description": "The date the access token expires." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "accessToken", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The access token for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "value": "OnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the access token has the given scopes." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "Whether the access token is expired." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "Converts an object with data into a Session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "Checks whether the given session is equal to this session." + }, + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", + "description": "Converts the session into an array of key-value pairs." } ], - "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" - }, - "StoredOnlineAccessInfo": { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StoredOnlineAccessInfo", - "value": "Omit & {\n associated_user: Partial;\n}", - "description": "" - }, - "WebhookAdminContext": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookAdminContext", - "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", - "description": "" - }, - "AdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminContext", - "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", - "description": "" + "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" }, - "NonEmbeddedAdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "NonEmbeddedAdminContext", + "OnlineAccessInfo": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessInfo", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", - "examples": [ - { - "title": "Using offline sessions", - "description": "Get your app's shop-specific data using an offline session.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Using online sessions", - "description": "Get your app's user-specific data using an online session.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - } - ] + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + "name": "associated_user_scope", + "value": "string", + "description": "The effective set of scopes for the session." }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." + } + ], + "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" + }, + "OnlineAccessUser": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessUser", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", - "examples": [ - { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" - } - ] - } - ] - } - ], - "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" - }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." + }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "email", + "value": "string", + "description": "The user's email address." }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "email_verified", + "value": "boolean", + "description": "Whether the user has verified their email address." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "first_name", + "value": "string", + "description": "The user's first name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", + "value": "string", + "description": "The user's last name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "locale", + "value": "string", + "description": "The user's locale." } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" + "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", + "AuthScopes": { + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "name": "AuthScopes", + "description": "A class that represents a set of access token scopes.", "members": [ { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes includes the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes equals the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "toString", + "value": "() => string", + "description": "Returns a comma-separated string with the current set of scopes." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." + "name": "toArray", + "value": "() => any[]", + "description": "Returns an array with the current set of scopes." } ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", + "SessionParams": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "SessionParams", "description": "", "members": [ { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", + "name": "[key: string]", + "value": "any" + }, + { + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "path", + "name": "accessToken", "value": "string", - "description": "The path to the resource, relative to the API version root." + "description": "The access token for the session.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true + "name": "id", + "value": "string", + "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "query", - "value": "SearchParams", - "description": "Query parameters to be sent with the request.", - "isOptional": true + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true - } - ], - "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" - }, - "DataType": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "DataType", - "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "JSON", - "value": "application/json" }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GraphQL", - "value": "application/graphql" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain." }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "URLEncoded", - "value": "application/x-www-form-urlencoded" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } - ] - }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] + ], + "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" }, - "PostRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", + "StoredOnlineAccessInfo": { + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "PostRequestParams", - "value": "GetRequestParams & {\n data: Record | string;\n}", + "name": "StoredOnlineAccessInfo", + "value": "Omit & {\n associated_user: Partial;\n}", "description": "" }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", + "name": "data", + "value": "string | Record", + "description": "The request body.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "Query parameters to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", "name": "tries", "value": "number", - "description": "The total number of times to try the request if it fails.", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, + "DataType": { + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "name": "DataType", + "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", "members": [ { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "JSON", + "value": "application/json" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GraphQL", + "value": "application/graphql" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" } ] }, + "PostRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "value": "GetRequestParams & {\n data: Record | string;\n}", + "description": "" + }, "BillingContext": { "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "BillingContext", @@ -7176,51 +7176,23 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "require", - "value": "(options: RequireBillingOptions) => Promise", - "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", + "description": "Cancels an ongoing subscription, given its ID.", "examples": [ { - "title": "Requesting billing right away", - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Redirect to a plan selection page", - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "title": "Cancelling a subscription", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", "tabs": [ { - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] - }, - { - "title": "Requesting billing with line items", - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] } ] }, @@ -7273,123 +7245,87 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", + "name": "require", + "value": "(options: RequireBillingOptions) => Promise", + "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", "examples": [ { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Requesting billing right away", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", - "title": "/app/routes/cancel-subscription.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] + }, + { + "title": "Redirect to a plan selection page", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Requesting billing with line items", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] } ] } ], "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, - "RequireBillingOptions": { + "CancelBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", + "name": "CancelBillingOptions", "description": "", "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "onFailure", - "value": "(error: any) => Promise", - "description": "How to handle the request if the shop doesn't have an active payment for any plan." - }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", - "description": "Whether to consider test purchases.", + "description": "", "isOptional": true - } - ], - "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" - }, - "BillingCheckResponseObject": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "BillingCheckResponseObject", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "hasActivePayment", - "value": "boolean", - "description": "Whether the user has an active payment method." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "oneTimePurchases", - "value": "OneTimePurchase[]", - "description": "The one-time purchases the shop has." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "appSubscriptions", - "value": "AppSubscription[]", - "description": "The active subscriptions the shop has." - } - ], - "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" - }, - "OneTimePurchase": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "OneTimePurchase", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the one-time purchase." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." }, { - "filePath": "../shopify-api/lib/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "test", + "name": "prorate", "value": "boolean", - "description": "Whether this is a test purchase." + "description": "Whether to prorate the cancellation.\n\n\n\n\n", + "isOptional": true }, { - "filePath": "../shopify-api/lib/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "status", + "name": "subscriptionId", "value": "string", - "description": "The status of the one-time purchase." + "description": "The ID of the subscription to cancel." } ], - "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" }, "AppSubscription": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -7403,6 +7339,14 @@ "value": "string", "description": "The ID of the app subscription." }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "lineItems", + "value": "ActiveSubscriptionLineItem[]", + "description": "", + "isOptional": true + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", @@ -7416,14 +7360,6 @@ "name": "test", "value": "boolean", "description": "Whether this is a test subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "lineItems", - "value": "ActiveSubscriptionLineItem[]", - "description": "", - "isOptional": true } ], "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" @@ -7473,112 +7409,84 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "name": "discount", + "value": "AppPlanDiscount", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "price", - "value": "Money", + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "AppPlanDiscount", + "name": "price", + "value": "Money", "description": "" } ], "value": "export interface RecurringAppPlan {\n /*\n * The interval for this plan is charged on.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /*\n * The price of the plan.\n */\n price: Money;\n /*\n * The discount applied to the plan.\n */\n discount: AppPlanDiscount;\n}" }, - "BillingInterval": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingInterval", - "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "OneTime", - "value": "ONE_TIME" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Every30Days", - "value": "EVERY_30_DAYS" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Annual", - "value": "ANNUAL" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Usage", - "value": "USAGE" - } - ] - }, - "Money": { + "AppPlanDiscount": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "Money", + "name": "AppPlanDiscount", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", + "name": "durationLimitInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", + "name": "priceAfterDiscount", + "value": "Money", "description": "" - } - ], - "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" - }, - "AppPlanDiscount": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppPlanDiscount", - "description": "", - "members": [ + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "durationLimitInIntervals", + "name": "remainingDurationInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "remainingDurationInIntervals", - "value": "number", + "name": "value", + "value": "AppPlanDiscountAmount", "description": "" - }, + } + ], + "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + }, + "Money": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "Money", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "priceAfterDiscount", - "value": "Money", + "name": "amount", + "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "value", - "value": "AppPlanDiscountAmount", + "name": "currencyCode", + "value": "string", "description": "" } ], - "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" }, "AppPlanDiscountAmount": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -7633,6 +7541,34 @@ ], "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" }, + "BillingInterval": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingInterval", + "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "OneTime", + "value": "ONE_TIME" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Annual", + "value": "ANNUAL" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, "UsageAppPlan": { "filePath": "../shopify-api/lib/billing/types.ts", "name": "UsageAppPlan", @@ -7667,13 +7603,6 @@ "name": "CheckBillingOptions", "description": "", "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -7681,22 +7610,87 @@ "value": "boolean", "description": "Whether to consider test purchases.", "isOptional": true - } - ], - "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" - }, - "RequestBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + } + ], + "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + }, + "BillingCheckResponseObject": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "BillingCheckResponseObject", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plan", - "value": "keyof Config[\"billing\"]", - "description": "The plan to request. Must be one of the values defined in the `billing` config option." + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "The active subscriptions the shop has." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "Whether the user has an active payment method." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "The one-time purchases the shop has." + } + ], + "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "The ID of the one-time purchase." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "The name of the purchased plan." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "The status of the one-time purchase." }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "Whether this is a test purchase." + } + ], + "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -7705,6 +7699,13 @@ "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", "isOptional": true }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plan", + "value": "keyof Config[\"billing\"]", + "description": "The plan to request. Must be one of the values defined in the `billing` config option." + }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -7716,36 +7717,35 @@ ], "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" }, - "CancelBillingOptions": { + "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "name": "RequireBillingOptions", "description": "", "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", - "value": "string", - "description": "The ID of the subscription to cancel." + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", - "isOptional": true + "name": "onFailure", + "value": "(error: any) => Promise", + "description": "How to handle the request if the shop doesn't have an active payment for any plan." }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." } ], - "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" + "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" }, "EnsureCORSFunction": { "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", @@ -7762,21 +7762,31 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Using the decoded session token", - "description": "Get user-specific data using the `sessionToken` object.", + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" } ] } @@ -7851,31 +7861,21 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Using the decoded session token", + "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -7884,25 +7884,52 @@ ], "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, - "JwtPayload": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "JwtPayload", + "RedirectFunction": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "name": "RedirectFunction", "description": "", - "members": [ + "params": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "iss", + "name": "url", + "description": "", "value": "string", - "description": "The shop's admin domain." + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "dest", - "value": "string", - "description": "The shop's domain." - }, + "name": "init", + "description": "", + "value": "RedirectInit", + "isOptional": true, + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "description": "", + "name": "TypedResponse", + "value": "TypedResponse" + }, + "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" + }, + "RedirectInit": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectInit", + "value": "number | (ResponseInit & {target?: RedirectTarget})", + "description": "" + }, + "RedirectTarget": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectTarget", + "value": "'_self' | '_parent' | '_top'", + "description": "" + }, + "JwtPayload": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "JwtPayload", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", @@ -7913,9 +7940,9 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sub", + "name": "dest", "value": "string", - "description": "The User that the session token is intended for." + "description": "The shop's domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -7927,16 +7954,16 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "nbf", + "name": "iat", "value": "number", - "description": "When the session token activates." + "description": "When the session token was issued." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "iat", - "value": "number", - "description": "When the session token was issued." + "name": "iss", + "value": "string", + "description": "The shop's admin domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -7945,56 +7972,29 @@ "value": "string", "description": "A secure random UUID." }, + { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "When the session token activates." + }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", "name": "sid", "value": "string", "description": "A unique session ID per user and app." - } - ], - "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" - }, - "RedirectFunction": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", - "description": "", - "params": [ - { - "name": "url", - "description": "", - "value": "string", - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" }, { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "The User that the session token is intended for." } ], - "returns": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "description": "", - "name": "TypedResponse", - "value": "TypedResponse" - }, - "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" - }, - "RedirectInit": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", - "description": "" + "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" }, "LegacyWebhookAdminApiContext": { "filePath": "src/server/authenticate/webhooks/types.ts", @@ -8004,16 +8004,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClient & Resources", - "description": "A REST client." + "name": "graphql", + "value": "InstanceType", + "description": "A GraphQL client." }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "InstanceType", - "description": "A GraphQL client." + "name": "rest", + "value": "RestClient & Resources", + "description": "A REST client." } ], "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" @@ -8125,13 +8125,6 @@ "name": "PageInfo", "description": "", "members": [ - { - "filePath": "../shopify-api/lib/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "limit", - "value": "string", - "description": "" - }, { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", @@ -8143,8 +8136,15 @@ { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "previousPageUrl", + "name": "limit", "value": "string", + "description": "" + }, + { + "filePath": "../shopify-api/lib/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "nextPage", + "value": "PageInfoParams", "description": "", "isOptional": true }, @@ -8159,15 +8159,15 @@ { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "prevPage", - "value": "PageInfoParams", + "name": "previousPageUrl", + "value": "string", "description": "", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "nextPage", + "name": "prevPage", "value": "PageInfoParams", "description": "", "isOptional": true @@ -8276,16 +8276,16 @@ ] }, { - "title": "shop", + "title": "payload", "examples": [ { - "description": "Get the shop that triggered a webhook.", + "description": "Get the request's POST payload.", "codeblock": { - "title": "Webhook shop", + "title": "Webhook payload", "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -8294,16 +8294,16 @@ ] }, { - "title": "topic", + "title": "shop", "examples": [ { - "description": "Get the event topic for the webhook.", + "description": "Get the shop that triggered a webhook.", "codeblock": { - "title": "Webhook topic", + "title": "Webhook shop", "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -8312,16 +8312,16 @@ ] }, { - "title": "webhookId", + "title": "subTopic", "examples": [ { - "description": "Get the webhook ID.", + "description": "Get the webhook sub-topic.", "codeblock": { - "title": "Webhook ID", + "title": "Webhook sub-topic", "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -8330,16 +8330,16 @@ ] }, { - "title": "payload", + "title": "topic", "examples": [ { - "description": "Get the request's POST payload.", + "description": "Get the event topic for the webhook.", "codeblock": { - "title": "Webhook payload", + "title": "Webhook topic", "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "language": "typescript" } ] @@ -8348,16 +8348,16 @@ ] }, { - "title": "subTopic", + "title": "webhookId", "examples": [ { - "description": "Get the webhook sub-topic.", + "description": "Get the webhook ID.", "codeblock": { - "title": "Webhook sub-topic", + "title": "Webhook ID", "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -8388,85 +8388,85 @@ { "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] }, { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] - }, + } + ] + }, + { + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] - } - ] - }, - { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ + }, { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] }, { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -8476,41 +8476,156 @@ ], "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "MethodDeclaration", "name": "put", "value": "(params: PostRequestParams) => Promise", @@ -8640,9 +8755,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -8654,9 +8769,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -8669,23 +8784,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -8704,23 +8812,30 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", "value": "string", - "description": "The user's locale." + "description": "The user's last name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "locale", + "value": "string", + "description": "The user's locale." } ], "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" @@ -8774,23 +8889,25 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "id", + "name": "accessToken", "value": "string", - "description": "The unique identifier for the session." + "description": "The access token for the session.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "state", + "name": "id", "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "description": "The unique identifier for the session." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -8802,34 +8919,32 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "accessToken", + "name": "shop", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The Shopify shop domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" @@ -8849,25 +8964,25 @@ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "path", - "value": "string", - "description": "The path to the resource, relative to the API version root." + "name": "data", + "value": "string | Record", + "description": "The request body.", + "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." }, { "filePath": "../shopify-api/lib/clients/types.ts", @@ -8880,22 +8995,30 @@ { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "tries", + "value": "number", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, "DataType": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", @@ -8919,135 +9042,12 @@ } ] }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, "PostRequestParams": { "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "PostRequestParams", "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" - }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", - "description": "", - "members": [ - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", - "isOptional": true - }, - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The total number of times to try the request if it fails.", - "isOptional": true - } - ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" - }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" - } - ] } } } @@ -9071,98 +9071,98 @@ "description": "", "exampleGroups": [ { - "title": "rest", + "title": "graphql", "examples": [ { - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "description": "Use `admin.graphql` to make query / mutation requests.", "codeblock": { - "title": "Using REST resources", + "title": "Querying the GraphQL API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } }, { - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", "codeblock": { - "title": "Performing a GET request to the REST API", + "title": "Handling GraphQL errors", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } - }, + } + ] + }, + { + "title": "rest", + "examples": [ { - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "codeblock": { - "title": "Performing a POST request to the REST API", + "title": "Using REST resources", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } - } - ] - }, - { - "title": "graphql", - "examples": [ + }, { - "description": "Use `admin.graphql` to make query / mutation requests.", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "codeblock": { - "title": "Querying the GraphQL API", + "title": "Performing a GET request to the REST API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] } }, { - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "codeblock": { - "title": "Handling GraphQL errors", + "title": "Performing a POST request to the REST API", "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -9258,14 +9258,6 @@ "name": "GraphQLQueryOptions", "description": "", "members": [ - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true - }, { "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", @@ -9289,6 +9281,14 @@ "value": "number", "description": "The total number of times to try the request if it fails.", "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true } ], "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" @@ -9485,17 +9485,21 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionStorage", - "value": "SessionStorageType", - "description": "The `SessionStorage` instance you passed in as a config option.", + "name": "addDocumentResponseHeaders", + "value": "AddDocumentResponseHeaders", + "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", "examples": [ { - "title": "Storing sessions with Prisma", - "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", + "title": "Return headers on all requests", + "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", - "title": "/app/shopify.server.ts" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", + "title": "~/shopify.server.ts" + }, + { + "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", + "title": "entry.server.tsx" } ] } @@ -9504,21 +9508,21 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "addDocumentResponseHeaders", - "value": "AddDocumentResponseHeaders", - "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", + "name": "authenticate", + "value": "Authenticate", + "description": "Ways to authenticate requests from different surfaces across Shopify.", "examples": [ { - "title": "Return headers on all requests", - "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", + "title": "Authenticate Shopify requests", + "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", - "title": "~/shopify.server.ts" + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" }, { - "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", - "title": "entry.server.tsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" } ] } @@ -9546,21 +9550,17 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "authenticate", - "value": "Authenticate", - "description": "Ways to authenticate requests from different surfaces across Shopify.", + "name": "sessionStorage", + "value": "SessionStorageType", + "description": "The `SessionStorage` instance you passed in as a config option.", "examples": [ { - "title": "Authenticate Shopify requests", - "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "title": "Storing sessions with Prisma", + "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", "title": "/app/shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" } ] } @@ -9591,13 +9591,6 @@ } ] }, - "SessionStorageType": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "SessionStorageType", - "value": "Config['sessionStorage'] extends SessionStorage\n ? Config['sessionStorage']\n : SessionStorage", - "description": "" - }, "AddDocumentResponseHeaders": { "filePath": "src/server/types.ts", "name": "AddDocumentResponseHeaders", @@ -9632,938 +9625,895 @@ "description": "", "members": [] }, - "RegisterWebhooks": { + "Authenticate": { "filePath": "src/server/types.ts", - "name": "RegisterWebhooks", + "name": "Authenticate", "description": "", - "params": [ + "members": [ { - "name": "options", - "description": "", - "value": "RegisterWebhooksOptions", - "filePath": "src/server/types.ts" - } - ], - "returns": { - "filePath": "src/server/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" - }, - "RegisterWebhooksOptions": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "name": "RegisterWebhooksOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The Shopify session used to register webhooks using the Admin API." - } - ], - "value": "export interface RegisterWebhooksOptions {\n /**\n * The Shopify session used to register webhooks using the Admin API.\n */\n session: Session;\n}" - }, - "Session": { - "filePath": "../shopify-api/lib/session/session.ts", - "name": "Session", - "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", - "members": [ - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain, such as `example.myshopify.com`." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "scope", - "value": "string", - "description": "The desired scopes for the access token, at the time the session was created." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "expires", - "value": "Date", - "description": "The date the access token expires." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "accessToken", - "value": "string", - "description": "The access token for the session." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "PropertyDeclaration", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions." - }, - { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isActive", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." + "name": "admin", + "value": "AuthenticateAdmin>", + "description": "Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n\nExamples of when to use this are requests from your app's UI, or requests from admin extensions.\n\nIf there is no session for the Request, this will redirect the merchant to correct auth flows.", + "examples": [ + { + "title": "Authenticating a request for an embedded app", + "description": "", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" + }, + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isScopeChanged", - "value": "(scopes: string | string[] | AuthScopes) => boolean", - "description": "Whether the access token has the given scopes." + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "flow", + "value": "AuthenticateFlow>", + "description": "Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access the API, and the payload of the request.\n\nIf there is no session for the Request, this will return an HTTP 400 error.\n\nNote that this will always be a POST request.", + "examples": [ + { + "title": "Authenticating a Flow extension request", + "description": "", + "tabs": [ + { + "code": "import { ActionFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const {admin, session, payload} = authenticate.flow(request);\n\n // Perform flow extension logic\n\n // Return a 200 response\n return null;\n}", + "title": "/app/routes/**\\/*.jsx" + }, + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "isExpired", - "value": "(withinMillisecondsOfExpiry?: number) => boolean", - "description": "Whether the access token is expired." + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "fulfillmentService", + "value": "AuthenticateFulfillmentService>", + "description": "Authenticate a request from a fulfillment service and get back an authenticated context.", + "examples": [ + { + "title": "Shopify session for the fulfillment service request", + "description": "Use the session associated with this request to use the Admin GraphQL API", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.fulfillmentService(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation acceptFulfillmentRequest {\n fulfillmentOrderAcceptFulfillmentRequest(\n id: \"gid://shopify/FulfillmentOrder/5014440902678\",\n message: \"Reminder that tomorrow is a holiday. We won't be able to ship this until Monday.\"){\n fulfillmentOrder {\n status\n requestStatus\n }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "/app/routes/fulfillment_order_notification.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toObject", - "value": "() => SessionParams", - "description": "Converts an object with data into a Session." + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "public", + "value": "AuthenticatePublic", + "description": "Authenticate a public request and get back a session token.", + "examples": [ + { + "title": "Authenticating a request from a checkout extension", + "description": "", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\nimport { getWidgets } from \"~/db/widgets\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {sessionToken} = authenticate.public.checkout(request);\n\n return json(await getWidgets(sessionToken));\n}", + "title": "/app/routes/api/checkout.jsx" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(other: Session) => boolean", - "description": "Checks whether the given session is equal to this session." - }, + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "webhook", + "value": "AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >", + "description": "Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request", + "examples": [ + { + "title": "Authenticating a webhook request", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n },\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop, session } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n if (session) {\n await db.session.deleteMany({ where: { shop } });\n }\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n\n throw new Response();\n};", + "title": "/app/routes/webhooks.ts" + } + ] + } + ] + } + ], + "value": "interface Authenticate {\n /**\n * Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n *\n * Examples of when to use this are requests from your app's UI, or requests from admin extensions.\n *\n * If there is no session for the Request, this will redirect the merchant to correct auth flows.\n *\n * @example\n * Authenticating a request for an embedded app.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n admin: AuthenticateAdmin>;\n\n /**\n * Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access\n * the API, and the payload of the request.\n *\n * If there is no session for the Request, this will return an HTTP 400 error.\n *\n * Note that this will always be a POST request.\n *\n * @example\n * Authenticating a Flow extension request.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { ActionFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const {admin, session, payload} = authenticate.flow(request);\n *\n * // Perform flow extension logic\n *\n * // Return a 200 response\n * return null;\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n flow: AuthenticateFlow>;\n\n /**\n * Authenticate a request from a fulfillment service and get back an authenticated context.\n *\n * @example\n * Shopify session for the fulfillment service request.\n * Use the session associated with this request to use the Admin GraphQL API \n * ```ts\n * // /app/routes/fulfillment_order_notification.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.fulfillmentService(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation acceptFulfillmentRequest {\n * fulfillmentOrderAcceptFulfillmentRequest(\n * id: \"gid://shopify/FulfillmentOrder/5014440902678\",\n * message: \"Reminder that tomorrow is a holiday. We won't be able to ship this until Monday.\"){\n * fulfillmentOrder {\n * status\n * requestStatus\n * }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n * */\n fulfillmentService: AuthenticateFulfillmentService>;\n\n /**\n * Authenticate a public request and get back a session token.\n *\n * @example\n * Authenticating a request from a checkout extension\n *\n * ```ts\n * // /app/routes/api/checkout.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n * import { getWidgets } from \"~/db/widgets\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {sessionToken} = authenticate.public.checkout(request);\n *\n * return json(await getWidgets(sessionToken));\n * }\n * ```\n */\n public: AuthenticatePublic;\n\n /**\n * Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request\n *\n * @example\n * Authenticating a webhook request\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * },\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/webhooks.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop, session } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * if (session) {\n * await db.session.deleteMany({ where: { shop } });\n * }\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n *\n * throw new Response();\n * };\n * ```\n */\n webhook: AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >;\n}" + }, + "AuthenticateAdmin": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "AuthenticateAdmin", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/session/session.ts", - "syntaxKind": "MethodDeclaration", - "name": "toPropertyArray", - "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", - "description": "Converts the session into an array of key-value pairs." + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/admin/types.ts" } ], - "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" + "returns": { + "filePath": "src/server/authenticate/admin/types.ts", + "description": "", + "name": "Promise>", + "value": "Promise>" + }, + "value": "export type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" }, - "OnlineAccessInfo": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessInfo", + "AdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminContext", + "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", + "description": "" + }, + "NonEmbeddedAdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "NonEmbeddedAdminContext", "description": "", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user_scope", - "value": "string", - "description": "The effective set of scopes for the session." + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." - } + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", + "examples": [ + { + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "examples": [ + { + "title": "Using offline sessions", + "description": "Get your app's shop-specific data using an offline session.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Using online sessions", + "description": "Get your app's user-specific data using an online session.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] + } ], - "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" + "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" }, - "OnlineAccessUser": { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "name": "OnlineAccessUser", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", "description": "", "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." - }, + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + } + ], + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "email", - "value": "string", - "description": "The user's email address." - }, + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "email_verified", - "value": "boolean", - "description": "Whether the user has verified their email address." + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", - "value": "string", - "description": "The user's locale." + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true } ], - "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" }, - "AuthScopes": { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "name": "AuthScopes", - "description": "A class that represents a set of access token scopes.", + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", "members": [ { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "has", - "value": "(scope: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes includes the given one." + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(otherScopes: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes equals the given one." + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "toString", - "value": "() => string", - "description": "Returns a comma-separated string with the current set of scopes." + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" }, { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "toArray", - "value": "() => any[]", - "description": "Returns an array with the current set of scopes." + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" } - ], - "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" + ] }, - "SessionParams": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "SessionParams", + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", "description": "", "members": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "[key: string]", - "value": "any" + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "Session": { + "filePath": "../shopify-api/lib/session/session.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "id", "value": "string", "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "shop", "value": "string", - "description": "The Shopify shop domain." + "description": "The Shopify shop domain, such as `example.myshopify.com`." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "state", "value": "string", "description": "The state of the session. Used for the OAuth authentication code flow." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "isOnline", "value": "boolean", "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "scope", "value": "string", - "description": "The scopes for the access token.", - "isOptional": true + "description": "The desired scopes for the access token, at the time the session was created." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "expires", "value": "Date", - "description": "The date the access token expires.", - "isOptional": true + "description": "The date the access token expires." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "accessToken", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The access token for the session." }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "PropertyDeclaration", "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true - } - ], - "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" - }, - "StoredOnlineAccessInfo": { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StoredOnlineAccessInfo", - "value": "Omit & {\n associated_user: Partial;\n}", - "description": "" - }, - "RegisterReturn": { - "filePath": "../shopify-api/lib/webhooks/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RegisterReturn", - "value": "Record", - "description": "", - "members": [] - }, - "Authenticate": { - "filePath": "src/server/types.ts", - "name": "Authenticate", - "description": "", - "members": [ - { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AuthenticateAdmin>", - "description": "Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n\nExamples of when to use this are requests from your app's UI, or requests from admin extensions.\n\nIf there is no session for the Request, this will redirect the merchant to correct auth flows.", - "examples": [ - { - "title": "Authenticating a request for an embedded app", - "description": "", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" - }, - { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "flow", - "value": "AuthenticateFlow>", - "description": "Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access the API, and the payload of the request.\n\nIf there is no session for the Request, this will return an HTTP 400 error.\n\nNote that this will always be a POST request.", - "examples": [ - { - "title": "Authenticating a Flow extension request", - "description": "", - "tabs": [ - { - "code": "import { ActionFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const {admin, session, payload} = authenticate.flow(request);\n\n // Perform flow extension logic\n\n // Return a 200 response\n return null;\n}", - "title": "/app/routes/**\\/*.jsx" - }, - { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "value": "OnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions." }, { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "fulfillmentService", - "value": "AuthenticateFulfillmentService>", - "description": "Authenticate a request from a fulfillment service and get back an authenticated context.", - "examples": [ - { - "title": "Shopify session for the fulfillment service request", - "description": "Use the session associated with this request to use the Admin GraphQL API", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.fulfillmentService(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation acceptFulfillmentRequest {\n fulfillmentOrderAcceptFulfillmentRequest(\n id: \"gid://shopify/FulfillmentOrder/5014440902678\",\n message: \"Reminder that tomorrow is a holiday. We won't be able to ship this until Monday.\"){\n fulfillmentOrder {\n status\n requestStatus\n }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "/app/routes/fulfillment_order_notification.ts" - } - ] - } - ] + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes." }, { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "public", - "value": "AuthenticatePublic", - "description": "Authenticate a public request and get back a session token.", - "examples": [ - { - "title": "Authenticating a request from a checkout extension", - "description": "", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\nimport { getWidgets } from \"~/db/widgets\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {sessionToken} = authenticate.public.checkout(request);\n\n return json(await getWidgets(sessionToken));\n}", - "title": "/app/routes/api/checkout.jsx" - } - ] - } - ] + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "Whether the access token has the given scopes." }, { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "webhook", - "value": "AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >", - "description": "Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request", - "examples": [ - { - "title": "Authenticating a webhook request", - "description": "", - "tabs": [ - { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n },\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - }, - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop, session } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n if (session) {\n await db.session.deleteMany({ where: { shop } });\n }\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n\n throw new Response();\n};", - "title": "/app/routes/webhooks.ts" - } - ] - } - ] - } - ], - "value": "interface Authenticate {\n /**\n * Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n *\n * Examples of when to use this are requests from your app's UI, or requests from admin extensions.\n *\n * If there is no session for the Request, this will redirect the merchant to correct auth flows.\n *\n * @example\n * Authenticating a request for an embedded app.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n admin: AuthenticateAdmin>;\n\n /**\n * Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access\n * the API, and the payload of the request.\n *\n * If there is no session for the Request, this will return an HTTP 400 error.\n *\n * Note that this will always be a POST request.\n *\n * @example\n * Authenticating a Flow extension request.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { ActionFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const {admin, session, payload} = authenticate.flow(request);\n *\n * // Perform flow extension logic\n *\n * // Return a 200 response\n * return null;\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n flow: AuthenticateFlow>;\n\n /**\n * Authenticate a request from a fulfillment service and get back an authenticated context.\n *\n * @example\n * Shopify session for the fulfillment service request.\n * Use the session associated with this request to use the Admin GraphQL API \n * ```ts\n * // /app/routes/fulfillment_order_notification.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.fulfillmentService(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation acceptFulfillmentRequest {\n * fulfillmentOrderAcceptFulfillmentRequest(\n * id: \"gid://shopify/FulfillmentOrder/5014440902678\",\n * message: \"Reminder that tomorrow is a holiday. We won't be able to ship this until Monday.\"){\n * fulfillmentOrder {\n * status\n * requestStatus\n * }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n * */\n fulfillmentService: AuthenticateFulfillmentService>;\n\n /**\n * Authenticate a public request and get back a session token.\n *\n * @example\n * Authenticating a request from a checkout extension\n *\n * ```ts\n * // /app/routes/api/checkout.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n * import { getWidgets } from \"~/db/widgets\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {sessionToken} = authenticate.public.checkout(request);\n *\n * return json(await getWidgets(sessionToken));\n * }\n * ```\n */\n public: AuthenticatePublic;\n\n /**\n * Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request\n *\n * @example\n * Authenticating a webhook request\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * },\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/webhooks.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop, session } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * if (session) {\n * await db.session.deleteMany({ where: { shop } });\n * }\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n *\n * throw new Response();\n * };\n * ```\n */\n webhook: AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >;\n}" - }, - "AuthenticateAdmin": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "AuthenticateAdmin", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/admin/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/admin/types.ts", - "description": "", - "name": "Promise>", - "value": "Promise>" - }, - "value": "export type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" - }, - "AdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminContext", - "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", - "description": "" - }, - "NonEmbeddedAdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "NonEmbeddedAdminContext", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", - "examples": [ - { - "title": "Using offline sessions", - "description": "Get your app's shop-specific data using an offline session.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Using online sessions", - "description": "Get your app's user-specific data using an online session.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - } - ] + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "Whether the access token is expired." }, { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "Converts an object with data into a Session." }, { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "Checks whether the given session is equal to this session." }, { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", - "examples": [ - { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" - } - ] - } - ] + "filePath": "../shopify-api/lib/session/session.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "(returnUserData?: boolean) => [string, string | number | boolean][]", + "description": "Converts the session into an array of key-value pairs." } ], - "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + "value": "export class Session {\n public static fromPropertyArray(\n entries: [string, string | number | boolean][],\n returnUserData = false,\n ): Session {\n if (!Array.isArray(entries)) {\n throw new InvalidSession(\n 'The parameter is not an array: a Session cannot be created from this object.',\n );\n }\n\n const obj = Object.fromEntries(\n entries\n .filter(([_key, value]) => value !== null && value !== undefined)\n // Sanitize keys\n .map(([key, value]) => {\n switch (key.toLowerCase()) {\n case 'isonline':\n return ['isOnline', value];\n case 'accesstoken':\n return ['accessToken', value];\n case 'onlineaccessinfo':\n return ['onlineAccessInfo', value];\n case 'userid':\n return ['userId', value];\n case 'firstname':\n return ['firstName', value];\n case 'lastname':\n return ['lastName', value];\n case 'accountowner':\n return ['accountOwner', value];\n case 'emailverified':\n return ['emailVerified', value];\n default:\n return [key.toLowerCase(), value];\n }\n }),\n );\n\n const sessionData = {} as SessionParams;\n const onlineAccessInfo = {\n associated_user: {},\n } as OnlineAccessInfo;\n Object.entries(obj).forEach(([key, value]) => {\n switch (key) {\n case 'isOnline':\n if (typeof value === 'string') {\n sessionData[key] = value.toString().toLowerCase() === 'true';\n } else if (typeof value === 'number') {\n sessionData[key] = Boolean(value);\n } else {\n sessionData[key] = value;\n }\n break;\n case 'scope':\n sessionData[key] = value.toString();\n break;\n case 'expires':\n sessionData[key] = value ? new Date(Number(value)) : undefined;\n break;\n case 'onlineAccessInfo':\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n case 'userId':\n if (returnUserData) {\n onlineAccessInfo.associated_user.id = Number(value);\n break;\n }\n case 'firstName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.first_name = String(value);\n break;\n }\n case 'lastName':\n if (returnUserData) {\n onlineAccessInfo.associated_user.last_name = String(value);\n break;\n }\n case 'email':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email = String(value);\n break;\n }\n case 'accountOwner':\n if (returnUserData) {\n onlineAccessInfo.associated_user.account_owner = Boolean(value);\n break;\n }\n case 'locale':\n if (returnUserData) {\n onlineAccessInfo.associated_user.locale = String(value);\n break;\n }\n case 'collaborator':\n if (returnUserData) {\n onlineAccessInfo.associated_user.collaborator = Boolean(value);\n break;\n }\n case 'emailVerified':\n if (returnUserData) {\n onlineAccessInfo.associated_user.email_verified = Boolean(value);\n break;\n }\n // Return any user keys as passed in\n default:\n sessionData[key] = value;\n }\n });\n if (sessionData.isOnline) {\n sessionData.onlineAccessInfo = onlineAccessInfo;\n }\n const session = new Session(sessionData);\n return session;\n }\n\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain, such as `example.myshopify.com`.\n */\n public shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n public state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n public isOnline: boolean;\n /**\n * The desired scopes for the access token, at the time the session was created.\n */\n public scope?: string;\n /**\n * The date the access token expires.\n */\n public expires?: Date;\n /**\n * The access token for the session.\n */\n public accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n public onlineAccessInfo?: OnlineAccessInfo;\n\n constructor(params: SessionParams) {\n Object.assign(this, params);\n }\n\n /**\n * Whether the session is active. Active sessions have an access token that is not expired, and has the given scopes.\n */\n public isActive(scopes: AuthScopes | string | string[]): boolean {\n return (\n !this.isScopeChanged(scopes) &&\n Boolean(this.accessToken) &&\n !this.isExpired()\n );\n }\n\n /**\n * Whether the access token has the given scopes.\n */\n public isScopeChanged(scopes: AuthScopes | string | string[]): boolean {\n const scopesObject =\n scopes instanceof AuthScopes ? scopes : new AuthScopes(scopes);\n\n return !scopesObject.equals(this.scope);\n }\n\n /**\n * Whether the access token is expired.\n */\n public isExpired(withinMillisecondsOfExpiry = 0): boolean {\n return Boolean(\n this.expires &&\n this.expires.getTime() - withinMillisecondsOfExpiry < Date.now(),\n );\n }\n\n /**\n * Converts an object with data into a Session.\n */\n public toObject(): SessionParams {\n const object: SessionParams = {\n id: this.id,\n shop: this.shop,\n state: this.state,\n isOnline: this.isOnline,\n };\n\n if (this.scope) {\n object.scope = this.scope;\n }\n if (this.expires) {\n object.expires = this.expires;\n }\n if (this.accessToken) {\n object.accessToken = this.accessToken;\n }\n if (this.onlineAccessInfo) {\n object.onlineAccessInfo = this.onlineAccessInfo;\n }\n return object;\n }\n\n /**\n * Checks whether the given session is equal to this session.\n */\n public equals(other: Session | undefined): boolean {\n if (!other) return false;\n\n const mandatoryPropsMatch =\n this.id === other.id &&\n this.shop === other.shop &&\n this.state === other.state &&\n this.isOnline === other.isOnline;\n\n if (!mandatoryPropsMatch) return false;\n\n const copyA = this.toPropertyArray(true);\n copyA.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n const copyB = other.toPropertyArray(true);\n copyB.sort(([k1], [k2]) => (k1 < k2 ? -1 : 1));\n\n return JSON.stringify(copyA) === JSON.stringify(copyB);\n }\n\n /**\n * Converts the session into an array of key-value pairs.\n */\n public toPropertyArray(\n returnUserData = false,\n ): [string, string | number | boolean][] {\n return (\n Object.entries(this)\n .filter(\n ([key, value]) =>\n propertiesToSave.includes(key) &&\n value !== undefined &&\n value !== null,\n )\n // Prepare values for db storage\n .flatMap(([key, value]): [string, string | number | boolean][] => {\n switch (key) {\n case 'expires':\n return [[key, value ? value.getTime() : undefined]];\n case 'onlineAccessInfo':\n // eslint-disable-next-line no-negated-condition\n if (!returnUserData) {\n return [[key, value.associated_user.id]];\n } else {\n return [\n ['userId', value?.associated_user?.id],\n ['firstName', value?.associated_user?.first_name],\n ['lastName', value?.associated_user?.last_name],\n ['email', value?.associated_user?.email],\n ['locale', value?.associated_user?.locale],\n ['emailVerified', value?.associated_user?.email_verified],\n ['accountOwner', value?.associated_user?.account_owner],\n ['collaborator', value?.associated_user?.collaborator],\n ];\n }\n default:\n return [[key, value]];\n }\n })\n // Filter out tuples with undefined values\n .filter(([_key, value]) => value !== undefined)\n );\n }\n}" }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", + "OnlineAccessInfo": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessInfo", "description": "", "members": [ { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "associated_user_scope", + "value": "string", + "description": "The effective set of scopes for the session." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" + "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", + "OnlineAccessUser": { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "name": "OnlineAccessUser", "description": "", "members": [ { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "email", + "value": "string", + "description": "The user's email address." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "email_verified", + "value": "boolean", + "description": "Whether the user has verified their email address." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "first_name", + "value": "string", + "description": "The user's first name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", + "value": "string", + "description": "The user's last name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "locale", + "value": "string", + "description": "The user's locale." + } + ], + "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" + }, + "AuthScopes": { + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "name": "AuthScopes", + "description": "A class that represents a set of access token scopes.", + "members": [ + { + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes includes the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes equals the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "toString", + "value": "() => string", + "description": "Returns a comma-separated string with the current set of scopes." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." + "name": "toArray", + "value": "() => any[]", + "description": "Returns an array with the current set of scopes." } ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", + "SessionParams": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "SessionParams", "description": "", "members": [ { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "path", - "value": "string", - "description": "The path to the resource, relative to the API version root." + "filePath": "../shopify-api/lib/session/types.ts", + "name": "[key: string]", + "value": "any" }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "accessToken", + "value": "string", + "description": "The access token for the session.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "query", - "value": "SearchParams", - "description": "Query parameters to be sent with the request.", - "isOptional": true + "name": "id", + "value": "string", + "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", - "isOptional": true + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true - } - ], - "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" - }, - "DataType": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "DataType", - "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "JSON", - "value": "application/json" }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GraphQL", - "value": "application/graphql" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "URLEncoded", - "value": "application/x-www-form-urlencoded" - } - ] - }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] - }, - "PostRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "PostRequestParams", - "value": "GetRequestParams & {\n data: Record | string;\n}", - "description": "" - }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain." }, { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "StoredOnlineAccessInfo": { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StoredOnlineAccessInfo", + "value": "Omit & {\n associated_user: Partial;\n}", + "description": "" + }, + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", + "name": "data", + "value": "string | Record", + "description": "The request body.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "Query parameters to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", "name": "tries", "value": "number", - "description": "The total number of times to try the request if it fails.", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, + "DataType": { + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "name": "DataType", + "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", "members": [ { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "JSON", + "value": "application/json" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GraphQL", + "value": "application/graphql" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" } ] }, + "PostRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "value": "GetRequestParams & {\n data: Record | string;\n}", + "description": "" + }, "BillingContext": { "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "BillingContext", @@ -10572,51 +10522,23 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "require", - "value": "(options: RequireBillingOptions) => Promise", - "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", + "description": "Cancels an ongoing subscription, given its ID.", "examples": [ { - "title": "Requesting billing right away", - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Redirect to a plan selection page", - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "title": "Cancelling a subscription", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", "tabs": [ { - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] - }, - { - "title": "Requesting billing with line items", - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] } ] }, @@ -10669,123 +10591,87 @@ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", + "name": "require", + "value": "(options: RequireBillingOptions) => Promise", + "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", "examples": [ { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Requesting billing right away", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", - "title": "/app/routes/cancel-subscription.ts" + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" }, { "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] - } - ] - } - ], - "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" - }, - "RequireBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "onFailure", - "value": "(error: any) => Promise", - "description": "How to handle the request if the shop doesn't have an active payment for any plan." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true - } - ], - "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" - }, - "BillingCheckResponseObject": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "BillingCheckResponseObject", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "hasActivePayment", - "value": "boolean", - "description": "Whether the user has an active payment method." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "oneTimePurchases", - "value": "OneTimePurchase[]", - "description": "The one-time purchases the shop has." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "appSubscriptions", - "value": "AppSubscription[]", - "description": "The active subscriptions the shop has." + }, + { + "title": "Redirect to a plan selection page", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Requesting billing with line items", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] } ], - "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" + "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, - "OneTimePurchase": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "OneTimePurchase", + "CancelBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "CancelBillingOptions", "description": "", "members": [ { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the one-time purchase." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true }, { - "filePath": "../shopify-api/lib/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "test", + "name": "prorate", "value": "boolean", - "description": "Whether this is a test purchase." + "description": "Whether to prorate the cancellation.\n\n\n\n\n", + "isOptional": true }, { - "filePath": "../shopify-api/lib/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "status", + "name": "subscriptionId", "value": "string", - "description": "The status of the one-time purchase." + "description": "The ID of the subscription to cancel." } ], - "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" }, "AppSubscription": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -10799,6 +10685,14 @@ "value": "string", "description": "The ID of the app subscription." }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "lineItems", + "value": "ActiveSubscriptionLineItem[]", + "description": "", + "isOptional": true + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", @@ -10812,14 +10706,6 @@ "name": "test", "value": "boolean", "description": "Whether this is a test subscription." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "lineItems", - "value": "ActiveSubscriptionLineItem[]", - "description": "", - "isOptional": true } ], "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n\n /*\n * The line items for this plan. This will become mandatory in v10.\n */\n lineItems?: ActiveSubscriptionLineItem[];\n}" @@ -10869,112 +10755,84 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "name": "discount", + "value": "AppPlanDiscount", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "price", - "value": "Money", + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "AppPlanDiscount", + "name": "price", + "value": "Money", "description": "" } ], "value": "export interface RecurringAppPlan {\n /*\n * The interval for this plan is charged on.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /*\n * The price of the plan.\n */\n price: Money;\n /*\n * The discount applied to the plan.\n */\n discount: AppPlanDiscount;\n}" }, - "BillingInterval": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingInterval", - "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "OneTime", - "value": "ONE_TIME" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Every30Days", - "value": "EVERY_30_DAYS" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Annual", - "value": "ANNUAL" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "Usage", - "value": "USAGE" - } - ] - }, - "Money": { + "AppPlanDiscount": { "filePath": "../shopify-api/lib/billing/types.ts", - "name": "Money", + "name": "AppPlanDiscount", "description": "", "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", + "name": "durationLimitInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", + "name": "priceAfterDiscount", + "value": "Money", "description": "" - } - ], - "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" - }, - "AppPlanDiscount": { - "filePath": "../shopify-api/lib/billing/types.ts", - "name": "AppPlanDiscount", - "description": "", - "members": [ + }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "durationLimitInIntervals", + "name": "remainingDurationInIntervals", "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "remainingDurationInIntervals", - "value": "number", + "name": "value", + "value": "AppPlanDiscountAmount", "description": "" - }, + } + ], + "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + }, + "Money": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "Money", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "priceAfterDiscount", - "value": "Money", + "name": "amount", + "value": "number", "description": "" }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "value", - "value": "AppPlanDiscountAmount", + "name": "currencyCode", + "value": "string", "description": "" } ], - "value": "export interface AppPlanDiscount {\n /*\n * The total number of intervals the discount applies to.\n */\n durationLimitInIntervals: number;\n /*\n * The remaining number of intervals the discount applies to.\n */\n remainingDurationInIntervals: number;\n /*\n * The price after the discount is applied.\n */\n priceAfterDiscount: Money;\n /*\n * The value of the discount applied every billing interval.\n */\n value: AppPlanDiscountAmount;\n}" + "value": "interface Money {\n amount: number;\n currencyCode: string;\n}" }, "AppPlanDiscountAmount": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -11029,6 +10887,34 @@ ], "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" }, + "BillingInterval": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingInterval", + "value": "export enum BillingInterval {\n OneTime = 'ONE_TIME',\n Every30Days = 'EVERY_30_DAYS',\n Annual = 'ANNUAL',\n Usage = 'USAGE',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "OneTime", + "value": "ONE_TIME" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Annual", + "value": "ANNUAL" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, "UsageAppPlan": { "filePath": "../shopify-api/lib/billing/types.ts", "name": "UsageAppPlan", @@ -11051,48 +10937,106 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "terms", + "name": "terms", + "value": "string", + "description": "" + } + ], + "value": "export interface UsageAppPlan {\n /*\n * The total usage records for interval.\n */\n balanceUsed: Money;\n /*\n * The capped amount prevents the merchant from being charged for any usage over that amount during a billing period.\n */\n cappedAmount: Money;\n /*\n * The terms and conditions for app usage pricing.\n */\n terms: string;\n}" + }, + "CheckBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "CheckBillingOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + } + ], + "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + }, + "BillingCheckResponseObject": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "The active subscriptions the shop has." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "Whether the user has an active payment method." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "The one-time purchases the shop has." + } + ], + "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../shopify-api/lib/billing/types.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "The ID of the one-time purchase." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "name", "value": "string", - "description": "" - } - ], - "value": "export interface UsageAppPlan {\n /*\n * The total usage records for interval.\n */\n balanceUsed: Money;\n /*\n * The capped amount prevents the merchant from being charged for any usage over that amount during a billing period.\n */\n cappedAmount: Money;\n /*\n * The terms and conditions for app usage pricing.\n */\n terms: string;\n}" - }, - "CheckBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CheckBillingOptions", - "description": "", - "members": [ + "description": "The name of the purchased plan." + }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." + "name": "status", + "value": "string", + "description": "The status of the one-time purchase." }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", + "name": "test", "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true + "description": "Whether this is a test purchase." } ], - "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" }, "RequestBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "RequestBillingOptions", "description": "", "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plan", - "value": "keyof Config[\"billing\"]", - "description": "The plan to request. Must be one of the values defined in the `billing` config option." - }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -11101,6 +11045,13 @@ "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", "isOptional": true }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "plan", + "value": "keyof Config[\"billing\"]", + "description": "The plan to request. Must be one of the values defined in the `billing` config option." + }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", @@ -11112,36 +11063,35 @@ ], "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" }, - "CancelBillingOptions": { + "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "name": "RequireBillingOptions", "description": "", "members": [ { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", - "value": "string", - "description": "The ID of the subscription to cancel." + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", - "isOptional": true + "name": "onFailure", + "value": "(error: any) => Promise", + "description": "How to handle the request if the shop doesn't have an active payment for any plan." }, { "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." } ], - "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" + "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" }, "EnsureCORSFunction": { "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", @@ -11158,21 +11108,31 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Using the decoded session token", - "description": "Get user-specific data using the `sessionToken` object.", + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" } ] } @@ -11247,31 +11207,21 @@ { "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Using the decoded session token", + "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -11280,25 +11230,52 @@ ], "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, - "JwtPayload": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "JwtPayload", + "RedirectFunction": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "name": "RedirectFunction", "description": "", - "members": [ + "params": [ { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "iss", + "name": "url", + "description": "", "value": "string", - "description": "The shop's admin domain." + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" }, { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "dest", - "value": "string", - "description": "The shop's domain." - }, + "name": "init", + "description": "", + "value": "RedirectInit", + "isOptional": true, + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "description": "", + "name": "TypedResponse", + "value": "TypedResponse" + }, + "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" + }, + "RedirectInit": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectInit", + "value": "number | (ResponseInit & {target?: RedirectTarget})", + "description": "" + }, + "RedirectTarget": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectTarget", + "value": "'_self' | '_parent' | '_top'", + "description": "" + }, + "JwtPayload": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "JwtPayload", + "description": "", + "members": [ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", @@ -11309,9 +11286,9 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sub", + "name": "dest", "value": "string", - "description": "The User that the session token is intended for." + "description": "The shop's domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -11323,16 +11300,16 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "nbf", + "name": "iat", "value": "number", - "description": "When the session token activates." + "description": "When the session token was issued." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "iat", - "value": "number", - "description": "When the session token was issued." + "name": "iss", + "value": "string", + "description": "The shop's admin domain." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -11344,53 +11321,26 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "sid", - "value": "string", - "description": "A unique session ID per user and app." - } - ], - "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" - }, - "RedirectFunction": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", - "description": "", - "params": [ - { - "name": "url", - "description": "", - "value": "string", - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" - }, - { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "description": "", - "name": "TypedResponse", - "value": "TypedResponse" - }, - "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" - }, - "RedirectInit": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", - "description": "" + "name": "nbf", + "value": "number", + "description": "When the session token activates." + }, + { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "A unique session ID per user and app." + }, + { + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "The User that the session token is intended for." + } + ], + "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" }, "RestResourcesType": { "filePath": "src/server/types.ts", @@ -11435,16 +11385,16 @@ { "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", + "name": "admin", + "value": "AdminApiContext", + "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Shopify session for the Flow request", - "description": "Use the session associated with this request to use REST resources.", + "title": "Flow admin context", + "description": "Use the `admin` object in the context to interact with the Admin API.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "title": "/app/routes/flow.tsx" } ] @@ -11473,16 +11423,16 @@ { "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Flow admin context", - "description": "Use the `admin` object in the context to interact with the Admin API.", + "title": "Shopify session for the Flow request", + "description": "Use the session associated with this request to use REST resources.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "title": "/app/routes/flow.tsx" } ] @@ -11517,25 +11467,6 @@ "name": "FulfillmentServiceContext", "description": "", "members": [ - { - "filePath": "src/server/authenticate/fulfillment-service/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "Shopify session for the fulfillment service notification request", - "description": "Use the session associated with this request to use REST resources.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", - "title": "/app/routes/fulfillment_service_notification.tsx" - } - ] - } - ] - }, { "filePath": "src/server/authenticate/fulfillment-service/types.ts", "syntaxKind": "PropertySignature", @@ -11573,6 +11504,25 @@ ] } ] + }, + { + "filePath": "src/server/authenticate/fulfillment-service/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", + "examples": [ + { + "title": "Shopify session for the fulfillment service notification request", + "description": "Use the session associated with this request to use REST resources.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\n export const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.fulfillmentService(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "title": "/app/routes/fulfillment_service_notification.tsx" + } + ] + } + ] } ], "value": "export interface FulfillmentServiceContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n * @example\n * Shopify session for the fulfillment service notification request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/fulfillment_service_notification.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { session, admin } = await authenticate.fulfillmentService(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n * */\n session: Session;\n /**\n *\n * An admin context for the fulfillment service request.\n *\n * Returned only if there is a session for the shop.\n * @example\n * Shopify session for the fulfillment service request.\n * Use the session associated with this request to use the Admin GraphQL API \n * ```ts\n * // /app/routes/fulfillment_order_notification.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.fulfillmentService(request);\n * const response = await admin?.graphql(\n * `#graphql\n * query {\n * shop {\n * assignedFulfillmentOrders(first: 10, assignmentStatus: FULFILLMENT_REQUESTED) {\n * edges {\n * node {\n * id\n * destination {\n * firstName\n * lastName\n * }\n * lineItems(first: 10) {\n * edges {\n * node {\n * id\n * productTitle\n * sku\n * remainingQuantity\n * }\n * }\n * }\n * merchantRequests(first: 10, kind: FULFILLMENT_REQUEST) {\n * edges {\n * node {\n * message\n * }\n * }\n * }\n * }\n * }\n * }\n * }\n * }`);\n *\n * const fulfillments = await response.json();\n * return json({ data: fulfillments.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * The payload from the fulfillment service request.\n *\n * @example\n * Fulfillment service request payload.\n * Get the request's POST payload.\n * ```ts\n * /app/routes/fulfillment_order_notification.ts\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { payload } = await authenticate.fulfillmentService(request);\n * if(payload.kind === 'FULFILLMENT_REQUEST') {\n * // handle fulfillment request\n * } else if (payload.kind === 'CANCELLATION_REQUEST') {\n * // handle cancellation request\n * };\n * return new Response();\n * ```\n */\n payload: Record & {\n kind: string;\n };\n}" @@ -11589,25 +11539,6 @@ "name": "AuthenticatePublicObject", "description": "", "members": [ - { - "filePath": "src/server/authenticate/public/types.ts", - "syntaxKind": "PropertySignature", - "name": "checkout", - "value": "AuthenticateCheckout", - "description": "Authenticate a request from a checkout extension", - "examples": [ - { - "title": "Authenticating a checkout extension request", - "description": "", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n );\n return cors(json({my: \"data\", shop: sessionToken.dest}));\n};", - "title": "/app/routes/public/widgets.ts" - } - ] - } - ] - }, { "filePath": "src/server/authenticate/public/types.ts", "syntaxKind": "PropertySignature", @@ -11626,98 +11557,28 @@ ] } ] - } - ], - "value": "export interface AuthenticatePublicObject {\n /**\n * Authenticate a request from a checkout extension\n *\n * @example\n * Authenticating a checkout extension request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * );\n * return cors(json({my: \"data\", shop: sessionToken.dest}));\n * };\n * ```\n */\n checkout: AuthenticateCheckout;\n\n /**\n * Authenticate a request from an app proxy\n *\n * @example\n * Authenticating an app proxy request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * await authenticate.public.appProxy(\n * request,\n * );\n *\n * const {searchParams} = new URL(request.url);\n * const shop = searchParams.get(\"shop\");\n * const customerId = searchParams.get(\"logged_in_customer_id\")\n *\n * return json({my: \"data\", shop, customerId});\n * };\n * ```\n */\n appProxy: AuthenticateAppProxy;\n}" - }, - "AuthenticateCheckout": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckout", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/public/checkout/types.ts" - }, - { - "name": "options", - "description": "", - "value": "AuthenticateCheckoutOptions", - "isOptional": true, - "filePath": "src/server/authenticate/public/checkout/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" - }, - "AuthenticateCheckoutOptions": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckoutOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "corsHeaders", - "value": "string[]", - "description": "", - "isOptional": true - } - ], - "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" - }, - "CheckoutContext": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "CheckoutContext", - "description": "Authenticated Context for a checkout request", - "members": [ - { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", - "examples": [ - { - "title": "Using the decoded session token", - "description": "Get store-specific data using the `sessionToken` object.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", - "title": "app/routes/public/my-route.ts" - } - ] - } - ] }, { - "filePath": "src/server/authenticate/public/checkout/types.ts", + "filePath": "src/server/authenticate/public/types.ts", "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "checkout", + "value": "AuthenticateCheckout", + "description": "Authenticate a request from a checkout extension", "examples": [ { - "title": "Setting CORS headers for a public request", - "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", + "title": "Authenticating a checkout extension request", + "description": "", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", - "title": "app/routes/public/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n );\n return cors(json({my: \"data\", shop: sessionToken.dest}));\n};", + "title": "/app/routes/public/widgets.ts" } ] } ] } ], - "value": "export interface CheckoutContext {\n /**\n * The decoded and validated session token for the request\n *\n * Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).\n *\n * @example\n * Using the decoded session token.\n * Get store-specific data using the `sessionToken` object.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({shop: sessionToken.dest}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that ensures the CORS headers are set correctly for the response.\n *\n * @example\n * Setting CORS headers for a public request.\n * Use the `cors` helper to ensure your app can respond to checkout extension requests.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * { corsHeaders: [\"X-My-Custom-Header\"] }\n * );\n * const data = await getMyAppData({shop: sessionToken.dest});\n * return cors(json(data));\n * };\n * ```\n */\n cors: EnsureCORSFunction;\n}" + "value": "export interface AuthenticatePublicObject {\n /**\n * Authenticate a request from a checkout extension\n *\n * @example\n * Authenticating a checkout extension request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * );\n * return cors(json({my: \"data\", shop: sessionToken.dest}));\n * };\n * ```\n */\n checkout: AuthenticateCheckout;\n\n /**\n * Authenticate a request from an app proxy\n *\n * @example\n * Authenticating an app proxy request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * await authenticate.public.appProxy(\n * request,\n * );\n *\n * const {searchParams} = new URL(request.url);\n * const shop = searchParams.get(\"shop\");\n * const customerId = searchParams.get(\"logged_in_customer_id\")\n *\n * return json({my: \"data\", shop, customerId});\n * };\n * ```\n */\n appProxy: AuthenticateAppProxy;\n}" }, "AuthenticateAppProxy": { "filePath": "src/server/authenticate/public/appProxy/types.ts", @@ -11744,13 +11605,6 @@ "name": "AppProxyContext", "description": "", "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -11758,13 +11612,6 @@ "value": "undefined", "description": "No session is available for the shop that made this request. Therefore no methods for interacting with the GraphQL / REST Admin APIs are available." }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "undefined", - "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." - }, { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", @@ -11803,6 +11650,20 @@ ] } ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "undefined", + "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "undefined", + "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." } ], "value": "export interface AppProxyContext extends Context {\n /**\n * No session is available for the shop that made this request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n */\n session: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.\n */\n admin: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no method for interacting with the Storefront API is available.\n */\n storefront: undefined;\n}" @@ -11852,60 +11713,22 @@ }, "AppProxyContextWithSession": { "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "AppProxyContextWithSession", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", - "examples": [ - { - "title": "Using the session object", - "description": "Get the session for the shop that initiated the request to the app proxy.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.", - "examples": [ - { - "title": "Interacting with the Admin API", - "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, + "name": "AppProxyContextWithSession", + "description": "", + "members": [ { "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "StorefrontContext", - "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.", "examples": [ { - "title": "Interacting with the Storefront API", - "description": "Use the `storefront` object to interact with the GraphQL API.", + "title": "Interacting with the Admin API", + "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", "tabs": [ { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "title": "app/routes/**\\/.ts" } ] @@ -11950,6 +11773,44 @@ ] } ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "examples": [ + { + "title": "Using the session object", + "description": "Get the session for the shop that initiated the request to the app proxy.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", + "title": "app/routes/**\\/.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "StorefrontContext", + "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", + "examples": [ + { + "title": "Interacting with the Storefront API", + "description": "Use the `storefront` object to interact with the GraphQL API.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", + "title": "app/routes/**\\/.ts" + } + ] + } + ] } ], "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } =\n * await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(\n * await getMyAppModelData({shop: session.shop})\n * );\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(\n * `#graphql\n * query blogIds {\n * blogs(first: 10) {\n * edges {\n * node {\n * id\n * }\n * }\n * }\n * }`\n * );\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" @@ -11995,6 +11856,95 @@ ], "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * try {\n * const response = await storefront.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // { errors: { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] } }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, + "AuthenticateCheckout": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckout", + "description": "", + "params": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/public/checkout/types.ts" + }, + { + "name": "options", + "description": "", + "value": "AuthenticateCheckoutOptions", + "isOptional": true, + "filePath": "src/server/authenticate/public/checkout/types.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" + }, + "AuthenticateCheckoutOptions": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckoutOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "syntaxKind": "PropertySignature", + "name": "corsHeaders", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" + }, + "CheckoutContext": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "CheckoutContext", + "description": "Authenticated Context for a checkout request", + "members": [ + { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", + "examples": [ + { + "title": "Setting CORS headers for a public request", + "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", + "title": "app/routes/public/my-route.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "syntaxKind": "PropertySignature", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", + "examples": [ + { + "title": "Using the decoded session token", + "description": "Get store-specific data using the `sessionToken` object.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", + "title": "app/routes/public/my-route.ts" + } + ] + } + ] + } + ], + "value": "export interface CheckoutContext {\n /**\n * The decoded and validated session token for the request\n *\n * Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).\n *\n * @example\n * Using the decoded session token.\n * Get store-specific data using the `sessionToken` object.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({shop: sessionToken.dest}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that ensures the CORS headers are set correctly for the response.\n *\n * @example\n * Setting CORS headers for a public request.\n * Use the `cors` helper to ensure your app can respond to checkout extension requests.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * { corsHeaders: [\"X-My-Custom-Header\"] }\n * );\n * const data = await getMyAppData({shop: sessionToken.dest});\n * return cors(json(data));\n * };\n * ```\n */\n cors: EnsureCORSFunction;\n}" + }, "AuthenticatePublicLegacy": { "filePath": "src/server/authenticate/public/types.ts", "syntaxKind": "TypeAliasDeclaration", @@ -12034,13 +11984,6 @@ "name": "WebhookContextWithoutSession", "description": "", "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "" - }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -12070,16 +12013,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The shop where the webhook was triggered.", + "name": "payload", + "value": "Record", + "description": "The payload from the webhook request.", "examples": [ { - "title": "Webhook shop", - "description": "Get the shop that triggered a webhook.", + "title": "Webhook payload", + "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12089,16 +12032,23 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "topic", - "value": "Topics", - "description": "The topic of the webhook.", + "name": "session", + "value": "undefined", + "description": "" + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "examples": [ { - "title": "Webhook topic", - "description": "Get the event topic for the webhook.", + "title": "Webhook shop", + "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12108,16 +12058,17 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "webhookId", + "name": "subTopic", "value": "string", - "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", + "description": "The sub-topic of the webhook. This is only available for certain webhooks.", + "isOptional": true, "examples": [ { - "title": "Webhook ID", - "description": "Get the webhook ID.", + "title": "Webhook sub-topic", + "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12127,16 +12078,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "payload", - "value": "Record", - "description": "The payload from the webhook request.", + "name": "topic", + "value": "Topics", + "description": "The topic of the webhook.", "examples": [ { - "title": "Webhook payload", - "description": "Get the request's POST payload.", + "title": "Webhook topic", + "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12146,17 +12097,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "subTopic", + "name": "webhookId", "value": "string", - "description": "The sub-topic of the webhook. This is only available for certain webhooks.", - "isOptional": true, + "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", "examples": [ { - "title": "Webhook sub-topic", - "description": "Get the webhook sub-topic.", + "title": "Webhook ID", + "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12171,13 +12121,6 @@ "name": "WebhookContextWithSession", "description": "", "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop." - }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", @@ -12229,16 +12172,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The shop where the webhook was triggered.", + "name": "payload", + "value": "Record", + "description": "The payload from the webhook request.", "examples": [ { - "title": "Webhook shop", - "description": "Get the shop that triggered a webhook.", + "title": "Webhook payload", + "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12248,16 +12191,23 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "topic", - "value": "Topics", - "description": "The topic of the webhook.", + "name": "session", + "value": "Session", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop." + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "examples": [ { - "title": "Webhook topic", - "description": "Get the event topic for the webhook.", + "title": "Webhook shop", + "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12267,16 +12217,17 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "webhookId", + "name": "subTopic", "value": "string", - "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", + "description": "The sub-topic of the webhook. This is only available for certain webhooks.", + "isOptional": true, "examples": [ { - "title": "Webhook ID", - "description": "Get the webhook ID.", + "title": "Webhook sub-topic", + "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12286,16 +12237,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "payload", - "value": "Record", - "description": "The payload from the webhook request.", + "name": "topic", + "value": "Topics", + "description": "The topic of the webhook.", "examples": [ { - "title": "Webhook payload", - "description": "Get the request's POST payload.", + "title": "Webhook topic", + "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12305,17 +12256,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "subTopic", + "name": "webhookId", "value": "string", - "description": "The sub-topic of the webhook. This is only available for certain webhooks.", - "isOptional": true, + "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", "examples": [ { - "title": "Webhook sub-topic", - "description": "Get the webhook sub-topic.", + "title": "Webhook ID", + "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -12340,16 +12290,16 @@ { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClient & Resources", - "description": "A REST client." + "name": "graphql", + "value": "InstanceType", + "description": "A GraphQL client." }, { "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "InstanceType", - "description": "A GraphQL client." + "name": "rest", + "value": "RestClient & Resources", + "description": "A REST client." } ], "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" @@ -12453,13 +12403,6 @@ "name": "PageInfo", "description": "", "members": [ - { - "filePath": "../shopify-api/lib/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "limit", - "value": "string", - "description": "" - }, { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", @@ -12471,8 +12414,15 @@ { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "previousPageUrl", + "name": "limit", "value": "string", + "description": "" + }, + { + "filePath": "../shopify-api/lib/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "nextPage", + "value": "PageInfoParams", "description": "", "isOptional": true }, @@ -12487,15 +12437,15 @@ { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "prevPage", - "value": "PageInfoParams", + "name": "previousPageUrl", + "value": "string", "description": "", "isOptional": true }, { "filePath": "../shopify-api/lib/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "nextPage", + "name": "prevPage", "value": "PageInfoParams", "description": "", "isOptional": true @@ -12532,6 +12482,56 @@ "value": "'CUSTOMERS_DATA_REQUEST' | 'CUSTOMERS_REDACT' | 'SHOP_REDACT'", "description": "" }, + "RegisterWebhooks": { + "filePath": "src/server/types.ts", + "name": "RegisterWebhooks", + "description": "", + "params": [ + { + "name": "options", + "description": "", + "value": "RegisterWebhooksOptions", + "filePath": "src/server/types.ts" + } + ], + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" + }, + "RegisterWebhooksOptions": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "RegisterWebhooksOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The Shopify session used to register webhooks using the Admin API." + } + ], + "value": "export interface RegisterWebhooksOptions {\n /**\n * The Shopify session used to register webhooks using the Admin API.\n */\n session: Session;\n}" + }, + "RegisterReturn": { + "filePath": "../shopify-api/lib/webhooks/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RegisterReturn", + "value": "Record", + "description": "", + "members": [] + }, + "SessionStorageType": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "SessionStorageType", + "value": "Config['sessionStorage'] extends SessionStorage\n ? Config['sessionStorage']\n : SessionStorage", + "description": "" + }, "Unauthenticated": { "filePath": "src/server/unauthenticated/types.ts", "name": "Unauthenticated", @@ -12607,25 +12607,6 @@ "name": "UnauthenticatedAdminContext", "description": "", "members": [ - { - "filePath": "src/server/unauthenticated/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the given shop.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nThis will always be an offline session. You can use to get shop-specific data.", - "examples": [ - { - "title": "Using the offline session", - "description": "Get your app's shop-specific data using the returned offline `session` object.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const shop = getShopFromExternalRequest(request);\n const { session } = await unauthenticated.admin(shop);\n return json(await getMyAppData({shop: session.shop));\n};", - "title": "/app/routes/**\\/*.ts" - } - ] - } - ] - }, { "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", @@ -12662,6 +12643,25 @@ ] } ] + }, + { + "filePath": "src/server/unauthenticated/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the given shop.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nThis will always be an offline session. You can use to get shop-specific data.", + "examples": [ + { + "title": "Using the offline session", + "description": "Get your app's shop-specific data using the returned offline `session` object.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const shop = getShopFromExternalRequest(request);\n const { session } = await unauthenticated.admin(shop);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "/app/routes/**\\/*.ts" + } + ] + } + ] } ], "value": "export interface UnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use to get shop-specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.admin(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the given store.\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await unauthenticated.admin(request);\n *\n * const response = await admin.rest.get(\n * {\n * path: \"/customers/count.json\"\n * }\n * );\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n *\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await unauthenticated.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n admin: AdminApiContext;\n}" @@ -12735,17 +12735,21 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionStorage", - "value": "SessionStorageType", - "description": "The `SessionStorage` instance you passed in as a config option.", + "name": "addDocumentResponseHeaders", + "value": "AddDocumentResponseHeaders", + "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", "examples": [ { - "title": "Storing sessions with Prisma", - "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", + "title": "Return headers on all requests", + "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", - "title": "/app/shopify.server.ts" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", + "title": "~/shopify.server.ts" + }, + { + "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", + "title": "entry.server.tsx" } ] } @@ -12754,21 +12758,21 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "addDocumentResponseHeaders", - "value": "AddDocumentResponseHeaders", - "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", + "name": "authenticate", + "value": "Authenticate", + "description": "Ways to authenticate requests from different surfaces across Shopify.", "examples": [ { - "title": "Return headers on all requests", - "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", + "title": "Authenticate Shopify requests", + "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", - "title": "~/shopify.server.ts" + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" }, { - "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", - "title": "entry.server.tsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" } ] } @@ -12796,21 +12800,17 @@ { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "authenticate", - "value": "Authenticate", - "description": "Ways to authenticate requests from different surfaces across Shopify.", + "name": "sessionStorage", + "value": "SessionStorageType", + "description": "The `SessionStorage` instance you passed in as a config option.", "examples": [ { - "title": "Authenticate Shopify requests", - "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "title": "Storing sessions with Prisma", + "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", "title": "/app/shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" } ] } @@ -12942,94 +12942,34 @@ { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "appUrl", - "value": "string", - "description": "The URL your app is running on.\n\nThe `@shopify/cli` provides this URL as `process.env.SHOPIFY_APP_URL`. For development this is probably a tunnel URL that points to your local machine. If this is a production app, this is your production URL." - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "sessionStorage", - "value": "Storage", - "description": "An adaptor for storing sessions in your database of choice.\n\nShopify provides multiple session storage adaptors and you can create your own.\n\n\n\n\n", - "examples": [ - { - "title": "Storing sessions with Prisma", - "description": "Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.", - "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n // ... etc\n sessionStorage: new PrismaSessionStorage(prisma),\n});\nexport default shopify;", - "title": "Example" - } - ] - } - ] - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "useOnlineTokens", + "name": "_logDisabledFutureFlags", "value": "boolean", - "description": "Whether your app use online or offline tokens.\n\nIf your app uses online tokens, then both online and offline tokens will be saved to your database. This ensures your app can perform background jobs.\n\n\n\n\n", - "isOptional": true, - "defaultValue": "`false`" - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "webhooks", - "value": "WebhookConfig", - "description": "The config for the webhook topics your app would like to subscribe to.\n\n\n\n\n\n\n\nThis can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.", + "description": "Whether to log disabled future flags at startup.", "isOptional": true, - "examples": [ - { - "title": "Registering for a webhook when a merchant uninstalls your app", - "description": "", - "tabs": [ - { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/webhooks.jsx\nimport { ActionFunctionArgs } from \"@remix-run/node\";\n\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n await db.session.deleteMany({ where: { shop } });\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n throw new Response();\n};", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "isPrivate": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "hooks", - "value": "HooksConfig", - "description": "Functions to call at key places during your apps lifecycle.\n\nThese functions are called in the context of the request that triggered them. This means you can access the session.", - "isOptional": true, - "examples": [ - { - "title": "Seeding your database custom data when a merchant installs your app", - "description": "", - "tabs": [ - { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n seedStoreData({session})\n }\n },\n // ...etc\n});", - "title": "Example" - } - ] - } - ] + "name": "adminApiAccessToken", + "value": "string", + "description": "An app-wide API access token.\n\nOnly applies to custom apps.", + "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "isEmbeddedApp", - "value": "boolean", - "description": "Does your app render embedded inside the Shopify Admin or on its own.\n\nUnless you have very specific needs, this should be true.", - "isOptional": true, - "defaultValue": "`true`" + "name": "apiKey", + "value": "string", + "description": "The API key for your app.\n\nAlso known as Client ID in your Partner Dashboard.", + "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "distribution", - "value": "AppDistribution", - "description": "How your app is distributed. Default is `AppDistribution.AppStore`.\n\n\n\n\n", - "isOptional": true + "name": "apiSecretKey", + "value": "string", + "description": "The API secret key for your app.\n\nAlso known as Client Secret in your Partner Dashboard." }, { "filePath": "src/server/config-types.ts", @@ -13052,6 +12992,13 @@ } ] }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "appUrl", + "value": "string", + "description": "The URL your app is running on.\n\nThe `@shopify/cli` provides this URL as `process.env.SHOPIFY_APP_URL`. For development this is probably a tunnel URL that points to your local machine. If this is a production app, this is your production URL." + }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", @@ -13076,72 +13023,78 @@ { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "future", - "value": "Future", - "description": "Features that will be introduced in future releases of this package.\n\nYou can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future releases in advance and provide feedback on the new features.", + "name": "billing", + "value": "BillingConfig", + "description": "Billing configurations for the app.", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "apiKey", - "value": "string", - "description": "The API key for your app.\n\nAlso known as Client ID in your Partner Dashboard.", + "name": "customShopDomains", + "value": "(string | RegExp)[]", + "description": "Override values for Shopify shop domains.", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "apiSecretKey", - "value": "string", - "description": "The API secret key for your app.\n\nAlso known as Client Secret in your Partner Dashboard." - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "scopes", - "value": "string[] | AuthScopes", - "description": "The scopes your app needs to access the API.", + "name": "distribution", + "value": "AppDistribution", + "description": "How your app is distributed. Default is `AppDistribution.AppStore`.\n\n\n\n\n", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "adminApiAccessToken", - "value": "string", - "description": "An app-wide API access token.\n\nOnly applies to custom apps.", + "name": "future", + "value": "Future", + "description": "Features that will be introduced in future releases of this package.\n\nYou can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future releases in advance and provide feedback on the new features.", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "userAgentPrefix", - "value": "string", - "description": "The user agent prefix to use for API requests.", - "isOptional": true + "name": "hooks", + "value": "HooksConfig", + "description": "Functions to call at key places during your apps lifecycle.\n\nThese functions are called in the context of the request that triggered them. This means you can access the session.", + "isOptional": true, + "examples": [ + { + "title": "Seeding your database custom data when a merchant installs your app", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n seedStoreData({session})\n }\n },\n // ...etc\n});", + "title": "Example" + } + ] + } + ] }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "privateAppStorefrontAccessToken", - "value": "string", - "description": "An app-wide API access token for the storefront API.\n\nOnly applies to custom apps.", - "isOptional": true + "name": "isEmbeddedApp", + "value": "boolean", + "description": "Does your app render embedded inside the Shopify Admin or on its own.\n\nUnless you have very specific needs, this should be true.", + "isOptional": true, + "defaultValue": "`true`" }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "customShopDomains", - "value": "(string | RegExp)[]", - "description": "Override values for Shopify shop domains.", + "name": "logger", + "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", + "description": "Customization options for Shopify logs.", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingConfig", - "description": "Billing configurations for the app.", + "name": "privateAppStorefrontAccessToken", + "value": "string", + "description": "An app-wide API access token for the storefront API.\n\nOnly applies to custom apps.", "isOptional": true }, { @@ -13155,80 +13108,69 @@ { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "logger", - "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", - "description": "Customization options for Shopify logs.", + "name": "scopes", + "value": "string[] | AuthScopes", + "description": "The scopes your app needs to access the API.", "isOptional": true }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "_logDisabledFutureFlags", - "value": "boolean", - "description": "Whether to log disabled future flags at startup.", - "isOptional": true, - "isPrivate": true - } - ], - "value": "export interface AppConfigArg<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n Storage extends SessionStorage = SessionStorage,\n Future extends FutureFlagOptions = FutureFlagOptions,\n> extends Omit<\n ApiConfigArg>,\n | 'hostName'\n | 'hostScheme'\n | 'isEmbeddedApp'\n | 'apiVersion'\n | 'isCustomStoreApp'\n | 'future'\n > {\n /**\n * The URL your app is running on.\n *\n * The `@shopify/cli` provides this URL as `process.env.SHOPIFY_APP_URL`. For development this is probably a tunnel URL that points to your local machine. If this is a production app, this is your production URL.\n */\n appUrl: string;\n\n /**\n * An adaptor for storing sessions in your database of choice.\n *\n * Shopify provides multiple session storage adaptors and you can create your own.\n *\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options}\n *\n * @example\n * Storing sessions with Prisma.\n * Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.\n * ```ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n *\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * // ... etc\n * sessionStorage: new PrismaSessionStorage(prisma),\n * });\n * export default shopify;\n * ```\n */\n sessionStorage: Storage;\n\n /**\n * Whether your app use online or offline tokens.\n *\n * If your app uses online tokens, then both online and offline tokens will be saved to your database. This ensures your app can perform background jobs.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/access-modes}\n *\n * @defaultValue `false`\n */\n useOnlineTokens?: boolean;\n\n /**\n * The config for the webhook topics your app would like to subscribe to.\n *\n * {@link https://shopify.dev/docs/apps/webhooks}\n *\n * This can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.\n *\n * @example\n * Registering for a webhook when a merchant uninstalls your app.\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/webhooks.jsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n *\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * await db.session.deleteMany({ where: { shop } });\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n * throw new Response();\n * };\n * ```\n */\n webhooks?: WebhookConfig;\n\n /**\n * Functions to call at key places during your apps lifecycle.\n *\n * These functions are called in the context of the request that triggered them. This means you can access the session.\n *\n * @example\n * Seeding your database custom data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * seedStoreData({session})\n * }\n * },\n * // ...etc\n * });\n * ```\n */\n hooks?: HooksConfig;\n\n /**\n * Does your app render embedded inside the Shopify Admin or on its own.\n *\n * Unless you have very specific needs, this should be true.\n *\n * @defaultValue `true`\n */\n isEmbeddedApp?: boolean;\n\n /**\n * How your app is distributed. Default is `AppDistribution.AppStore`.\n *\n * {@link https://shopify.dev/docs/apps/distribution}\n */\n distribution?: AppDistribution;\n\n /**\n * What version of Shopify's Admin API's would you like to use.\n *\n * {@link https://shopify.dev/docs/api/}\n *\n * @defaultValue `LATEST_API_VERSION` from `@shopify/shopify-app-remix`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * ```\n */\n apiVersion?: ApiVersion;\n\n /**\n * A path that Shopify can reserve for auth related endpoints.\n *\n * This must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.\n *\n * @default `\"/auth\"`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/auth/$.jsx\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * await authenticate.admin(request);\n *\n * return null\n * }\n * ```\n */\n authPathPrefix?: string;\n\n /**\n * Features that will be introduced in future releases of this package.\n *\n * You can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future\n * releases in advance and provide feedback on the new features.\n */\n future?: Future;\n}" - }, - "WebhookConfig": { - "filePath": "src/server/config-types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookConfig", - "value": "Record", - "description": "", - "members": [] - }, - "HooksConfig": { - "filePath": "src/server/config-types.ts", - "name": "HooksConfig", - "description": "", - "members": [ - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "afterAuth", - "value": "(options: AfterAuthOptions) => void | Promise", - "description": "A function to call after a merchant installs your app", - "isOptional": true, + "name": "sessionStorage", + "value": "Storage", + "description": "An adaptor for storing sessions in your database of choice.\n\nShopify provides multiple session storage adaptors and you can create your own.\n\n\n\n\n", "examples": [ { - "title": "Registering webhooks and seeding data when a merchant installs your app", - "description": "", + "title": "Storing sessions with Prisma", + "description": "Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n seedStoreData({session})\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n // ... etc\n sessionStorage: new PrismaSessionStorage(prisma),\n});\nexport default shopify;", "title": "Example" } ] } ] - } - ], - "value": "interface HooksConfig {\n /**\n * A function to call after a merchant installs your app\n *\n * @param context - An object with context about the request that triggered the hook.\n * @param context.session - The session of the merchant that installed your app. This is the output of sessionStorage.loadSession in case people want to load their own.\n * @param context.admin - An object with access to the Shopify Admin API's.\n *\n * @example\n * Registering webhooks and seeding data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * seedStoreData({session})\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n afterAuth?: (options: AfterAuthOptions) => void | Promise;\n}" - }, - "AfterAuthOptions": { - "filePath": "src/server/config-types.ts", - "name": "AfterAuthOptions", - "description": "", - "members": [ + }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "" + "name": "useOnlineTokens", + "value": "boolean", + "description": "Whether your app use online or offline tokens.\n\nIf your app uses online tokens, then both online and offline tokens will be saved to your database. This ensures your app can perform background jobs.\n\n\n\n\n", + "isOptional": true, + "defaultValue": "`false`" }, { "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "" + "name": "userAgentPrefix", + "value": "string", + "description": "The user agent prefix to use for API requests.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "webhooks", + "value": "WebhookConfig", + "description": "The config for the webhook topics your app would like to subscribe to.\n\n\n\n\n\n\n\nThis can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.", + "isOptional": true, + "examples": [ + { + "title": "Registering for a webhook when a merchant uninstalls your app", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/webhooks.jsx\nimport { ActionFunctionArgs } from \"@remix-run/node\";\n\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n await db.session.deleteMany({ where: { shop } });\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n throw new Response();\n};", + "title": "/app/shopify.server.ts" + } + ] + } + ] } ], - "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" + "value": "export interface AppConfigArg<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n Storage extends SessionStorage = SessionStorage,\n Future extends FutureFlagOptions = FutureFlagOptions,\n> extends Omit<\n ApiConfigArg>,\n | 'hostName'\n | 'hostScheme'\n | 'isEmbeddedApp'\n | 'apiVersion'\n | 'isCustomStoreApp'\n | 'future'\n > {\n /**\n * The URL your app is running on.\n *\n * The `@shopify/cli` provides this URL as `process.env.SHOPIFY_APP_URL`. For development this is probably a tunnel URL that points to your local machine. If this is a production app, this is your production URL.\n */\n appUrl: string;\n\n /**\n * An adaptor for storing sessions in your database of choice.\n *\n * Shopify provides multiple session storage adaptors and you can create your own.\n *\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options}\n *\n * @example\n * Storing sessions with Prisma.\n * Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.\n * ```ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n *\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * // ... etc\n * sessionStorage: new PrismaSessionStorage(prisma),\n * });\n * export default shopify;\n * ```\n */\n sessionStorage: Storage;\n\n /**\n * Whether your app use online or offline tokens.\n *\n * If your app uses online tokens, then both online and offline tokens will be saved to your database. This ensures your app can perform background jobs.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/access-modes}\n *\n * @defaultValue `false`\n */\n useOnlineTokens?: boolean;\n\n /**\n * The config for the webhook topics your app would like to subscribe to.\n *\n * {@link https://shopify.dev/docs/apps/webhooks}\n *\n * This can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.\n *\n * @example\n * Registering for a webhook when a merchant uninstalls your app.\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/webhooks.jsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n *\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * await db.session.deleteMany({ where: { shop } });\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n * throw new Response();\n * };\n * ```\n */\n webhooks?: WebhookConfig;\n\n /**\n * Functions to call at key places during your apps lifecycle.\n *\n * These functions are called in the context of the request that triggered them. This means you can access the session.\n *\n * @example\n * Seeding your database custom data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * seedStoreData({session})\n * }\n * },\n * // ...etc\n * });\n * ```\n */\n hooks?: HooksConfig;\n\n /**\n * Does your app render embedded inside the Shopify Admin or on its own.\n *\n * Unless you have very specific needs, this should be true.\n *\n * @defaultValue `true`\n */\n isEmbeddedApp?: boolean;\n\n /**\n * How your app is distributed. Default is `AppDistribution.AppStore`.\n *\n * {@link https://shopify.dev/docs/apps/distribution}\n */\n distribution?: AppDistribution;\n\n /**\n * What version of Shopify's Admin API's would you like to use.\n *\n * {@link https://shopify.dev/docs/api/}\n *\n * @defaultValue `LATEST_API_VERSION` from `@shopify/shopify-app-remix`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * ```\n */\n apiVersion?: ApiVersion;\n\n /**\n * A path that Shopify can reserve for auth related endpoints.\n *\n * This must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.\n *\n * @default `\"/auth\"`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/auth/$.jsx\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * await authenticate.admin(request);\n *\n * return null\n * }\n * ```\n */\n authPathPrefix?: string;\n\n /**\n * Features that will be introduced in future releases of this package.\n *\n * You can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future\n * releases in advance and provide feedback on the new features.\n */\n future?: Future;\n}" }, "BillingConfig": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -13247,7 +13189,7 @@ "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "BillingConfigItem", - "value": "FeatureEnabled extends true\n ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan\n : BillingConfigLegacyItem", + "value": "FeatureEnabled extends true\n ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan\n : BillingConfigLegacyItem", "description": "" }, "BillingConfigOneTimePlan": { @@ -13255,13 +13197,6 @@ "name": "BillingConfigOneTimePlan", "description": "", "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.OneTime", - "description": "Interval for this plan.\n\nMust be set to `OneTime`." - }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", @@ -13275,6 +13210,13 @@ "name": "currencyCode", "value": "string", "description": "Currency code for this plan." + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.OneTime", + "description": "Interval for this plan.\n\nMust be set to `OneTime`." } ], "value": "export interface BillingConfigOneTimePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `OneTime`.\n */\n interval: BillingInterval.OneTime;\n}" @@ -13284,54 +13226,31 @@ "name": "BillingConfigSubscriptionLineItemPlan", "description": "", "members": [ - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "replacementBehavior", - "value": "BillingReplacementBehavior", - "description": "The replacement behavior to use for this plan.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "trialDays", - "value": "number", - "description": "How many trial days to give before charging for this plan.", - "isOptional": true - }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", "name": "lineItems", "value": "(BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[]", "description": "The line items for this plan." - } - ], - "value": "export interface BillingConfigSubscriptionLineItemPlan {\n /**\n * The replacement behavior to use for this plan.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The line items for this plan.\n */\n lineItems: (BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[];\n}" - }, - "BillingReplacementBehavior": { - "filePath": "../shopify-api/lib/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingReplacementBehavior", - "value": "export enum BillingReplacementBehavior {\n ApplyImmediately = 'APPLY_IMMEDIATELY',\n ApplyOnNextBillingCycle = 'APPLY_ON_NEXT_BILLING_CYCLE',\n Standard = 'STANDARD',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/types.ts", - "name": "ApplyImmediately", - "value": "APPLY_IMMEDIATELY" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "ApplyOnNextBillingCycle", - "value": "APPLY_ON_NEXT_BILLING_CYCLE" + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "The replacement behavior to use for this plan.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "Standard", - "value": "STANDARD" + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "How many trial days to give before charging for this plan.", + "isOptional": true } - ] + ], + "value": "export interface BillingConfigSubscriptionLineItemPlan {\n /**\n * The replacement behavior to use for this plan.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The line items for this plan.\n */\n lineItems: (BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[];\n}" }, "BillingConfigRecurringLineItem": { "filePath": "../shopify-api/lib/billing/types.ts", @@ -13341,31 +13260,31 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", - "description": "The recurring interval for this line item.\n\nMust be either `Every30Days` or `Annual`." + "name": "amount", + "value": "number", + "description": "The amount to charge for this line item." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "BillingConfigSubscriptionPlanDiscount", - "description": "An optional discount to apply for this line item.", - "isOptional": true + "name": "currencyCode", + "value": "string", + "description": "The currency code for this line item." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "The amount to charge for this line item." + "name": "discount", + "value": "BillingConfigSubscriptionPlanDiscount", + "description": "An optional discount to apply for this line item.", + "isOptional": true }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "The currency code for this line item." + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "description": "The recurring interval for this line item.\n\nMust be either `Every30Days` or `Annual`." } ], "value": "export interface BillingConfigRecurringLineItem extends BillingConfigLineItem {\n /**\n * The recurring interval for this line item.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /**\n * An optional discount to apply for this line item.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" @@ -13401,34 +13320,57 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Usage", - "description": "The usage interval for this line item.\n\nMust be set to `Usage`." + "name": "amount", + "value": "number", + "description": "The amount to charge for this line item." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "terms", + "name": "currencyCode", "value": "string", - "description": "Usage terms for this line item." + "description": "The currency code for this line item." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "The amount to charge for this line item." + "name": "interval", + "value": "BillingInterval.Usage", + "description": "The usage interval for this line item.\n\nMust be set to `Usage`." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", + "name": "terms", "value": "string", - "description": "The currency code for this line item." + "description": "Usage terms for this line item." } ], "value": "export interface BillingConfigUsageLineItem extends BillingConfigLineItem {\n /**\n * The usage interval for this line item.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this line item.\n */\n terms: string;\n}" }, + "BillingReplacementBehavior": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingReplacementBehavior", + "value": "export enum BillingReplacementBehavior {\n ApplyImmediately = 'APPLY_IMMEDIATELY',\n ApplyOnNextBillingCycle = 'APPLY_ON_NEXT_BILLING_CYCLE',\n Standard = 'STANDARD',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "ApplyImmediately", + "value": "APPLY_IMMEDIATELY" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "ApplyOnNextBillingCycle", + "value": "APPLY_ON_NEXT_BILLING_CYCLE" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Standard", + "value": "STANDARD" + } + ] + }, "BillingConfigLegacyItem": { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "TypeAliasDeclaration", @@ -13444,25 +13386,16 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "Exclude", - "description": "Recurring interval for this plan.\n\nMust be either `Every30Days` or `Annual`." - }, - { - "filePath": "../shopify-api/lib/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "trialDays", + "name": "amount", "value": "number", - "description": "How many trial days to give before charging for this plan.", - "isOptional": true + "description": "Amount to charge for this plan." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "replacementBehavior", - "value": "BillingReplacementBehavior", - "description": "The behavior to use when replacing an existing subscription with a new one.", - "isOptional": true + "name": "currencyCode", + "value": "string", + "description": "Currency code for this plan." }, { "filePath": "../shopify-api/lib/billing/types.ts", @@ -13475,16 +13408,25 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "Amount to charge for this plan." + "name": "interval", + "value": "Exclude", + "description": "Recurring interval for this plan.\n\nMust be either `Every30Days` or `Annual`." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "Currency code for this plan." + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "The behavior to use when replacing an existing subscription with a new one.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "How many trial days to give before charging for this plan.", + "isOptional": true } ], "value": "export interface BillingConfigSubscriptionPlan extends BillingConfigPlan {\n /**\n * Recurring interval for this plan.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: Exclude;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * The discount to apply to this plan.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" @@ -13504,24 +13446,23 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Usage", - "description": "Interval for this plan.\n\nMust be set to `Usage`." + "name": "amount", + "value": "number", + "description": "Amount to charge for this plan." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "usageTerms", + "name": "currencyCode", "value": "string", - "description": "Usage terms for this plan." + "description": "Currency code for this plan." }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "trialDays", - "value": "number", - "description": "How many trial days to give before charging for this plan.", - "isOptional": true + "name": "interval", + "value": "BillingInterval.Usage", + "description": "Interval for this plan.\n\nMust be set to `Usage`." }, { "filePath": "../shopify-api/lib/billing/types.ts", @@ -13534,20 +13475,71 @@ { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", + "name": "trialDays", "value": "number", - "description": "Amount to charge for this plan." + "description": "How many trial days to give before charging for this plan.", + "isOptional": true }, { "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", + "name": "usageTerms", "value": "string", - "description": "Currency code for this plan." + "description": "Usage terms for this plan." } ], "value": "export interface BillingConfigUsagePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this plan.\n */\n usageTerms: string;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n}" }, + "HooksConfig": { + "filePath": "src/server/config-types.ts", + "name": "HooksConfig", + "description": "", + "members": [ + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "afterAuth", + "value": "(options: AfterAuthOptions) => void | Promise", + "description": "A function to call after a merchant installs your app", + "isOptional": true, + "examples": [ + { + "title": "Registering webhooks and seeding data when a merchant installs your app", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n seedStoreData({session})\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", + "title": "Example" + } + ] + } + ] + } + ], + "value": "interface HooksConfig {\n /**\n * A function to call after a merchant installs your app\n *\n * @param context - An object with context about the request that triggered the hook.\n * @param context.session - The session of the merchant that installed your app. This is the output of sessionStorage.loadSession in case people want to load their own.\n * @param context.admin - An object with access to the Shopify Admin API's.\n *\n * @example\n * Registering webhooks and seeding data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * seedStoreData({session})\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n afterAuth?: (options: AfterAuthOptions) => void | Promise;\n}" + }, + "AfterAuthOptions": { + "filePath": "src/server/config-types.ts", + "name": "AfterAuthOptions", + "description": "", + "members": [ + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "" + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + } + ], + "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" + }, "LogFunction": { "filePath": "../shopify-api/lib/base-types.ts", "name": "LogFunction", @@ -13601,6 +13593,14 @@ "value": 3 } ] + }, + "WebhookConfig": { + "filePath": "src/server/config-types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookConfig", + "value": "Record", + "description": "", + "members": [] } } }, @@ -13617,9 +13617,9 @@ { "filePath": "src/server/future/flags.ts", "syntaxKind": "PropertySignature", - "name": "v3_webhookAdminContext", + "name": "unstable_newEmbeddedAuthStrategy", "value": "boolean", - "description": "When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.", + "description": "When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange). This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).", "isOptional": true, "defaultValue": "false" }, @@ -13643,9 +13643,9 @@ { "filePath": "src/server/future/flags.ts", "syntaxKind": "PropertySignature", - "name": "unstable_newEmbeddedAuthStrategy", + "name": "v3_webhookAdminContext", "value": "boolean", - "description": "When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange). This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).", + "description": "When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.", "isOptional": true, "defaultValue": "false" } @@ -13689,16 +13689,21 @@ "description": "", "exampleGroups": [ { - "title": "sessionStorage", + "title": "addDocumentResponseHeaders", "examples": [ { - "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", + "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", "codeblock": { - "title": "Storing sessions with Prisma", + "title": "Return headers on all requests", "tabs": [ { - "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", + "title": "~/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", + "language": "typescript" + }, + { + "title": "entry.server.tsx", + "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n <RemixServer context={remixContext} url={request.url} />\n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"<!DOCTYPE html>\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", "language": "typescript" } ] @@ -13707,21 +13712,21 @@ ] }, { - "title": "addDocumentResponseHeaders", + "title": "authenticate", "examples": [ { - "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", + "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", "codeblock": { - "title": "Return headers on all requests", + "title": "Authenticate Shopify requests", "tabs": [ { - "title": "~/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", + "title": "/app/shopify.server.ts", + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", "language": "typescript" }, { - "title": "entry.server.tsx", - "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n <RemixServer context={remixContext} url={request.url} />\n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"<!DOCTYPE html>\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", + "title": "/app/routes/**\\/*.jsx", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", "language": "typescript" } ] @@ -13748,21 +13753,16 @@ ] }, { - "title": "authenticate", + "title": "sessionStorage", "examples": [ { - "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", "codeblock": { - "title": "Authenticate Shopify requests", - "tabs": [ - { - "title": "/app/shopify.server.ts", - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", - "language": "typescript" - }, + "title": "Storing sessions with Prisma", + "tabs": [ { - "title": "/app/routes/**\\/*.jsx", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", "language": "typescript" } ] @@ -13856,6 +13856,43 @@ "name": "UnauthenticatedAdminContext", "description": "", "members": [ + { + "filePath": "src/server/unauthenticated/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the given store.", + "examples": [ + { + "title": "Performing a GET request to the REST API", + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await unauthenticated.admin(request);\n\n const response = await admin.rest.get(\n {\n path: \"/customers/count.json\"\n }\n );\n const customers = await response.json();\n\n return json({ customers });\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\n\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await unauthenticated.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + }, { "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", @@ -13874,38 +13911,97 @@ ] } ] + } + ], + "value": "export interface UnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use to get shop-specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.admin(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the given store.\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await unauthenticated.admin(request);\n *\n * const response = await admin.rest.get(\n * {\n * path: \"/customers/count.json\"\n * }\n * );\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n *\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await unauthenticated.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n admin: AdminApiContext;\n}" + }, + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `admin.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] }, { - "filePath": "src/server/unauthenticated/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the given store.", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, { "title": "Performing a GET request to the REST API", "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await unauthenticated.admin(request);\n\n const response = await admin.rest.get(\n {\n path: \"/customers/count.json\"\n }\n );\n const customers = await response.json();\n\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\n\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] }, { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", + "title": "Performing a POST request to the REST API", + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await unauthenticated.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -13913,7 +14009,172 @@ ] } ], - "value": "export interface UnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use to get shop-specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.admin(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the given store.\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await unauthenticated.admin(request);\n *\n * const response = await admin.rest.get(\n * {\n * path: \"/customers/count.json\"\n * }\n * );\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n *\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await unauthenticated.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n admin: AdminApiContext;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../shopify-api/lib/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "members": [ + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "April24", + "value": "2024-04" + }, + { + "filePath": "../shopify-api/lib/types.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" }, "Session": { "filePath": "../shopify-api/lib/session/session.ts", @@ -14029,9 +14290,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -14043,9 +14304,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -14058,23 +14319,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -14093,496 +14347,242 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "locale", - "value": "string", - "description": "The user's locale." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." - } - ], - "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" - }, - "AuthScopes": { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "name": "AuthScopes", - "description": "A class that represents a set of access token scopes.", - "members": [ - { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "has", - "value": "(scope: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes includes the given one." - }, - { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "equals", - "value": "(otherScopes: string | string[] | AuthScopes) => boolean", - "description": "Checks whether the current set of scopes equals the given one." - }, - { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "toString", - "value": "() => string", - "description": "Returns a comma-separated string with the current set of scopes." - }, - { - "filePath": "../shopify-api/lib/auth/scopes/index.ts", - "syntaxKind": "MethodDeclaration", - "name": "toArray", - "value": "() => any[]", - "description": "Returns an array with the current set of scopes." - } - ], - "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" - }, - "SessionParams": { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "SessionParams", - "description": "", - "members": [ - { - "filePath": "../shopify-api/lib/session/types.ts", - "name": "[key: string]", - "value": "any" - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The unique identifier for the session." - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "state", - "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "isOnline", - "value": "boolean", - "description": "Whether the access token in the session is online or offline." - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "accessToken", - "value": "string", - "description": "The access token for the session.", - "isOptional": true - }, - { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true - } - ], - "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" - }, - "StoredOnlineAccessInfo": { - "filePath": "../shopify-api/lib/session/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StoredOnlineAccessInfo", - "value": "Omit & {\n associated_user: Partial;\n}", - "description": "" - }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ - { - "filePath": "src/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", - "examples": [ - { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a GET request to the REST API", - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - }, - { - "title": "Handling GraphQL errors", - "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", + "value": "string", + "description": "The user's last name." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "locale", + "value": "string", + "description": "The user's locale." } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" + "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" }, - "RemixRestClient": { - "filePath": "src/server/clients/admin/rest.ts", - "name": "RemixRestClient", - "description": "", + "AuthScopes": { + "filePath": "../shopify-api/lib/auth/scopes/index.ts", + "name": "AuthScopes", + "description": "A class that represents a set of access token scopes.", "members": [ { - "filePath": "src/server/clients/admin/rest.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "get", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a GET request on the given path." + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes includes the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "post", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a POST request on the given path." + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "Checks whether the current set of scopes equals the given one." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "put", - "value": "(params: PostRequestParams) => Promise", - "description": "Performs a PUT request on the given path." + "name": "toString", + "value": "() => string", + "description": "Returns a comma-separated string with the current set of scopes." }, { - "filePath": "src/server/clients/admin/rest.ts", + "filePath": "../shopify-api/lib/auth/scopes/index.ts", "syntaxKind": "MethodDeclaration", - "name": "delete", - "value": "(params: GetRequestParams) => Promise", - "description": "Performs a DELETE request on the given path." + "name": "toArray", + "value": "() => any[]", + "description": "Returns an array with the current set of scopes." } ], - "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + "value": "class AuthScopes {\n public static SCOPE_DELIMITER = ',';\n\n private compressedScopes: Set;\n private expandedScopes: Set;\n\n constructor(scopes: string | string[] | AuthScopes | undefined) {\n let scopesArray: string[] = [];\n if (typeof scopes === 'string') {\n scopesArray = scopes.split(\n new RegExp(`${AuthScopes.SCOPE_DELIMITER}\\\\s*`),\n );\n } else if (Array.isArray(scopes)) {\n scopesArray = scopes;\n } else if (scopes) {\n scopesArray = Array.from(scopes.expandedScopes);\n }\n\n scopesArray = scopesArray\n .map((scope) => scope.trim())\n .filter((scope) => scope.length);\n\n const impliedScopes = this.getImpliedScopes(scopesArray);\n\n const scopeSet = new Set(scopesArray);\n const impliedSet = new Set(impliedScopes);\n\n this.compressedScopes = new Set(\n [...scopeSet].filter((x) => !impliedSet.has(x)),\n );\n this.expandedScopes = new Set([...scopeSet, ...impliedSet]);\n }\n\n /**\n * Checks whether the current set of scopes includes the given one.\n */\n public has(scope: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (scope instanceof AuthScopes) {\n other = scope;\n } else {\n other = new AuthScopes(scope);\n }\n\n return (\n other.toArray().filter((x) => !this.expandedScopes.has(x)).length === 0\n );\n }\n\n /**\n * Checks whether the current set of scopes equals the given one.\n */\n public equals(otherScopes: string | string[] | AuthScopes | undefined) {\n let other: AuthScopes;\n\n if (otherScopes instanceof AuthScopes) {\n other = otherScopes;\n } else {\n other = new AuthScopes(otherScopes);\n }\n\n return (\n this.compressedScopes.size === other.compressedScopes.size &&\n this.toArray().filter((x) => !other.has(x)).length === 0\n );\n }\n\n /**\n * Returns a comma-separated string with the current set of scopes.\n */\n public toString() {\n return this.toArray().join(AuthScopes.SCOPE_DELIMITER);\n }\n\n /**\n * Returns an array with the current set of scopes.\n */\n public toArray() {\n return [...this.compressedScopes];\n }\n\n private getImpliedScopes(scopesArray: string[]): string[] {\n return scopesArray.reduce((array: string[], current: string) => {\n const matches = current.match(/^(unauthenticated_)?write_(.*)$/);\n if (matches) {\n array.push(`${matches[1] ? matches[1] : ''}read_${matches[2]}`);\n }\n\n return array;\n }, []);\n }\n}" }, - "GetRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GetRequestParams", + "SessionParams": { + "filePath": "../shopify-api/lib/session/types.ts", + "name": "SessionParams", "description": "", "members": [ { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", + "name": "[key: string]", + "value": "any" + }, + { + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "path", + "name": "accessToken", "value": "string", - "description": "The path to the resource, relative to the API version root." + "description": "The access token for the session.", + "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "type", - "value": "DataType", - "description": "The type of data expected in the response.", + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "data", - "value": "string | Record", - "description": "The request body.", - "isOptional": true + "name": "id", + "value": "string", + "description": "The unique identifier for the session." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "query", - "value": "SearchParams", - "description": "Query parameters to be sent with the request.", - "isOptional": true + "name": "isOnline", + "value": "boolean", + "description": "Whether the access token in the session is online or offline." }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "extraHeaders", - "value": "HeaderParams", - "description": "Additional headers to be sent with the request.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { - "filePath": "../shopify-api/lib/clients/types.ts", + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "tries", - "value": "number", - "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true - } - ], - "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" - }, - "DataType": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "DataType", - "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", - "members": [ - { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "JSON", - "value": "application/json" }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "GraphQL", - "value": "application/graphql" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "The Shopify shop domain." }, { - "filePath": "../shopify-api/lib/clients/types.ts", - "name": "URLEncoded", - "value": "application/x-www-form-urlencoded" + "filePath": "../shopify-api/lib/session/types.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } - ] - }, - "HeaderParams": { - "filePath": "../shopify-api/lib/clients/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "HeaderParams", - "value": "Record", - "description": "Headers to be sent with the request.", - "members": [] + ], + "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" }, - "PostRequestParams": { - "filePath": "../shopify-api/lib/clients/types.ts", + "StoredOnlineAccessInfo": { + "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "PostRequestParams", - "value": "GetRequestParams & {\n data: Record | string;\n}", + "name": "StoredOnlineAccessInfo", + "value": "Omit & {\n associated_user: Partial;\n}", "description": "" }, - "GraphQLClient": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLClient", - "description": "", - "params": [ - { - "name": "query", - "description": "", - "value": "Operation extends keyof Operations", - "filePath": "src/server/clients/types.ts" - }, - { - "name": "options", - "description": "", - "value": "GraphQLQueryOptions", - "isOptional": true, - "filePath": "src/server/clients/types.ts" - } - ], - "returns": { - "filePath": "src/server/clients/types.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" - }, - "GraphQLQueryOptions": { - "filePath": "src/server/clients/types.ts", - "name": "GraphQLQueryOptions", + "GetRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", + "name": "data", + "value": "string | Record", + "description": "The request body.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "The version of the API to use for the request.", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to include in the request.", + "name": "path", + "value": "string", + "description": "The path to the resource, relative to the API version root." + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "Query parameters to be sent with the request.", "isOptional": true }, { - "filePath": "src/server/clients/types.ts", + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "PropertySignature", "name": "tries", "value": "number", - "description": "The total number of times to try the request if it fails.", + "description": "The maximum number of times the request can be made if it fails with a throttling or server error.", + "isOptional": true + }, + { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "The type of data expected in the response.", "isOptional": true } ], - "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + "value": "export interface GetRequestParams {\n /**\n * The path to the resource, relative to the API version root.\n */\n path: string;\n /**\n * The type of data expected in the response.\n */\n type?: DataType;\n /**\n * The request body.\n */\n data?: Record | string;\n /**\n * Query parameters to be sent with the request.\n */\n query?: SearchParams;\n /**\n * Additional headers to be sent with the request.\n */\n extraHeaders?: HeaderParams;\n /**\n * The maximum number of times the request can be made if it fails with a throttling or server error.\n */\n tries?: number;\n}" }, - "ApiVersion": { - "filePath": "../shopify-api/lib/types.ts", + "HeaderParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "Headers to be sent with the request.", + "members": [] + }, + "DataType": { + "filePath": "../shopify-api/lib/clients/types.ts", "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export enum ApiVersion {\n October22 = '2022-10',\n January23 = '2023-01',\n April23 = '2023-04',\n July23 = '2023-07',\n October23 = '2023-10',\n January24 = '2024-01',\n April24 = '2024-04',\n Unstable = 'unstable',\n}", + "name": "DataType", + "value": "export enum DataType {\n JSON = 'application/json',\n GraphQL = 'application/graphql',\n URLEncoded = 'application/x-www-form-urlencoded',\n}", "members": [ { - "filePath": "../shopify-api/lib/types.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../shopify-api/lib/types.ts", - "name": "January24", - "value": "2024-01" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "JSON", + "value": "application/json" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "April24", - "value": "2024-04" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "GraphQL", + "value": "application/graphql" }, { - "filePath": "../shopify-api/lib/types.ts", - "name": "Unstable", - "value": "unstable" + "filePath": "../shopify-api/lib/clients/types.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" } ] + }, + "PostRequestParams": { + "filePath": "../shopify-api/lib/clients/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "value": "GetRequestParams & {\n data: Record | string;\n}", + "description": "" } } } @@ -14600,24 +14600,6 @@ "examples": { "description": "", "exampleGroups": [ - { - "title": "session", - "examples": [ - { - "description": "Get your app's shop-specific data using the returned offline `session` object.", - "codeblock": { - "title": "Using the offline session", - "tabs": [ - { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const shop = getShopFromExternalRequest(request);\n const { session } = await unauthenticated.admin(shop);\n return json(await getMyAppData({shop: session.shop));\n};", - "language": "typescript" - } - ] - } - } - ] - }, { "title": "admin", "examples": [ @@ -14658,6 +14640,24 @@ } } ] + }, + { + "title": "session", + "examples": [ + { + "description": "Get your app's shop-specific data using the returned offline `session` object.", + "codeblock": { + "title": "Using the offline session", + "tabs": [ + { + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { unauthenticated } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const shop = getShopFromExternalRequest(request);\n const { session } = await unauthenticated.admin(shop);\n return json(await getMyAppData({shop: session.shop));\n};", + "language": "typescript" + } + ] + } + } + ] } ] } @@ -14842,9 +14842,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "expires_in", - "value": "number", - "description": "How long the access token is valid for, in seconds." + "name": "associated_user", + "value": "OnlineAccessUser", + "description": "The user associated with the access token." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -14856,9 +14856,9 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "associated_user", - "value": "OnlineAccessUser", - "description": "The user associated with the access token." + "name": "expires_in", + "value": "number", + "description": "How long the access token is valid for, in seconds." } ], "value": "export interface OnlineAccessInfo {\n /**\n * How long the access token is valid for, in seconds.\n */\n expires_in: number;\n /**\n * The effective set of scopes for the session.\n */\n associated_user_scope: string;\n /**\n * The user associated with the access token.\n */\n associated_user: OnlineAccessUser;\n}" @@ -14871,23 +14871,16 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "id", - "value": "number", - "description": "The user's ID." - }, - { - "filePath": "../shopify-api/lib/auth/oauth/types.ts", - "syntaxKind": "PropertySignature", - "name": "first_name", - "value": "string", - "description": "The user's first name." + "name": "account_owner", + "value": "boolean", + "description": "Whether the user is the account owner." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "last_name", - "value": "string", - "description": "The user's last name." + "name": "collaborator", + "value": "boolean", + "description": "Whether the user is a collaborator." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", @@ -14906,23 +14899,30 @@ { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "account_owner", - "value": "boolean", - "description": "Whether the user is the account owner." + "name": "first_name", + "value": "string", + "description": "The user's first name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "locale", + "name": "id", + "value": "number", + "description": "The user's ID." + }, + { + "filePath": "../shopify-api/lib/auth/oauth/types.ts", + "syntaxKind": "PropertySignature", + "name": "last_name", "value": "string", - "description": "The user's locale." + "description": "The user's last name." }, { "filePath": "../shopify-api/lib/auth/oauth/types.ts", "syntaxKind": "PropertySignature", - "name": "collaborator", - "value": "boolean", - "description": "Whether the user is a collaborator." + "name": "locale", + "value": "string", + "description": "The user's locale." } ], "value": "export interface OnlineAccessUser {\n /**\n * The user's ID.\n */\n id: number;\n /**\n * The user's first name.\n */\n first_name: string;\n /**\n * The user's last name.\n */\n last_name: string;\n /**\n * The user's email address.\n */\n email: string;\n /**\n * Whether the user has verified their email address.\n */\n email_verified: boolean;\n /**\n * Whether the user is the account owner.\n */\n account_owner: boolean;\n /**\n * The user's locale.\n */\n locale: string;\n /**\n * Whether the user is a collaborator.\n */\n collaborator: boolean;\n}" @@ -14976,23 +14976,25 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "id", + "name": "accessToken", "value": "string", - "description": "The unique identifier for the session." + "description": "The access token for the session.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The Shopify shop domain." + "name": "expires", + "value": "Date", + "description": "The date the access token expires.", + "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "state", + "name": "id", "value": "string", - "description": "The state of the session. Used for the OAuth authentication code flow." + "description": "The unique identifier for the session." }, { "filePath": "../shopify-api/lib/session/types.ts", @@ -15004,34 +15006,32 @@ { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "scope", - "value": "string", - "description": "The scopes for the access token.", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo | StoredOnlineAccessInfo", + "description": "Information on the user for the session. Only present for online sessions.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "expires", - "value": "Date", - "description": "The date the access token expires.", + "name": "scope", + "value": "string", + "description": "The scopes for the access token.", "isOptional": true }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "accessToken", + "name": "shop", "value": "string", - "description": "The access token for the session.", - "isOptional": true + "description": "The Shopify shop domain." }, { "filePath": "../shopify-api/lib/session/types.ts", "syntaxKind": "PropertySignature", - "name": "onlineAccessInfo", - "value": "OnlineAccessInfo | StoredOnlineAccessInfo", - "description": "Information on the user for the session. Only present for online sessions.", - "isOptional": true + "name": "state", + "value": "string", + "description": "The state of the session. Used for the OAuth authentication code flow." } ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo | StoredOnlineAccessInfo;\n /**\n * Additional properties of the session allowing for extension\n */\n [key: string]: any;\n}" @@ -15116,14 +15116,6 @@ "name": "GraphQLQueryOptions", "description": "", "members": [ - { - "filePath": "src/server/clients/types.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to pass to the operation.", - "isOptional": true - }, { "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", @@ -15147,6 +15139,14 @@ "value": "number", "description": "The total number of times to try the request if it fails.", "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true } ], "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index eac9c1486f..fb51d01567 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -43,9 +43,6 @@ export interface FutureFlags { // When adding new flags, use this format: // vX_myFutureFlag: Future extends FutureFlags ? Future['vX_myFutureFlag'] : false; export interface ApiFutureFlags { - v10_lineItemBilling?: Future extends FutureFlags - ? Future['v3_lineItemBilling'] - : false; v11_lineItemBilling: Future extends FutureFlags ? Future['v3_lineItemBilling'] : false; From 65325b86fdea6b269e7714289c735d19cd0f2b62 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Tue, 23 Apr 2024 09:59:49 -0500 Subject: [PATCH 17/32] changeset Add config test for alias Update types and rename flag Updates based on review --- .changeset/gorgeous-rivers-jump.md | 6 ++++ packages/apps/shopify-api/future/flags.ts | 13 ++++---- .../shopify-api/lib/__tests__/config.test.ts | 7 ++++ .../shopify-api/lib/__tests__/test-config.ts | 2 +- .../billing/__tests__/legacy-request.test.ts | 22 ++++++------- .../lib/billing/__tests__/request.test.ts | 2 +- .../apps/shopify-api/lib/billing/types.ts | 22 +++++++++++-- .../admin/__tests__/rest_client.test.ts | 32 +++++++++++++------ packages/apps/shopify-api/lib/config.ts | 4 +-- .../lib/logger/__tests__/logger.test.ts | 7 ++-- .../docs/generated/generated_docs_data.json | 4 +-- .../src/server/future/flags.ts | 2 +- .../src/server/shopify-app.ts | 2 +- 13 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 .changeset/gorgeous-rivers-jump.md diff --git a/.changeset/gorgeous-rivers-jump.md b/.changeset/gorgeous-rivers-jump.md new file mode 100644 index 0000000000..286687e939 --- /dev/null +++ b/.changeset/gorgeous-rivers-jump.md @@ -0,0 +1,6 @@ +--- +"@shopify/shopify-app-remix": patch +"@shopify/shopify-api": patch +--- + +Change v10_lineItemBilling flag to lineItemBilling diff --git a/packages/apps/shopify-api/future/flags.ts b/packages/apps/shopify-api/future/flags.ts index 9d41d99bbe..6f82208802 100644 --- a/packages/apps/shopify-api/future/flags.ts +++ b/packages/apps/shopify-api/future/flags.ts @@ -7,14 +7,14 @@ import {type ConfigInterface} from '../lib/base-types'; export interface FutureFlags { /** * Enable line item billing, to make billing configuration more similar to the GraphQL API. Default enabling of this - * feature has been moved to v11. Use v11_lineItemBilling instead. + * feature has been moved to v11. Use lineItemBilling instead. */ v10_lineItemBilling?: boolean; /** * Enable line item billing, to make billing configuration more similar to the GraphQL API. */ - v11_lineItemBilling?: boolean; + lineItemBilling?: boolean; } /** @@ -42,15 +42,16 @@ export function logDisabledFutureFlags( const logFlag = (flag: string, message: string) => logger.info(`Future flag ${flag} is disabled.\n\n ${message}\n`); - if (!config.future?.v11_lineItemBilling) { + if (!config.future?.lineItemBilling) { logFlag( - 'v11_lineItemBilling', + 'lineItemBilling', 'Enable this flag to use the new billing API, that supports multiple line items per plan.', ); } if (config.future?.v10_lineItemBilling) { - logger.info( - 'This feature will become enabled in v11. Use flag v11_lineItemBilling instead', + logger.deprecated( + '11.0.0', + 'v10_lineItemBilling will become enabled in v11. Use flag lineItemBilling instead', ); } } diff --git a/packages/apps/shopify-api/lib/__tests__/config.test.ts b/packages/apps/shopify-api/lib/__tests__/config.test.ts index 93a48536f9..bc6db75e84 100644 --- a/packages/apps/shopify-api/lib/__tests__/config.test.ts +++ b/packages/apps/shopify-api/lib/__tests__/config.test.ts @@ -169,4 +169,11 @@ describe('Config object', () => { expect(LATEST_API_VERSION).toEqual(enumVersions[enumVersions.length - 2]); }); + + it('can alias billing future flags', () => { + validParams.future = {v10_lineItemBilling: true}; + const config = validateConfig(validParams); + + expect(config.future?.lineItemBilling).toBeTruthy(); + }); }); diff --git a/packages/apps/shopify-api/lib/__tests__/test-config.ts b/packages/apps/shopify-api/lib/__tests__/test-config.ts index ab5850e3ba..dae759eb39 100644 --- a/packages/apps/shopify-api/lib/__tests__/test-config.ts +++ b/packages/apps/shopify-api/lib/__tests__/test-config.ts @@ -56,7 +56,7 @@ const TEST_FUTURE_FLAGS: Required<{ [key in keyof FutureFlags]: true; }> = { v10_lineItemBilling: true, - v11_lineItemBilling: true, + lineItemBilling: true, } as const; const TEST_CONFIG = { diff --git a/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts b/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts index 74cd7f4889..dfbad37e03 100644 --- a/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts +++ b/packages/apps/shopify-api/lib/billing/__tests__/legacy-request.test.ts @@ -23,7 +23,7 @@ const GRAPHQL_BASE_REQUEST = { interface TestConfigInterface { name: string; - billingConfig: BillingConfig | BillingConfig<{v11_lineItemBilling: false}>; + billingConfig: BillingConfig | BillingConfig<{lineItemBilling: false}>; paymentResponse: string; responseObject: any; errorResponse: string; @@ -195,7 +195,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: undefined, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -222,7 +222,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -259,7 +259,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -296,7 +296,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -328,7 +328,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -386,7 +386,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -417,7 +417,7 @@ describe('shopify.billing.request', () => { testConfig({ billing: config.billingConfig, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -497,7 +497,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -530,7 +530,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); @@ -568,7 +568,7 @@ describe('shopify.billing.request', () => { }, }, future: { - v11_lineItemBilling: false, + lineItemBilling: false, }, }), ); diff --git a/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts b/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts index d3d3117829..5c1a97abe7 100644 --- a/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts +++ b/packages/apps/shopify-api/lib/billing/__tests__/request.test.ts @@ -23,7 +23,7 @@ const GRAPHQL_BASE_REQUEST = { interface TestConfigInterface { name: string; - billingConfig: BillingConfig<{v11_lineItemBilling: true}>; + billingConfig: BillingConfig<{lineItemBilling: true}>; paymentResponse: string; responseObject: any; errorResponse: string; diff --git a/packages/apps/shopify-api/lib/billing/types.ts b/packages/apps/shopify-api/lib/billing/types.ts index b9d9cd5254..9e4e04c2bf 100644 --- a/packages/apps/shopify-api/lib/billing/types.ts +++ b/packages/apps/shopify-api/lib/billing/types.ts @@ -4,7 +4,11 @@ import { RecurringBillingIntervals, } from '../types'; import {Session} from '../session/session'; -import {FeatureEnabled, FutureFlagOptions} from '../../future/flags'; +import { + FeatureEnabled, + FutureFlagOptions, + FutureFlags, +} from '../../future/flags'; export interface BillingConfigPlan { /** @@ -119,7 +123,7 @@ export type BillingConfigLegacyItem = export type BillingConfigItem< Future extends FutureFlagOptions = FutureFlagOptions, > = - FeatureEnabled extends true + FeatureEnabled extends true ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan : BillingConfigLegacyItem; @@ -134,7 +138,19 @@ export interface BillingConfig< /** * An individual billing plan. */ - [plan: string]: BillingConfigItem; + [plan: string]: BillingConfigItem< + Future & { + lineItemBilling: Future extends FutureFlags + ? Future['lineItemBilling'] extends true + ? true + : Future['lineItemBilling'] extends false + ? false + : Future['v10_lineItemBilling'] extends true + ? true + : false + : false; + } + >; } export type RequestConfigOverrides = diff --git a/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts b/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts index d129ddee2a..f94395e349 100644 --- a/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts +++ b/packages/apps/shopify-api/lib/clients/admin/__tests__/rest_client.test.ts @@ -555,7 +555,11 @@ describe('REST client', () => { }); it('logs deprecation headers to the console when they are present', async () => { - const shopify = shopifyApi(testConfig()); + const shopify = shopifyApi( + testConfig({ + future: {lineItemBilling: true, v10_lineItemBilling: false}, + }), + ); const client = new shopify.clients.Rest({session}); @@ -594,7 +598,7 @@ describe('REST client', () => { await client.get({path: '/url/path'}); // first call to .log is .debug with package and runtime info during initialization - expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.stringContaining('API Deprecation Notice'), @@ -615,7 +619,7 @@ describe('REST client', () => { data: postBody, }); - expect(shopify.config.logger.log).toHaveBeenCalledTimes(4); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.stringContaining( @@ -631,7 +635,11 @@ describe('REST client', () => { it('will wait 5 minutes before logging repeat deprecation alerts', async () => { jest.useFakeTimers(); - const shopify = shopifyApi(testConfig()); + const shopify = shopifyApi( + testConfig({ + future: {lineItemBilling: true, v10_lineItemBilling: false}, + }), + ); const client = new shopify.clients.Rest({session}); @@ -678,7 +686,7 @@ describe('REST client', () => { // this one should skip it await client.get({path: '/url/path'}); // first call to .log is .debug with package and runtime info during initialization - expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.anything(), @@ -691,7 +699,7 @@ describe('REST client', () => { // should warn a second time since 5 mins have passed await client.get({path: '/url/path'}); - expect(shopify.config.logger.log).toHaveBeenCalledTimes(4); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(3); expect(shopify.config.logger.log).toHaveBeenLastCalledWith( LogSeverity.Warning, expect.anything(), @@ -856,7 +864,11 @@ describe('REST client', () => { }); it('throws exceptions with response details on any other errors', async () => { - const shopify = shopifyApi(testConfig()); + const shopify = shopifyApi( + testConfig({ + future: {lineItemBilling: true, v10_lineItemBilling: false}, + }), + ); const client = new shopify.clients.Rest({session}); @@ -882,6 +894,7 @@ describe('REST client', () => { const shopify = shopifyApi( testConfig({ logger: {level: LogSeverity.Debug, httpRequests: false, log: jest.fn()}, + future: {lineItemBilling: true, v10_lineItemBilling: false}, }), ); @@ -893,13 +906,14 @@ describe('REST client', () => { await client.post({path: '/url/path', data}); // The first log call is the runtime info - expect(shopify.config.logger.log).toHaveBeenCalledTimes(2); + expect(shopify.config.logger.log).toHaveBeenCalledTimes(1); }); it('logs HTTP requests when the setting is on', async () => { const shopify = shopifyApi( testConfig({ logger: {level: LogSeverity.Debug, httpRequests: true, log: jest.fn()}, + future: {lineItemBilling: true, v10_lineItemBilling: false}, }), ); @@ -915,7 +929,7 @@ describe('REST client', () => { expect.anything(), ); const logMessage = (shopify.config.logger.log as jest.Mock).mock - .calls[2][1]; + .calls[1][1]; expect(logMessage).toContain('Received response for HTTP'); expect(logMessage).toContain( `https://test-shop.myshopify.io/admin/api/${shopify.config.apiVersion}/url/path`, diff --git a/packages/apps/shopify-api/lib/config.ts b/packages/apps/shopify-api/lib/config.ts index 0ce4d3b47f..7d2d8783f0 100644 --- a/packages/apps/shopify-api/lib/config.ts +++ b/packages/apps/shopify-api/lib/config.ts @@ -56,10 +56,10 @@ export function validateConfig( ); } - // Alias the v10_lineItemBilling flag to v11_lineItemBilling because we aren't releasing in v10 + // Alias the v10_lineItemBilling flag to lineItemBilling because we aren't releasing in v10 const future = params.future?.v10_lineItemBilling ? { - v11_lineItemBilling: params.future?.v10_lineItemBilling, + lineItemBilling: params.future?.v10_lineItemBilling, ...params.future, } : params.future; diff --git a/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts b/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts index f2a4cc3f71..19bcd093fa 100644 --- a/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts +++ b/packages/apps/shopify-api/lib/logger/__tests__/logger.test.ts @@ -183,7 +183,10 @@ describe('shopify.logger', () => { config, )}`, async () => { const shopify = shopifyApi( - testConfig({logger: {level: config.logLevel}}), + testConfig({ + logger: {level: config.logLevel}, + future: {lineItemBilling: true, v10_lineItemBilling: false}, + }), ); shopify.logger.log(LogSeverity.Debug, 'debug message'); @@ -194,7 +197,7 @@ describe('shopify.logger', () => { // We always log an INFO line with the runtime when starting up const expectedCallCount = config.expectedLevels.length + - (config.logLevel >= LogSeverity.Info ? 2 : 0); + (config.logLevel >= LogSeverity.Info ? 1 : 0); expect(shopify.config.logger.log).toHaveBeenCalledTimes( expectedCallCount, diff --git a/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json b/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json index 96d021c5c1..35cd79c94d 100644 --- a/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/apps/shopify-app-remix/docs/generated/generated_docs_data.json @@ -13189,7 +13189,7 @@ "filePath": "../shopify-api/lib/billing/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "BillingConfigItem", - "value": "FeatureEnabled extends true\n ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan\n : BillingConfigLegacyItem", + "value": "FeatureEnabled extends true\n ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan\n : BillingConfigLegacyItem", "description": "" }, "BillingConfigOneTimePlan": { @@ -15273,4 +15273,4 @@ ] } } -] \ No newline at end of file +] diff --git a/packages/apps/shopify-app-remix/src/server/future/flags.ts b/packages/apps/shopify-app-remix/src/server/future/flags.ts index fb51d01567..8f75f1f454 100644 --- a/packages/apps/shopify-app-remix/src/server/future/flags.ts +++ b/packages/apps/shopify-app-remix/src/server/future/flags.ts @@ -43,7 +43,7 @@ export interface FutureFlags { // When adding new flags, use this format: // vX_myFutureFlag: Future extends FutureFlags ? Future['vX_myFutureFlag'] : false; export interface ApiFutureFlags { - v11_lineItemBilling: Future extends FutureFlags + lineItemBilling: Future extends FutureFlags ? Future['v3_lineItemBilling'] : false; } diff --git a/packages/apps/shopify-app-remix/src/server/shopify-app.ts b/packages/apps/shopify-app-remix/src/server/shopify-app.ts index 8b81042b96..f6f79ddf85 100644 --- a/packages/apps/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/apps/shopify-app-remix/src/server/shopify-app.ts @@ -162,7 +162,7 @@ function deriveApi(appConfig: AppConfigArg) { apiVersion: appConfig.apiVersion ?? LATEST_API_VERSION, isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin, future: { - v11_lineItemBilling: appConfig.future?.v3_lineItemBilling, + lineItemBilling: appConfig.future?.v3_lineItemBilling, }, _logDisabledFutureFlags: false, }); From c5a9c9b8aea56f11233742cb277d447714746f0e Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:06:27 -0400 Subject: [PATCH 18/32] Fixing changesets for shopify-api release --- .changeset/sweet-drinks-film.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.changeset/sweet-drinks-film.md b/.changeset/sweet-drinks-film.md index 518182f76e..09cd898fa0 100644 --- a/.changeset/sweet-drinks-film.md +++ b/.changeset/sweet-drinks-film.md @@ -1,5 +1,17 @@ --- "@shopify/shopify-api": major +"@shopify/shopify-app-session-storage": patch +"@shopify/shopify-app-session-storage-drizzle": patch +"@shopify/shopify-app-session-storage-dynamodb": patch +"@shopify/shopify-app-session-storage-kv": patch +"@shopify/shopify-app-session-storage-memory": patch +"@shopify/shopify-app-session-storage-mongodb": patch +"@shopify/shopify-app-session-storage-mysql": patch +"@shopify/shopify-app-session-storage-postgresql": patch +"@shopify/shopify-app-session-storage-prisma": patch +"@shopify/shopify-app-session-storage-redis": patch +"@shopify/shopify-app-session-storage-sqlite": patch +"@shopify/shopify-app-session-storage-test-utils": patch --- Changed the package's build process to produce both ESM and CJS outputs. @@ -22,3 +34,4 @@ import 'node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/cli // Unchanged import '@shopify/shopify-api/adapters/node' ``` + From 7cdbc69e9de475dd6f7b61829b6d6e16f9ce799d Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:28:25 -0400 Subject: [PATCH 19/32] Attempt #2 to fix changeset config --- .changeset/config.json | 3 ++- .changeset/sweet-drinks-film.md | 12 ------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index ac8d21f84d..0be4842ff3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,6 +9,7 @@ "updateInternalDependencies": "patch", "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { - "updateInternalDependents": "always" + "updateInternalDependents": "always", + "onlyUpdatePeerDependentsWhenOutOfRange": true } } diff --git a/.changeset/sweet-drinks-film.md b/.changeset/sweet-drinks-film.md index 09cd898fa0..a269fd374c 100644 --- a/.changeset/sweet-drinks-film.md +++ b/.changeset/sweet-drinks-film.md @@ -1,17 +1,5 @@ --- "@shopify/shopify-api": major -"@shopify/shopify-app-session-storage": patch -"@shopify/shopify-app-session-storage-drizzle": patch -"@shopify/shopify-app-session-storage-dynamodb": patch -"@shopify/shopify-app-session-storage-kv": patch -"@shopify/shopify-app-session-storage-memory": patch -"@shopify/shopify-app-session-storage-mongodb": patch -"@shopify/shopify-app-session-storage-mysql": patch -"@shopify/shopify-app-session-storage-postgresql": patch -"@shopify/shopify-app-session-storage-prisma": patch -"@shopify/shopify-app-session-storage-redis": patch -"@shopify/shopify-app-session-storage-sqlite": patch -"@shopify/shopify-app-session-storage-test-utils": patch --- Changed the package's build process to produce both ESM and CJS outputs. From a8d4b3ea9712a09253266dc3cfda14c587299964 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:33:22 -0400 Subject: [PATCH 20/32] Make shopify-api dependency more flexible --- .changeset/config.json | 3 +-- .changeset/mean-plums-compare.md | 16 ++++++++++++++++ .../package.json | 2 +- .../package.json | 2 +- .../shopify-app-session-storage-kv/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 4 ++-- .../shopify-app-session-storage/package.json | 4 ++-- 14 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 .changeset/mean-plums-compare.md diff --git a/.changeset/config.json b/.changeset/config.json index 0be4842ff3..ac8d21f84d 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,7 +9,6 @@ "updateInternalDependencies": "patch", "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { - "updateInternalDependents": "always", - "onlyUpdatePeerDependentsWhenOutOfRange": true + "updateInternalDependents": "always" } } diff --git a/.changeset/mean-plums-compare.md b/.changeset/mean-plums-compare.md new file mode 100644 index 0000000000..193c67507e --- /dev/null +++ b/.changeset/mean-plums-compare.md @@ -0,0 +1,16 @@ +--- +"@shopify/shopify-app-session-storage-postgresql": patch +"@shopify/shopify-app-session-storage-test-utils": patch +"@shopify/shopify-app-session-storage-dynamodb": patch +"@shopify/shopify-app-session-storage-drizzle": patch +"@shopify/shopify-app-session-storage-mongodb": patch +"@shopify/shopify-app-session-storage-memory": patch +"@shopify/shopify-app-session-storage-prisma": patch +"@shopify/shopify-app-session-storage-sqlite": patch +"@shopify/shopify-app-session-storage-mysql": patch +"@shopify/shopify-app-session-storage-redis": patch +"@shopify/shopify-app-session-storage-kv": patch +"@shopify/shopify-app-session-storage": patch +--- + +Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json index 72ea8a51a0..dfe0ca43b5 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json @@ -45,7 +45,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4", "drizzle-orm": "^0.30.6" }, diff --git a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json index f17ce681f3..3747f8f7e8 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json @@ -47,7 +47,7 @@ "@aws-sdk/util-dynamodb": "^3.554.0" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-kv/package.json b/packages/apps/session-storage/shopify-app-session-storage-kv/package.json index 192257b162..555b67b120 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-kv/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-kv/package.json @@ -46,7 +46,7 @@ "semver": "^7.6.0" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-memory/package.json b/packages/apps/session-storage/shopify-app-session-storage-memory/package.json index ffb4d1e5a0..0711958794 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-memory/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-memory/package.json @@ -42,7 +42,7 @@ ], "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json b/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json index d1760469eb..0f8979b879 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json @@ -45,7 +45,7 @@ "mongodb": "^6.3.0" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json index 0412c47b67..995a0503ff 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json @@ -46,7 +46,7 @@ "mysql2": "^3.9.2" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json b/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json index 83fc5ad1bd..26d2fb59ad 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json @@ -47,7 +47,7 @@ "pg-connection-string": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json b/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json index 42b8ac6b30..b88441dab1 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json @@ -44,7 +44,7 @@ "dependencies": {}, "peerDependencies": { "@prisma/client": "^5.10.2", - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4", "prisma": "^5.12.1" }, diff --git a/packages/apps/session-storage/shopify-app-session-storage-redis/package.json b/packages/apps/session-storage/shopify-app-session-storage-redis/package.json index 7160f2e8f3..74925bf3c9 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-redis/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-redis/package.json @@ -46,7 +46,7 @@ "redis": "^4.6.12" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json b/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json index 029a7681a7..3eed7db135 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json @@ -46,7 +46,7 @@ "sqlite3": "^5.1.7" }, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { diff --git a/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json b/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json index fa66bad377..0e282d70b3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json @@ -44,11 +44,11 @@ ], "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-api": "^9.7.2", + "@shopify/shopify-api": "^9.7.2 || ^10.0.0", "@shopify/shopify-app-session-storage": "^2.1.4" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage/package.json b/packages/apps/session-storage/shopify-app-session-storage/package.json index 9f0ca56a6c..6a885dad6d 100644 --- a/packages/apps/session-storage/shopify-app-session-storage/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage/package.json @@ -43,10 +43,10 @@ ], "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.7.2" + "@shopify/shopify-api": "^9.7.2 || ^10.0.0" }, "devDependencies": { - "@shopify/shopify-api": "^9.7.2" + "@shopify/shopify-api": "^9.7.2 || ^10.0.0" }, "files": [ "dist/*", From 0c9ce10c5932f44be73a64930e9b2435add2e2b0 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Wed, 24 Apr 2024 14:39:04 -0500 Subject: [PATCH 21/32] Update test data --- packages/apps/shopify-api/lib/session/__tests__/session.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts index 2f2401f68b..5634f8881a 100644 --- a/packages/apps/shopify-api/lib/session/__tests__/session.test.ts +++ b/packages/apps/shopify-api/lib/session/__tests__/session.test.ts @@ -111,7 +111,7 @@ it('returns false if checking scopes and scopes are not equal', () => { state: 'not_same', isOnline: true, scope: 'test_scope', - expires: new Date(Date.now() - 1), + expires: new Date(Date.now() + 86400), }); expect(session.isActive('fake_scope')).toBeFalsy(); }); From fb850c346539543e9effc3db77a3aca2b7ae8b99 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Wed, 24 Apr 2024 16:17:58 -0400 Subject: [PATCH 22/32] Avoid major bumps in peer dep updates --- .changeset/config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/config.json b/.changeset/config.json index ac8d21f84d..0be4842ff3 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -9,6 +9,7 @@ "updateInternalDependencies": "patch", "ignore": [], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { - "updateInternalDependents": "always" + "updateInternalDependents": "always", + "onlyUpdatePeerDependentsWhenOutOfRange": true } } From 715a1204eb997fe456fe979535ffe93fea4648c9 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:35:52 -0400 Subject: [PATCH 23/32] Fix pacakge minification process --- .changeset/gentle-apricots-work.md | 6 ++++++ config/rollup/rollup-utils.js | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/gentle-apricots-work.md diff --git a/.changeset/gentle-apricots-work.md b/.changeset/gentle-apricots-work.md new file mode 100644 index 0000000000..53059f8dbc --- /dev/null +++ b/.changeset/gentle-apricots-work.md @@ -0,0 +1,6 @@ +--- +"@shopify/graphql-client": patch +"@shopify/storefront-api-client": patch +--- + +Fixed the minified build process to not mangle the `fetch` function, which led to requests failing in the final package. diff --git a/config/rollup/rollup-utils.js b/config/rollup/rollup-utils.js index 59e76422ac..6a2bc5825e 100644 --- a/config/rollup/rollup-utils.js +++ b/config/rollup/rollup-utils.js @@ -37,7 +37,14 @@ export function getPlugins({ declaration: false, moduleResolution: 'Bundler', }), - ...(minify === true ? [terser({keep_fnames: new RegExp('fetch')})] : []), + ...(minify === true + ? [ + terser({ + keep_fnames: new RegExp('fetch'), + mangle: {reserved: ['fetch']}, + }), + ] + : []), ]; } From 9eef6b9f8dc65b6f33a06aabdf18b1a457b9edcf Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Thu, 25 Apr 2024 10:34:09 -0500 Subject: [PATCH 24/32] Update migration guide with lineItemBilling flag updates --- .../apps/shopify-api/docs/migrating-to-v10.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/apps/shopify-api/docs/migrating-to-v10.md b/packages/apps/shopify-api/docs/migrating-to-v10.md index 03302cf6b6..e5ce2a5b00 100644 --- a/packages/apps/shopify-api/docs/migrating-to-v10.md +++ b/packages/apps/shopify-api/docs/migrating-to-v10.md @@ -7,3 +7,20 @@ This document outlines the changes you need to make to your app to migrate from The `scopes` property on the config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. Using both the `scopes` property and managed install can lead to unexpected behavior if these values are not kept in sync. + +If you are directly accessing the scopes from the config object, you should update your code to handle the case where the `scopes` property is not present. + +For example, but not limited to: +```js +// Before +const scopes = shopify.config.scopes.toString(); + +// After +const scopes = shopify.config.scopes + ? shopify.config.scopes.toString() + : ''; +``` + +## v10_lineItemBilling future flag has been renamed to lineItemBilling + +The `lineItemBilling` feature will **not** be enabled by default in v10. Because of this it has been renamed `lineItemBilling`. If you are using the `v10_lineItemBilling` future flag, you can optionally update your code to use the `lineItemBilling` feature flag instead. From 379206c9376984a0381c6942e7a51cb3132b81ab Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:41:53 -0400 Subject: [PATCH 25/32] Remove deprecations for v10 release --- .changeset/fifty-trains-brake.md | 7 +++ .changeset/tricky-actors-laugh.md | 5 ++ .../apps/shopify-api/docs/migrating-to-v10.md | 55 ++++++++++++++++++- .../lib/clients/admin/graphql/client.ts | 2 +- .../lib/clients/storefront/client.ts | 2 +- .../lib/webhooks/__tests__/validate.test.ts | 17 +++++- .../apps/shopify-api/lib/webhooks/process.ts | 4 ++ .../apps/shopify-api/lib/webhooks/validate.ts | 9 --- 8 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 .changeset/fifty-trains-brake.md create mode 100644 .changeset/tricky-actors-laugh.md diff --git a/.changeset/fifty-trains-brake.md b/.changeset/fifty-trains-brake.md new file mode 100644 index 0000000000..5902ea94e0 --- /dev/null +++ b/.changeset/fifty-trains-brake.md @@ -0,0 +1,7 @@ +--- +"@shopify/shopify-api": major +--- + +Webhook validation will now return a different `reason` value when the HMAC value is missing from the request. Instead of returning `WebhookValidationErrorReason.MissingHeaders` as it does for the other headers it validates, it will now return a new `WebhookValidationErrorReason.MissingHmac` error so this check matches other HMAC validations. + +If your app doesn't explicitly check for the error after calling `webhook.validate()`, you don't need to make any changes. diff --git a/.changeset/tricky-actors-laugh.md b/.changeset/tricky-actors-laugh.md new file mode 100644 index 0000000000..bd58f50c2c --- /dev/null +++ b/.changeset/tricky-actors-laugh.md @@ -0,0 +1,5 @@ +--- +"@shopify/shopify-api": patch +--- + +Postponed deprecating the GraphQL clients' `query` method because they haven't been deprecated for long enough. They'll be removed when v11 is released instead. diff --git a/packages/apps/shopify-api/docs/migrating-to-v10.md b/packages/apps/shopify-api/docs/migrating-to-v10.md index e5ce2a5b00..1470ee7f3d 100644 --- a/packages/apps/shopify-api/docs/migrating-to-v10.md +++ b/packages/apps/shopify-api/docs/migrating-to-v10.md @@ -11,16 +11,65 @@ Using both the `scopes` property and managed install can lead to unexpected beha If you are directly accessing the scopes from the config object, you should update your code to handle the case where the `scopes` property is not present. For example, but not limited to: + ```js // Before const scopes = shopify.config.scopes.toString(); // After -const scopes = shopify.config.scopes - ? shopify.config.scopes.toString() - : ''; +const scopes = shopify.config.scopes ? shopify.config.scopes.toString() : ''; ``` ## v10_lineItemBilling future flag has been renamed to lineItemBilling The `lineItemBilling` feature will **not** be enabled by default in v10. Because of this it has been renamed `lineItemBilling`. If you are using the `v10_lineItemBilling` future flag, you can optionally update your code to use the `lineItemBilling` feature flag instead. + +## Webhook validation no longer returns `MissingHeaders` when HMAC header is missing + +Webhook validation will now return a different `reason` value when the HMAC value is missing from the request. + +Instead of returning `WebhookValidationErrorReason.MissingHeaders` as it does for the other headers it validates, it will now return a new `WebhookValidationErrorReason.MissingHmac` error so this check matches other HMAC validations. + +```ts +import {type WebhookValidationErrorReason} from '@shopify/shopify-api'; + +const check = await shopify.webhooks.validate({ + rawBody: (req as any).rawBody, + rawRequest: req, + rawResponse: res, +}); + +// Before +if ( + !check.valid && + check.reason === WebhookValidationErrorReason.MissingHeaders && + check.missingHeaders.includes(ShopifyHeader.Hmac) +) { + // Handle error +} + +// After +if (!check.valid && check.reason === WebhookValidationErrorReason.MissingHmac) { + // Handle error +} +``` + +## Internal build paths changed to introduce ESM and CJS exports + +We started exporting both CJS and ESM outputs in this version, which affected how we export the files from the package internally. + +While this should have no effect on most apps, if you're directly importing a file from the package, its path will have changed. + +Regular imports for package files remain unchanged. + +```ts +// Before +import 'node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client'; +import '@shopify/shopify-api/adapters/node'; + +// After +// Add `dist/esm|cjs/` before the file +import 'node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client'; +// Unchanged +import '@shopify/shopify-api/adapters/node'; +``` diff --git a/packages/apps/shopify-api/lib/clients/admin/graphql/client.ts b/packages/apps/shopify-api/lib/clients/admin/graphql/client.ts index b1546a3415..1c5aa044d0 100644 --- a/packages/apps/shopify-api/lib/clients/admin/graphql/client.ts +++ b/packages/apps/shopify-api/lib/clients/admin/graphql/client.ts @@ -69,7 +69,7 @@ export class GraphqlClient { params: GraphqlParams, ): Promise> { logger(this.graphqlClass().config).deprecated( - '10.0.0', + '11.0.0', 'The query method is deprecated, and was replaced with the request method.\n' + 'See the migration guide: https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/migrating-to-v9.md#using-the-new-clients.', ); diff --git a/packages/apps/shopify-api/lib/clients/storefront/client.ts b/packages/apps/shopify-api/lib/clients/storefront/client.ts index bf838ac5ac..4ec92e1a5e 100644 --- a/packages/apps/shopify-api/lib/clients/storefront/client.ts +++ b/packages/apps/shopify-api/lib/clients/storefront/client.ts @@ -84,7 +84,7 @@ export class StorefrontClient { params: GraphqlParams, ): Promise> { logger(this.storefrontClass().config).deprecated( - '10.0.0', + '11.0.0', 'The query method is deprecated, and was replaced with the request method.\n' + 'See the migration guide: https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/migrating-to-v9.md#using-the-new-clients.', ); diff --git a/packages/apps/shopify-api/lib/webhooks/__tests__/validate.test.ts b/packages/apps/shopify-api/lib/webhooks/__tests__/validate.test.ts index 833f26b763..12ded14638 100644 --- a/packages/apps/shopify-api/lib/webhooks/__tests__/validate.test.ts +++ b/packages/apps/shopify-api/lib/webhooks/__tests__/validate.test.ts @@ -33,7 +33,6 @@ describe('shopify.webhooks.validate', () => { it.each([ {headers: {apiVersion: ''}, missingHeader: ShopifyHeader.ApiVersion}, {headers: {domain: ''}, missingHeader: ShopifyHeader.Domain}, - {headers: {hmac: ''}, missingHeader: ShopifyHeader.Hmac}, {headers: {topic: ''}, missingHeader: ShopifyHeader.Topic}, {headers: {webhookId: ''}, missingHeader: ShopifyHeader.WebhookId}, ])(`returns false on missing header $missingHeader`, async (config) => { @@ -73,6 +72,22 @@ describe('shopify.webhooks.validate', () => { }); }); + it('returns false on missing HMAC', async () => { + const shopify = shopifyApi(testConfig()); + const app = getTestApp(shopify); + + const response = await request(app) + .post('/webhooks') + .set(headers({hmac: ''})) + .send(rawBody) + .expect(200); + + expect(response.body.data).toEqual({ + valid: false, + reason: WebhookValidationErrorReason.MissingHmac, + }); + }); + it('returns false on invalid HMAC', async () => { const shopify = shopifyApi(testConfig()); const app = getTestApp(shopify); diff --git a/packages/apps/shopify-api/lib/webhooks/process.ts b/packages/apps/shopify-api/lib/webhooks/process.ts index 5f2f3cc77e..1124a0e969 100644 --- a/packages/apps/shopify-api/lib/webhooks/process.ts +++ b/packages/apps/shopify-api/lib/webhooks/process.ts @@ -181,6 +181,10 @@ async function handleInvalidWebhook( response.statusCode = StatusCode.BadRequest; response.errorMessage = 'No body was received when processing webhook'; break; + case WebhookValidationErrorReason.MissingHmac: + response.statusCode = StatusCode.BadRequest; + response.errorMessage = `Missing HMAC header in request`; + break; case WebhookValidationErrorReason.InvalidHmac: response.statusCode = StatusCode.Unauthorized; response.errorMessage = `Could not validate request HMAC`; diff --git a/packages/apps/shopify-api/lib/webhooks/validate.ts b/packages/apps/shopify-api/lib/webhooks/validate.ts index 3a5352b945..c235dc4a4c 100644 --- a/packages/apps/shopify-api/lib/webhooks/validate.ts +++ b/packages/apps/shopify-api/lib/webhooks/validate.ts @@ -48,15 +48,6 @@ export function validateFactory(config: ConfigInterface) { }); if (!validHmacResult.valid) { - // Deprecated: this is for backwards compatibility with the old HMAC validation - // This will be removed in the next major version, and missing_hmac will be returned instead of missing_header when the hmac is missing - if (validHmacResult.reason === ValidationErrorReason.MissingHmac) { - return { - valid: false, - reason: WebhookValidationErrorReason.MissingHeaders, - missingHeaders: [ShopifyHeader.Hmac], - }; - } if (validHmacResult.reason === ValidationErrorReason.InvalidHmac) { const log = logger(config); await log.debug( From fa94e857dd72911dbbf3c97a5c51c9c3e5d946e8 Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:08:01 -0400 Subject: [PATCH 26/32] Don't consider Shopify POS / Mobile requests as bots --- .changeset/honest-lizards-bow.md | 5 +++ .../__tests__/reject-bot-request.test.ts | 41 +++++++++++++++++++ .../helpers/reject-bot-request.ts | 20 ++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 .changeset/honest-lizards-bow.md create mode 100644 packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts diff --git a/.changeset/honest-lizards-bow.md b/.changeset/honest-lizards-bow.md new file mode 100644 index 0000000000..dda002ddc5 --- /dev/null +++ b/.changeset/honest-lizards-bow.md @@ -0,0 +1,5 @@ +--- +"@shopify/shopify-app-remix": patch +--- + +Fix an issue that rejected requests from Shopify POS / Mobile due to their User-Agents being labeled as bots. diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts new file mode 100644 index 0000000000..c588ec903f --- /dev/null +++ b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts @@ -0,0 +1,41 @@ +import {shopifyApp} from '../../../shopify-app'; +import {APP_URL, getThrownResponse, testConfig} from '../../../__test-helpers'; + +describe('Reject bot requests', () => { + // This test is relevant for the following one - our bot check rejects agents from google as bots by default, so this + // test helps prevent regressions in that behaviour. + it('rejects user agents that contain the word google', async () => { + // GIVEN + const shopify = shopifyApp(testConfig()); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(APP_URL, {headers: {'User-Agent': 'google'}}), + ); + + // THEN + expect(response.status).toBe(410); + }); + + it.each([ + // Accepted parts + 'Google Shopify POS/1.0.0/Android', + 'Google Shopify Mobile/Android/1.0.0', + // Full agents + 'Mozilla/5.0 (Linux; Android 14; sdk_gphone64_arm64 Build/UE1A.230829.036.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.136 Mobile Safari/537.36 com.jadedpixel.pos Shopify POS/9.7.0/Android/14/google/sdk_gphone64_arm64/development cart MobileMiddlewareSupported #', + 'Mozilla/5.0 (Linux; Android 14; sdk_gphone64_arm64 Build/UE1A.230829.036.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.136 Mobile Safari/537.36 Unframed Frameless Shopify Mobile/Android/9.157.0 (Build 1 with API 34 on Google sdk_gphone64_arm64 debug)', + ])('allows Shopify request with User-Agent: %s', async (shopifyAgent) => { + // GIVEN + const shopify = shopifyApp(testConfig()); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(APP_URL, {headers: {'User-Agent': shopifyAgent}}), + ); + + // THEN + expect(response.status).toBe(302); + }); +}); diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts index a64c7b6c58..1ddf9b6a2f 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts @@ -2,11 +2,29 @@ import {isbot} from 'isbot'; import type {BasicParams} from '../../types'; +const SHOPIFY_POS_USER_AGENT = /Shopify POS\/[0-9]+\.[0-9]+\.[0-9]+\/Android/; +const SHOPIFY_MOBILE_ANDROID_USER_AGENT = + /Shopify Mobile\/Android\/[0-9]+\.[0-9]+\.[0-9]+/; + +const SHOPIFY_USER_AGENTS = [ + SHOPIFY_POS_USER_AGENT, + SHOPIFY_MOBILE_ANDROID_USER_AGENT, +]; + export function respondToBotRequest( {logger}: BasicParams, request: Request, ): void | never { - if (isbot(request.headers.get('User-Agent'))) { + const userAgent = request.headers.get('User-Agent') ?? ''; + + // We call isbot below to prevent good (self-identifying) bots from triggering auth requests, but there are some + // Shopify-specific cases we want to allow that are identified as bots by isbot. + if (SHOPIFY_USER_AGENTS.some((agent) => agent.test(userAgent))) { + logger.debug('Request is from a Shopify agent, allow'); + return; + } + + if (isbot(userAgent)) { logger.debug('Request is from a bot, skipping auth'); throw new Response(undefined, {status: 410, statusText: 'Gone'}); } From 414fcebc48914076c4d11b50d1c0ae1e222b6b4b Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:24:08 -0400 Subject: [PATCH 27/32] Applying comments from review --- .../helpers/__tests__/reject-bot-request.test.ts | 4 ++-- .../server/authenticate/helpers/reject-bot-request.ts | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts index c588ec903f..60eba4e9e6 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/__tests__/reject-bot-request.test.ts @@ -20,8 +20,8 @@ describe('Reject bot requests', () => { it.each([ // Accepted parts - 'Google Shopify POS/1.0.0/Android', - 'Google Shopify Mobile/Android/1.0.0', + 'Google Shopify POS/something', + 'Google Shopify Mobile/something', // Full agents 'Mozilla/5.0 (Linux; Android 14; sdk_gphone64_arm64 Build/UE1A.230829.036.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.136 Mobile Safari/537.36 com.jadedpixel.pos Shopify POS/9.7.0/Android/14/google/sdk_gphone64_arm64/development cart MobileMiddlewareSupported #', 'Mozilla/5.0 (Linux; Android 14; sdk_gphone64_arm64 Build/UE1A.230829.036.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.136 Mobile Safari/537.36 Unframed Frameless Shopify Mobile/Android/9.157.0 (Build 1 with API 34 on Google sdk_gphone64_arm64 debug)', diff --git a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts index 1ddf9b6a2f..c6108edbb5 100644 --- a/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts +++ b/packages/apps/shopify-app-remix/src/server/authenticate/helpers/reject-bot-request.ts @@ -2,14 +2,10 @@ import {isbot} from 'isbot'; import type {BasicParams} from '../../types'; -const SHOPIFY_POS_USER_AGENT = /Shopify POS\/[0-9]+\.[0-9]+\.[0-9]+\/Android/; -const SHOPIFY_MOBILE_ANDROID_USER_AGENT = - /Shopify Mobile\/Android\/[0-9]+\.[0-9]+\.[0-9]+/; +const SHOPIFY_POS_USER_AGENT = /Shopify POS\//; +const SHOPIFY_MOBILE_USER_AGENT = /Shopify Mobile\//; -const SHOPIFY_USER_AGENTS = [ - SHOPIFY_POS_USER_AGENT, - SHOPIFY_MOBILE_ANDROID_USER_AGENT, -]; +const SHOPIFY_USER_AGENTS = [SHOPIFY_POS_USER_AGENT, SHOPIFY_MOBILE_USER_AGENT]; export function respondToBotRequest( {logger}: BasicParams, From 353882a64acb573d257e5dff87e70df2f982e255 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 25 Apr 2024 18:46:19 +0000 Subject: [PATCH 28/32] Version Packages --- .changeset/fifty-trains-brake.md | 7 ---- .changeset/fuzzy-months-hug.md | 2 - .changeset/gentle-apricots-work.md | 6 --- .changeset/gorgeous-rivers-jump.md | 6 --- .changeset/honest-lizards-bow.md | 5 --- .changeset/lovely-balloons-punch.md | 7 ---- .changeset/mean-plums-compare.md | 16 -------- .changeset/short-pillows-own.md | 5 --- .changeset/sweet-drinks-film.md | 25 ------------ .changeset/tall-brooms-reflect.md | 5 --- .changeset/tricky-actors-laugh.md | 5 --- .../api-clients/admin-api-client/CHANGELOG.md | 7 ++++ .../api-clients/admin-api-client/package.json | 4 +- .../api-clients/graphql-client/CHANGELOG.md | 6 +++ .../api-clients/graphql-client/package.json | 2 +- .../storefront-api-client/CHANGELOG.md | 8 ++++ .../storefront-api-client/package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 4 +- .../CHANGELOG.md | 6 +++ .../package.json | 6 +-- .../shopify-app-session-storage/CHANGELOG.md | 6 +++ .../shopify-app-session-storage/package.json | 4 +- packages/apps/shopify-api/CHANGELOG.md | 39 +++++++++++++++++++ packages/apps/shopify-api/package.json | 6 +-- .../apps/shopify-app-express/CHANGELOG.md | 16 ++++++++ .../apps/shopify-app-express/package.json | 8 ++-- packages/apps/shopify-app-remix/CHANGELOG.md | 21 ++++++++++ packages/apps/shopify-app-remix/package.json | 12 +++--- 47 files changed, 212 insertions(+), 132 deletions(-) delete mode 100644 .changeset/fifty-trains-brake.md delete mode 100644 .changeset/fuzzy-months-hug.md delete mode 100644 .changeset/gentle-apricots-work.md delete mode 100644 .changeset/gorgeous-rivers-jump.md delete mode 100644 .changeset/honest-lizards-bow.md delete mode 100644 .changeset/lovely-balloons-punch.md delete mode 100644 .changeset/mean-plums-compare.md delete mode 100644 .changeset/short-pillows-own.md delete mode 100644 .changeset/sweet-drinks-film.md delete mode 100644 .changeset/tall-brooms-reflect.md delete mode 100644 .changeset/tricky-actors-laugh.md diff --git a/.changeset/fifty-trains-brake.md b/.changeset/fifty-trains-brake.md deleted file mode 100644 index 5902ea94e0..0000000000 --- a/.changeset/fifty-trains-brake.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@shopify/shopify-api": major ---- - -Webhook validation will now return a different `reason` value when the HMAC value is missing from the request. Instead of returning `WebhookValidationErrorReason.MissingHeaders` as it does for the other headers it validates, it will now return a new `WebhookValidationErrorReason.MissingHmac` error so this check matches other HMAC validations. - -If your app doesn't explicitly check for the error after calling `webhook.validate()`, you don't need to make any changes. diff --git a/.changeset/fuzzy-months-hug.md b/.changeset/fuzzy-months-hug.md deleted file mode 100644 index a845151cc8..0000000000 --- a/.changeset/fuzzy-months-hug.md +++ /dev/null @@ -1,2 +0,0 @@ ---- ---- diff --git a/.changeset/gentle-apricots-work.md b/.changeset/gentle-apricots-work.md deleted file mode 100644 index 53059f8dbc..0000000000 --- a/.changeset/gentle-apricots-work.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@shopify/graphql-client": patch -"@shopify/storefront-api-client": patch ---- - -Fixed the minified build process to not mangle the `fetch` function, which led to requests failing in the final package. diff --git a/.changeset/gorgeous-rivers-jump.md b/.changeset/gorgeous-rivers-jump.md deleted file mode 100644 index 286687e939..0000000000 --- a/.changeset/gorgeous-rivers-jump.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@shopify/shopify-app-remix": patch -"@shopify/shopify-api": patch ---- - -Change v10_lineItemBilling flag to lineItemBilling diff --git a/.changeset/honest-lizards-bow.md b/.changeset/honest-lizards-bow.md deleted file mode 100644 index dda002ddc5..0000000000 --- a/.changeset/honest-lizards-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@shopify/shopify-app-remix": patch ---- - -Fix an issue that rejected requests from Shopify POS / Mobile due to their User-Agents being labeled as bots. diff --git a/.changeset/lovely-balloons-punch.md b/.changeset/lovely-balloons-punch.md deleted file mode 100644 index 156a3dc1e1..0000000000 --- a/.changeset/lovely-balloons-punch.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@shopify/shopify-api": major -"@shopify/shopify-app-express": patch -"@shopify/shopify-app-remix": patch ---- - -This `scopes` field on the API config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. diff --git a/.changeset/mean-plums-compare.md b/.changeset/mean-plums-compare.md deleted file mode 100644 index 193c67507e..0000000000 --- a/.changeset/mean-plums-compare.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -"@shopify/shopify-app-session-storage-postgresql": patch -"@shopify/shopify-app-session-storage-test-utils": patch -"@shopify/shopify-app-session-storage-dynamodb": patch -"@shopify/shopify-app-session-storage-drizzle": patch -"@shopify/shopify-app-session-storage-mongodb": patch -"@shopify/shopify-app-session-storage-memory": patch -"@shopify/shopify-app-session-storage-prisma": patch -"@shopify/shopify-app-session-storage-sqlite": patch -"@shopify/shopify-app-session-storage-mysql": patch -"@shopify/shopify-app-session-storage-redis": patch -"@shopify/shopify-app-session-storage-kv": patch -"@shopify/shopify-app-session-storage": patch ---- - -Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. diff --git a/.changeset/short-pillows-own.md b/.changeset/short-pillows-own.md deleted file mode 100644 index 99566792f5..0000000000 --- a/.changeset/short-pillows-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@shopify/shopify-api": patch ---- - -Fixing REST resource `find()` methods to fail when missing all ids, instead of defaulting to the same behaviour as `all()`. diff --git a/.changeset/sweet-drinks-film.md b/.changeset/sweet-drinks-film.md deleted file mode 100644 index a269fd374c..0000000000 --- a/.changeset/sweet-drinks-film.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -"@shopify/shopify-api": major ---- - -Changed the package's build process to produce both ESM and CJS outputs. - -While this should have no effect on most apps, if you're directly importing a file from the package, its path will have changed. -Regular imports for package files remain unchanged. - -Before: - -```ts -import 'node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client' -import '@shopify/shopify-api/adapters/node' -``` - -After: - -```ts -// Add `dist/esm|cjs/` before the file -import 'node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client' -// Unchanged -import '@shopify/shopify-api/adapters/node' -``` - diff --git a/.changeset/tall-brooms-reflect.md b/.changeset/tall-brooms-reflect.md deleted file mode 100644 index 70748e8472..0000000000 --- a/.changeset/tall-brooms-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@shopify/shopify-app-remix": patch ---- - -Removed `@remix-run/node` as a direct dependency. Any app using the Vercel adapter already needs `@remix-run/node`, so this shouldn't affect any apps. diff --git a/.changeset/tricky-actors-laugh.md b/.changeset/tricky-actors-laugh.md deleted file mode 100644 index bd58f50c2c..0000000000 --- a/.changeset/tricky-actors-laugh.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@shopify/shopify-api": patch ---- - -Postponed deprecating the GraphQL clients' `query` method because they haven't been deprecated for long enough. They'll be removed when v11 is released instead. diff --git a/packages/api-clients/admin-api-client/CHANGELOG.md b/packages/api-clients/admin-api-client/CHANGELOG.md index 036e820629..25d6b0e4b5 100644 --- a/packages/api-clients/admin-api-client/CHANGELOG.md +++ b/packages/api-clients/admin-api-client/CHANGELOG.md @@ -1,5 +1,12 @@ # @shopify/admin-api-client +## 0.2.9 + +### Patch Changes + +- Updated dependencies [715a120] + - @shopify/graphql-client@0.10.4 + ## 0.2.8 ### Patch Changes diff --git a/packages/api-clients/admin-api-client/package.json b/packages/api-clients/admin-api-client/package.json index 1474f255c9..8aa678c2ef 100644 --- a/packages/api-clients/admin-api-client/package.json +++ b/packages/api-clients/admin-api-client/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/admin-api-client", - "version": "0.2.8", + "version": "0.2.9", "description": "Shopify Admin API Client - A lightweight JS client to interact with Shopify's Admin API", "repository": { "type": "git", @@ -60,7 +60,7 @@ "dist/**/*.*" ], "dependencies": { - "@shopify/graphql-client": "^0.10.3" + "@shopify/graphql-client": "^0.10.4" }, "devDependencies": { "jest-environment-jsdom": "^29.5.0", diff --git a/packages/api-clients/graphql-client/CHANGELOG.md b/packages/api-clients/graphql-client/CHANGELOG.md index a28af1f772..26ee272b5e 100644 --- a/packages/api-clients/graphql-client/CHANGELOG.md +++ b/packages/api-clients/graphql-client/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/graphql-client +## 0.10.4 + +### Patch Changes + +- 715a120: Fixed the minified build process to not mangle the `fetch` function, which led to requests failing in the final package. + ## 0.10.3 ### Patch Changes diff --git a/packages/api-clients/graphql-client/package.json b/packages/api-clients/graphql-client/package.json index 3d20a6f0e4..7b0f6ad855 100644 --- a/packages/api-clients/graphql-client/package.json +++ b/packages/api-clients/graphql-client/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/graphql-client", - "version": "0.10.3", + "version": "0.10.4", "description": "Shopify GraphQL Client - A lightweight generic GraphQL JS client to interact with Shopify GraphQL APIs", "repository": { "type": "git", diff --git a/packages/api-clients/storefront-api-client/CHANGELOG.md b/packages/api-clients/storefront-api-client/CHANGELOG.md index d629de3356..9d2991f6a0 100644 --- a/packages/api-clients/storefront-api-client/CHANGELOG.md +++ b/packages/api-clients/storefront-api-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @shopify/storefront-api-client +## 0.3.4 + +### Patch Changes + +- 715a120: Fixed the minified build process to not mangle the `fetch` function, which led to requests failing in the final package. +- Updated dependencies [715a120] + - @shopify/graphql-client@0.10.4 + ## 0.3.3 ### Patch Changes diff --git a/packages/api-clients/storefront-api-client/package.json b/packages/api-clients/storefront-api-client/package.json index 049392b757..6aa2a1c21c 100644 --- a/packages/api-clients/storefront-api-client/package.json +++ b/packages/api-clients/storefront-api-client/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/storefront-api-client", - "version": "0.3.3", + "version": "0.3.4", "description": "Shopify Storefront API Client - A lightweight JS client to interact with Shopify's Storefront API", "repository": { "type": "git", @@ -83,7 +83,7 @@ "!node_modules" ], "dependencies": { - "@shopify/graphql-client": "^0.10.3" + "@shopify/graphql-client": "^0.10.4" }, "devDependencies": { "jest-environment-jsdom": "^29.5.0" diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-drizzle/CHANGELOG.md index bb6c6e2e39..9e836ad3ef 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-drizzle +## 1.1.2 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 1.1.1 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json index dfe0ca43b5..4dc3851f2d 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-drizzle", - "version": "1.1.1", + "version": "1.1.2", "description": "Shopify App Session Storage for Drizzle", "repository": { "type": "git", @@ -51,7 +51,7 @@ }, "devDependencies": { "@libsql/client": "^0.4.0-pre.7", - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4", + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "@types/better-sqlite3": "^7.6.9", "better-sqlite3": "^9.4.3", "drizzle-kit": "^0.20.12", diff --git a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/CHANGELOG.md index eff8e00bac..69541c766a 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-dynamodb +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json index 3747f8f7e8..926b598165 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-dynamodb", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for DynamoDB", "repository": { "type": "git", @@ -51,7 +51,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4" + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage-kv/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-kv/CHANGELOG.md index df4f36956f..f68815cd01 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-kv/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-kv/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-kv +## 3.0.6 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.5 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-kv/package.json b/packages/apps/session-storage/shopify-app-session-storage-kv/package.json index 555b67b120..7d12cc5990 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-kv/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-kv/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-kv", - "version": "3.0.5", + "version": "3.0.6", "description": "Shopify App Session Storage for KV", "repository": { "type": "git", @@ -51,7 +51,7 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20240208.0", - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4", + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "miniflare": "^3.20240304.2" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage-memory/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-memory/CHANGELOG.md index 8a06870524..d1a3accce3 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-memory/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-memory/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-memory +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-memory/package.json b/packages/apps/session-storage/shopify-app-session-storage-memory/package.json index 0711958794..18f6f4a4a4 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-memory/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-memory/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-memory", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for Memory", "repository": { "type": "git", @@ -46,7 +46,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4" + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage-mongodb/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-mongodb/CHANGELOG.md index a416537530..2e5f2fb9da 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mongodb/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-mongodb/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-mongodb +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json b/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json index 0f8979b879..5ff0d77bbe 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-mongodb/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-mongodb", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for MongoDB", "repository": { "type": "git", @@ -49,7 +49,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4" + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage-mysql/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-mysql/CHANGELOG.md index a96ea65ad8..c70522cc05 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mysql/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-mysql/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-mysql +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json index 995a0503ff..cd4dc52585 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-mysql", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for MySQL", "repository": { "type": "git", @@ -50,7 +50,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4" + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage-postgresql/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-postgresql/CHANGELOG.md index 66dfa5bd21..96dd5b1e2a 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-postgresql/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-postgresql/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-postgresql +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json b/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json index 26d2fb59ad..4b61fd0b32 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-postgresql/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-postgresql", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for PostgreSQL", "repository": { "type": "git", @@ -51,7 +51,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4", + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "@types/pg": "^8.11.5" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage-prisma/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-prisma/CHANGELOG.md index 838799535a..38b3ef6767 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-prisma/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-prisma/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-prisma +## 4.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 4.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json b/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json index b88441dab1..e50c1a91d0 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-prisma/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-prisma", - "version": "4.0.4", + "version": "4.0.5", "description": "Shopify App Session Storage for Prisma", "repository": { "type": "git", @@ -50,7 +50,7 @@ }, "devDependencies": { "@prisma/client": "^5.10.2", - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4", + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "prisma": "^5.12.1" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage-redis/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-redis/CHANGELOG.md index 2d638f5858..889b2fd7d0 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-redis/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-redis/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-redis +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-redis/package.json b/packages/apps/session-storage/shopify-app-session-storage-redis/package.json index 74925bf3c9..cd500ddee9 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-redis/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-redis/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-redis", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for Redis", "repository": { "type": "git", @@ -50,7 +50,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4" + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage-sqlite/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-sqlite/CHANGELOG.md index 86a2c30c6a..2b5b164d43 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-sqlite/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-sqlite/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-sqlite +## 3.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 3.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json b/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json index 3eed7db135..3c7662971a 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-sqlite/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-sqlite", - "version": "3.0.4", + "version": "3.0.5", "description": "Shopify App Session Storage for SQLite", "repository": { "type": "git", @@ -50,7 +50,7 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-app-session-storage-test-utils": "^2.0.4", + "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "@types/sqlite3": "^3.1.8" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage-test-utils/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage-test-utils/CHANGELOG.md index 9992728d73..e0b097f728 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-test-utils/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage-test-utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage-test-utils +## 2.0.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 2.0.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json b/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json index 0e282d70b3..47d25cfe77 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage-test-utils", - "version": "2.0.4", + "version": "2.0.5", "description": "Utilities for testing Shopify App Session Storage", "repository": { "type": "git", @@ -48,8 +48,8 @@ "@shopify/shopify-app-session-storage": "^2.1.4" }, "devDependencies": { - "@shopify/shopify-api": "^9.7.2 || ^10.0.0", - "@shopify/shopify-app-session-storage": "^2.1.4" + "@shopify/shopify-api": "^10.0.0", + "@shopify/shopify-app-session-storage": "^2.1.5" }, "files": [ "dist/*", diff --git a/packages/apps/session-storage/shopify-app-session-storage/CHANGELOG.md b/packages/apps/session-storage/shopify-app-session-storage/CHANGELOG.md index 118055faf7..bae15e5dc7 100644 --- a/packages/apps/session-storage/shopify-app-session-storage/CHANGELOG.md +++ b/packages/apps/session-storage/shopify-app-session-storage/CHANGELOG.md @@ -1,5 +1,11 @@ # @shopify/shopify-app-session-storage +## 2.1.5 + +### Patch Changes + +- a8d4b3e: Updated @shopify/shopify-api dependency to also allow v10+ since there were no breaking changes affecting this package. + ## 2.1.4 ### Patch Changes diff --git a/packages/apps/session-storage/shopify-app-session-storage/package.json b/packages/apps/session-storage/shopify-app-session-storage/package.json index 6a885dad6d..4457fdade6 100644 --- a/packages/apps/session-storage/shopify-app-session-storage/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-session-storage", - "version": "2.1.4", + "version": "2.1.5", "description": "Shopify App Session Storage - abstract class", "repository": { "type": "git", @@ -46,7 +46,7 @@ "@shopify/shopify-api": "^9.7.2 || ^10.0.0" }, "devDependencies": { - "@shopify/shopify-api": "^9.7.2 || ^10.0.0" + "@shopify/shopify-api": "^10.0.0" }, "files": [ "dist/*", diff --git a/packages/apps/shopify-api/CHANGELOG.md b/packages/apps/shopify-api/CHANGELOG.md index e2fb4c8412..f6f5ad3084 100644 --- a/packages/apps/shopify-api/CHANGELOG.md +++ b/packages/apps/shopify-api/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## 10.0.0 + +### Major Changes + +- 379206c: Webhook validation will now return a different `reason` value when the HMAC value is missing from the request. Instead of returning `WebhookValidationErrorReason.MissingHeaders` as it does for the other headers it validates, it will now return a new `WebhookValidationErrorReason.MissingHmac` error so this check matches other HMAC validations. + + If your app doesn't explicitly check for the error after calling `webhook.validate()`, you don't need to make any changes. + +- 637c6c3: This `scopes` field on the API config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. +- 61576be: Changed the package's build process to produce both ESM and CJS outputs. + + While this should have no effect on most apps, if you're directly importing a file from the package, its path will have changed. + Regular imports for package files remain unchanged. + + Before: + + ```ts + import "node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client"; + import "@shopify/shopify-api/adapters/node"; + ``` + + After: + + ```ts + // Add `dist/esm|cjs/` before the file + import "node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client"; + // Unchanged + import "@shopify/shopify-api/adapters/node"; + ``` + +### Patch Changes + +- 65325b8: Change v10_lineItemBilling flag to lineItemBilling +- 6f1a98e: Fixing REST resource `find()` methods to fail when missing all ids, instead of defaulting to the same behaviour as `all()`. +- 379206c: Postponed deprecating the GraphQL clients' `query` method because they haven't been deprecated for long enough. They'll be removed when v11 is released instead. +- Updated dependencies [715a120] + - @shopify/storefront-api-client@0.3.4 + - @shopify/admin-api-client@0.2.9 + ## 9.7.2 ### Patch Changes diff --git a/packages/apps/shopify-api/package.json b/packages/apps/shopify-api/package.json index 9daa99ca72..8e8f710f50 100644 --- a/packages/apps/shopify-api/package.json +++ b/packages/apps/shopify-api/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-api", - "version": "9.7.2", + "version": "10.0.0", "description": "Shopify API Library for Node - accelerate development with support for authentication, graphql proxy, webhooks", "types": "./dist/ts/lib/index.d.ts", "module": "./dist/esm/lib/index.mjs", @@ -73,9 +73,9 @@ "node-fetch": "^2.6.7" }, "dependencies": { - "@shopify/admin-api-client": "^0.2.8", + "@shopify/admin-api-client": "^0.2.9", "@shopify/network": "^3.2.1", - "@shopify/storefront-api-client": "^0.3.3", + "@shopify/storefront-api-client": "^0.3.4", "compare-versions": "^6.1.0", "isbot": "^5.1.4", "jose": "^5.2.3", diff --git a/packages/apps/shopify-app-express/CHANGELOG.md b/packages/apps/shopify-app-express/CHANGELOG.md index 7d3c1d9b2a..7a5aecae67 100644 --- a/packages/apps/shopify-app-express/CHANGELOG.md +++ b/packages/apps/shopify-app-express/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 4.1.6 + +### Patch Changes + +- 637c6c3: This `scopes` field on the API config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. +- Updated dependencies [379206c] +- Updated dependencies [65325b8] +- Updated dependencies [637c6c3] +- Updated dependencies [a8d4b3e] +- Updated dependencies [6f1a98e] +- Updated dependencies [61576be] +- Updated dependencies [379206c] + - @shopify/shopify-api@10.0.0 + - @shopify/shopify-app-session-storage-memory@3.0.5 + - @shopify/shopify-app-session-storage@2.1.5 + ## 4.1.5 ### Patch Changes diff --git a/packages/apps/shopify-app-express/package.json b/packages/apps/shopify-app-express/package.json index d4a07cddd2..26fe918c4f 100644 --- a/packages/apps/shopify-app-express/package.json +++ b/packages/apps/shopify-app-express/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-express", - "version": "4.1.5", + "version": "4.1.6", "description": "Shopify Express Middleware - to simplify the building of Shopify Apps with Express", "repository": { "type": "git", @@ -41,9 +41,9 @@ "Storefront API" ], "dependencies": { - "@shopify/shopify-api": "^9.7.2", - "@shopify/shopify-app-session-storage": "^2.1.4", - "@shopify/shopify-app-session-storage-memory": "^3.0.4", + "@shopify/shopify-api": "^10.0.0", + "@shopify/shopify-app-session-storage": "^2.1.5", + "@shopify/shopify-app-session-storage-memory": "^3.0.5", "cookie-parser": "^1.4.6", "express": "^4.19.2", "semver": "^7.6.0" diff --git a/packages/apps/shopify-app-remix/CHANGELOG.md b/packages/apps/shopify-app-remix/CHANGELOG.md index 709942fb10..eb430bc134 100644 --- a/packages/apps/shopify-app-remix/CHANGELOG.md +++ b/packages/apps/shopify-app-remix/CHANGELOG.md @@ -1,5 +1,26 @@ # @shopify/shopify-app-remix +## 2.8.2 + +### Patch Changes + +- 65325b8: Change v10_lineItemBilling flag to lineItemBilling +- fa94e85: Fix an issue that rejected requests from Shopify POS / Mobile due to their User-Agents being labeled as bots. +- 637c6c3: This `scopes` field on the API config object is now optional. If your app is using the new [managed install flow](https://shopify.dev/docs/apps/auth/installation), it is now recommended you omit the `scopes` property from the config object. +- 1b5d80e: Removed `@remix-run/node` as a direct dependency. Any app using the Vercel adapter already needs `@remix-run/node`, so this shouldn't affect any apps. +- Updated dependencies [379206c] +- Updated dependencies [715a120] +- Updated dependencies [65325b8] +- Updated dependencies [637c6c3] +- Updated dependencies [a8d4b3e] +- Updated dependencies [6f1a98e] +- Updated dependencies [61576be] +- Updated dependencies [379206c] + - @shopify/shopify-api@10.0.0 + - @shopify/storefront-api-client@0.3.4 + - @shopify/shopify-app-session-storage@2.1.5 + - @shopify/admin-api-client@0.2.9 + ## 2.8.1 ### Patch Changes diff --git a/packages/apps/shopify-app-remix/package.json b/packages/apps/shopify-app-remix/package.json index 368f39b964..5463b80e95 100644 --- a/packages/apps/shopify-app-remix/package.json +++ b/packages/apps/shopify-app-remix/package.json @@ -1,6 +1,6 @@ { "name": "@shopify/shopify-app-remix", - "version": "2.8.1", + "version": "2.8.2", "description": "Shopify Remix - to simplify the building of Shopify Apps with Remix", "repository": { "type": "git", @@ -81,7 +81,7 @@ "@shopify/generate-docs": "^0.15.2", "@shopify/polaris": "^12.18.0", "@shopify/react-testing": "^5.1.3", - "@shopify/shopify-app-session-storage-memory": "^3.0.4", + "@shopify/shopify-app-session-storage-memory": "^3.0.5", "@types/jsonwebtoken": "^9.0.5", "@types/react": "^18.2.18", "@types/semver": "^7.5.8", @@ -93,10 +93,10 @@ }, "dependencies": { "@remix-run/server-runtime": "^2.5.1", - "@shopify/admin-api-client": "^0.2.8", - "@shopify/shopify-api": "^9.7.2", - "@shopify/shopify-app-session-storage": "^2.1.4", - "@shopify/storefront-api-client": "^0.3.3", + "@shopify/admin-api-client": "^0.2.9", + "@shopify/shopify-api": "^10.0.0", + "@shopify/shopify-app-session-storage": "^2.1.5", + "@shopify/storefront-api-client": "^0.3.4", "isbot": "^5.1.4", "semver": "^7.6.0" }, From c2b14a0a72f66c5086ad814a34093a864cf00a4d Mon Sep 17 00:00:00 2001 From: Paulo Margarido <64600052+paulomarg@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:51:58 -0400 Subject: [PATCH 29/32] Preparing for release --- packages/apps/shopify-api/CHANGELOG.md | 27 ++++++++++--------- packages/apps/shopify-api/lib/version.ts | 2 +- .../apps/shopify-app-express/src/version.ts | 2 +- .../shopify-app-remix/src/server/version.ts | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/apps/shopify-api/CHANGELOG.md b/packages/apps/shopify-api/CHANGELOG.md index f6f5ad3084..a2d27abfb1 100644 --- a/packages/apps/shopify-api/CHANGELOG.md +++ b/packages/apps/shopify-api/CHANGELOG.md @@ -4,6 +4,9 @@ ### Major Changes +> [!NOTE] +> For more details on migrating to v10, please follow see the [migration guide](./docs/migrating-to-v10.md). + - 379206c: Webhook validation will now return a different `reason` value when the HMAC value is missing from the request. Instead of returning `WebhookValidationErrorReason.MissingHeaders` as it does for the other headers it validates, it will now return a new `WebhookValidationErrorReason.MissingHmac` error so this check matches other HMAC validations. If your app doesn't explicitly check for the error after calling `webhook.validate()`, you don't need to make any changes. @@ -17,17 +20,17 @@ Before: ```ts - import "node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client"; - import "@shopify/shopify-api/adapters/node"; + import 'node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client'; + import '@shopify/shopify-api/adapters/node'; ``` After: ```ts // Add `dist/esm|cjs/` before the file - import "node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client"; + import 'node_modules/@shopify/shopify-api/dist/esm/lib/clients/admin/graphql/client'; // Unchanged - import "@shopify/shopify-api/adapters/node"; + import '@shopify/shopify-api/adapters/node'; ``` ### Patch Changes @@ -139,7 +142,7 @@
```ts - const { session, headers } = shopify.auth.callback({ + const {session, headers} = shopify.auth.callback({ rawRequest: req, rawResponse: res, }); @@ -343,13 +346,13 @@ Before: ```ts - import { gdprTopics } from "@shopify/shopify-api"; + import {gdprTopics} from '@shopify/shopify-api'; ``` After: ```ts - import { privacyTopics } from "@shopify/shopify-api"; + import {privacyTopics} from '@shopify/shopify-api'; ``` ### Minor Changes @@ -402,13 +405,13 @@ { interval: BillingInterval.Usage, amount: 30, - currencyCode: "USD", - terms: "per 1000 emails", + currencyCode: 'USD', + terms: 'per 1000 emails', }, { interval: BillingInterval.Every30Days, amount: 30, - currencyCode: "USD", + currencyCode: 'USD', discount: { durationLimitInIntervals: 3, value: { @@ -780,7 +783,7 @@ Before: ```js - app.post("/graphql", async (req, res) => { + app.post('/graphql', async (req, res) => { await Shopify.Utils.graphqlProxy(req, res); }); ``` @@ -788,7 +791,7 @@ After: ```js - app.post("/graphql", async (req, res) => { + app.post('/graphql', async (req, res) => { const response = await Shopify.Utils.graphqlProxy(req, res); res.status(200).send(response.body); }); diff --git a/packages/apps/shopify-api/lib/version.ts b/packages/apps/shopify-api/lib/version.ts index 836fe2e6be..6deb7810de 100644 --- a/packages/apps/shopify-api/lib/version.ts +++ b/packages/apps/shopify-api/lib/version.ts @@ -1 +1 @@ -export const SHOPIFY_API_LIBRARY_VERSION = '9.7.2'; +export const SHOPIFY_API_LIBRARY_VERSION = '10.0.0'; diff --git a/packages/apps/shopify-app-express/src/version.ts b/packages/apps/shopify-app-express/src/version.ts index ca70988029..9480c1efcb 100644 --- a/packages/apps/shopify-app-express/src/version.ts +++ b/packages/apps/shopify-app-express/src/version.ts @@ -1 +1 @@ -export const SHOPIFY_EXPRESS_LIBRARY_VERSION = '4.1.5'; +export const SHOPIFY_EXPRESS_LIBRARY_VERSION = '4.1.6'; diff --git a/packages/apps/shopify-app-remix/src/server/version.ts b/packages/apps/shopify-app-remix/src/server/version.ts index 81c90c3503..39aa50da50 100644 --- a/packages/apps/shopify-app-remix/src/server/version.ts +++ b/packages/apps/shopify-app-remix/src/server/version.ts @@ -1 +1 @@ -export const SHOPIFY_REMIX_LIBRARY_VERSION = '2.8.1'; +export const SHOPIFY_REMIX_LIBRARY_VERSION = '2.8.2'; From 435f56b915e583a4c86af471b30ff509b1fb5ca5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:35:33 +0000 Subject: [PATCH 30/32] Bump better-sqlite3 from 9.4.3 to 9.5.0 Bumps [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) from 9.4.3 to 9.5.0. - [Release notes](https://github.com/WiseLibs/better-sqlite3/releases) - [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v9.4.3...v9.5.0) --- updated-dependencies: - dependency-name: better-sqlite3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .../package.json | 2 +- yarn.lock | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json index 4dc3851f2d..8d3c490c1d 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json @@ -53,7 +53,7 @@ "@libsql/client": "^0.4.0-pre.7", "@shopify/shopify-app-session-storage-test-utils": "^2.0.5", "@types/better-sqlite3": "^7.6.9", - "better-sqlite3": "^9.4.3", + "better-sqlite3": "^9.5.0", "drizzle-kit": "^0.20.12", "drizzle-orm": "^0.29.3", "mysql2": "^3.9.2", diff --git a/yarn.lock b/yarn.lock index b8f20e0346..779000f0de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5606,10 +5606,10 @@ better-path-resolve@1.0.0: dependencies: is-windows "^1.0.0" -better-sqlite3@^9.4.3: - version "9.4.3" - resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.4.3.tgz#7df39ba3273fbb7c0561cf913572547142868cc4" - integrity sha512-ud0bTmD9O3uWJGuXDltyj3R47Nz0OHX8iqPOT5PMspGqlu/qQFn+5S2eFBUCrySpavTjFXbi4EgrfVvPAHlImw== +better-sqlite3@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.5.0.tgz#0e10766cfb7f9b8916be3ab95ad9d5bcc4e6e6fd" + integrity sha512-01qVcM4gPNwE+PX7ARNiHINwzVuD6nx0gdldaAAcu+MrzyIAukQ31ZDKEpzRO/CNA9sHpxoTZ8rdjoyAin4dyg== dependencies: bindings "^1.5.0" prebuild-install "^7.1.1" @@ -11862,7 +11862,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11930,7 +11939,14 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12888,7 +12904,7 @@ wrangler@^3.33.0: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12906,6 +12922,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 0ddaa655b5bdc08482673e92840874d3854cd4eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:47:34 +0000 Subject: [PATCH 31/32] Bump mysql2 from 3.9.2 to 3.9.7 Bumps [mysql2](https://github.com/sidorares/node-mysql2) from 3.9.2 to 3.9.7. - [Release notes](https://github.com/sidorares/node-mysql2/releases) - [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md) - [Commits](https://github.com/sidorares/node-mysql2/compare/v3.9.2...v3.9.7) --- updated-dependencies: - dependency-name: mysql2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../shopify-app-session-storage-drizzle/package.json | 2 +- .../shopify-app-session-storage-mysql/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json index 8d3c490c1d..8c1e22e3ce 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-drizzle/package.json @@ -56,7 +56,7 @@ "better-sqlite3": "^9.5.0", "drizzle-kit": "^0.20.12", "drizzle-orm": "^0.29.3", - "mysql2": "^3.9.2", + "mysql2": "^3.9.7", "pg": "^8.11.5" }, "files": [ diff --git a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json index cd4dc52585..a1df72cf75 100644 --- a/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json +++ b/packages/apps/session-storage/shopify-app-session-storage-mysql/package.json @@ -43,7 +43,7 @@ "session storage" ], "dependencies": { - "mysql2": "^3.9.2" + "mysql2": "^3.9.7" }, "peerDependencies": { "@shopify/shopify-api": "^9.7.2 || ^10.0.0", diff --git a/yarn.lock b/yarn.lock index 779000f0de..f8ac58a0ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10042,10 +10042,10 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mysql2@^3.9.2: - version "3.9.2" - resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.9.2.tgz#567343581f9742032598b6c15bd7aa65d2f7d4af" - integrity sha512-3Cwg/UuRkAv/wm6RhtPE5L7JlPB877vwSF6gfLAS68H+zhH+u5oa3AieqEd0D0/kC3W7qIhYbH419f7O9i/5nw== +mysql2@^3.9.7: + version "3.9.7" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.9.7.tgz#843755daf65b5ef08afe545fe14b8fb62824741a" + integrity sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw== dependencies: denque "^2.1.0" generate-function "^2.3.1" From ec6bdfcf64be7512e79c6f8620d98cdc297890e8 Mon Sep 17 00:00:00 2001 From: Elizabeth Kenyon Date: Thu, 25 Apr 2024 17:07:55 -0500 Subject: [PATCH 32/32] changeset --- .changeset/cold-eagles-leave.md | 6 ++++++ .changeset/unlucky-onions-hunt.md | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/cold-eagles-leave.md create mode 100644 .changeset/unlucky-onions-hunt.md diff --git a/.changeset/cold-eagles-leave.md b/.changeset/cold-eagles-leave.md new file mode 100644 index 0000000000..04d239c23e --- /dev/null +++ b/.changeset/cold-eagles-leave.md @@ -0,0 +1,6 @@ +--- +"@shopify/shopify-app-session-storage-drizzle": patch +"@shopify/shopify-app-session-storage-mysql": patch +--- + +Bump mysql2 from 3.9.2 to 3.9.7 diff --git a/.changeset/unlucky-onions-hunt.md b/.changeset/unlucky-onions-hunt.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/unlucky-onions-hunt.md @@ -0,0 +1,2 @@ +--- +---