@shopify/shopify-app-remix 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/build/cjs/server/adapters/node/index.js +1 -0
- package/build/cjs/server/authenticate/admin/authenticate.js +19 -14
- package/build/cjs/server/authenticate/admin/billing/cancel.js +1 -1
- package/build/cjs/server/authenticate/admin/billing/check.js +39 -0
- package/build/cjs/server/authenticate/admin/billing/require.js +1 -1
- package/build/cjs/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
- package/build/cjs/server/authenticate/admin/helpers/handle-client-error.js +8 -4
- package/build/cjs/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
- package/build/cjs/server/authenticate/admin/helpers/redirect.js +1 -5
- package/build/cjs/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
- package/build/cjs/server/authenticate/admin/strategies/auth-code-flow.js +31 -4
- package/build/cjs/server/authenticate/admin/strategies/token-exchange.js +155 -0
- package/build/cjs/server/authenticate/const.js +4 -0
- package/build/cjs/server/authenticate/flow/authenticate.js +68 -0
- package/build/cjs/server/authenticate/helpers/idempotent-promise-handler.js +40 -0
- package/build/cjs/server/authenticate/helpers/respond-to-invalid-session-token.js +33 -0
- package/build/cjs/server/authenticate/login/login.js +5 -1
- package/build/cjs/server/authenticate/public/checkout/authenticate.js +1 -0
- package/build/cjs/server/authenticate/webhooks/authenticate.js +1 -1
- package/build/cjs/server/authenticate/webhooks/register.js +16 -0
- package/build/cjs/server/errors.js +9 -0
- package/build/cjs/server/index.js +2 -0
- package/build/cjs/server/shopify-app.js +9 -4
- package/build/cjs/server/types.js +4 -0
- package/build/cjs/server/unauthenticated/admin/factory.js +2 -2
- package/build/cjs/server/unauthenticated/storefront/factory.js +2 -2
- package/build/cjs/server/version.js +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.js +1 -1
- package/build/ts/react/components/AppProvider/AppProvider.js.map +1 -1
- package/build/ts/server/__test-helpers/const.d.ts +2 -1
- package/build/ts/server/__test-helpers/const.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/const.js +3 -2
- package/build/ts/server/__test-helpers/const.js.map +1 -1
- package/build/ts/server/__test-helpers/expect-document-request-headers.js.map +1 -1
- package/build/ts/server/__test-helpers/expect-exit-iframe.js.map +1 -1
- package/build/ts/server/__test-helpers/get-thrown-response.js.map +1 -1
- package/build/ts/server/__test-helpers/request-mock.js.map +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.d.ts +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/setup-valid-session.js +5 -5
- package/build/ts/server/__test-helpers/setup-valid-session.js.map +1 -1
- package/build/ts/server/__test-helpers/test-config.d.ts +1 -0
- package/build/ts/server/__test-helpers/test-config.d.ts.map +1 -1
- package/build/ts/server/__test-helpers/test-config.js +1 -0
- package/build/ts/server/__test-helpers/test-config.js.map +1 -1
- package/build/ts/server/adapters/node/index.js.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.d.ts +7 -1
- package/build/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/authenticate.js +15 -14
- package/build/ts/server/authenticate/admin/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/admin/billing/check.d.ts +6 -0
- package/build/ts/server/authenticate/admin/billing/check.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/billing/check.js +43 -0
- package/build/ts/server/authenticate/admin/billing/check.js.map +1 -0
- package/build/ts/server/authenticate/admin/billing/index.d.ts +1 -0
- package/build/ts/server/authenticate/admin/billing/index.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/billing/index.js +3 -1
- package/build/ts/server/authenticate/admin/billing/index.js.map +1 -1
- package/build/ts/server/authenticate/admin/billing/request.js.map +1 -1
- package/build/ts/server/authenticate/admin/billing/types.d.ts +59 -1
- package/build/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts +2 -2
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js +2 -5
- package/build/ts/server/authenticate/admin/helpers/create-admin-api-context.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts +2 -5
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.js +6 -5
- package/build/ts/server/authenticate/admin/helpers/handle-client-error.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js +4 -2
- package/build/ts/server/authenticate/admin/helpers/redirect-to-bounce-page.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect-with-exitiframe.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/redirect.js +0 -3
- package/build/ts/server/authenticate/admin/helpers/redirect.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/render-app-bridge.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts +2 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js +2 -2
- package/build/ts/server/authenticate/admin/helpers/trigger-after-auth-hook.js.map +1 -1
- package/build/ts/server/authenticate/admin/helpers/validate-shop-and-host-params.js.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts +4 -2
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js +26 -4
- package/build/ts/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
- package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts +17 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.js +163 -0
- package/build/ts/server/authenticate/admin/strategies/token-exchange.js.map +1 -0
- package/build/ts/server/authenticate/admin/strategies/types.d.ts +16 -5
- package/build/ts/server/authenticate/admin/strategies/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/admin/types.d.ts +0 -4
- package/build/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/build/ts/server/authenticate/const.d.ts +3 -0
- package/build/ts/server/authenticate/const.d.ts.map +1 -1
- package/build/ts/server/authenticate/const.js +4 -1
- package/build/ts/server/authenticate/const.js.map +1 -1
- package/build/ts/server/authenticate/flow/authenticate.d.ts +5 -0
- package/build/ts/server/authenticate/flow/authenticate.d.ts.map +1 -0
- package/build/ts/server/authenticate/flow/authenticate.flow.doc.d.ts +4 -0
- package/build/ts/server/authenticate/flow/authenticate.flow.doc.d.ts.map +1 -0
- package/build/ts/server/authenticate/flow/authenticate.flow.doc.js +33 -0
- package/build/ts/server/authenticate/flow/authenticate.flow.doc.js.map +1 -0
- package/build/ts/server/authenticate/flow/authenticate.js +67 -0
- package/build/ts/server/authenticate/flow/authenticate.js.map +1 -0
- package/build/ts/server/authenticate/flow/types.d.ts +82 -0
- package/build/ts/server/authenticate/flow/types.d.ts.map +1 -0
- package/build/ts/server/authenticate/flow/types.js +3 -0
- package/build/ts/server/authenticate/flow/types.js.map +1 -0
- package/build/ts/server/authenticate/helpers/add-response-headers.js.map +1 -1
- package/build/ts/server/authenticate/helpers/ensure-cors-headers.js.map +1 -1
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts +12 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.d.ts.map +1 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js +53 -0
- package/build/ts/server/authenticate/helpers/idempotent-promise-handler.js.map +1 -0
- package/build/ts/server/authenticate/helpers/index.d.ts +1 -0
- package/build/ts/server/authenticate/helpers/index.d.ts.map +1 -1
- package/build/ts/server/authenticate/helpers/index.js +1 -0
- package/build/ts/server/authenticate/helpers/index.js.map +1 -1
- package/build/ts/server/authenticate/helpers/reject-bot-request.js.map +1 -1
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts +9 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.d.ts.map +1 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js +20 -0
- package/build/ts/server/authenticate/helpers/respond-to-invalid-session-token.js.map +1 -0
- package/build/ts/server/authenticate/helpers/respond-to-options-request.js.map +1 -1
- package/build/ts/server/authenticate/login/login.d.ts.map +1 -1
- package/build/ts/server/authenticate/login/login.js +6 -2
- package/build/ts/server/authenticate/login/login.js.map +1 -1
- package/build/ts/server/authenticate/public/appProxy/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/public/checkout/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/public/factory.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/register.d.ts +1 -1
- package/build/ts/server/authenticate/webhooks/register.d.ts.map +1 -1
- package/build/ts/server/authenticate/webhooks/register.js +20 -1
- package/build/ts/server/authenticate/webhooks/register.js.map +1 -1
- package/build/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/build/ts/server/boundary/error.js.map +1 -1
- package/build/ts/server/boundary/headers.js.map +1 -1
- package/build/ts/server/clients/admin/rest.js.map +1 -1
- package/build/ts/server/clients/admin/types.d.ts +12 -0
- package/build/ts/server/clients/admin/types.d.ts.map +1 -1
- package/build/ts/server/clients/types.d.ts +1 -3
- package/build/ts/server/clients/types.d.ts.map +1 -1
- package/build/ts/server/config-types.d.ts +3 -3
- package/build/ts/server/config-types.d.ts.map +1 -1
- package/build/ts/server/errors.d.ts +4 -0
- package/build/ts/server/errors.d.ts.map +1 -0
- package/build/ts/server/errors.js +14 -0
- package/build/ts/server/errors.js.map +1 -0
- package/build/ts/server/future/flags.d.ts +9 -0
- package/build/ts/server/future/flags.d.ts.map +1 -1
- package/build/ts/server/index.d.ts +1 -0
- package/build/ts/server/index.d.ts.map +1 -1
- package/build/ts/server/index.js +2 -0
- package/build/ts/server/index.js.map +1 -1
- package/build/ts/server/override-logger.js.map +1 -1
- package/build/ts/server/shopify-app.d.ts.map +1 -1
- package/build/ts/server/shopify-app.doc.d.ts.map +1 -1
- package/build/ts/server/shopify-app.doc.js +1 -0
- package/build/ts/server/shopify-app.doc.js.map +1 -1
- package/build/ts/server/shopify-app.js +8 -3
- package/build/ts/server/shopify-app.js.map +1 -1
- package/build/ts/server/types.d.ts +54 -12
- package/build/ts/server/types.d.ts.map +1 -1
- package/build/ts/server/types.js +2 -2
- package/build/ts/server/types.js.map +1 -1
- package/build/ts/server/unauthenticated/admin/factory.d.ts.map +1 -1
- package/build/ts/server/unauthenticated/admin/factory.js +2 -2
- package/build/ts/server/unauthenticated/admin/factory.js.map +1 -1
- package/build/ts/server/unauthenticated/admin/types.d.ts +76 -0
- package/build/ts/server/unauthenticated/admin/types.d.ts.map +1 -1
- package/build/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts.map +1 -1
- package/build/ts/server/unauthenticated/admin/unauthenticated.admin.doc.js +2 -5
- package/build/ts/server/unauthenticated/admin/unauthenticated.admin.doc.js.map +1 -1
- package/build/ts/server/unauthenticated/storefront/factory.d.ts.map +1 -1
- package/build/ts/server/unauthenticated/storefront/factory.js +2 -2
- package/build/ts/server/unauthenticated/storefront/factory.js.map +1 -1
- package/build/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts.map +1 -1
- package/build/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.js +1 -4
- package/build/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.js.map +1 -1
- package/build/ts/server/version.d.ts +1 -1
- package/build/ts/server/version.js +1 -1
- package/package.json +12 -16
package/README.md
CHANGED
|
@@ -174,6 +174,50 @@ Here are some guides to help you set up your app:
|
|
|
174
174
|
You can also authenticate requests from surfaces other than the admin.
|
|
175
175
|
To see all supported methods, see [the `shopify.authenticate` object documentation](https://shopify.dev/docs/api/shopify-app-remix/latest/authenticate).
|
|
176
176
|
|
|
177
|
+
### New embedded app authorization strategy
|
|
178
|
+
|
|
179
|
+
> [!TIP]
|
|
180
|
+
> If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
|
181
|
+
> with [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) instead of the legacy authorization code grant flow.
|
|
182
|
+
|
|
183
|
+
We've introduced a new installation and authorization strategy for **embedded apps** that
|
|
184
|
+
eliminates the redirects that were previously necessary.
|
|
185
|
+
It replaces the existing [installation and authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant).
|
|
186
|
+
|
|
187
|
+
This is achieved by using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
|
188
|
+
to handle automatic app installations and scope updates, while utilizing
|
|
189
|
+
[token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) to retrieve an access token for
|
|
190
|
+
authenticated API access.
|
|
191
|
+
|
|
192
|
+
##### Enabling this new strategy in your app
|
|
193
|
+
|
|
194
|
+
> [!NOTE]
|
|
195
|
+
> Newly created Remix apps from the template after February 1st 2024 has this feature enabled by default.
|
|
196
|
+
|
|
197
|
+
1. Enable [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
|
198
|
+
by configuring your scopes [through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration).
|
|
199
|
+
2. Enable the future flag `unstable_newEmbeddedAuthStrategy` in your app's server configuration file.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
// my-app/app/shopify.server.ts
|
|
203
|
+
const shopify = shopifyApp({
|
|
204
|
+
...
|
|
205
|
+
isEmbeddedApp: true,
|
|
206
|
+
future: {
|
|
207
|
+
unstable_newEmbeddedAuthStrategy: true,
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
3. Enjoy a smoother and faster app installation process.
|
|
214
|
+
|
|
215
|
+
###### Learn more about:
|
|
216
|
+
|
|
217
|
+
- [How token exchange works](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange)
|
|
218
|
+
- [Using Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
|
|
219
|
+
- [Configuring access scopes through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration)
|
|
220
|
+
|
|
177
221
|
## Gotchas / Troubleshooting
|
|
178
222
|
|
|
179
223
|
If you're experiencing unexpected behaviors when using this package, check our [app template's documentation](https://github.com/Shopify/shopify-app-template-remix#gotchas--troubleshooting) for the solution to common issues.
|
|
@@ -4,6 +4,7 @@ var crypto = require('crypto');
|
|
|
4
4
|
var runtime = require('@shopify/shopify-api/runtime');
|
|
5
5
|
var appBridgeUrl = require('../../authenticate/helpers/app-bridge-url.js');
|
|
6
6
|
require('isbot');
|
|
7
|
+
require('@remix-run/server-runtime');
|
|
7
8
|
|
|
8
9
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
10
|
|
|
@@ -7,14 +7,15 @@ var validateSessionToken = require('../helpers/validate-session-token.js');
|
|
|
7
7
|
var getSessionTokenHeader = require('../helpers/get-session-token-header.js');
|
|
8
8
|
var rejectBotRequest = require('../helpers/reject-bot-request.js');
|
|
9
9
|
var respondToOptionsRequest = require('../helpers/respond-to-options-request.js');
|
|
10
|
+
require('@remix-run/server-runtime');
|
|
10
11
|
var cancel = require('./billing/cancel.js');
|
|
11
12
|
var require$1 = require('./billing/require.js');
|
|
12
13
|
var request = require('./billing/request.js');
|
|
14
|
+
var check = require('./billing/check.js');
|
|
13
15
|
var createAdminApiContext = require('./helpers/create-admin-api-context.js');
|
|
14
16
|
var ensureAppIsEmbeddedIfRequired = require('./helpers/ensure-app-is-embedded-if-required.js');
|
|
15
17
|
var ensureSessionTokenSearchParamIfRequired = require('./helpers/ensure-session-token-search-param-if-required.js');
|
|
16
18
|
require('@shopify/shopify-api');
|
|
17
|
-
require('@remix-run/server-runtime');
|
|
18
19
|
var redirect = require('./helpers/redirect.js');
|
|
19
20
|
var renderAppBridge = require('./helpers/render-app-bridge.js');
|
|
20
21
|
var validateShopAndHostParams = require('./helpers/validate-shop-and-host-params.js');
|
|
@@ -55,15 +56,12 @@ function authStrategyFactory({
|
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
function createContext(request$1, session, sessionToken) {
|
|
59
|
+
function createContext(request$1, session, authStrategy, sessionToken) {
|
|
59
60
|
const context = {
|
|
60
|
-
admin: createAdminApiContext.createAdminApiContext(request$1,
|
|
61
|
-
api,
|
|
62
|
-
logger,
|
|
63
|
-
config
|
|
64
|
-
}),
|
|
61
|
+
admin: createAdminApiContext.createAdminApiContext(session, params, authStrategy.handleClientError(request$1)),
|
|
65
62
|
billing: {
|
|
66
63
|
require: require$1.requireBillingFactory(params, request$1, session),
|
|
64
|
+
check: check.checkBillingFactory(params, request$1, session),
|
|
67
65
|
request: request.requestBillingFactory(params, request$1, session),
|
|
68
66
|
cancel: cancel.cancelBillingFactory(params, request$1, session)
|
|
69
67
|
},
|
|
@@ -99,18 +97,23 @@ function authStrategyFactory({
|
|
|
99
97
|
const {
|
|
100
98
|
payload,
|
|
101
99
|
shop,
|
|
102
|
-
sessionId
|
|
100
|
+
sessionId,
|
|
101
|
+
sessionToken
|
|
103
102
|
} = await getSessionTokenContext(params, request);
|
|
104
103
|
logger.debug('Loading session from storage', {
|
|
105
104
|
sessionId
|
|
106
105
|
});
|
|
107
106
|
const existingSession = sessionId ? await config.sessionStorage.loadSession(sessionId) : undefined;
|
|
108
|
-
const session = await strategy.authenticate(request,
|
|
107
|
+
const session = await strategy.authenticate(request, {
|
|
108
|
+
session: existingSession,
|
|
109
|
+
sessionToken,
|
|
110
|
+
shop
|
|
111
|
+
});
|
|
109
112
|
logger.debug('Request is valid, loaded session from session token', {
|
|
110
113
|
shop: session.shop,
|
|
111
114
|
isOnline: session.isOnline
|
|
112
115
|
});
|
|
113
|
-
return createContext(request, session, payload);
|
|
116
|
+
return createContext(request, session, strategy, payload);
|
|
114
117
|
} catch (errorOrResponse) {
|
|
115
118
|
if (errorOrResponse instanceof Response) {
|
|
116
119
|
ensureCorsHeaders.ensureCORSHeadersFactory(params, request)(errorOrResponse);
|
|
@@ -129,10 +132,10 @@ async function getSessionTokenContext(params, request) {
|
|
|
129
132
|
const searchParamSessionToken = getSessionTokenHeader.getSessionTokenFromUrlParam(request);
|
|
130
133
|
const sessionToken = headerSessionToken || searchParamSessionToken;
|
|
131
134
|
logger.debug('Attempting to authenticate session token', {
|
|
132
|
-
sessionToken: {
|
|
135
|
+
sessionToken: JSON.stringify({
|
|
133
136
|
header: headerSessionToken,
|
|
134
137
|
search: searchParamSessionToken
|
|
135
|
-
}
|
|
138
|
+
})
|
|
136
139
|
});
|
|
137
140
|
if (config.isEmbeddedApp) {
|
|
138
141
|
const payload = await validateSessionToken.validateSessionToken({
|
|
@@ -150,7 +153,8 @@ async function getSessionTokenContext(params, request) {
|
|
|
150
153
|
return {
|
|
151
154
|
shop,
|
|
152
155
|
payload,
|
|
153
|
-
sessionId
|
|
156
|
+
sessionId,
|
|
157
|
+
sessionToken
|
|
154
158
|
};
|
|
155
159
|
}
|
|
156
160
|
const url = new URL(request.url);
|
|
@@ -162,7 +166,8 @@ async function getSessionTokenContext(params, request) {
|
|
|
162
166
|
return {
|
|
163
167
|
shop,
|
|
164
168
|
sessionId,
|
|
165
|
-
payload: undefined
|
|
169
|
+
payload: undefined,
|
|
170
|
+
sessionToken
|
|
166
171
|
};
|
|
167
172
|
}
|
|
168
173
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
7
6
|
require('@remix-run/server-runtime');
|
|
7
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
8
|
|
|
9
9
|
function cancelBillingFactory(params, request, session) {
|
|
10
10
|
return async function cancelBilling(options) {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
+
require('@remix-run/server-runtime');
|
|
7
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
|
+
|
|
9
|
+
function checkBillingFactory(params, request, session) {
|
|
10
|
+
return async function checkBilling(options) {
|
|
11
|
+
const {
|
|
12
|
+
api,
|
|
13
|
+
logger
|
|
14
|
+
} = params;
|
|
15
|
+
logger.debug('Checking billing plans', {
|
|
16
|
+
shop: session.shop,
|
|
17
|
+
...options
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
return await api.billing.check({
|
|
21
|
+
session,
|
|
22
|
+
plans: options.plans,
|
|
23
|
+
isTest: options.isTest,
|
|
24
|
+
returnObject: true
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error instanceof shopifyApi.HttpResponseError && error.response.code === 401) {
|
|
28
|
+
logger.debug('API token was invalid, redirecting to OAuth', {
|
|
29
|
+
shop: session.shop
|
|
30
|
+
});
|
|
31
|
+
throw await redirectToAuthPage.redirectToAuthPage(params, request, session.shop);
|
|
32
|
+
} else {
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.checkBillingFactory = checkBillingFactory;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
7
6
|
require('@remix-run/server-runtime');
|
|
7
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
8
|
|
|
9
9
|
function requireBillingFactory(params, request, session) {
|
|
10
10
|
const {
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var factory = require('../../../clients/admin/factory.js');
|
|
6
|
-
var handleClientError = require('./handle-client-error.js');
|
|
7
6
|
|
|
8
|
-
function createAdminApiContext(
|
|
7
|
+
function createAdminApiContext(session, params, handleClientError) {
|
|
9
8
|
return factory.adminClientFactory({
|
|
10
9
|
session,
|
|
11
10
|
params,
|
|
12
|
-
handleClientError
|
|
13
|
-
request
|
|
14
|
-
})
|
|
11
|
+
handleClientError
|
|
15
12
|
});
|
|
16
13
|
}
|
|
17
14
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
-
var redirectToAuthPage = require('./redirect-to-auth-page.js');
|
|
7
6
|
|
|
8
7
|
function handleClientErrorFactory({
|
|
9
|
-
request
|
|
8
|
+
request,
|
|
9
|
+
onError
|
|
10
10
|
}) {
|
|
11
11
|
return async function handleClientError({
|
|
12
12
|
error,
|
|
@@ -22,8 +22,12 @@ function handleClientErrorFactory({
|
|
|
22
22
|
statusText: error.response.statusText,
|
|
23
23
|
body: JSON.stringify(error.response.body)
|
|
24
24
|
});
|
|
25
|
-
if (
|
|
26
|
-
|
|
25
|
+
if (onError) {
|
|
26
|
+
await onError({
|
|
27
|
+
request,
|
|
28
|
+
session,
|
|
29
|
+
error
|
|
30
|
+
});
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// forward a minimal copy of the upstream HTTP response instead of an Error:
|
|
@@ -11,12 +11,14 @@ const redirectToBouncePage = (params, url) => {
|
|
|
11
11
|
|
|
12
12
|
// Make sure we always point to the configured app URL so it also works behind reverse proxies (that alter the Host
|
|
13
13
|
// header).
|
|
14
|
-
|
|
14
|
+
const searchParams = url.searchParams;
|
|
15
|
+
searchParams.delete('id_token');
|
|
16
|
+
searchParams.set('shopify-reload', `${config.appUrl}${url.pathname}?${searchParams.toString()}`);
|
|
15
17
|
|
|
16
18
|
// eslint-disable-next-line no-warning-comments
|
|
17
19
|
// TODO Make sure this works on chrome without a tunnel (weird HTTPS redirect issue)
|
|
18
20
|
// https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28376650
|
|
19
|
-
throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}
|
|
21
|
+
throw serverRuntime.redirect(`${config.auth.patchSessionTokenPath}?${searchParams.toString()}`);
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
exports.redirectToBouncePage = redirectToBouncePage;
|
|
@@ -49,13 +49,9 @@ function isBounceRequest(request) {
|
|
|
49
49
|
return Boolean(getSessionTokenHeader.getSessionTokenHeader(request)) && request.headers.has('X-Shopify-Bounce');
|
|
50
50
|
}
|
|
51
51
|
function isDataRequest(request) {
|
|
52
|
-
const {
|
|
53
|
-
searchParams
|
|
54
|
-
} = new URL(request.url);
|
|
55
52
|
const isGet = request.method === 'GET';
|
|
56
53
|
const sessionTokenHeader = Boolean(getSessionTokenHeader.getSessionTokenHeader(request));
|
|
57
|
-
|
|
58
|
-
return sessionTokenHeader && !sessionTokenSearchParam && !isBounceRequest(request) && (!isEmbeddedRequest(request) || !isGet);
|
|
54
|
+
return sessionTokenHeader && !isBounceRequest(request) && (!isEmbeddedRequest(request) || !isGet);
|
|
59
55
|
}
|
|
60
56
|
function isEmbeddedRequest(request) {
|
|
61
57
|
const {
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var createAdminApiContext = require('./create-admin-api-context.js');
|
|
6
6
|
|
|
7
|
-
async function triggerAfterAuthHook(params, session, request) {
|
|
7
|
+
async function triggerAfterAuthHook(params, session, request, authStrategy) {
|
|
8
8
|
const {
|
|
9
9
|
config,
|
|
10
10
|
logger
|
|
@@ -13,7 +13,7 @@ async function triggerAfterAuthHook(params, session, request) {
|
|
|
13
13
|
logger.info('Running afterAuth hook');
|
|
14
14
|
await config.hooks.afterAuth({
|
|
15
15
|
session,
|
|
16
|
-
admin: createAdminApiContext.createAdminApiContext(
|
|
16
|
+
admin: createAdminApiContext.createAdminApiContext(session, params, authStrategy.handleClientError(request))
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -4,9 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var shopifyApi = require('@shopify/shopify-api');
|
|
6
6
|
var beginAuth = require('../helpers/begin-auth.js');
|
|
7
|
-
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
8
7
|
var redirectToShopifyOrAppRoot = require('../helpers/redirect-to-shopify-or-app-root.js');
|
|
9
8
|
require('@remix-run/server-runtime');
|
|
9
|
+
var handleClientError = require('../helpers/handle-client-error.js');
|
|
10
|
+
var redirectToAuthPage = require('../helpers/redirect-to-auth-page.js');
|
|
10
11
|
var redirectWithExitiframe = require('../helpers/redirect-with-exitiframe.js');
|
|
11
12
|
var getSessionTokenHeader = require('../../helpers/get-session-token-header.js');
|
|
12
13
|
var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
|
|
@@ -52,12 +53,16 @@ class AuthCodeFlowStrategy {
|
|
|
52
53
|
await this.ensureInstalledOnShop(request);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
async authenticate(request,
|
|
56
|
+
async authenticate(request, sessionContext) {
|
|
56
57
|
const {
|
|
57
58
|
api,
|
|
58
59
|
config,
|
|
59
60
|
logger
|
|
60
61
|
} = this;
|
|
62
|
+
const {
|
|
63
|
+
shop,
|
|
64
|
+
session
|
|
65
|
+
} = sessionContext;
|
|
61
66
|
if (!session) {
|
|
62
67
|
logger.debug('No session found, redirecting to OAuth', {
|
|
63
68
|
shop
|
|
@@ -82,6 +87,28 @@ class AuthCodeFlowStrategy {
|
|
|
82
87
|
});
|
|
83
88
|
return session;
|
|
84
89
|
}
|
|
90
|
+
handleClientError(request) {
|
|
91
|
+
const {
|
|
92
|
+
api,
|
|
93
|
+
config,
|
|
94
|
+
logger
|
|
95
|
+
} = this;
|
|
96
|
+
return handleClientError.handleClientErrorFactory({
|
|
97
|
+
request,
|
|
98
|
+
onError: async ({
|
|
99
|
+
session,
|
|
100
|
+
error
|
|
101
|
+
}) => {
|
|
102
|
+
if (error.response.code === 401) {
|
|
103
|
+
throw await redirectToAuthPage.redirectToAuthPage({
|
|
104
|
+
api,
|
|
105
|
+
config,
|
|
106
|
+
logger
|
|
107
|
+
}, request, session.shop);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
85
112
|
async ensureInstalledOnShop(request) {
|
|
86
113
|
const {
|
|
87
114
|
api,
|
|
@@ -197,7 +224,7 @@ class AuthCodeFlowStrategy {
|
|
|
197
224
|
api,
|
|
198
225
|
config,
|
|
199
226
|
logger
|
|
200
|
-
}, session, request);
|
|
227
|
+
}, session, request, this);
|
|
201
228
|
throw await redirectToShopifyOrAppRoot.redirectToShopifyOrAppRoot(request, {
|
|
202
229
|
api,
|
|
203
230
|
config,
|
|
@@ -293,7 +320,7 @@ class AuthCodeFlowStrategy {
|
|
|
293
320
|
shop
|
|
294
321
|
};
|
|
295
322
|
if (error.response) {
|
|
296
|
-
context.response = JSON.stringify(error.
|
|
323
|
+
context.response = JSON.stringify(error.body);
|
|
297
324
|
}
|
|
298
325
|
logger.error(`Unexpected error during session validation: ${error.message}`, context);
|
|
299
326
|
throw new Response(undefined, {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var shopifyApi = require('@shopify/shopify-api');
|
|
6
|
+
require('isbot');
|
|
7
|
+
var respondToInvalidSessionToken = require('../../helpers/respond-to-invalid-session-token.js');
|
|
8
|
+
require('@remix-run/server-runtime');
|
|
9
|
+
var handleClientError = require('../helpers/handle-client-error.js');
|
|
10
|
+
var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
|
|
11
|
+
|
|
12
|
+
class TokenExchangeStrategy {
|
|
13
|
+
constructor({
|
|
14
|
+
api,
|
|
15
|
+
config,
|
|
16
|
+
logger
|
|
17
|
+
}) {
|
|
18
|
+
this.api = void 0;
|
|
19
|
+
this.config = void 0;
|
|
20
|
+
this.logger = void 0;
|
|
21
|
+
this.api = api;
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.logger = logger;
|
|
24
|
+
}
|
|
25
|
+
async respondToOAuthRequests(_request) {}
|
|
26
|
+
async authenticate(request, sessionContext) {
|
|
27
|
+
const {
|
|
28
|
+
api,
|
|
29
|
+
config,
|
|
30
|
+
logger
|
|
31
|
+
} = this;
|
|
32
|
+
const {
|
|
33
|
+
shop,
|
|
34
|
+
session,
|
|
35
|
+
sessionToken
|
|
36
|
+
} = sessionContext;
|
|
37
|
+
if (!sessionToken) throw new shopifyApi.InvalidJwtError();
|
|
38
|
+
if (!session || session.isExpired()) {
|
|
39
|
+
logger.info('No valid session found');
|
|
40
|
+
logger.info('Requesting offline access token');
|
|
41
|
+
const {
|
|
42
|
+
session: offlineSession
|
|
43
|
+
} = await this.exchangeToken({
|
|
44
|
+
request,
|
|
45
|
+
sessionToken,
|
|
46
|
+
shop,
|
|
47
|
+
requestedTokenType: shopifyApi.RequestedTokenType.OfflineAccessToken
|
|
48
|
+
});
|
|
49
|
+
await config.sessionStorage.storeSession(offlineSession);
|
|
50
|
+
let newSession = offlineSession;
|
|
51
|
+
if (config.useOnlineTokens) {
|
|
52
|
+
logger.info('Requesting online access token');
|
|
53
|
+
const {
|
|
54
|
+
session: onlineSession
|
|
55
|
+
} = await this.exchangeToken({
|
|
56
|
+
request,
|
|
57
|
+
sessionToken,
|
|
58
|
+
shop,
|
|
59
|
+
requestedTokenType: shopifyApi.RequestedTokenType.OnlineAccessToken
|
|
60
|
+
});
|
|
61
|
+
await config.sessionStorage.storeSession(onlineSession);
|
|
62
|
+
newSession = onlineSession;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await this.handleAfterAuthHook({
|
|
66
|
+
api,
|
|
67
|
+
config,
|
|
68
|
+
logger
|
|
69
|
+
}, newSession, request, sessionToken);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Response(undefined, {
|
|
72
|
+
status: 500,
|
|
73
|
+
statusText: 'Internal Server Error'
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return newSession;
|
|
77
|
+
}
|
|
78
|
+
return session;
|
|
79
|
+
}
|
|
80
|
+
handleClientError(request) {
|
|
81
|
+
const {
|
|
82
|
+
api,
|
|
83
|
+
config,
|
|
84
|
+
logger
|
|
85
|
+
} = this;
|
|
86
|
+
return handleClientError.handleClientErrorFactory({
|
|
87
|
+
request,
|
|
88
|
+
onError: async ({
|
|
89
|
+
session,
|
|
90
|
+
error
|
|
91
|
+
}) => {
|
|
92
|
+
if (error.response.code === 401) {
|
|
93
|
+
config.sessionStorage.deleteSession(session.id);
|
|
94
|
+
respondToInvalidSessionToken.respondToInvalidSessionToken({
|
|
95
|
+
params: {
|
|
96
|
+
config,
|
|
97
|
+
api,
|
|
98
|
+
logger
|
|
99
|
+
},
|
|
100
|
+
request
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async exchangeToken({
|
|
107
|
+
request,
|
|
108
|
+
shop,
|
|
109
|
+
sessionToken,
|
|
110
|
+
requestedTokenType
|
|
111
|
+
}) {
|
|
112
|
+
const {
|
|
113
|
+
api,
|
|
114
|
+
config,
|
|
115
|
+
logger
|
|
116
|
+
} = this;
|
|
117
|
+
try {
|
|
118
|
+
return await api.auth.tokenExchange({
|
|
119
|
+
sessionToken,
|
|
120
|
+
shop,
|
|
121
|
+
requestedTokenType
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
var _error$response$body;
|
|
125
|
+
if (error instanceof shopifyApi.InvalidJwtError || error instanceof shopifyApi.HttpResponseError && error.response.code === 400 && ((_error$response$body = error.response.body) === null || _error$response$body === void 0 ? void 0 : _error$response$body.error) === 'invalid_subject_token') {
|
|
126
|
+
throw respondToInvalidSessionToken.respondToInvalidSessionToken({
|
|
127
|
+
params: {
|
|
128
|
+
api,
|
|
129
|
+
config,
|
|
130
|
+
logger
|
|
131
|
+
},
|
|
132
|
+
request,
|
|
133
|
+
retryRequest: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
throw new Response(undefined, {
|
|
137
|
+
status: 500,
|
|
138
|
+
statusText: 'Internal Server Error'
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async handleAfterAuthHook(params, session, request, sessionToken) {
|
|
143
|
+
const {
|
|
144
|
+
config
|
|
145
|
+
} = params;
|
|
146
|
+
await config.idempotentPromiseHandler.handlePromise({
|
|
147
|
+
promiseFunction: () => {
|
|
148
|
+
return triggerAfterAuthHook.triggerAfterAuthHook(params, session, request, this);
|
|
149
|
+
},
|
|
150
|
+
identifier: sessionToken
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exports.TokenExchangeStrategy = TokenExchangeStrategy;
|
|
@@ -4,6 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
const APP_BRIDGE_URL = 'https://cdn.shopify.com/shopifycloud/app-bridge.js';
|
|
6
6
|
const REAUTH_URL_HEADER = 'X-Shopify-API-Request-Failure-Reauthorize-Url';
|
|
7
|
+
const RETRY_INVALID_SESSION_HEADER = {
|
|
8
|
+
'X-Shopify-Retry-Invalid-Session-Request': '1'
|
|
9
|
+
};
|
|
7
10
|
|
|
8
11
|
exports.APP_BRIDGE_URL = APP_BRIDGE_URL;
|
|
9
12
|
exports.REAUTH_URL_HEADER = REAUTH_URL_HEADER;
|
|
13
|
+
exports.RETRY_INVALID_SESSION_HEADER = RETRY_INVALID_SESSION_HEADER;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var factory = require('../../clients/admin/factory.js');
|
|
6
|
+
|
|
7
|
+
function authenticateFlowFactory(params) {
|
|
8
|
+
const {
|
|
9
|
+
api,
|
|
10
|
+
config,
|
|
11
|
+
logger
|
|
12
|
+
} = params;
|
|
13
|
+
return async function authenticate(request) {
|
|
14
|
+
logger.info('Authenticating flow request');
|
|
15
|
+
if (request.method !== 'POST') {
|
|
16
|
+
logger.debug('Received a non-POST request for flow. Only POST requests are allowed.', {
|
|
17
|
+
url: request.url,
|
|
18
|
+
method: request.method
|
|
19
|
+
});
|
|
20
|
+
throw new Response(undefined, {
|
|
21
|
+
status: 405,
|
|
22
|
+
statusText: 'Method not allowed'
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const rawBody = await request.text();
|
|
26
|
+
const result = await api.flow.validate({
|
|
27
|
+
rawBody,
|
|
28
|
+
rawRequest: request
|
|
29
|
+
});
|
|
30
|
+
if (!result.valid) {
|
|
31
|
+
logger.error('Received an invalid flow request', {
|
|
32
|
+
reason: result.reason
|
|
33
|
+
});
|
|
34
|
+
throw new Response(undefined, {
|
|
35
|
+
status: 400,
|
|
36
|
+
statusText: 'Bad Request'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const payload = JSON.parse(rawBody);
|
|
40
|
+
logger.debug('Flow request is valid, looking for an offline session', {
|
|
41
|
+
shop: payload.shopify_domain
|
|
42
|
+
});
|
|
43
|
+
const sessionId = api.session.getOfflineId(payload.shopify_domain);
|
|
44
|
+
const session = await config.sessionStorage.loadSession(sessionId);
|
|
45
|
+
if (!session) {
|
|
46
|
+
logger.info('Flow request could not find session', {
|
|
47
|
+
shop: payload.shopify_domain
|
|
48
|
+
});
|
|
49
|
+
throw new Response(undefined, {
|
|
50
|
+
status: 400,
|
|
51
|
+
statusText: 'Bad Request'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
logger.debug('Found a session for the flow request', {
|
|
55
|
+
shop: session.shop
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
session,
|
|
59
|
+
payload,
|
|
60
|
+
admin: factory.adminClientFactory({
|
|
61
|
+
params,
|
|
62
|
+
session
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
exports.authenticateFlowFactory = authenticateFlowFactory;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const IDENTIFIER_TTL_MS = 60000;
|
|
6
|
+
class IdempotentPromiseHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.identifiers = void 0;
|
|
9
|
+
this.identifiers = new Map();
|
|
10
|
+
}
|
|
11
|
+
async handlePromise({
|
|
12
|
+
promiseFunction,
|
|
13
|
+
identifier
|
|
14
|
+
}) {
|
|
15
|
+
try {
|
|
16
|
+
if (this.isPromiseRunnable(identifier)) {
|
|
17
|
+
await promiseFunction();
|
|
18
|
+
}
|
|
19
|
+
} finally {
|
|
20
|
+
this.clearStaleIdentifiers();
|
|
21
|
+
}
|
|
22
|
+
return Promise.resolve();
|
|
23
|
+
}
|
|
24
|
+
isPromiseRunnable(identifier) {
|
|
25
|
+
if (!this.identifiers.has(identifier)) {
|
|
26
|
+
this.identifiers.set(identifier, Date.now());
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
async clearStaleIdentifiers() {
|
|
32
|
+
this.identifiers.forEach((date, identifier, map) => {
|
|
33
|
+
if (Date.now() - date > IDENTIFIER_TTL_MS) {
|
|
34
|
+
map.delete(identifier);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.IdempotentPromiseHandler = IdempotentPromiseHandler;
|