@moonbase.sh/storefront-api 3.6.0 → 3.8.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 +41 -1
- package/dist/index.cjs +188 -29
- package/dist/index.d.cts +1762 -1571
- package/dist/index.d.ts +1762 -1571
- package/dist/index.js +187 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Configuration notes:
|
|
|
24
24
|
|
|
25
25
|
- `endpoint`: your Moonbase storefront URL.
|
|
26
26
|
- `includeManifests`: include downloadable file manifests when available.
|
|
27
|
-
- `persistUtm`: store detected UTM values in localStorage instead of sessionStorage.
|
|
27
|
+
- `persistUtm`: store detected UTM values and ad click IDs in localStorage instead of sessionStorage.
|
|
28
28
|
|
|
29
29
|
## Common usage
|
|
30
30
|
|
|
@@ -172,6 +172,38 @@ refused with `CaptchaRequiredError` (a `MoonbaseError`). A site with a
|
|
|
172
172
|
Content-Security-Policy has to allow the store's `https://<account-id>.moonbase.sh`
|
|
173
173
|
in `frame-src`.
|
|
174
174
|
|
|
175
|
+
### Where a customer came from
|
|
176
|
+
|
|
177
|
+
Pass the UTM tags, ad click IDs and referrer the visitor arrived with, the same
|
|
178
|
+
shape orders take, so the merchant can see how the customer was acquired:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { parseOffSiteReferrer } from '@moonbase.sh/storefront-api'
|
|
182
|
+
|
|
183
|
+
const params = new URLSearchParams(window.location.search)
|
|
184
|
+
|
|
185
|
+
await client.identity.signUp('Jane', 'jane@example.com', 'password', null, true, true, true, {
|
|
186
|
+
utm: {
|
|
187
|
+
source: params.get('utm_source') || undefined,
|
|
188
|
+
campaign: params.get('utm_campaign') || undefined,
|
|
189
|
+
// Click IDs ad platforms add to the link, often with no UTM tags beside them.
|
|
190
|
+
gclid: params.get('gclid') || undefined,
|
|
191
|
+
fbclid: params.get('fbclid') || undefined,
|
|
192
|
+
msclkid: params.get('msclkid') || undefined,
|
|
193
|
+
ttclid: params.get('ttclid') || undefined,
|
|
194
|
+
// The referring site's host only, and nothing when the visitor came from this site.
|
|
195
|
+
referrer: parseOffSiteReferrer(document.referrer, window.location.origin),
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Fields left out are not sent. The click IDs (`gclid`, `gbraid` and `wbraid` from
|
|
201
|
+
Google Ads, `fbclid` from Meta, `msclkid` from Microsoft Advertising and `ttclid`
|
|
202
|
+
from TikTok) go to the API under those names, and the rest as `utm_*`.
|
|
203
|
+
`useAuth().signUp` in `@moonbase.sh/vue`, and so the storefront widget, fills this
|
|
204
|
+
in from the tags and click IDs it has already picked up, unless you pass your own
|
|
205
|
+
`utm`.
|
|
206
|
+
|
|
175
207
|
## Inventory flow
|
|
176
208
|
|
|
177
209
|
Most inventory endpoints require an authenticated user:
|
|
@@ -191,6 +223,14 @@ while (page.hasMore && page.next) {
|
|
|
191
223
|
}
|
|
192
224
|
```
|
|
193
225
|
|
|
226
|
+
A product's release history needs no sign-in. `getReleases` lists its published releases, highest
|
|
227
|
+
version first, each with its version, release notes, publish date and files, and whether the caller
|
|
228
|
+
may download it (`downloadsAllowed`). It pages the same way:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const releases = await client.inventory.products.getReleases('my-product')
|
|
232
|
+
```
|
|
233
|
+
|
|
194
234
|
## Endpoint groups
|
|
195
235
|
|
|
196
236
|
`client.storefront`, `client.identity`, `client.orders`, `client.vouchers`, `client.inventory`, `client.activationRequests`, `client.vendor`, `client.communications`, `client.groups`.
|
package/dist/index.cjs
CHANGED
|
@@ -51,6 +51,7 @@ __export(index_exports, {
|
|
|
51
51
|
SubscriptionCancellationReason: () => SubscriptionCancellationReason,
|
|
52
52
|
SubscriptionStatus: () => SubscriptionStatus,
|
|
53
53
|
TokenStore: () => TokenStore,
|
|
54
|
+
accessTokenLifetime: () => accessTokenLifetime,
|
|
54
55
|
imageWidths: () => imageWidths,
|
|
55
56
|
isLocalStorageAvailable: () => isLocalStorageAvailable,
|
|
56
57
|
isPromotionDisplay: () => isPromotionDisplay,
|
|
@@ -119,7 +120,13 @@ function utmToObject(utm) {
|
|
|
119
120
|
utm_campaign: utm == null ? void 0 : utm.campaign,
|
|
120
121
|
utm_term: utm == null ? void 0 : utm.term,
|
|
121
122
|
utm_content: utm == null ? void 0 : utm.content,
|
|
122
|
-
utm_referrer: utm == null ? void 0 : utm.referrer
|
|
123
|
+
utm_referrer: utm == null ? void 0 : utm.referrer,
|
|
124
|
+
gclid: utm == null ? void 0 : utm.gclid,
|
|
125
|
+
gbraid: utm == null ? void 0 : utm.gbraid,
|
|
126
|
+
wbraid: utm == null ? void 0 : utm.wbraid,
|
|
127
|
+
fbclid: utm == null ? void 0 : utm.fbclid,
|
|
128
|
+
msclkid: utm == null ? void 0 : utm.msclkid,
|
|
129
|
+
ttclid: utm == null ? void 0 : utm.ttclid
|
|
123
130
|
}).filter(([_, value]) => value !== void 0).reduce((obj, [key, value]) => ({
|
|
124
131
|
...obj,
|
|
125
132
|
[key]: value
|
|
@@ -211,6 +218,7 @@ var schemas_exports = {};
|
|
|
211
218
|
__export(schemas_exports, {
|
|
212
219
|
downloadSchema: () => downloadSchema,
|
|
213
220
|
manifestSchema: () => manifestSchema,
|
|
221
|
+
productReleaseSchema: () => productReleaseSchema,
|
|
214
222
|
productSummarySchema: () => productSummarySchema
|
|
215
223
|
});
|
|
216
224
|
var import_zod2 = require("zod");
|
|
@@ -273,10 +281,20 @@ var productSummarySchema = import_zod2.z.object({
|
|
|
273
281
|
downloadsNeedsOwnership: import_zod2.z.boolean(),
|
|
274
282
|
downloadsNeedsGroupMembership: import_zod2.z.boolean(),
|
|
275
283
|
// Whether this caller clears every requirement above, including the group membership the client
|
|
276
|
-
// can't see for itself.
|
|
284
|
+
// can't see for itself. False with none of them set means the merchant closed the release to everyone.
|
|
277
285
|
downloadsAllowed: import_zod2.z.boolean(),
|
|
278
286
|
downloads: import_zod2.z.array(downloadSchema).optional()
|
|
279
287
|
});
|
|
288
|
+
var productReleaseSchema = import_zod2.z.object({
|
|
289
|
+
version: import_zod2.z.string(),
|
|
290
|
+
description: import_zod2.z.string().nullish(),
|
|
291
|
+
publishedAt: import_zod2.z.coerce.date(),
|
|
292
|
+
// Whether this caller may download the release, resolved against its own access control: a
|
|
293
|
+
// merchant can keep past releases to owners while the current one is open to anyone.
|
|
294
|
+
downloadsAllowed: import_zod2.z.boolean(),
|
|
295
|
+
downloads: import_zod2.z.array(downloadSchema),
|
|
296
|
+
properties: import_zod2.z.record(rawPropertyValueSchema).nullish()
|
|
297
|
+
});
|
|
280
298
|
|
|
281
299
|
// src/storefront/schemas.ts
|
|
282
300
|
var storefrontProductSchema = import_zod3.z.object({
|
|
@@ -460,7 +478,10 @@ var activationRequestSchema = import_zod4.z.object({
|
|
|
460
478
|
requiresAccount: import_zod4.z.boolean(),
|
|
461
479
|
requiresNewsletterOptIn: import_zod4.z.boolean(),
|
|
462
480
|
numberOfDaysPerProduct: import_zod4.z.number(),
|
|
463
|
-
numberOfDaysRemaining: import_zod4.z.number()
|
|
481
|
+
numberOfDaysRemaining: import_zod4.z.number(),
|
|
482
|
+
// The trial never expires, so the day counts are not worth showing. Absent from a backend that
|
|
483
|
+
// predates endless trials.
|
|
484
|
+
endless: import_zod4.z.boolean().default(false)
|
|
464
485
|
}).optional(),
|
|
465
486
|
licenseEligibility: import_zod4.z.object({
|
|
466
487
|
eligible: import_zod4.z.boolean()
|
|
@@ -837,8 +858,12 @@ var IdentityEndpoints = class {
|
|
|
837
858
|
return userSchema.parse(identity);
|
|
838
859
|
}
|
|
839
860
|
async signUp(name, email, password, address, acceptedPrivacyPolicy, acceptedTermsAndConditions, communicationOptIn, options) {
|
|
840
|
-
const
|
|
841
|
-
|
|
861
|
+
const query = new URLSearchParams({
|
|
862
|
+
scheme: "JWT",
|
|
863
|
+
communicationOptIn: communicationOptIn ? "true" : "false",
|
|
864
|
+
...utmToObject(options == null ? void 0 : options.utm)
|
|
865
|
+
});
|
|
866
|
+
const response = await fetch(`${this.api.baseUrl}/api/customer/identity/sign-up?${query.toString()}`, {
|
|
842
867
|
method: "POST",
|
|
843
868
|
headers: {
|
|
844
869
|
"Accept": "application/json",
|
|
@@ -874,8 +899,13 @@ var IdentityEndpoints = class {
|
|
|
874
899
|
this.tokenStore.setUser(identity);
|
|
875
900
|
return { status: "signed-in", user: userSchema.parse(identity) };
|
|
876
901
|
}
|
|
902
|
+
/**
|
|
903
|
+
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
904
|
+
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
905
|
+
* the buyer is signed out in this browser whatever happens to that request.
|
|
906
|
+
*/
|
|
877
907
|
async signOut() {
|
|
878
|
-
this.tokenStore.
|
|
908
|
+
await this.tokenStore.signOut();
|
|
879
909
|
}
|
|
880
910
|
async update(name, email, emailConfirmationToken, communicationPreferences) {
|
|
881
911
|
const response = await this.api.authenticatedFetch("/api/customer/identity", userSchema, {
|
|
@@ -1082,6 +1112,15 @@ var ProductEndpoints = class {
|
|
|
1082
1112
|
const response = await this.api.fetch(`/api/customer/inventory/products/${productId}${version2 ? `?version=${version2}${this.configuration.includeManifests ? "&includeManifests=true" : ""}` : this.configuration.includeManifests ? "?includeManifests=true" : ""}`, productSummarySchema);
|
|
1083
1113
|
return response.data;
|
|
1084
1114
|
}
|
|
1115
|
+
/**
|
|
1116
|
+
* The product's published releases, highest version first, with their release notes and files,
|
|
1117
|
+
* and whether the caller may download each. Open to anyone who can see the product, signed in or
|
|
1118
|
+
* not. A page can hold fewer releases than its size, so keep following `next` while `hasMore` is set.
|
|
1119
|
+
*/
|
|
1120
|
+
async getReleases(productId, nextUrl) {
|
|
1121
|
+
const response = await this.api.fetch(nextUrl || `/api/customer/inventory/products/${productId}/releases${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productReleaseSchema));
|
|
1122
|
+
return response.data;
|
|
1123
|
+
}
|
|
1085
1124
|
async getOwned(nextUrl) {
|
|
1086
1125
|
const response = await this.api.authenticatedFetch(nextUrl || `/api/customer/inventory/products${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productSummarySchema));
|
|
1087
1126
|
return response.data;
|
|
@@ -1108,7 +1147,7 @@ var ProductEndpoints = class {
|
|
|
1108
1147
|
var import_zod14 = require("zod");
|
|
1109
1148
|
|
|
1110
1149
|
// package.json
|
|
1111
|
-
var version = "3.
|
|
1150
|
+
var version = "3.8.0";
|
|
1112
1151
|
|
|
1113
1152
|
// src/utils/client.ts
|
|
1114
1153
|
function buildClientHeader(sdk) {
|
|
@@ -1787,24 +1826,36 @@ var InMemoryStore = class {
|
|
|
1787
1826
|
};
|
|
1788
1827
|
|
|
1789
1828
|
// src/utils/tokenStore.ts
|
|
1829
|
+
var defaultAccessTokenLifetime = 15 * 60 * 1e3;
|
|
1830
|
+
var renewBeforeExpiry = 5 * 60 * 1e3;
|
|
1831
|
+
var expiryMargin = 30 * 1e3;
|
|
1832
|
+
var refreshLockName = "moonbase_auth_refresh";
|
|
1833
|
+
function accessTokenLifetime(accessToken) {
|
|
1834
|
+
try {
|
|
1835
|
+
const payload = accessToken.split(".")[1].replaceAll("-", "+").replaceAll("_", "/");
|
|
1836
|
+
const claims = JSON.parse(atob(payload.padEnd(Math.ceil(payload.length / 4) * 4, "=")));
|
|
1837
|
+
const lifetime = (Number(claims.exp) - Number(claims.iat)) * 1e3;
|
|
1838
|
+
if (Number.isFinite(lifetime) && lifetime > 0 && lifetime <= 24 * 60 * 60 * 1e3)
|
|
1839
|
+
return lifetime;
|
|
1840
|
+
} catch (e) {
|
|
1841
|
+
}
|
|
1842
|
+
return defaultAccessTokenLifetime;
|
|
1843
|
+
}
|
|
1790
1844
|
var _TokenStore = class _TokenStore {
|
|
1791
1845
|
constructor(configuration) {
|
|
1792
1846
|
this.configuration = configuration;
|
|
1793
1847
|
this.tokens = null;
|
|
1794
1848
|
this.refreshTimeoutId = null;
|
|
1795
1849
|
this.refreshPromise = null;
|
|
1850
|
+
this.waitingForVisibility = false;
|
|
1851
|
+
this.sessionEndedListeners = [];
|
|
1796
1852
|
var _a, _b;
|
|
1797
1853
|
this.logger = (_a = configuration.logger) != null ? _a : new ConsoleLogger();
|
|
1798
1854
|
this.store = (_b = configuration.store) != null ? _b : isLocalStorageAvailable() ? new LocalStorageStore((err) => this.warnAboutStorage(err)) : new InMemoryStore();
|
|
1799
1855
|
if (typeof window !== "undefined" && !configuration.store && !(this.store instanceof LocalStorageStore))
|
|
1800
1856
|
this.warnAboutStorage();
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
this.tokens = {
|
|
1804
|
-
...storedToken,
|
|
1805
|
-
expiresAt: new Date(storedToken.expiresAt)
|
|
1806
|
-
};
|
|
1807
|
-
}
|
|
1857
|
+
this.tokens = this.readStoredTokens();
|
|
1858
|
+
this.scheduleRefresh();
|
|
1808
1859
|
this.store.listen(_TokenStore.storageKey, (updatedTokens) => this.handleStorageUpdate(updatedTokens));
|
|
1809
1860
|
}
|
|
1810
1861
|
get user() {
|
|
@@ -1838,29 +1889,91 @@ var _TokenStore = class _TokenStore {
|
|
|
1838
1889
|
} else {
|
|
1839
1890
|
this.tokens = {
|
|
1840
1891
|
...identity,
|
|
1841
|
-
|
|
1842
|
-
expiresAt: new Date((/* @__PURE__ */ new Date()).getTime() + 15 * 60 * 1e3)
|
|
1892
|
+
expiresAt: new Date(Date.now() + accessTokenLifetime(identity.accessToken))
|
|
1843
1893
|
};
|
|
1844
1894
|
}
|
|
1845
1895
|
this.store.set(_TokenStore.storageKey, this.tokens);
|
|
1846
|
-
|
|
1847
|
-
if (this.refreshTimeoutId != null)
|
|
1848
|
-
window.clearTimeout(this.refreshTimeoutId);
|
|
1849
|
-
this.refreshTimeoutId = window.setTimeout(() => {
|
|
1850
|
-
this.startRefresh().catch((err) => this.logger.warn("Could not refresh access token", err));
|
|
1851
|
-
}, 10 * 60 * 1e3);
|
|
1852
|
-
}
|
|
1896
|
+
this.scheduleRefresh();
|
|
1853
1897
|
return this.tokens;
|
|
1854
1898
|
}
|
|
1855
1899
|
clear() {
|
|
1856
1900
|
this.tokens = null;
|
|
1857
1901
|
this.store.remove(_TokenStore.storageKey);
|
|
1858
|
-
|
|
1859
|
-
window.clearTimeout(this.refreshTimeoutId);
|
|
1902
|
+
this.scheduleRefresh();
|
|
1860
1903
|
return null;
|
|
1861
1904
|
}
|
|
1905
|
+
async signOut() {
|
|
1906
|
+
const tokens = this.tokens;
|
|
1907
|
+
this.clear();
|
|
1908
|
+
if (!tokens)
|
|
1909
|
+
return;
|
|
1910
|
+
try {
|
|
1911
|
+
await fetch(`${this.configuration.endpoint}/api/customer/identity/revoke?token=${encodeURIComponent(tokens.refreshToken)}`, {
|
|
1912
|
+
method: "POST",
|
|
1913
|
+
keepalive: true,
|
|
1914
|
+
headers: {
|
|
1915
|
+
"Accept": "application/json",
|
|
1916
|
+
"Content-Type": "text/plain",
|
|
1917
|
+
"x-mb-cors": "1",
|
|
1918
|
+
"x-mb-client": buildClientHeader(this.configuration.sdk)
|
|
1919
|
+
},
|
|
1920
|
+
body: tokens.accessToken
|
|
1921
|
+
});
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
this.logger.warn("Could not end the session on the server", err);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
onSessionEnded(listener) {
|
|
1927
|
+
this.sessionEndedListeners.push(listener);
|
|
1928
|
+
}
|
|
1862
1929
|
get isExpired() {
|
|
1863
|
-
return this.tokens != null && this.tokens.expiresAt
|
|
1930
|
+
return this.tokens != null && this.tokens.expiresAt.getTime() - expiryMargin < Date.now();
|
|
1931
|
+
}
|
|
1932
|
+
readStoredTokens() {
|
|
1933
|
+
const stored = this.store.get(_TokenStore.storageKey);
|
|
1934
|
+
return stored ? { ...stored, expiresAt: new Date(stored.expiresAt) } : null;
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* (Re)arms the timer that renews the access token shortly before it runs out, or disarms
|
|
1938
|
+
* it when there is nothing to renew. Every tab arms one, and `refreshTokens` makes sure
|
|
1939
|
+
* only one of them actually renews.
|
|
1940
|
+
*/
|
|
1941
|
+
scheduleRefresh() {
|
|
1942
|
+
if (typeof window === "undefined")
|
|
1943
|
+
return;
|
|
1944
|
+
if (this.refreshTimeoutId != null)
|
|
1945
|
+
window.clearTimeout(this.refreshTimeoutId);
|
|
1946
|
+
this.refreshTimeoutId = null;
|
|
1947
|
+
if (!this.tokens)
|
|
1948
|
+
return;
|
|
1949
|
+
const delay = Math.max(0, this.renewalDueAt(this.tokens) - Date.now());
|
|
1950
|
+
this.refreshTimeoutId = window.setTimeout(() => this.renewInBackground(), delay);
|
|
1951
|
+
}
|
|
1952
|
+
renewalDueAt(tokens) {
|
|
1953
|
+
const lead = Math.min(renewBeforeExpiry, accessTokenLifetime(tokens.accessToken) / 3);
|
|
1954
|
+
return tokens.expiresAt.getTime() - lead;
|
|
1955
|
+
}
|
|
1956
|
+
renewInBackground() {
|
|
1957
|
+
var _a;
|
|
1958
|
+
this.refreshTimeoutId = null;
|
|
1959
|
+
if (!this.tokens)
|
|
1960
|
+
return;
|
|
1961
|
+
if (this.renewalDueAt(this.tokens) > Date.now()) {
|
|
1962
|
+
this.scheduleRefresh();
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
1966
|
+
if (!this.waitingForVisibility) {
|
|
1967
|
+
this.waitingForVisibility = true;
|
|
1968
|
+
document.addEventListener("visibilitychange", () => {
|
|
1969
|
+
this.waitingForVisibility = false;
|
|
1970
|
+
this.renewInBackground();
|
|
1971
|
+
}, { once: true });
|
|
1972
|
+
}
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
const pending = (_a = this.refreshPromise) != null ? _a : this.startRefresh();
|
|
1976
|
+
pending.catch((err) => this.logger.warn("Could not refresh access token", err));
|
|
1864
1977
|
}
|
|
1865
1978
|
/**
|
|
1866
1979
|
* Start a refresh and hold it so concurrent callers join it rather than firing
|
|
@@ -1877,7 +1990,45 @@ var _TokenStore = class _TokenStore {
|
|
|
1877
1990
|
this.refreshPromise = pending;
|
|
1878
1991
|
return pending;
|
|
1879
1992
|
}
|
|
1993
|
+
/**
|
|
1994
|
+
* A refresh token can be used once: the server answers a second use by revoking the
|
|
1995
|
+
* session, taking it for a stolen token. So refreshes are serialised across every tab of
|
|
1996
|
+
* the origin, and whoever gets the lock second takes up what the first one stored rather
|
|
1997
|
+
* than spending the same refresh token again. The stored copy has to be read, not waited
|
|
1998
|
+
* for: storage events never reach the document that wrote them, so a second client on
|
|
1999
|
+
* the same page would not hear about it.
|
|
2000
|
+
*/
|
|
1880
2001
|
async refreshTokens() {
|
|
2002
|
+
var _a;
|
|
2003
|
+
const spending = (_a = this.tokens) == null ? void 0 : _a.refreshToken;
|
|
2004
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
2005
|
+
if (locks == null ? void 0 : locks.request) {
|
|
2006
|
+
let held = false;
|
|
2007
|
+
try {
|
|
2008
|
+
return await locks.request(refreshLockName, () => {
|
|
2009
|
+
held = true;
|
|
2010
|
+
return this.refreshHoldingLock(spending);
|
|
2011
|
+
});
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
if (held)
|
|
2014
|
+
throw err;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return await this.refreshHoldingLock(spending);
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* @param spending The refresh token this refresh set out to use. If the session has moved
|
|
2021
|
+
* on from it while we waited for the lock, someone else has renewed already.
|
|
2022
|
+
*/
|
|
2023
|
+
async refreshHoldingLock(spending) {
|
|
2024
|
+
var _a;
|
|
2025
|
+
const stored = this.readStoredTokens();
|
|
2026
|
+
if (stored && stored.refreshToken !== ((_a = this.tokens) == null ? void 0 : _a.refreshToken))
|
|
2027
|
+
this.tokens = stored;
|
|
2028
|
+
if (this.tokens && this.tokens.refreshToken !== spending && !this.isExpired) {
|
|
2029
|
+
this.scheduleRefresh();
|
|
2030
|
+
return this.tokens;
|
|
2031
|
+
}
|
|
1881
2032
|
if (!this.tokens)
|
|
1882
2033
|
throw new Error("No tokens found to refresh");
|
|
1883
2034
|
const response = await fetch(`${this.configuration.endpoint}/api/customer/identity/refresh?token=${encodeURIComponent(this.tokens.refreshToken)}`, {
|
|
@@ -1893,6 +2044,13 @@ var _TokenStore = class _TokenStore {
|
|
|
1893
2044
|
if (response.status !== 200) {
|
|
1894
2045
|
if (response.status === 403 || response.status === 404) {
|
|
1895
2046
|
this.clear();
|
|
2047
|
+
for (const listener of this.sessionEndedListeners) {
|
|
2048
|
+
try {
|
|
2049
|
+
listener();
|
|
2050
|
+
} catch (err) {
|
|
2051
|
+
this.logger.warn("A listener for the end of the session failed", err);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
1896
2054
|
return null;
|
|
1897
2055
|
}
|
|
1898
2056
|
throw new MoonbaseError("Unexpected result", `Could not refresh access token, status code ${response.status}`, response.status);
|
|
@@ -1903,11 +2061,10 @@ var _TokenStore = class _TokenStore {
|
|
|
1903
2061
|
}
|
|
1904
2062
|
handleStorageUpdate(updatedTokens) {
|
|
1905
2063
|
this.tokens = updatedTokens;
|
|
1906
|
-
if (this.refreshTimeoutId != null && typeof window !== "undefined")
|
|
1907
|
-
window.clearTimeout(this.refreshTimeoutId);
|
|
1908
2064
|
if (this.tokens) {
|
|
1909
2065
|
this.tokens.expiresAt = new Date(this.tokens.expiresAt);
|
|
1910
2066
|
}
|
|
2067
|
+
this.scheduleRefresh();
|
|
1911
2068
|
}
|
|
1912
2069
|
warnAboutStorage(err) {
|
|
1913
2070
|
this.logger.warn(
|
|
@@ -2604,7 +2761,8 @@ function parseOffSiteReferrer(rawReferrer, currentOrigin) {
|
|
|
2604
2761
|
}
|
|
2605
2762
|
function resolveUtm(cached, fresh) {
|
|
2606
2763
|
const freshHasUtm = !!(fresh.source || fresh.medium || fresh.campaign || fresh.term || fresh.content);
|
|
2607
|
-
const
|
|
2764
|
+
const freshHasClickId = !!(fresh.gclid || fresh.gbraid || fresh.wbraid || fresh.fbclid || fresh.msclkid || fresh.ttclid);
|
|
2765
|
+
const freshIsExternalArrival = freshHasUtm || freshHasClickId || !!fresh.referrer;
|
|
2608
2766
|
if (freshIsExternalArrival)
|
|
2609
2767
|
return fresh;
|
|
2610
2768
|
return cached != null ? cached : fresh;
|
|
@@ -2663,6 +2821,7 @@ var MoonbaseClient = class {
|
|
|
2663
2821
|
SubscriptionCancellationReason,
|
|
2664
2822
|
SubscriptionStatus,
|
|
2665
2823
|
TokenStore,
|
|
2824
|
+
accessTokenLifetime,
|
|
2666
2825
|
imageWidths,
|
|
2667
2826
|
isLocalStorageAvailable,
|
|
2668
2827
|
isPromotionDisplay,
|