@moonbase.sh/storefront-api 3.7.0 → 3.9.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 +133 -14
- package/dist/index.d.cts +1627 -1420
- package/dist/index.d.ts +1627 -1420
- package/dist/index.js +132 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -56,7 +56,13 @@ function utmToObject(utm) {
|
|
|
56
56
|
utm_campaign: utm == null ? void 0 : utm.campaign,
|
|
57
57
|
utm_term: utm == null ? void 0 : utm.term,
|
|
58
58
|
utm_content: utm == null ? void 0 : utm.content,
|
|
59
|
-
utm_referrer: utm == null ? void 0 : utm.referrer
|
|
59
|
+
utm_referrer: utm == null ? void 0 : utm.referrer,
|
|
60
|
+
gclid: utm == null ? void 0 : utm.gclid,
|
|
61
|
+
gbraid: utm == null ? void 0 : utm.gbraid,
|
|
62
|
+
wbraid: utm == null ? void 0 : utm.wbraid,
|
|
63
|
+
fbclid: utm == null ? void 0 : utm.fbclid,
|
|
64
|
+
msclkid: utm == null ? void 0 : utm.msclkid,
|
|
65
|
+
ttclid: utm == null ? void 0 : utm.ttclid
|
|
60
66
|
}).filter(([_, value]) => value !== void 0).reduce((obj, [key, value]) => ({
|
|
61
67
|
...obj,
|
|
62
68
|
[key]: value
|
|
@@ -148,6 +154,7 @@ var schemas_exports = {};
|
|
|
148
154
|
__export(schemas_exports, {
|
|
149
155
|
downloadSchema: () => downloadSchema,
|
|
150
156
|
manifestSchema: () => manifestSchema,
|
|
157
|
+
productReleaseSchema: () => productReleaseSchema,
|
|
151
158
|
productSummarySchema: () => productSummarySchema
|
|
152
159
|
});
|
|
153
160
|
import { z as z2 } from "zod";
|
|
@@ -210,10 +217,20 @@ var productSummarySchema = z2.object({
|
|
|
210
217
|
downloadsNeedsOwnership: z2.boolean(),
|
|
211
218
|
downloadsNeedsGroupMembership: z2.boolean(),
|
|
212
219
|
// Whether this caller clears every requirement above, including the group membership the client
|
|
213
|
-
// can't see for itself.
|
|
220
|
+
// can't see for itself. False with none of them set means the merchant closed the release to everyone.
|
|
214
221
|
downloadsAllowed: z2.boolean(),
|
|
215
222
|
downloads: z2.array(downloadSchema).optional()
|
|
216
223
|
});
|
|
224
|
+
var productReleaseSchema = z2.object({
|
|
225
|
+
version: z2.string(),
|
|
226
|
+
description: z2.string().nullish(),
|
|
227
|
+
publishedAt: z2.coerce.date(),
|
|
228
|
+
// Whether this caller may download the release, resolved against its own access control: a
|
|
229
|
+
// merchant can keep past releases to owners while the current one is open to anyone.
|
|
230
|
+
downloadsAllowed: z2.boolean(),
|
|
231
|
+
downloads: z2.array(downloadSchema),
|
|
232
|
+
properties: z2.record(rawPropertyValueSchema).nullish()
|
|
233
|
+
});
|
|
217
234
|
|
|
218
235
|
// src/storefront/schemas.ts
|
|
219
236
|
var storefrontProductSchema = z3.object({
|
|
@@ -604,6 +621,12 @@ var CaptchaRequiredError = class extends MoonbaseError {
|
|
|
604
621
|
this.name = "CaptchaRequiredError";
|
|
605
622
|
}
|
|
606
623
|
};
|
|
624
|
+
var PasswordSetupRequiredError = class extends MoonbaseError {
|
|
625
|
+
constructor(detail, title, status) {
|
|
626
|
+
super(title != null ? title : "Password setup required", detail, status);
|
|
627
|
+
this.name = "PasswordSetupRequiredError";
|
|
628
|
+
}
|
|
629
|
+
};
|
|
607
630
|
|
|
608
631
|
// src/utils/problemDetails.ts
|
|
609
632
|
import { z as z7 } from "zod";
|
|
@@ -627,6 +650,7 @@ function camelCase(str) {
|
|
|
627
650
|
}).join("");
|
|
628
651
|
}
|
|
629
652
|
async function handleResponseProblem(response, logger) {
|
|
653
|
+
var _a;
|
|
630
654
|
if (response.status === 404)
|
|
631
655
|
throw new NotFoundError();
|
|
632
656
|
if (response.status === 401) {
|
|
@@ -648,6 +672,8 @@ async function handleResponseProblem(response, logger) {
|
|
|
648
672
|
throw new StoreClosedError(problemDetails.detail);
|
|
649
673
|
if (problemDetails.errorType === "CaptchaRequired")
|
|
650
674
|
throw new CaptchaRequiredError(problemDetails.detail, problemDetails.title);
|
|
675
|
+
if (problemDetails.errorType === "PasswordSetupRequired")
|
|
676
|
+
throw new PasswordSetupRequiredError(problemDetails.detail, problemDetails.title, (_a = problemDetails.status) != null ? _a : response.status);
|
|
651
677
|
throw new MoonbaseError(
|
|
652
678
|
problemDetails.title,
|
|
653
679
|
problemDetails.detail,
|
|
@@ -666,6 +692,7 @@ __export(schemas_exports5, {
|
|
|
666
692
|
identitySchema: () => identitySchema,
|
|
667
693
|
ilokConnectedAccountSchema: () => ilokConnectedAccountSchema,
|
|
668
694
|
pendingActivationSchema: () => pendingActivationSchema,
|
|
695
|
+
pendingPasswordProblemSchema: () => pendingPasswordProblemSchema,
|
|
669
696
|
userAccountConfirmedSchema: () => userAccountConfirmedSchema,
|
|
670
697
|
userAccountConfirmedStatusSchema: () => userAccountConfirmedStatusSchema,
|
|
671
698
|
userSchema: () => userSchema
|
|
@@ -739,13 +766,18 @@ var pendingActivationSchema = z8.object({
|
|
|
739
766
|
status: z8.literal("activation_pending"),
|
|
740
767
|
email: z8.string()
|
|
741
768
|
});
|
|
769
|
+
var pendingPasswordProblemSchema = z8.object({
|
|
770
|
+
errorType: z8.literal("PasswordSetupRequired"),
|
|
771
|
+
pendingPasswordToken: z8.string()
|
|
772
|
+
});
|
|
742
773
|
|
|
743
774
|
// src/identity/endpoints.ts
|
|
744
775
|
var IdentityEndpoints = class {
|
|
745
|
-
constructor(api, tokenStore, logger) {
|
|
776
|
+
constructor(api, tokenStore, logger, pendingPasswords) {
|
|
746
777
|
this.api = api;
|
|
747
778
|
this.tokenStore = tokenStore;
|
|
748
779
|
this.logger = logger;
|
|
780
|
+
this.pendingPasswords = pendingPasswords;
|
|
749
781
|
}
|
|
750
782
|
async get() {
|
|
751
783
|
const response = await this.api.authenticatedFetch("/api/customer/meta/user", userSchema);
|
|
@@ -777,8 +809,12 @@ var IdentityEndpoints = class {
|
|
|
777
809
|
return userSchema.parse(identity);
|
|
778
810
|
}
|
|
779
811
|
async signUp(name, email, password, address, acceptedPrivacyPolicy, acceptedTermsAndConditions, communicationOptIn, options) {
|
|
780
|
-
const
|
|
781
|
-
|
|
812
|
+
const query = new URLSearchParams({
|
|
813
|
+
scheme: "JWT",
|
|
814
|
+
communicationOptIn: communicationOptIn ? "true" : "false",
|
|
815
|
+
...utmToObject(options == null ? void 0 : options.utm)
|
|
816
|
+
});
|
|
817
|
+
const response = await fetch(`${this.api.baseUrl}/api/customer/identity/sign-up?${query.toString()}`, {
|
|
782
818
|
method: "POST",
|
|
783
819
|
headers: {
|
|
784
820
|
"Accept": "application/json",
|
|
@@ -797,8 +833,11 @@ var IdentityEndpoints = class {
|
|
|
797
833
|
groupIds: normalizeGroupIds(options == null ? void 0 : options.groupIds)
|
|
798
834
|
})
|
|
799
835
|
});
|
|
800
|
-
if (response.status >= 400)
|
|
836
|
+
if (response.status >= 400) {
|
|
837
|
+
if (response.status === 409)
|
|
838
|
+
await this.keepPendingPassword(email, response.clone());
|
|
801
839
|
await handleResponseProblem(response, this.logger);
|
|
840
|
+
}
|
|
802
841
|
const data = await response.json();
|
|
803
842
|
if (response.status === 202) {
|
|
804
843
|
const parsed = pendingActivationSchema.parse(data);
|
|
@@ -814,6 +853,19 @@ var IdentityEndpoints = class {
|
|
|
814
853
|
this.tokenStore.setUser(identity);
|
|
815
854
|
return { status: "signed-in", user: userSchema.parse(identity) };
|
|
816
855
|
}
|
|
856
|
+
/**
|
|
857
|
+
* The address already has an account with no password, so the server emailed it a link and
|
|
858
|
+
* handed back the password just typed, sealed, for that link to set once it is opened here.
|
|
859
|
+
* Never thrown: the caller still gets the `PasswordSetupRequiredError` from the response.
|
|
860
|
+
*/
|
|
861
|
+
async keepPendingPassword(email, response) {
|
|
862
|
+
try {
|
|
863
|
+
const problem = pendingPasswordProblemSchema.safeParse(await response.json());
|
|
864
|
+
if (problem.success)
|
|
865
|
+
this.pendingPasswords.set(email, problem.data.pendingPasswordToken);
|
|
866
|
+
} catch (e) {
|
|
867
|
+
}
|
|
868
|
+
}
|
|
817
869
|
/**
|
|
818
870
|
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
819
871
|
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
@@ -865,7 +917,13 @@ var IdentityEndpoints = class {
|
|
|
865
917
|
await handleResponseProblem(response, this.logger);
|
|
866
918
|
}
|
|
867
919
|
async confirmAccount(email, code) {
|
|
868
|
-
const
|
|
920
|
+
const pendingPasswordToken = this.pendingPasswords.get(email);
|
|
921
|
+
const response = await this.api.fetch(`/api/customer/identity/confirm-account?email=${encodeURIComponent(email)}&code=${encodeURIComponent(code.replaceAll(" ", "+"))}&scheme=JWT`, userAccountConfirmedSchema, {
|
|
922
|
+
method: "POST",
|
|
923
|
+
body: pendingPasswordToken ? { pendingPasswordToken } : void 0
|
|
924
|
+
});
|
|
925
|
+
if (pendingPasswordToken)
|
|
926
|
+
this.pendingPasswords.remove(email);
|
|
869
927
|
const result = response.data;
|
|
870
928
|
if (result.status === "SignedIn") {
|
|
871
929
|
if (!result.accessToken || !result.refreshToken) {
|
|
@@ -1027,6 +1085,15 @@ var ProductEndpoints = class {
|
|
|
1027
1085
|
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);
|
|
1028
1086
|
return response.data;
|
|
1029
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* The product's published releases, highest version first, with their release notes and files,
|
|
1090
|
+
* and whether the caller may download each. Open to anyone who can see the product, signed in or
|
|
1091
|
+
* not. A page can hold fewer releases than its size, so keep following `next` while `hasMore` is set.
|
|
1092
|
+
*/
|
|
1093
|
+
async getReleases(productId, nextUrl) {
|
|
1094
|
+
const response = await this.api.fetch(nextUrl || `/api/customer/inventory/products/${productId}/releases${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productReleaseSchema));
|
|
1095
|
+
return response.data;
|
|
1096
|
+
}
|
|
1030
1097
|
async getOwned(nextUrl) {
|
|
1031
1098
|
const response = await this.api.authenticatedFetch(nextUrl || `/api/customer/inventory/products${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productSummarySchema));
|
|
1032
1099
|
return response.data;
|
|
@@ -1053,7 +1120,7 @@ var ProductEndpoints = class {
|
|
|
1053
1120
|
import { z as z14 } from "zod";
|
|
1054
1121
|
|
|
1055
1122
|
// package.json
|
|
1056
|
-
var version = "3.
|
|
1123
|
+
var version = "3.9.0";
|
|
1057
1124
|
|
|
1058
1125
|
// src/utils/client.ts
|
|
1059
1126
|
function buildClientHeader(sdk) {
|
|
@@ -1061,6 +1128,11 @@ function buildClientHeader(sdk) {
|
|
|
1061
1128
|
}
|
|
1062
1129
|
|
|
1063
1130
|
// src/utils/api.ts
|
|
1131
|
+
function loggableBody(body) {
|
|
1132
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
1133
|
+
return body;
|
|
1134
|
+
return Object.fromEntries(Object.entries(body).map(([key, value]) => [key, /password|token/i.test(key) && value != null ? "[redacted]" : value]));
|
|
1135
|
+
}
|
|
1064
1136
|
function objectToQuery(obj) {
|
|
1065
1137
|
return Object.entries(obj != null ? obj : {}).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value instanceof Date ? value.toISOString() : value)}`).join("&");
|
|
1066
1138
|
}
|
|
@@ -1082,7 +1154,7 @@ var MoonbaseApi = class {
|
|
|
1082
1154
|
const contentType = (_a = options == null ? void 0 : options.contentType) != null ? _a : "application/json";
|
|
1083
1155
|
this.logger.debug("Making request to Moonbase API...", {
|
|
1084
1156
|
path,
|
|
1085
|
-
body: options == null ? void 0 : options.body
|
|
1157
|
+
body: loggableBody(options == null ? void 0 : options.body)
|
|
1086
1158
|
});
|
|
1087
1159
|
const startedAt = /* @__PURE__ */ new Date();
|
|
1088
1160
|
const request = {
|
|
@@ -1731,6 +1803,50 @@ var InMemoryStore = class {
|
|
|
1731
1803
|
}
|
|
1732
1804
|
};
|
|
1733
1805
|
|
|
1806
|
+
// src/utils/pendingPasswords.ts
|
|
1807
|
+
var lifetime = 24 * 60 * 60 * 1e3;
|
|
1808
|
+
function normalizeEmail(email) {
|
|
1809
|
+
return email.trim().toLowerCase();
|
|
1810
|
+
}
|
|
1811
|
+
var _PendingPasswordStore = class _PendingPasswordStore {
|
|
1812
|
+
constructor(store) {
|
|
1813
|
+
this.store = store != null ? store : isLocalStorageAvailable() ? new LocalStorageStore() : new InMemoryStore();
|
|
1814
|
+
}
|
|
1815
|
+
get(email) {
|
|
1816
|
+
var _a, _b;
|
|
1817
|
+
return (_b = (_a = this.read()[normalizeEmail(email)]) == null ? void 0 : _a.token) != null ? _b : null;
|
|
1818
|
+
}
|
|
1819
|
+
set(email, token) {
|
|
1820
|
+
this.write({ ...this.read(), [normalizeEmail(email)]: { token, storedAt: Date.now() } });
|
|
1821
|
+
}
|
|
1822
|
+
remove(email) {
|
|
1823
|
+
const entries = this.read();
|
|
1824
|
+
delete entries[normalizeEmail(email)];
|
|
1825
|
+
this.write(entries);
|
|
1826
|
+
}
|
|
1827
|
+
/** The entries still worth keeping. Drops the rest from storage as it goes. */
|
|
1828
|
+
read() {
|
|
1829
|
+
const stored = this.store.get(_PendingPasswordStore.storageKey);
|
|
1830
|
+
if (!stored || typeof stored !== "object")
|
|
1831
|
+
return {};
|
|
1832
|
+
const now = Date.now();
|
|
1833
|
+
const fresh = Object.fromEntries(Object.entries(stored).filter(
|
|
1834
|
+
([, entry]) => typeof (entry == null ? void 0 : entry.token) === "string" && typeof entry.storedAt === "number" && now - entry.storedAt < lifetime
|
|
1835
|
+
));
|
|
1836
|
+
if (Object.keys(fresh).length !== Object.keys(stored).length)
|
|
1837
|
+
this.write(fresh);
|
|
1838
|
+
return fresh;
|
|
1839
|
+
}
|
|
1840
|
+
write(entries) {
|
|
1841
|
+
if (Object.keys(entries).length > 0)
|
|
1842
|
+
this.store.set(_PendingPasswordStore.storageKey, entries);
|
|
1843
|
+
else
|
|
1844
|
+
this.store.remove(_PendingPasswordStore.storageKey);
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
_PendingPasswordStore.storageKey = "moonbase_pending_passwords";
|
|
1848
|
+
var PendingPasswordStore = _PendingPasswordStore;
|
|
1849
|
+
|
|
1734
1850
|
// src/utils/tokenStore.ts
|
|
1735
1851
|
var defaultAccessTokenLifetime = 15 * 60 * 1e3;
|
|
1736
1852
|
var renewBeforeExpiry = 5 * 60 * 1e3;
|
|
@@ -1740,9 +1856,9 @@ function accessTokenLifetime(accessToken) {
|
|
|
1740
1856
|
try {
|
|
1741
1857
|
const payload = accessToken.split(".")[1].replaceAll("-", "+").replaceAll("_", "/");
|
|
1742
1858
|
const claims = JSON.parse(atob(payload.padEnd(Math.ceil(payload.length / 4) * 4, "=")));
|
|
1743
|
-
const
|
|
1744
|
-
if (Number.isFinite(
|
|
1745
|
-
return
|
|
1859
|
+
const lifetime2 = (Number(claims.exp) - Number(claims.iat)) * 1e3;
|
|
1860
|
+
if (Number.isFinite(lifetime2) && lifetime2 > 0 && lifetime2 <= 24 * 60 * 60 * 1e3)
|
|
1861
|
+
return lifetime2;
|
|
1746
1862
|
} catch (e) {
|
|
1747
1863
|
}
|
|
1748
1864
|
return defaultAccessTokenLifetime;
|
|
@@ -2667,7 +2783,8 @@ function parseOffSiteReferrer(rawReferrer, currentOrigin) {
|
|
|
2667
2783
|
}
|
|
2668
2784
|
function resolveUtm(cached, fresh) {
|
|
2669
2785
|
const freshHasUtm = !!(fresh.source || fresh.medium || fresh.campaign || fresh.term || fresh.content);
|
|
2670
|
-
const
|
|
2786
|
+
const freshHasClickId = !!(fresh.gclid || fresh.gbraid || fresh.wbraid || fresh.fbclid || fresh.msclkid || fresh.ttclid);
|
|
2787
|
+
const freshIsExternalArrival = freshHasUtm || freshHasClickId || !!fresh.referrer;
|
|
2671
2788
|
if (freshIsExternalArrival)
|
|
2672
2789
|
return fresh;
|
|
2673
2790
|
return cached != null ? cached : fresh;
|
|
@@ -2683,7 +2800,7 @@ var MoonbaseClient = class {
|
|
|
2683
2800
|
this.tokenStore = new TokenStore(configuration);
|
|
2684
2801
|
this.api = new MoonbaseApi(this.configuration.endpoint, this.tokenStore, this.logger, this.configuration.sdk);
|
|
2685
2802
|
this.storefront = new StorefrontEndpoints(this.api, this.configuration);
|
|
2686
|
-
this.identity = new IdentityEndpoints(this.api, this.tokenStore, this.logger);
|
|
2803
|
+
this.identity = new IdentityEndpoints(this.api, this.tokenStore, this.logger, new PendingPasswordStore(configuration.store));
|
|
2687
2804
|
this.communications = new CommunicationsEndpoints(this.api);
|
|
2688
2805
|
this.groups = new GroupEndpoints(this.api);
|
|
2689
2806
|
this.vouchers = new VoucherEndpoints(this.api);
|
|
@@ -2720,6 +2837,7 @@ export {
|
|
|
2720
2837
|
NotFoundError,
|
|
2721
2838
|
OfferUtils,
|
|
2722
2839
|
OrderStatus,
|
|
2840
|
+
PasswordSetupRequiredError,
|
|
2723
2841
|
Platform,
|
|
2724
2842
|
StoreClosedError,
|
|
2725
2843
|
SubscriptionCancellationReason,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moonbase.sh/storefront-api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.9.0",
|
|
5
5
|
"description": "Package to let you build storefronts with Moonbase.sh as payment and delivery provider",
|
|
6
6
|
"author": "Tobias Lønnerød Madsen <m@dsen.tv>",
|
|
7
7
|
"license": "MIT",
|