@moonbase.sh/storefront-api 3.8.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/dist/index.cjs +97 -9
- package/dist/index.d.cts +65 -5
- package/dist/index.d.ts +65 -5
- package/dist/index.js +96 -9
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
NotFoundError: () => NotFoundError,
|
|
47
47
|
OfferUtils: () => OfferUtils,
|
|
48
48
|
OrderStatus: () => OrderStatus,
|
|
49
|
+
PasswordSetupRequiredError: () => PasswordSetupRequiredError,
|
|
49
50
|
Platform: () => Platform,
|
|
50
51
|
StoreClosedError: () => StoreClosedError,
|
|
51
52
|
SubscriptionCancellationReason: () => SubscriptionCancellationReason,
|
|
@@ -685,6 +686,12 @@ var CaptchaRequiredError = class extends MoonbaseError {
|
|
|
685
686
|
this.name = "CaptchaRequiredError";
|
|
686
687
|
}
|
|
687
688
|
};
|
|
689
|
+
var PasswordSetupRequiredError = class extends MoonbaseError {
|
|
690
|
+
constructor(detail, title, status) {
|
|
691
|
+
super(title != null ? title : "Password setup required", detail, status);
|
|
692
|
+
this.name = "PasswordSetupRequiredError";
|
|
693
|
+
}
|
|
694
|
+
};
|
|
688
695
|
|
|
689
696
|
// src/utils/problemDetails.ts
|
|
690
697
|
var import_zod7 = require("zod");
|
|
@@ -708,6 +715,7 @@ function camelCase(str) {
|
|
|
708
715
|
}).join("");
|
|
709
716
|
}
|
|
710
717
|
async function handleResponseProblem(response, logger) {
|
|
718
|
+
var _a;
|
|
711
719
|
if (response.status === 404)
|
|
712
720
|
throw new NotFoundError();
|
|
713
721
|
if (response.status === 401) {
|
|
@@ -729,6 +737,8 @@ async function handleResponseProblem(response, logger) {
|
|
|
729
737
|
throw new StoreClosedError(problemDetails.detail);
|
|
730
738
|
if (problemDetails.errorType === "CaptchaRequired")
|
|
731
739
|
throw new CaptchaRequiredError(problemDetails.detail, problemDetails.title);
|
|
740
|
+
if (problemDetails.errorType === "PasswordSetupRequired")
|
|
741
|
+
throw new PasswordSetupRequiredError(problemDetails.detail, problemDetails.title, (_a = problemDetails.status) != null ? _a : response.status);
|
|
732
742
|
throw new MoonbaseError(
|
|
733
743
|
problemDetails.title,
|
|
734
744
|
problemDetails.detail,
|
|
@@ -747,6 +757,7 @@ __export(schemas_exports5, {
|
|
|
747
757
|
identitySchema: () => identitySchema,
|
|
748
758
|
ilokConnectedAccountSchema: () => ilokConnectedAccountSchema,
|
|
749
759
|
pendingActivationSchema: () => pendingActivationSchema,
|
|
760
|
+
pendingPasswordProblemSchema: () => pendingPasswordProblemSchema,
|
|
750
761
|
userAccountConfirmedSchema: () => userAccountConfirmedSchema,
|
|
751
762
|
userAccountConfirmedStatusSchema: () => userAccountConfirmedStatusSchema,
|
|
752
763
|
userSchema: () => userSchema
|
|
@@ -820,13 +831,18 @@ var pendingActivationSchema = import_zod8.z.object({
|
|
|
820
831
|
status: import_zod8.z.literal("activation_pending"),
|
|
821
832
|
email: import_zod8.z.string()
|
|
822
833
|
});
|
|
834
|
+
var pendingPasswordProblemSchema = import_zod8.z.object({
|
|
835
|
+
errorType: import_zod8.z.literal("PasswordSetupRequired"),
|
|
836
|
+
pendingPasswordToken: import_zod8.z.string()
|
|
837
|
+
});
|
|
823
838
|
|
|
824
839
|
// src/identity/endpoints.ts
|
|
825
840
|
var IdentityEndpoints = class {
|
|
826
|
-
constructor(api, tokenStore, logger) {
|
|
841
|
+
constructor(api, tokenStore, logger, pendingPasswords) {
|
|
827
842
|
this.api = api;
|
|
828
843
|
this.tokenStore = tokenStore;
|
|
829
844
|
this.logger = logger;
|
|
845
|
+
this.pendingPasswords = pendingPasswords;
|
|
830
846
|
}
|
|
831
847
|
async get() {
|
|
832
848
|
const response = await this.api.authenticatedFetch("/api/customer/meta/user", userSchema);
|
|
@@ -882,8 +898,11 @@ var IdentityEndpoints = class {
|
|
|
882
898
|
groupIds: normalizeGroupIds(options == null ? void 0 : options.groupIds)
|
|
883
899
|
})
|
|
884
900
|
});
|
|
885
|
-
if (response.status >= 400)
|
|
901
|
+
if (response.status >= 400) {
|
|
902
|
+
if (response.status === 409)
|
|
903
|
+
await this.keepPendingPassword(email, response.clone());
|
|
886
904
|
await handleResponseProblem(response, this.logger);
|
|
905
|
+
}
|
|
887
906
|
const data = await response.json();
|
|
888
907
|
if (response.status === 202) {
|
|
889
908
|
const parsed = pendingActivationSchema.parse(data);
|
|
@@ -899,6 +918,19 @@ var IdentityEndpoints = class {
|
|
|
899
918
|
this.tokenStore.setUser(identity);
|
|
900
919
|
return { status: "signed-in", user: userSchema.parse(identity) };
|
|
901
920
|
}
|
|
921
|
+
/**
|
|
922
|
+
* The address already has an account with no password, so the server emailed it a link and
|
|
923
|
+
* handed back the password just typed, sealed, for that link to set once it is opened here.
|
|
924
|
+
* Never thrown: the caller still gets the `PasswordSetupRequiredError` from the response.
|
|
925
|
+
*/
|
|
926
|
+
async keepPendingPassword(email, response) {
|
|
927
|
+
try {
|
|
928
|
+
const problem = pendingPasswordProblemSchema.safeParse(await response.json());
|
|
929
|
+
if (problem.success)
|
|
930
|
+
this.pendingPasswords.set(email, problem.data.pendingPasswordToken);
|
|
931
|
+
} catch (e) {
|
|
932
|
+
}
|
|
933
|
+
}
|
|
902
934
|
/**
|
|
903
935
|
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
904
936
|
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
@@ -950,7 +982,13 @@ var IdentityEndpoints = class {
|
|
|
950
982
|
await handleResponseProblem(response, this.logger);
|
|
951
983
|
}
|
|
952
984
|
async confirmAccount(email, code) {
|
|
953
|
-
const
|
|
985
|
+
const pendingPasswordToken = this.pendingPasswords.get(email);
|
|
986
|
+
const response = await this.api.fetch(`/api/customer/identity/confirm-account?email=${encodeURIComponent(email)}&code=${encodeURIComponent(code.replaceAll(" ", "+"))}&scheme=JWT`, userAccountConfirmedSchema, {
|
|
987
|
+
method: "POST",
|
|
988
|
+
body: pendingPasswordToken ? { pendingPasswordToken } : void 0
|
|
989
|
+
});
|
|
990
|
+
if (pendingPasswordToken)
|
|
991
|
+
this.pendingPasswords.remove(email);
|
|
954
992
|
const result = response.data;
|
|
955
993
|
if (result.status === "SignedIn") {
|
|
956
994
|
if (!result.accessToken || !result.refreshToken) {
|
|
@@ -1147,7 +1185,7 @@ var ProductEndpoints = class {
|
|
|
1147
1185
|
var import_zod14 = require("zod");
|
|
1148
1186
|
|
|
1149
1187
|
// package.json
|
|
1150
|
-
var version = "3.
|
|
1188
|
+
var version = "3.9.0";
|
|
1151
1189
|
|
|
1152
1190
|
// src/utils/client.ts
|
|
1153
1191
|
function buildClientHeader(sdk) {
|
|
@@ -1155,6 +1193,11 @@ function buildClientHeader(sdk) {
|
|
|
1155
1193
|
}
|
|
1156
1194
|
|
|
1157
1195
|
// src/utils/api.ts
|
|
1196
|
+
function loggableBody(body) {
|
|
1197
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
1198
|
+
return body;
|
|
1199
|
+
return Object.fromEntries(Object.entries(body).map(([key, value]) => [key, /password|token/i.test(key) && value != null ? "[redacted]" : value]));
|
|
1200
|
+
}
|
|
1158
1201
|
function objectToQuery(obj) {
|
|
1159
1202
|
return Object.entries(obj != null ? obj : {}).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value instanceof Date ? value.toISOString() : value)}`).join("&");
|
|
1160
1203
|
}
|
|
@@ -1176,7 +1219,7 @@ var MoonbaseApi = class {
|
|
|
1176
1219
|
const contentType = (_a = options == null ? void 0 : options.contentType) != null ? _a : "application/json";
|
|
1177
1220
|
this.logger.debug("Making request to Moonbase API...", {
|
|
1178
1221
|
path,
|
|
1179
|
-
body: options == null ? void 0 : options.body
|
|
1222
|
+
body: loggableBody(options == null ? void 0 : options.body)
|
|
1180
1223
|
});
|
|
1181
1224
|
const startedAt = /* @__PURE__ */ new Date();
|
|
1182
1225
|
const request = {
|
|
@@ -1825,6 +1868,50 @@ var InMemoryStore = class {
|
|
|
1825
1868
|
}
|
|
1826
1869
|
};
|
|
1827
1870
|
|
|
1871
|
+
// src/utils/pendingPasswords.ts
|
|
1872
|
+
var lifetime = 24 * 60 * 60 * 1e3;
|
|
1873
|
+
function normalizeEmail(email) {
|
|
1874
|
+
return email.trim().toLowerCase();
|
|
1875
|
+
}
|
|
1876
|
+
var _PendingPasswordStore = class _PendingPasswordStore {
|
|
1877
|
+
constructor(store) {
|
|
1878
|
+
this.store = store != null ? store : isLocalStorageAvailable() ? new LocalStorageStore() : new InMemoryStore();
|
|
1879
|
+
}
|
|
1880
|
+
get(email) {
|
|
1881
|
+
var _a, _b;
|
|
1882
|
+
return (_b = (_a = this.read()[normalizeEmail(email)]) == null ? void 0 : _a.token) != null ? _b : null;
|
|
1883
|
+
}
|
|
1884
|
+
set(email, token) {
|
|
1885
|
+
this.write({ ...this.read(), [normalizeEmail(email)]: { token, storedAt: Date.now() } });
|
|
1886
|
+
}
|
|
1887
|
+
remove(email) {
|
|
1888
|
+
const entries = this.read();
|
|
1889
|
+
delete entries[normalizeEmail(email)];
|
|
1890
|
+
this.write(entries);
|
|
1891
|
+
}
|
|
1892
|
+
/** The entries still worth keeping. Drops the rest from storage as it goes. */
|
|
1893
|
+
read() {
|
|
1894
|
+
const stored = this.store.get(_PendingPasswordStore.storageKey);
|
|
1895
|
+
if (!stored || typeof stored !== "object")
|
|
1896
|
+
return {};
|
|
1897
|
+
const now = Date.now();
|
|
1898
|
+
const fresh = Object.fromEntries(Object.entries(stored).filter(
|
|
1899
|
+
([, entry]) => typeof (entry == null ? void 0 : entry.token) === "string" && typeof entry.storedAt === "number" && now - entry.storedAt < lifetime
|
|
1900
|
+
));
|
|
1901
|
+
if (Object.keys(fresh).length !== Object.keys(stored).length)
|
|
1902
|
+
this.write(fresh);
|
|
1903
|
+
return fresh;
|
|
1904
|
+
}
|
|
1905
|
+
write(entries) {
|
|
1906
|
+
if (Object.keys(entries).length > 0)
|
|
1907
|
+
this.store.set(_PendingPasswordStore.storageKey, entries);
|
|
1908
|
+
else
|
|
1909
|
+
this.store.remove(_PendingPasswordStore.storageKey);
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
_PendingPasswordStore.storageKey = "moonbase_pending_passwords";
|
|
1913
|
+
var PendingPasswordStore = _PendingPasswordStore;
|
|
1914
|
+
|
|
1828
1915
|
// src/utils/tokenStore.ts
|
|
1829
1916
|
var defaultAccessTokenLifetime = 15 * 60 * 1e3;
|
|
1830
1917
|
var renewBeforeExpiry = 5 * 60 * 1e3;
|
|
@@ -1834,9 +1921,9 @@ function accessTokenLifetime(accessToken) {
|
|
|
1834
1921
|
try {
|
|
1835
1922
|
const payload = accessToken.split(".")[1].replaceAll("-", "+").replaceAll("_", "/");
|
|
1836
1923
|
const claims = JSON.parse(atob(payload.padEnd(Math.ceil(payload.length / 4) * 4, "=")));
|
|
1837
|
-
const
|
|
1838
|
-
if (Number.isFinite(
|
|
1839
|
-
return
|
|
1924
|
+
const lifetime2 = (Number(claims.exp) - Number(claims.iat)) * 1e3;
|
|
1925
|
+
if (Number.isFinite(lifetime2) && lifetime2 > 0 && lifetime2 <= 24 * 60 * 60 * 1e3)
|
|
1926
|
+
return lifetime2;
|
|
1840
1927
|
} catch (e) {
|
|
1841
1928
|
}
|
|
1842
1929
|
return defaultAccessTokenLifetime;
|
|
@@ -2778,7 +2865,7 @@ var MoonbaseClient = class {
|
|
|
2778
2865
|
this.tokenStore = new TokenStore(configuration);
|
|
2779
2866
|
this.api = new MoonbaseApi(this.configuration.endpoint, this.tokenStore, this.logger, this.configuration.sdk);
|
|
2780
2867
|
this.storefront = new StorefrontEndpoints(this.api, this.configuration);
|
|
2781
|
-
this.identity = new IdentityEndpoints(this.api, this.tokenStore, this.logger);
|
|
2868
|
+
this.identity = new IdentityEndpoints(this.api, this.tokenStore, this.logger, new PendingPasswordStore(configuration.store));
|
|
2782
2869
|
this.communications = new CommunicationsEndpoints(this.api);
|
|
2783
2870
|
this.groups = new GroupEndpoints(this.api);
|
|
2784
2871
|
this.vouchers = new VoucherEndpoints(this.api);
|
|
@@ -2816,6 +2903,7 @@ var MoonbaseClient = class {
|
|
|
2816
2903
|
NotFoundError,
|
|
2817
2904
|
OfferUtils,
|
|
2818
2905
|
OrderStatus,
|
|
2906
|
+
PasswordSetupRequiredError,
|
|
2819
2907
|
Platform,
|
|
2820
2908
|
StoreClosedError,
|
|
2821
2909
|
SubscriptionCancellationReason,
|
package/dist/index.d.cts
CHANGED
|
@@ -1214,6 +1214,33 @@ declare class GroupEndpoints {
|
|
|
1214
1214
|
join(groupId: string): Promise<void>;
|
|
1215
1215
|
}
|
|
1216
1216
|
|
|
1217
|
+
/**
|
|
1218
|
+
* The password a customer chose at sign-up for an address that already had an account without
|
|
1219
|
+
* one, kept until the link Moonbase emails comes back. The server hands it back sealed, as
|
|
1220
|
+
* `pendingPasswordToken`, and confirming the account with it sets that password rather than
|
|
1221
|
+
* asking for another.
|
|
1222
|
+
*
|
|
1223
|
+
* Kept in `localStorage`, so the link finds it in whichever tab of this site it opens in, and
|
|
1224
|
+
* only in the browser that typed the password: whoever signs up with somebody else's address
|
|
1225
|
+
* gets the token, but never the emailed code it needs. Like the token store, every operation
|
|
1226
|
+
* degrades to memory when storage fails, in which case the link asks for the password again.
|
|
1227
|
+
*
|
|
1228
|
+
* Only ever filled from a sign-up response, never from a URL parameter or anything else a link
|
|
1229
|
+
* can carry: a token planted from a link would let an attacker's password ride on the victim's
|
|
1230
|
+
* own click.
|
|
1231
|
+
*/
|
|
1232
|
+
declare class PendingPasswordStore {
|
|
1233
|
+
private readonly store;
|
|
1234
|
+
static readonly storageKey = "moonbase_pending_passwords";
|
|
1235
|
+
constructor(store?: IStore);
|
|
1236
|
+
get(email: string): string | null;
|
|
1237
|
+
set(email: string, token: string): void;
|
|
1238
|
+
remove(email: string): void;
|
|
1239
|
+
/** The entries still worth keeping. Drops the rest from storage as it goes. */
|
|
1240
|
+
private read;
|
|
1241
|
+
private write;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1217
1244
|
declare const moneySchema: z.ZodObject<{
|
|
1218
1245
|
currency: z.ZodString;
|
|
1219
1246
|
amount: z.ZodNumber;
|
|
@@ -2020,6 +2047,17 @@ declare const pendingActivationSchema: z.ZodObject<{
|
|
|
2020
2047
|
email: string;
|
|
2021
2048
|
status: "activation_pending";
|
|
2022
2049
|
}>;
|
|
2050
|
+
/** Sign-up's 409 for an address whose account has no password yet. See `PendingPasswordStore`. */
|
|
2051
|
+
declare const pendingPasswordProblemSchema: z.ZodObject<{
|
|
2052
|
+
errorType: z.ZodLiteral<"PasswordSetupRequired">;
|
|
2053
|
+
pendingPasswordToken: z.ZodString;
|
|
2054
|
+
}, "strip", z.ZodTypeAny, {
|
|
2055
|
+
errorType: "PasswordSetupRequired";
|
|
2056
|
+
pendingPasswordToken: string;
|
|
2057
|
+
}, {
|
|
2058
|
+
errorType: "PasswordSetupRequired";
|
|
2059
|
+
pendingPasswordToken: string;
|
|
2060
|
+
}>;
|
|
2023
2061
|
|
|
2024
2062
|
declare const schemas$9_addressSchema: typeof addressSchema;
|
|
2025
2063
|
declare const schemas$9_communicationPreferencesSchema: typeof communicationPreferencesSchema;
|
|
@@ -2028,11 +2066,12 @@ declare const schemas$9_connectionUrlSchema: typeof connectionUrlSchema;
|
|
|
2028
2066
|
declare const schemas$9_identitySchema: typeof identitySchema;
|
|
2029
2067
|
declare const schemas$9_ilokConnectedAccountSchema: typeof ilokConnectedAccountSchema;
|
|
2030
2068
|
declare const schemas$9_pendingActivationSchema: typeof pendingActivationSchema;
|
|
2069
|
+
declare const schemas$9_pendingPasswordProblemSchema: typeof pendingPasswordProblemSchema;
|
|
2031
2070
|
declare const schemas$9_userAccountConfirmedSchema: typeof userAccountConfirmedSchema;
|
|
2032
2071
|
declare const schemas$9_userAccountConfirmedStatusSchema: typeof userAccountConfirmedStatusSchema;
|
|
2033
2072
|
declare const schemas$9_userSchema: typeof userSchema;
|
|
2034
2073
|
declare namespace schemas$9 {
|
|
2035
|
-
export { schemas$9_addressSchema as addressSchema, schemas$9_communicationPreferencesSchema as communicationPreferencesSchema, schemas$9_connectedAccountSchema as connectedAccountSchema, schemas$9_connectionUrlSchema as connectionUrlSchema, schemas$9_identitySchema as identitySchema, schemas$9_ilokConnectedAccountSchema as ilokConnectedAccountSchema, schemas$9_pendingActivationSchema as pendingActivationSchema, schemas$9_userAccountConfirmedSchema as userAccountConfirmedSchema, schemas$9_userAccountConfirmedStatusSchema as userAccountConfirmedStatusSchema, schemas$9_userSchema as userSchema };
|
|
2074
|
+
export { schemas$9_addressSchema as addressSchema, schemas$9_communicationPreferencesSchema as communicationPreferencesSchema, schemas$9_connectedAccountSchema as connectedAccountSchema, schemas$9_connectionUrlSchema as connectionUrlSchema, schemas$9_identitySchema as identitySchema, schemas$9_ilokConnectedAccountSchema as ilokConnectedAccountSchema, schemas$9_pendingActivationSchema as pendingActivationSchema, schemas$9_pendingPasswordProblemSchema as pendingPasswordProblemSchema, schemas$9_userAccountConfirmedSchema as userAccountConfirmedSchema, schemas$9_userAccountConfirmedStatusSchema as userAccountConfirmedStatusSchema, schemas$9_userSchema as userSchema };
|
|
2036
2075
|
}
|
|
2037
2076
|
|
|
2038
2077
|
type CommunicationPreferences = z.infer<typeof communicationPreferencesSchema>;
|
|
@@ -2084,10 +2123,17 @@ declare class IdentityEndpoints {
|
|
|
2084
2123
|
private api;
|
|
2085
2124
|
private tokenStore;
|
|
2086
2125
|
private logger;
|
|
2087
|
-
|
|
2126
|
+
private pendingPasswords;
|
|
2127
|
+
constructor(api: MoonbaseApi, tokenStore: ITokenStore, logger: ILogger, pendingPasswords: PendingPasswordStore);
|
|
2088
2128
|
get(): Promise<User>;
|
|
2089
2129
|
signIn(email: string, password: string): Promise<User>;
|
|
2090
2130
|
signUp(name: string, email: string, password: string, address: Address | null | undefined, acceptedPrivacyPolicy: boolean, acceptedTermsAndConditions: boolean, communicationOptIn?: boolean, options?: SignUpOptions): Promise<SignUpResult>;
|
|
2131
|
+
/**
|
|
2132
|
+
* The address already has an account with no password, so the server emailed it a link and
|
|
2133
|
+
* handed back the password just typed, sealed, for that link to set once it is opened here.
|
|
2134
|
+
* Never thrown: the caller still gets the `PasswordSetupRequiredError` from the response.
|
|
2135
|
+
*/
|
|
2136
|
+
private keepPendingPassword;
|
|
2091
2137
|
/**
|
|
2092
2138
|
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
2093
2139
|
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
@@ -47317,6 +47363,20 @@ declare class MoonbaseError extends Error {
|
|
|
47317
47363
|
declare class CaptchaRequiredError extends MoonbaseError {
|
|
47318
47364
|
constructor(detail?: string, title?: string);
|
|
47319
47365
|
}
|
|
47366
|
+
/**
|
|
47367
|
+
* The address already has an account, but one without a password: the store gave the customer a
|
|
47368
|
+
* license, or they bought something without one. Anyone can type any address into a form, so the
|
|
47369
|
+
* password they typed is not set from here. Moonbase emails the address a link to finish instead.
|
|
47370
|
+
* `message` is the API's own copy for the customer, safe to show as is.
|
|
47371
|
+
*
|
|
47372
|
+
* From `identity.signUp`, that link sets the password the customer just chose and signs them in,
|
|
47373
|
+
* provided they open it in this same browser. Anywhere else, it asks them for a password again.
|
|
47374
|
+
*
|
|
47375
|
+
* A `MoonbaseError` like any other refusal, so code that already handles those keeps working.
|
|
47376
|
+
*/
|
|
47377
|
+
declare class PasswordSetupRequiredError extends MoonbaseError {
|
|
47378
|
+
constructor(detail?: string, title?: string, status?: number);
|
|
47379
|
+
}
|
|
47320
47380
|
|
|
47321
47381
|
/**
|
|
47322
47382
|
* Cleans up merchant-authored group IDs before they go on the wire.
|
|
@@ -47424,18 +47484,18 @@ declare const problemDetailsSchema: z.ZodObject<{
|
|
|
47424
47484
|
title: string;
|
|
47425
47485
|
type?: string | undefined;
|
|
47426
47486
|
status?: number | undefined;
|
|
47487
|
+
errorType?: string | undefined;
|
|
47427
47488
|
detail?: string | undefined;
|
|
47428
47489
|
instance?: string | undefined;
|
|
47429
47490
|
errors?: Record<string, string[]> | undefined;
|
|
47430
|
-
errorType?: string | undefined;
|
|
47431
47491
|
}, {
|
|
47432
47492
|
title: string;
|
|
47433
47493
|
type?: string | undefined;
|
|
47434
47494
|
status?: number | undefined;
|
|
47495
|
+
errorType?: string | undefined;
|
|
47435
47496
|
detail?: string | undefined;
|
|
47436
47497
|
instance?: string | undefined;
|
|
47437
47498
|
errors?: Record<string, string[]> | undefined;
|
|
47438
|
-
errorType?: string | undefined;
|
|
47439
47499
|
}>;
|
|
47440
47500
|
type ProblemDetails = z.infer<typeof problemDetailsSchema>;
|
|
47441
47501
|
|
|
@@ -47501,4 +47561,4 @@ declare class MoonbaseClient {
|
|
|
47501
47561
|
orders: OrderEndpoints;
|
|
47502
47562
|
}
|
|
47503
47563
|
|
|
47504
|
-
export { type Activation, ActivationMethod, type ActivationRequest, ActivationRequestFulfillmentType, ActivationRequestStatus, ActivationStatus, type Address, Architecture, type BundleLineItem, CaptchaRequiredError, type CartCatalog, type CartOrder, type CartTotal, type CartTotalCondition, CartUtils, type CommunicationPreferences, type CommunicationPreferencesInput, type CommunicationPreferencesView, type CompletedOrder, ConnectableAccountProvider, type ConnectedAccount, ConsoleLogger, CycleLength, type Discount, DiscountUtils, type Download, type DownloadManifest, type ILogger, type IRecurrence, type IStore, type ITokenStore, type Identity, type ImageFit, type ImageFormat, type ImageTransform, ImageUtils, InMemoryStore, type License, LicenseStatus, type LineItem, type LineItemTotal, LocalStorageStore, LogLevel, MarketingConsentType, type MilestoneProgress, type Money, type MoneyCollection, MoneyCollectionUtils, MoonbaseApi, MoonbaseClient, type MoonbaseConfiguration, MoonbaseError, NotAuthenticatedError, NotAuthorizedError, NotFoundError, type OfferCondition, type OfferConditionContext, type OfferProgress, type OfferScope, OfferUtils, type OpenOrder, type Order, OrderStatus, type OrderTotal, type OwnedProduct, type Page, Platform, type PricingTier, type PricingVariation, type ProblemDetails, type ProductLineItem, type ProductRelease, type PromotionDisplay, type Quantifiable, type RawPropertyArray, type RawPropertyObject, type RawPropertyValue, type SignUpOptions, type SignUpResult, type SingleOrMultiple, StoreClosedError, type Storefront, type StorefrontBundle, type StorefrontOffer, type StorefrontOfferTarget, type StorefrontProduct, type StorefrontPromotion, type StorefrontPromotionCta, type StorefrontPromotionTarget, type SubscribeInput, type SubscribeResponse, type Subscription, type SubscriptionCancellationFeedback, SubscriptionCancellationReason, SubscriptionStatus, type TaxEstimate, TokenStore, type UrchinTrackingModule, type User, type UserAccountConfirmed, type UserAccountConfirmedStatus, type Vendor, type VendorCaptcha, type VendorFeatures, type Voucher, accessTokenLifetime, imageWidths, isLocalStorageAvailable, isPromotionDisplay, isTransformableImageUrl, maxImageDimension, normalizeGroupIds, objectToQuery, parseOffSiteReferrer, problemDetailsSchema, promotionDisplays, resolveUtm, schemas, utmToObject };
|
|
47564
|
+
export { type Activation, ActivationMethod, type ActivationRequest, ActivationRequestFulfillmentType, ActivationRequestStatus, ActivationStatus, type Address, Architecture, type BundleLineItem, CaptchaRequiredError, type CartCatalog, type CartOrder, type CartTotal, type CartTotalCondition, CartUtils, type CommunicationPreferences, type CommunicationPreferencesInput, type CommunicationPreferencesView, type CompletedOrder, ConnectableAccountProvider, type ConnectedAccount, ConsoleLogger, CycleLength, type Discount, DiscountUtils, type Download, type DownloadManifest, type ILogger, type IRecurrence, type IStore, type ITokenStore, type Identity, type ImageFit, type ImageFormat, type ImageTransform, ImageUtils, InMemoryStore, type License, LicenseStatus, type LineItem, type LineItemTotal, LocalStorageStore, LogLevel, MarketingConsentType, type MilestoneProgress, type Money, type MoneyCollection, MoneyCollectionUtils, MoonbaseApi, MoonbaseClient, type MoonbaseConfiguration, MoonbaseError, NotAuthenticatedError, NotAuthorizedError, NotFoundError, type OfferCondition, type OfferConditionContext, type OfferProgress, type OfferScope, OfferUtils, type OpenOrder, type Order, OrderStatus, type OrderTotal, type OwnedProduct, type Page, PasswordSetupRequiredError, Platform, type PricingTier, type PricingVariation, type ProblemDetails, type ProductLineItem, type ProductRelease, type PromotionDisplay, type Quantifiable, type RawPropertyArray, type RawPropertyObject, type RawPropertyValue, type SignUpOptions, type SignUpResult, type SingleOrMultiple, StoreClosedError, type Storefront, type StorefrontBundle, type StorefrontOffer, type StorefrontOfferTarget, type StorefrontProduct, type StorefrontPromotion, type StorefrontPromotionCta, type StorefrontPromotionTarget, type SubscribeInput, type SubscribeResponse, type Subscription, type SubscriptionCancellationFeedback, SubscriptionCancellationReason, SubscriptionStatus, type TaxEstimate, TokenStore, type UrchinTrackingModule, type User, type UserAccountConfirmed, type UserAccountConfirmedStatus, type Vendor, type VendorCaptcha, type VendorFeatures, type Voucher, accessTokenLifetime, imageWidths, isLocalStorageAvailable, isPromotionDisplay, isTransformableImageUrl, maxImageDimension, normalizeGroupIds, objectToQuery, parseOffSiteReferrer, problemDetailsSchema, promotionDisplays, resolveUtm, schemas, utmToObject };
|
package/dist/index.d.ts
CHANGED
|
@@ -1214,6 +1214,33 @@ declare class GroupEndpoints {
|
|
|
1214
1214
|
join(groupId: string): Promise<void>;
|
|
1215
1215
|
}
|
|
1216
1216
|
|
|
1217
|
+
/**
|
|
1218
|
+
* The password a customer chose at sign-up for an address that already had an account without
|
|
1219
|
+
* one, kept until the link Moonbase emails comes back. The server hands it back sealed, as
|
|
1220
|
+
* `pendingPasswordToken`, and confirming the account with it sets that password rather than
|
|
1221
|
+
* asking for another.
|
|
1222
|
+
*
|
|
1223
|
+
* Kept in `localStorage`, so the link finds it in whichever tab of this site it opens in, and
|
|
1224
|
+
* only in the browser that typed the password: whoever signs up with somebody else's address
|
|
1225
|
+
* gets the token, but never the emailed code it needs. Like the token store, every operation
|
|
1226
|
+
* degrades to memory when storage fails, in which case the link asks for the password again.
|
|
1227
|
+
*
|
|
1228
|
+
* Only ever filled from a sign-up response, never from a URL parameter or anything else a link
|
|
1229
|
+
* can carry: a token planted from a link would let an attacker's password ride on the victim's
|
|
1230
|
+
* own click.
|
|
1231
|
+
*/
|
|
1232
|
+
declare class PendingPasswordStore {
|
|
1233
|
+
private readonly store;
|
|
1234
|
+
static readonly storageKey = "moonbase_pending_passwords";
|
|
1235
|
+
constructor(store?: IStore);
|
|
1236
|
+
get(email: string): string | null;
|
|
1237
|
+
set(email: string, token: string): void;
|
|
1238
|
+
remove(email: string): void;
|
|
1239
|
+
/** The entries still worth keeping. Drops the rest from storage as it goes. */
|
|
1240
|
+
private read;
|
|
1241
|
+
private write;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1217
1244
|
declare const moneySchema: z.ZodObject<{
|
|
1218
1245
|
currency: z.ZodString;
|
|
1219
1246
|
amount: z.ZodNumber;
|
|
@@ -2020,6 +2047,17 @@ declare const pendingActivationSchema: z.ZodObject<{
|
|
|
2020
2047
|
email: string;
|
|
2021
2048
|
status: "activation_pending";
|
|
2022
2049
|
}>;
|
|
2050
|
+
/** Sign-up's 409 for an address whose account has no password yet. See `PendingPasswordStore`. */
|
|
2051
|
+
declare const pendingPasswordProblemSchema: z.ZodObject<{
|
|
2052
|
+
errorType: z.ZodLiteral<"PasswordSetupRequired">;
|
|
2053
|
+
pendingPasswordToken: z.ZodString;
|
|
2054
|
+
}, "strip", z.ZodTypeAny, {
|
|
2055
|
+
errorType: "PasswordSetupRequired";
|
|
2056
|
+
pendingPasswordToken: string;
|
|
2057
|
+
}, {
|
|
2058
|
+
errorType: "PasswordSetupRequired";
|
|
2059
|
+
pendingPasswordToken: string;
|
|
2060
|
+
}>;
|
|
2023
2061
|
|
|
2024
2062
|
declare const schemas$9_addressSchema: typeof addressSchema;
|
|
2025
2063
|
declare const schemas$9_communicationPreferencesSchema: typeof communicationPreferencesSchema;
|
|
@@ -2028,11 +2066,12 @@ declare const schemas$9_connectionUrlSchema: typeof connectionUrlSchema;
|
|
|
2028
2066
|
declare const schemas$9_identitySchema: typeof identitySchema;
|
|
2029
2067
|
declare const schemas$9_ilokConnectedAccountSchema: typeof ilokConnectedAccountSchema;
|
|
2030
2068
|
declare const schemas$9_pendingActivationSchema: typeof pendingActivationSchema;
|
|
2069
|
+
declare const schemas$9_pendingPasswordProblemSchema: typeof pendingPasswordProblemSchema;
|
|
2031
2070
|
declare const schemas$9_userAccountConfirmedSchema: typeof userAccountConfirmedSchema;
|
|
2032
2071
|
declare const schemas$9_userAccountConfirmedStatusSchema: typeof userAccountConfirmedStatusSchema;
|
|
2033
2072
|
declare const schemas$9_userSchema: typeof userSchema;
|
|
2034
2073
|
declare namespace schemas$9 {
|
|
2035
|
-
export { schemas$9_addressSchema as addressSchema, schemas$9_communicationPreferencesSchema as communicationPreferencesSchema, schemas$9_connectedAccountSchema as connectedAccountSchema, schemas$9_connectionUrlSchema as connectionUrlSchema, schemas$9_identitySchema as identitySchema, schemas$9_ilokConnectedAccountSchema as ilokConnectedAccountSchema, schemas$9_pendingActivationSchema as pendingActivationSchema, schemas$9_userAccountConfirmedSchema as userAccountConfirmedSchema, schemas$9_userAccountConfirmedStatusSchema as userAccountConfirmedStatusSchema, schemas$9_userSchema as userSchema };
|
|
2074
|
+
export { schemas$9_addressSchema as addressSchema, schemas$9_communicationPreferencesSchema as communicationPreferencesSchema, schemas$9_connectedAccountSchema as connectedAccountSchema, schemas$9_connectionUrlSchema as connectionUrlSchema, schemas$9_identitySchema as identitySchema, schemas$9_ilokConnectedAccountSchema as ilokConnectedAccountSchema, schemas$9_pendingActivationSchema as pendingActivationSchema, schemas$9_pendingPasswordProblemSchema as pendingPasswordProblemSchema, schemas$9_userAccountConfirmedSchema as userAccountConfirmedSchema, schemas$9_userAccountConfirmedStatusSchema as userAccountConfirmedStatusSchema, schemas$9_userSchema as userSchema };
|
|
2036
2075
|
}
|
|
2037
2076
|
|
|
2038
2077
|
type CommunicationPreferences = z.infer<typeof communicationPreferencesSchema>;
|
|
@@ -2084,10 +2123,17 @@ declare class IdentityEndpoints {
|
|
|
2084
2123
|
private api;
|
|
2085
2124
|
private tokenStore;
|
|
2086
2125
|
private logger;
|
|
2087
|
-
|
|
2126
|
+
private pendingPasswords;
|
|
2127
|
+
constructor(api: MoonbaseApi, tokenStore: ITokenStore, logger: ILogger, pendingPasswords: PendingPasswordStore);
|
|
2088
2128
|
get(): Promise<User>;
|
|
2089
2129
|
signIn(email: string, password: string): Promise<User>;
|
|
2090
2130
|
signUp(name: string, email: string, password: string, address: Address | null | undefined, acceptedPrivacyPolicy: boolean, acceptedTermsAndConditions: boolean, communicationOptIn?: boolean, options?: SignUpOptions): Promise<SignUpResult>;
|
|
2131
|
+
/**
|
|
2132
|
+
* The address already has an account with no password, so the server emailed it a link and
|
|
2133
|
+
* handed back the password just typed, sealed, for that link to set once it is opened here.
|
|
2134
|
+
* Never thrown: the caller still gets the `PasswordSetupRequiredError` from the response.
|
|
2135
|
+
*/
|
|
2136
|
+
private keepPendingPassword;
|
|
2091
2137
|
/**
|
|
2092
2138
|
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
2093
2139
|
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
@@ -47317,6 +47363,20 @@ declare class MoonbaseError extends Error {
|
|
|
47317
47363
|
declare class CaptchaRequiredError extends MoonbaseError {
|
|
47318
47364
|
constructor(detail?: string, title?: string);
|
|
47319
47365
|
}
|
|
47366
|
+
/**
|
|
47367
|
+
* The address already has an account, but one without a password: the store gave the customer a
|
|
47368
|
+
* license, or they bought something without one. Anyone can type any address into a form, so the
|
|
47369
|
+
* password they typed is not set from here. Moonbase emails the address a link to finish instead.
|
|
47370
|
+
* `message` is the API's own copy for the customer, safe to show as is.
|
|
47371
|
+
*
|
|
47372
|
+
* From `identity.signUp`, that link sets the password the customer just chose and signs them in,
|
|
47373
|
+
* provided they open it in this same browser. Anywhere else, it asks them for a password again.
|
|
47374
|
+
*
|
|
47375
|
+
* A `MoonbaseError` like any other refusal, so code that already handles those keeps working.
|
|
47376
|
+
*/
|
|
47377
|
+
declare class PasswordSetupRequiredError extends MoonbaseError {
|
|
47378
|
+
constructor(detail?: string, title?: string, status?: number);
|
|
47379
|
+
}
|
|
47320
47380
|
|
|
47321
47381
|
/**
|
|
47322
47382
|
* Cleans up merchant-authored group IDs before they go on the wire.
|
|
@@ -47424,18 +47484,18 @@ declare const problemDetailsSchema: z.ZodObject<{
|
|
|
47424
47484
|
title: string;
|
|
47425
47485
|
type?: string | undefined;
|
|
47426
47486
|
status?: number | undefined;
|
|
47487
|
+
errorType?: string | undefined;
|
|
47427
47488
|
detail?: string | undefined;
|
|
47428
47489
|
instance?: string | undefined;
|
|
47429
47490
|
errors?: Record<string, string[]> | undefined;
|
|
47430
|
-
errorType?: string | undefined;
|
|
47431
47491
|
}, {
|
|
47432
47492
|
title: string;
|
|
47433
47493
|
type?: string | undefined;
|
|
47434
47494
|
status?: number | undefined;
|
|
47495
|
+
errorType?: string | undefined;
|
|
47435
47496
|
detail?: string | undefined;
|
|
47436
47497
|
instance?: string | undefined;
|
|
47437
47498
|
errors?: Record<string, string[]> | undefined;
|
|
47438
|
-
errorType?: string | undefined;
|
|
47439
47499
|
}>;
|
|
47440
47500
|
type ProblemDetails = z.infer<typeof problemDetailsSchema>;
|
|
47441
47501
|
|
|
@@ -47501,4 +47561,4 @@ declare class MoonbaseClient {
|
|
|
47501
47561
|
orders: OrderEndpoints;
|
|
47502
47562
|
}
|
|
47503
47563
|
|
|
47504
|
-
export { type Activation, ActivationMethod, type ActivationRequest, ActivationRequestFulfillmentType, ActivationRequestStatus, ActivationStatus, type Address, Architecture, type BundleLineItem, CaptchaRequiredError, type CartCatalog, type CartOrder, type CartTotal, type CartTotalCondition, CartUtils, type CommunicationPreferences, type CommunicationPreferencesInput, type CommunicationPreferencesView, type CompletedOrder, ConnectableAccountProvider, type ConnectedAccount, ConsoleLogger, CycleLength, type Discount, DiscountUtils, type Download, type DownloadManifest, type ILogger, type IRecurrence, type IStore, type ITokenStore, type Identity, type ImageFit, type ImageFormat, type ImageTransform, ImageUtils, InMemoryStore, type License, LicenseStatus, type LineItem, type LineItemTotal, LocalStorageStore, LogLevel, MarketingConsentType, type MilestoneProgress, type Money, type MoneyCollection, MoneyCollectionUtils, MoonbaseApi, MoonbaseClient, type MoonbaseConfiguration, MoonbaseError, NotAuthenticatedError, NotAuthorizedError, NotFoundError, type OfferCondition, type OfferConditionContext, type OfferProgress, type OfferScope, OfferUtils, type OpenOrder, type Order, OrderStatus, type OrderTotal, type OwnedProduct, type Page, Platform, type PricingTier, type PricingVariation, type ProblemDetails, type ProductLineItem, type ProductRelease, type PromotionDisplay, type Quantifiable, type RawPropertyArray, type RawPropertyObject, type RawPropertyValue, type SignUpOptions, type SignUpResult, type SingleOrMultiple, StoreClosedError, type Storefront, type StorefrontBundle, type StorefrontOffer, type StorefrontOfferTarget, type StorefrontProduct, type StorefrontPromotion, type StorefrontPromotionCta, type StorefrontPromotionTarget, type SubscribeInput, type SubscribeResponse, type Subscription, type SubscriptionCancellationFeedback, SubscriptionCancellationReason, SubscriptionStatus, type TaxEstimate, TokenStore, type UrchinTrackingModule, type User, type UserAccountConfirmed, type UserAccountConfirmedStatus, type Vendor, type VendorCaptcha, type VendorFeatures, type Voucher, accessTokenLifetime, imageWidths, isLocalStorageAvailable, isPromotionDisplay, isTransformableImageUrl, maxImageDimension, normalizeGroupIds, objectToQuery, parseOffSiteReferrer, problemDetailsSchema, promotionDisplays, resolveUtm, schemas, utmToObject };
|
|
47564
|
+
export { type Activation, ActivationMethod, type ActivationRequest, ActivationRequestFulfillmentType, ActivationRequestStatus, ActivationStatus, type Address, Architecture, type BundleLineItem, CaptchaRequiredError, type CartCatalog, type CartOrder, type CartTotal, type CartTotalCondition, CartUtils, type CommunicationPreferences, type CommunicationPreferencesInput, type CommunicationPreferencesView, type CompletedOrder, ConnectableAccountProvider, type ConnectedAccount, ConsoleLogger, CycleLength, type Discount, DiscountUtils, type Download, type DownloadManifest, type ILogger, type IRecurrence, type IStore, type ITokenStore, type Identity, type ImageFit, type ImageFormat, type ImageTransform, ImageUtils, InMemoryStore, type License, LicenseStatus, type LineItem, type LineItemTotal, LocalStorageStore, LogLevel, MarketingConsentType, type MilestoneProgress, type Money, type MoneyCollection, MoneyCollectionUtils, MoonbaseApi, MoonbaseClient, type MoonbaseConfiguration, MoonbaseError, NotAuthenticatedError, NotAuthorizedError, NotFoundError, type OfferCondition, type OfferConditionContext, type OfferProgress, type OfferScope, OfferUtils, type OpenOrder, type Order, OrderStatus, type OrderTotal, type OwnedProduct, type Page, PasswordSetupRequiredError, Platform, type PricingTier, type PricingVariation, type ProblemDetails, type ProductLineItem, type ProductRelease, type PromotionDisplay, type Quantifiable, type RawPropertyArray, type RawPropertyObject, type RawPropertyValue, type SignUpOptions, type SignUpResult, type SingleOrMultiple, StoreClosedError, type Storefront, type StorefrontBundle, type StorefrontOffer, type StorefrontOfferTarget, type StorefrontProduct, type StorefrontPromotion, type StorefrontPromotionCta, type StorefrontPromotionTarget, type SubscribeInput, type SubscribeResponse, type Subscription, type SubscriptionCancellationFeedback, SubscriptionCancellationReason, SubscriptionStatus, type TaxEstimate, TokenStore, type UrchinTrackingModule, type User, type UserAccountConfirmed, type UserAccountConfirmedStatus, type Vendor, type VendorCaptcha, type VendorFeatures, type Voucher, accessTokenLifetime, imageWidths, isLocalStorageAvailable, isPromotionDisplay, isTransformableImageUrl, maxImageDimension, normalizeGroupIds, objectToQuery, parseOffSiteReferrer, problemDetailsSchema, promotionDisplays, resolveUtm, schemas, utmToObject };
|
package/dist/index.js
CHANGED
|
@@ -621,6 +621,12 @@ var CaptchaRequiredError = class extends MoonbaseError {
|
|
|
621
621
|
this.name = "CaptchaRequiredError";
|
|
622
622
|
}
|
|
623
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
|
+
};
|
|
624
630
|
|
|
625
631
|
// src/utils/problemDetails.ts
|
|
626
632
|
import { z as z7 } from "zod";
|
|
@@ -644,6 +650,7 @@ function camelCase(str) {
|
|
|
644
650
|
}).join("");
|
|
645
651
|
}
|
|
646
652
|
async function handleResponseProblem(response, logger) {
|
|
653
|
+
var _a;
|
|
647
654
|
if (response.status === 404)
|
|
648
655
|
throw new NotFoundError();
|
|
649
656
|
if (response.status === 401) {
|
|
@@ -665,6 +672,8 @@ async function handleResponseProblem(response, logger) {
|
|
|
665
672
|
throw new StoreClosedError(problemDetails.detail);
|
|
666
673
|
if (problemDetails.errorType === "CaptchaRequired")
|
|
667
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);
|
|
668
677
|
throw new MoonbaseError(
|
|
669
678
|
problemDetails.title,
|
|
670
679
|
problemDetails.detail,
|
|
@@ -683,6 +692,7 @@ __export(schemas_exports5, {
|
|
|
683
692
|
identitySchema: () => identitySchema,
|
|
684
693
|
ilokConnectedAccountSchema: () => ilokConnectedAccountSchema,
|
|
685
694
|
pendingActivationSchema: () => pendingActivationSchema,
|
|
695
|
+
pendingPasswordProblemSchema: () => pendingPasswordProblemSchema,
|
|
686
696
|
userAccountConfirmedSchema: () => userAccountConfirmedSchema,
|
|
687
697
|
userAccountConfirmedStatusSchema: () => userAccountConfirmedStatusSchema,
|
|
688
698
|
userSchema: () => userSchema
|
|
@@ -756,13 +766,18 @@ var pendingActivationSchema = z8.object({
|
|
|
756
766
|
status: z8.literal("activation_pending"),
|
|
757
767
|
email: z8.string()
|
|
758
768
|
});
|
|
769
|
+
var pendingPasswordProblemSchema = z8.object({
|
|
770
|
+
errorType: z8.literal("PasswordSetupRequired"),
|
|
771
|
+
pendingPasswordToken: z8.string()
|
|
772
|
+
});
|
|
759
773
|
|
|
760
774
|
// src/identity/endpoints.ts
|
|
761
775
|
var IdentityEndpoints = class {
|
|
762
|
-
constructor(api, tokenStore, logger) {
|
|
776
|
+
constructor(api, tokenStore, logger, pendingPasswords) {
|
|
763
777
|
this.api = api;
|
|
764
778
|
this.tokenStore = tokenStore;
|
|
765
779
|
this.logger = logger;
|
|
780
|
+
this.pendingPasswords = pendingPasswords;
|
|
766
781
|
}
|
|
767
782
|
async get() {
|
|
768
783
|
const response = await this.api.authenticatedFetch("/api/customer/meta/user", userSchema);
|
|
@@ -818,8 +833,11 @@ var IdentityEndpoints = class {
|
|
|
818
833
|
groupIds: normalizeGroupIds(options == null ? void 0 : options.groupIds)
|
|
819
834
|
})
|
|
820
835
|
});
|
|
821
|
-
if (response.status >= 400)
|
|
836
|
+
if (response.status >= 400) {
|
|
837
|
+
if (response.status === 409)
|
|
838
|
+
await this.keepPendingPassword(email, response.clone());
|
|
822
839
|
await handleResponseProblem(response, this.logger);
|
|
840
|
+
}
|
|
823
841
|
const data = await response.json();
|
|
824
842
|
if (response.status === 202) {
|
|
825
843
|
const parsed = pendingActivationSchema.parse(data);
|
|
@@ -835,6 +853,19 @@ var IdentityEndpoints = class {
|
|
|
835
853
|
this.tokenStore.setUser(identity);
|
|
836
854
|
return { status: "signed-in", user: userSchema.parse(identity) };
|
|
837
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
|
+
}
|
|
838
869
|
/**
|
|
839
870
|
* Signs the buyer out here at once, and ends the session on the server so its refresh
|
|
840
871
|
* token stops working too. Resolves once the server has answered, and never rejects:
|
|
@@ -886,7 +917,13 @@ var IdentityEndpoints = class {
|
|
|
886
917
|
await handleResponseProblem(response, this.logger);
|
|
887
918
|
}
|
|
888
919
|
async confirmAccount(email, code) {
|
|
889
|
-
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);
|
|
890
927
|
const result = response.data;
|
|
891
928
|
if (result.status === "SignedIn") {
|
|
892
929
|
if (!result.accessToken || !result.refreshToken) {
|
|
@@ -1083,7 +1120,7 @@ var ProductEndpoints = class {
|
|
|
1083
1120
|
import { z as z14 } from "zod";
|
|
1084
1121
|
|
|
1085
1122
|
// package.json
|
|
1086
|
-
var version = "3.
|
|
1123
|
+
var version = "3.9.0";
|
|
1087
1124
|
|
|
1088
1125
|
// src/utils/client.ts
|
|
1089
1126
|
function buildClientHeader(sdk) {
|
|
@@ -1091,6 +1128,11 @@ function buildClientHeader(sdk) {
|
|
|
1091
1128
|
}
|
|
1092
1129
|
|
|
1093
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
|
+
}
|
|
1094
1136
|
function objectToQuery(obj) {
|
|
1095
1137
|
return Object.entries(obj != null ? obj : {}).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value instanceof Date ? value.toISOString() : value)}`).join("&");
|
|
1096
1138
|
}
|
|
@@ -1112,7 +1154,7 @@ var MoonbaseApi = class {
|
|
|
1112
1154
|
const contentType = (_a = options == null ? void 0 : options.contentType) != null ? _a : "application/json";
|
|
1113
1155
|
this.logger.debug("Making request to Moonbase API...", {
|
|
1114
1156
|
path,
|
|
1115
|
-
body: options == null ? void 0 : options.body
|
|
1157
|
+
body: loggableBody(options == null ? void 0 : options.body)
|
|
1116
1158
|
});
|
|
1117
1159
|
const startedAt = /* @__PURE__ */ new Date();
|
|
1118
1160
|
const request = {
|
|
@@ -1761,6 +1803,50 @@ var InMemoryStore = class {
|
|
|
1761
1803
|
}
|
|
1762
1804
|
};
|
|
1763
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
|
+
|
|
1764
1850
|
// src/utils/tokenStore.ts
|
|
1765
1851
|
var defaultAccessTokenLifetime = 15 * 60 * 1e3;
|
|
1766
1852
|
var renewBeforeExpiry = 5 * 60 * 1e3;
|
|
@@ -1770,9 +1856,9 @@ function accessTokenLifetime(accessToken) {
|
|
|
1770
1856
|
try {
|
|
1771
1857
|
const payload = accessToken.split(".")[1].replaceAll("-", "+").replaceAll("_", "/");
|
|
1772
1858
|
const claims = JSON.parse(atob(payload.padEnd(Math.ceil(payload.length / 4) * 4, "=")));
|
|
1773
|
-
const
|
|
1774
|
-
if (Number.isFinite(
|
|
1775
|
-
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;
|
|
1776
1862
|
} catch (e) {
|
|
1777
1863
|
}
|
|
1778
1864
|
return defaultAccessTokenLifetime;
|
|
@@ -2714,7 +2800,7 @@ var MoonbaseClient = class {
|
|
|
2714
2800
|
this.tokenStore = new TokenStore(configuration);
|
|
2715
2801
|
this.api = new MoonbaseApi(this.configuration.endpoint, this.tokenStore, this.logger, this.configuration.sdk);
|
|
2716
2802
|
this.storefront = new StorefrontEndpoints(this.api, this.configuration);
|
|
2717
|
-
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));
|
|
2718
2804
|
this.communications = new CommunicationsEndpoints(this.api);
|
|
2719
2805
|
this.groups = new GroupEndpoints(this.api);
|
|
2720
2806
|
this.vouchers = new VoucherEndpoints(this.api);
|
|
@@ -2751,6 +2837,7 @@ export {
|
|
|
2751
2837
|
NotFoundError,
|
|
2752
2838
|
OfferUtils,
|
|
2753
2839
|
OrderStatus,
|
|
2840
|
+
PasswordSetupRequiredError,
|
|
2754
2841
|
Platform,
|
|
2755
2842
|
StoreClosedError,
|
|
2756
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",
|