@saas-support/react 0.3.1 → 0.4.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 +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.js +326 -227
- package/dist/react.cjs +107 -2
- package/dist/react.d.ts +30 -0
- package/dist/react.js +965 -661
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
class
|
|
2
|
-
constructor(
|
|
3
|
-
super(
|
|
1
|
+
class g extends Error {
|
|
2
|
+
constructor(e, t, s = "unknown") {
|
|
3
|
+
super(t), this.name = "SaaSError", this.code = e, this.domain = s;
|
|
4
4
|
}
|
|
5
5
|
get isNotFound() {
|
|
6
6
|
return this.code === 404;
|
|
@@ -18,38 +18,88 @@ class b extends Error {
|
|
|
18
18
|
return this.code === 429;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
class
|
|
22
|
-
constructor(
|
|
23
|
-
this.baseUrl =
|
|
21
|
+
class k {
|
|
22
|
+
constructor(e, t) {
|
|
23
|
+
this.onUnauthorized = null, this.baseUrl = e, this.authMode = t;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/** Register a handler that refreshes tokens and returns a new access token, or null. */
|
|
26
|
+
setUnauthorizedHandler(e) {
|
|
27
|
+
this.onUnauthorized = e;
|
|
28
|
+
}
|
|
29
|
+
async request(e, t, s, r) {
|
|
30
|
+
try {
|
|
31
|
+
return await this.doRequest(e, t, s, r);
|
|
32
|
+
} catch (n) {
|
|
33
|
+
if (n instanceof g && n.isUnauthorized && this.onUnauthorized && (r != null && r.Authorization)) {
|
|
34
|
+
const i = await this.onUnauthorized();
|
|
35
|
+
if (i)
|
|
36
|
+
return this.doRequest(e, t, s, {
|
|
37
|
+
...r,
|
|
38
|
+
Authorization: `Bearer ${i}`
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
throw n;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async get(e, t) {
|
|
45
|
+
return this.request("GET", e, void 0, t);
|
|
46
|
+
}
|
|
47
|
+
async post(e, t, s) {
|
|
48
|
+
return this.request("POST", e, t, s);
|
|
49
|
+
}
|
|
50
|
+
async patch(e, t, s) {
|
|
51
|
+
return this.request("PATCH", e, t, s);
|
|
52
|
+
}
|
|
53
|
+
async del(e, t) {
|
|
54
|
+
return this.request("DELETE", e, void 0, t);
|
|
55
|
+
}
|
|
56
|
+
async uploadBinary(e, t, s) {
|
|
57
|
+
try {
|
|
58
|
+
return await this.doUploadBinary(e, t, s);
|
|
59
|
+
} catch (r) {
|
|
60
|
+
if (r instanceof g && r.isUnauthorized && this.onUnauthorized && (s != null && s.Authorization)) {
|
|
61
|
+
const n = await this.onUnauthorized();
|
|
62
|
+
if (n)
|
|
63
|
+
return this.doUploadBinary(e, t, {
|
|
64
|
+
...s,
|
|
65
|
+
Authorization: `Bearer ${n}`
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
throw r;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async doUploadBinary(e, t, s) {
|
|
72
|
+
const r = {
|
|
73
|
+
"Content-Type": "application/octet-stream",
|
|
74
|
+
...this.getAuthHeaders(),
|
|
75
|
+
...s
|
|
76
|
+
}, i = await (await fetch(`${this.baseUrl}${e}`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: r,
|
|
79
|
+
body: t
|
|
80
|
+
})).json();
|
|
81
|
+
if (i.code && i.code >= 400) {
|
|
82
|
+
const a = this.inferDomain(e);
|
|
83
|
+
throw new g(i.code, i.message || "Upload failed", a);
|
|
84
|
+
}
|
|
85
|
+
return i.data;
|
|
86
|
+
}
|
|
87
|
+
async doRequest(e, t, s, r) {
|
|
88
|
+
const n = {
|
|
27
89
|
"Content-Type": "application/json",
|
|
28
90
|
...this.getAuthHeaders(),
|
|
29
91
|
...r
|
|
30
|
-
}, a = await (await fetch(`${this.baseUrl}${
|
|
31
|
-
method:
|
|
32
|
-
headers:
|
|
92
|
+
}, a = await (await fetch(`${this.baseUrl}${t}`, {
|
|
93
|
+
method: e,
|
|
94
|
+
headers: n,
|
|
33
95
|
body: s ? JSON.stringify(s) : void 0
|
|
34
96
|
})).json();
|
|
35
97
|
if (a.code && a.code >= 400) {
|
|
36
|
-
const
|
|
37
|
-
throw new
|
|
98
|
+
const p = this.inferDomain(t);
|
|
99
|
+
throw new g(a.code, a.message || "Request failed", p);
|
|
38
100
|
}
|
|
39
101
|
return a.data;
|
|
40
102
|
}
|
|
41
|
-
async get(t, e) {
|
|
42
|
-
return this.request("GET", t, void 0, e);
|
|
43
|
-
}
|
|
44
|
-
async post(t, e, s) {
|
|
45
|
-
return this.request("POST", t, e, s);
|
|
46
|
-
}
|
|
47
|
-
async patch(t, e, s) {
|
|
48
|
-
return this.request("PATCH", t, e, s);
|
|
49
|
-
}
|
|
50
|
-
async del(t, e) {
|
|
51
|
-
return this.request("DELETE", t, void 0, e);
|
|
52
|
-
}
|
|
53
103
|
getAuthHeaders() {
|
|
54
104
|
switch (this.authMode.type) {
|
|
55
105
|
case "publishableKey":
|
|
@@ -60,16 +110,19 @@ class m {
|
|
|
60
110
|
return { Authorization: `Bearer ${this.authMode.token}` };
|
|
61
111
|
}
|
|
62
112
|
}
|
|
63
|
-
inferDomain(
|
|
64
|
-
return
|
|
113
|
+
inferDomain(e) {
|
|
114
|
+
return e.startsWith("/auth") ? "auth" : e.startsWith("/billing") ? "billing" : e.startsWith("/report") ? "report" : "unknown";
|
|
65
115
|
}
|
|
66
116
|
}
|
|
67
|
-
class
|
|
68
|
-
constructor(
|
|
69
|
-
this.accessToken = null, this.refreshToken = null, this.refreshTimer = null, this.onRefreshNeeded = null, this.storageKey = `ss_rt_${
|
|
117
|
+
class b {
|
|
118
|
+
constructor(e) {
|
|
119
|
+
this.accessToken = null, this.refreshToken = null, this.refreshTimer = null, this.refreshInFlight = null, this.onRefreshNeeded = null, this.onTokensChanged = null, this.boundHandleStorage = null, this.storageKey = `ss_rt_${e.slice(0, 12)}`, this.refreshToken = this.loadRefreshToken(), typeof window < "u" && (this.boundHandleStorage = this.handleStorageEvent.bind(this), window.addEventListener("storage", this.boundHandleStorage));
|
|
70
120
|
}
|
|
71
|
-
setRefreshCallback(
|
|
72
|
-
this.onRefreshNeeded =
|
|
121
|
+
setRefreshCallback(e) {
|
|
122
|
+
this.onRefreshNeeded = e;
|
|
123
|
+
}
|
|
124
|
+
setTokensChangedCallback(e) {
|
|
125
|
+
this.onTokensChanged = e;
|
|
73
126
|
}
|
|
74
127
|
getAccessToken() {
|
|
75
128
|
return this.accessToken;
|
|
@@ -80,34 +133,64 @@ class w {
|
|
|
80
133
|
hasRefreshToken() {
|
|
81
134
|
return this.refreshToken !== null;
|
|
82
135
|
}
|
|
83
|
-
setTokens(
|
|
84
|
-
this.accessToken =
|
|
136
|
+
setTokens(e, t) {
|
|
137
|
+
this.accessToken = e, this.refreshToken = t, this.saveRefreshToken(t), this.scheduleRefresh(e);
|
|
85
138
|
}
|
|
86
139
|
clearTokens() {
|
|
87
140
|
this.accessToken = null, this.refreshToken = null, this.removeRefreshToken(), this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null);
|
|
88
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Coalesces concurrent refresh calls within this tab and coordinates
|
|
144
|
+
* across tabs via Web Locks API (when available).
|
|
145
|
+
*/
|
|
146
|
+
async refreshOnce() {
|
|
147
|
+
return this.refreshInFlight ? this.refreshInFlight : (this.refreshInFlight = this.executeRefresh().finally(() => {
|
|
148
|
+
this.refreshInFlight = null;
|
|
149
|
+
}), this.refreshInFlight);
|
|
150
|
+
}
|
|
89
151
|
destroy() {
|
|
90
|
-
this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null);
|
|
152
|
+
this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null), typeof window < "u" && this.boundHandleStorage && (window.removeEventListener("storage", this.boundHandleStorage), this.boundHandleStorage = null);
|
|
153
|
+
}
|
|
154
|
+
async executeRefresh() {
|
|
155
|
+
if (!this.onRefreshNeeded)
|
|
156
|
+
throw new Error("No refresh callback configured");
|
|
157
|
+
typeof navigator < "u" && "locks" in navigator ? await navigator.locks.request(
|
|
158
|
+
`ss_refresh_lock_${this.storageKey}`,
|
|
159
|
+
async () => {
|
|
160
|
+
const e = this.loadRefreshToken();
|
|
161
|
+
e && e !== this.refreshToken && (this.refreshToken = e), await this.onRefreshNeeded();
|
|
162
|
+
}
|
|
163
|
+
) : await this.onRefreshNeeded();
|
|
91
164
|
}
|
|
92
|
-
scheduleRefresh(
|
|
93
|
-
var r;
|
|
165
|
+
scheduleRefresh(e) {
|
|
94
166
|
this.refreshTimer && clearTimeout(this.refreshTimer);
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
97
|
-
const s =
|
|
167
|
+
const t = this.getTokenExpiry(e);
|
|
168
|
+
if (!t) return;
|
|
169
|
+
const s = t * 1e3 - Date.now() - 6e4;
|
|
98
170
|
if (s <= 0) {
|
|
99
|
-
|
|
171
|
+
this.refreshOnce().catch(() => {
|
|
172
|
+
});
|
|
100
173
|
return;
|
|
101
174
|
}
|
|
102
175
|
this.refreshTimer = setTimeout(() => {
|
|
103
|
-
|
|
104
|
-
|
|
176
|
+
this.refreshOnce().catch(() => {
|
|
177
|
+
});
|
|
105
178
|
}, s);
|
|
106
179
|
}
|
|
107
|
-
|
|
180
|
+
handleStorageEvent(e) {
|
|
181
|
+
var t;
|
|
182
|
+
if (e.key === this.storageKey) {
|
|
183
|
+
if (e.newValue === null) {
|
|
184
|
+
this.accessToken = null, this.refreshToken = null, this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null), (t = this.onTokensChanged) == null || t.call(this);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
e.newValue !== this.refreshToken && (this.refreshToken = e.newValue, this.accessToken = null, this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
getTokenExpiry(e) {
|
|
108
191
|
try {
|
|
109
|
-
const
|
|
110
|
-
return JSON.parse(atob(
|
|
192
|
+
const t = e.split(".")[1];
|
|
193
|
+
return JSON.parse(atob(t)).exp ?? null;
|
|
111
194
|
} catch {
|
|
112
195
|
return null;
|
|
113
196
|
}
|
|
@@ -119,9 +202,9 @@ class w {
|
|
|
119
202
|
return null;
|
|
120
203
|
}
|
|
121
204
|
}
|
|
122
|
-
saveRefreshToken(
|
|
205
|
+
saveRefreshToken(e) {
|
|
123
206
|
try {
|
|
124
|
-
localStorage.setItem(this.storageKey,
|
|
207
|
+
localStorage.setItem(this.storageKey, e);
|
|
125
208
|
} catch {
|
|
126
209
|
}
|
|
127
210
|
}
|
|
@@ -136,41 +219,41 @@ class S {
|
|
|
136
219
|
constructor() {
|
|
137
220
|
this.listeners = /* @__PURE__ */ new Map();
|
|
138
221
|
}
|
|
139
|
-
on(
|
|
140
|
-
return this.listeners.has(
|
|
222
|
+
on(e, t) {
|
|
223
|
+
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), () => {
|
|
141
224
|
var s;
|
|
142
|
-
(s = this.listeners.get(
|
|
225
|
+
(s = this.listeners.get(e)) == null || s.delete(t);
|
|
143
226
|
};
|
|
144
227
|
}
|
|
145
|
-
emit(
|
|
228
|
+
emit(e, t) {
|
|
146
229
|
var s;
|
|
147
|
-
(s = this.listeners.get(
|
|
230
|
+
(s = this.listeners.get(e)) == null || s.forEach((r) => r(t));
|
|
148
231
|
}
|
|
149
232
|
removeAll() {
|
|
150
233
|
this.listeners.clear();
|
|
151
234
|
}
|
|
152
235
|
}
|
|
153
|
-
const
|
|
154
|
-
class
|
|
155
|
-
constructor(
|
|
156
|
-
this.cachedUser = null, this.cachedSettings = null, this.loaded = !1, this.transport =
|
|
236
|
+
const w = 500, T = 600, U = 5 * 60 * 1e3;
|
|
237
|
+
class $ {
|
|
238
|
+
constructor(e, t, s, r) {
|
|
239
|
+
this.cachedUser = null, this.cachedSettings = null, this.loaded = !1, this.transport = e, this.tokenManager = t, this.emitter = s, this.baseUrl = r;
|
|
157
240
|
}
|
|
158
241
|
// ---------------------------------------------------------------------------
|
|
159
242
|
// Lifecycle
|
|
160
243
|
// ---------------------------------------------------------------------------
|
|
161
244
|
async load() {
|
|
162
|
-
var
|
|
245
|
+
var e, t;
|
|
163
246
|
if (!this.loaded) {
|
|
164
247
|
try {
|
|
165
248
|
this.cachedSettings = await this.transport.get("/auth/settings");
|
|
166
249
|
} catch (s) {
|
|
167
250
|
console.warn("[SaaS Support] Failed to load project settings:", s);
|
|
168
251
|
}
|
|
169
|
-
if ((
|
|
252
|
+
if ((e = this.tokenManager) != null && e.hasRefreshToken())
|
|
170
253
|
try {
|
|
171
254
|
await this.performRefresh();
|
|
172
255
|
} catch {
|
|
173
|
-
(
|
|
256
|
+
(t = this.tokenManager) == null || t.clearTokens();
|
|
174
257
|
}
|
|
175
258
|
this.loaded = !0;
|
|
176
259
|
}
|
|
@@ -178,82 +261,82 @@ class R {
|
|
|
178
261
|
// ---------------------------------------------------------------------------
|
|
179
262
|
// Core auth operations
|
|
180
263
|
// ---------------------------------------------------------------------------
|
|
181
|
-
async signIn(
|
|
182
|
-
const s = await this.transport.post("/auth/login", { email:
|
|
264
|
+
async signIn(e, t) {
|
|
265
|
+
const s = await this.transport.post("/auth/login", { email: e, password: t });
|
|
183
266
|
if ("mfaRequired" in s && s.mfaRequired)
|
|
184
267
|
return s;
|
|
185
268
|
const r = s;
|
|
186
269
|
return this.setSession(r), r;
|
|
187
270
|
}
|
|
188
|
-
async signUp(
|
|
189
|
-
const s = await this.transport.post("/auth/register", { email:
|
|
271
|
+
async signUp(e, t) {
|
|
272
|
+
const s = await this.transport.post("/auth/register", { email: e, password: t });
|
|
190
273
|
return this.setSession(s), s;
|
|
191
274
|
}
|
|
192
275
|
async signOut() {
|
|
193
|
-
var
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
276
|
+
var t;
|
|
277
|
+
const e = (t = this.tokenManager) == null ? void 0 : t.getRefreshToken();
|
|
278
|
+
if (e)
|
|
196
279
|
try {
|
|
197
|
-
await this.transport.post("/auth/logout", { refreshToken:
|
|
280
|
+
await this.transport.post("/auth/logout", { refreshToken: e });
|
|
198
281
|
} catch {
|
|
199
282
|
}
|
|
200
283
|
this.clearSession();
|
|
201
284
|
}
|
|
202
|
-
async signInWithOAuth(
|
|
203
|
-
const
|
|
204
|
-
`/auth/oauth/${
|
|
205
|
-
),
|
|
285
|
+
async signInWithOAuth(e) {
|
|
286
|
+
const t = `${this.baseUrl}/auth/oauth/${e}/popup-callback`, { authUrl: s, state: r } = await this.transport.get(
|
|
287
|
+
`/auth/oauth/${e}?redirect_uri=${encodeURIComponent(t)}`
|
|
288
|
+
), n = window.screenX + (window.innerWidth - w) / 2, i = window.screenY + (window.innerHeight - T) / 2, a = window.open(
|
|
206
289
|
s,
|
|
207
290
|
"saas-support-oauth",
|
|
208
|
-
`width=${
|
|
291
|
+
`width=${w},height=${T},left=${n},top=${i},toolbar=no,menubar=no`
|
|
209
292
|
);
|
|
210
|
-
return new Promise((
|
|
211
|
-
let
|
|
212
|
-
const
|
|
213
|
-
var
|
|
214
|
-
if (((
|
|
215
|
-
if (
|
|
216
|
-
c(new Error(`OAuth error: ${
|
|
293
|
+
return new Promise((p, c) => {
|
|
294
|
+
let h = !1;
|
|
295
|
+
const l = async (u) => {
|
|
296
|
+
var m;
|
|
297
|
+
if (((m = u.data) == null ? void 0 : m.type) === "saas-support:oauth-callback" && !h) {
|
|
298
|
+
if (h = !0, window.removeEventListener("message", l), clearTimeout(y), clearInterval(f), a == null || a.close(), u.data.error) {
|
|
299
|
+
c(new Error(`OAuth error: ${u.data.error}`));
|
|
217
300
|
return;
|
|
218
301
|
}
|
|
219
302
|
try {
|
|
220
|
-
const
|
|
221
|
-
`/auth/oauth/${
|
|
222
|
-
{ code:
|
|
303
|
+
const d = await this.transport.post(
|
|
304
|
+
`/auth/oauth/${e}/callback`,
|
|
305
|
+
{ code: u.data.code, state: u.data.state || r }
|
|
223
306
|
);
|
|
224
|
-
this.setSession(
|
|
225
|
-
} catch (
|
|
226
|
-
c(
|
|
307
|
+
this.setSession(d), p(d);
|
|
308
|
+
} catch (d) {
|
|
309
|
+
c(d);
|
|
227
310
|
}
|
|
228
311
|
}
|
|
229
312
|
};
|
|
230
|
-
window.addEventListener("message",
|
|
313
|
+
window.addEventListener("message", l);
|
|
231
314
|
const y = setTimeout(() => {
|
|
232
|
-
|
|
233
|
-
},
|
|
234
|
-
a != null && a.closed && !
|
|
315
|
+
h || (h = !0, window.removeEventListener("message", l), clearInterval(f), a == null || a.close(), c(new Error("OAuth popup timed out")));
|
|
316
|
+
}, U), f = setInterval(() => {
|
|
317
|
+
a != null && a.closed && !h && (h = !0, clearInterval(f), clearTimeout(y), window.removeEventListener("message", l), c(new Error("OAuth popup was closed")));
|
|
235
318
|
}, 500);
|
|
236
319
|
});
|
|
237
320
|
}
|
|
238
|
-
async submitMfaCode(
|
|
239
|
-
const s = await this.transport.post("/auth/login/mfa", { mfaToken:
|
|
321
|
+
async submitMfaCode(e, t) {
|
|
322
|
+
const s = await this.transport.post("/auth/login/mfa", { mfaToken: e, code: t });
|
|
240
323
|
return this.setSession(s), s;
|
|
241
324
|
}
|
|
242
325
|
// ---------------------------------------------------------------------------
|
|
243
326
|
// Magic link & password reset
|
|
244
327
|
// ---------------------------------------------------------------------------
|
|
245
|
-
async sendMagicLink(
|
|
246
|
-
await this.transport.post("/auth/magic-link/send", { email:
|
|
328
|
+
async sendMagicLink(e, t) {
|
|
329
|
+
await this.transport.post("/auth/magic-link/send", { email: e, redirectUrl: t });
|
|
247
330
|
}
|
|
248
|
-
async verifyMagicLink(
|
|
249
|
-
const
|
|
250
|
-
return this.setSession(
|
|
331
|
+
async verifyMagicLink(e) {
|
|
332
|
+
const t = await this.transport.post("/auth/magic-link/verify", { token: e });
|
|
333
|
+
return this.setSession(t), t;
|
|
251
334
|
}
|
|
252
|
-
async sendPasswordReset(
|
|
253
|
-
await this.transport.post("/auth/password-reset/send", { email:
|
|
335
|
+
async sendPasswordReset(e, t) {
|
|
336
|
+
await this.transport.post("/auth/password-reset/send", { email: e, redirectUrl: t });
|
|
254
337
|
}
|
|
255
|
-
async resetPassword(
|
|
256
|
-
await this.transport.post("/auth/password-reset/verify", { token:
|
|
338
|
+
async resetPassword(e, t) {
|
|
339
|
+
await this.transport.post("/auth/password-reset/verify", { token: e, newPassword: t });
|
|
257
340
|
}
|
|
258
341
|
// ---------------------------------------------------------------------------
|
|
259
342
|
// MFA management
|
|
@@ -261,22 +344,22 @@ class R {
|
|
|
261
344
|
async setupMfa() {
|
|
262
345
|
return this.transport.post("/auth/mfa/setup", void 0, this.authHeaders());
|
|
263
346
|
}
|
|
264
|
-
async verifyMfa(
|
|
265
|
-
return this.transport.post("/auth/mfa/verify", { code:
|
|
347
|
+
async verifyMfa(e) {
|
|
348
|
+
return this.transport.post("/auth/mfa/verify", { code: e }, this.authHeaders());
|
|
266
349
|
}
|
|
267
|
-
async disableMfa(
|
|
268
|
-
await this.transport.post("/auth/mfa/disable", { code:
|
|
350
|
+
async disableMfa(e) {
|
|
351
|
+
await this.transport.post("/auth/mfa/disable", { code: e }, this.authHeaders());
|
|
269
352
|
}
|
|
270
353
|
// ---------------------------------------------------------------------------
|
|
271
354
|
// Token & user access
|
|
272
355
|
// ---------------------------------------------------------------------------
|
|
273
356
|
async getToken() {
|
|
274
|
-
var
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
357
|
+
var t, s, r;
|
|
358
|
+
const e = ((t = this.tokenManager) == null ? void 0 : t.getAccessToken()) ?? null;
|
|
359
|
+
if (e) return e;
|
|
277
360
|
if ((s = this.tokenManager) != null && s.hasRefreshToken())
|
|
278
361
|
try {
|
|
279
|
-
return await this.
|
|
362
|
+
return await this.tokenManager.refreshOnce(), ((r = this.tokenManager) == null ? void 0 : r.getAccessToken()) ?? null;
|
|
280
363
|
} catch {
|
|
281
364
|
return this.clearSession(), null;
|
|
282
365
|
}
|
|
@@ -284,10 +367,10 @@ class R {
|
|
|
284
367
|
}
|
|
285
368
|
async getUser() {
|
|
286
369
|
if (this.cachedUser) return this.cachedUser;
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
370
|
+
const e = await this.getToken();
|
|
371
|
+
if (!e) return null;
|
|
289
372
|
try {
|
|
290
|
-
return this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${
|
|
373
|
+
return this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${e}` }), this.cachedUser;
|
|
291
374
|
} catch {
|
|
292
375
|
return null;
|
|
293
376
|
}
|
|
@@ -306,18 +389,22 @@ class R {
|
|
|
306
389
|
return null;
|
|
307
390
|
}
|
|
308
391
|
}
|
|
309
|
-
onAuthStateChange(
|
|
310
|
-
return this.emitter.on("authStateChange",
|
|
392
|
+
onAuthStateChange(e) {
|
|
393
|
+
return this.emitter.on("authStateChange", e);
|
|
311
394
|
}
|
|
312
395
|
// ---------------------------------------------------------------------------
|
|
313
396
|
// Profile
|
|
314
397
|
// ---------------------------------------------------------------------------
|
|
315
|
-
async updateProfile(
|
|
316
|
-
const
|
|
317
|
-
return this.cachedUser =
|
|
398
|
+
async updateProfile(e) {
|
|
399
|
+
const t = await this.transport.patch("/auth/me", e, this.authHeaders());
|
|
400
|
+
return this.cachedUser = t, this.emitter.emit("authStateChange", t), t;
|
|
401
|
+
}
|
|
402
|
+
async uploadAvatar(e) {
|
|
403
|
+
const t = await this.transport.uploadBinary("/auth/avatar", e, this.authHeaders());
|
|
404
|
+
return this.cachedUser && (this.cachedUser = { ...this.cachedUser, avatarUrl: t.avatarUrl }, this.emitter.emit("authStateChange", this.cachedUser)), t;
|
|
318
405
|
}
|
|
319
|
-
async changePassword(
|
|
320
|
-
await this.transport.post("/auth/change-password", { currentPassword:
|
|
406
|
+
async changePassword(e, t) {
|
|
407
|
+
await this.transport.post("/auth/change-password", { currentPassword: e, newPassword: t }, this.authHeaders());
|
|
321
408
|
}
|
|
322
409
|
// ---------------------------------------------------------------------------
|
|
323
410
|
// Organizations
|
|
@@ -325,180 +412,192 @@ class R {
|
|
|
325
412
|
async listOrgs() {
|
|
326
413
|
return this.transport.get("/auth/orgs", this.authHeaders());
|
|
327
414
|
}
|
|
328
|
-
async createOrg(
|
|
329
|
-
return this.transport.post("/auth/orgs", { name:
|
|
415
|
+
async createOrg(e, t) {
|
|
416
|
+
return this.transport.post("/auth/orgs", { name: e, slug: t }, this.authHeaders());
|
|
330
417
|
}
|
|
331
|
-
async getOrg(
|
|
332
|
-
return this.transport.get(`/auth/orgs/${
|
|
418
|
+
async getOrg(e) {
|
|
419
|
+
return this.transport.get(`/auth/orgs/${e}`, this.authHeaders());
|
|
333
420
|
}
|
|
334
|
-
async updateOrg(
|
|
335
|
-
return this.transport.patch(`/auth/orgs/${
|
|
421
|
+
async updateOrg(e, t) {
|
|
422
|
+
return this.transport.patch(`/auth/orgs/${e}`, t, this.authHeaders());
|
|
336
423
|
}
|
|
337
|
-
async deleteOrg(
|
|
338
|
-
await this.transport.del(`/auth/orgs/${
|
|
424
|
+
async deleteOrg(e) {
|
|
425
|
+
await this.transport.del(`/auth/orgs/${e}`, this.authHeaders());
|
|
339
426
|
}
|
|
340
427
|
// ---------------------------------------------------------------------------
|
|
341
428
|
// Members & Invites
|
|
342
429
|
// ---------------------------------------------------------------------------
|
|
343
|
-
async listMembers(
|
|
344
|
-
return this.transport.get(`/auth/orgs/${
|
|
430
|
+
async listMembers(e) {
|
|
431
|
+
return this.transport.get(`/auth/orgs/${e}/members`, this.authHeaders());
|
|
345
432
|
}
|
|
346
|
-
async sendInvite(
|
|
347
|
-
return this.transport.post(`/auth/orgs/${
|
|
433
|
+
async sendInvite(e, t, s) {
|
|
434
|
+
return this.transport.post(`/auth/orgs/${e}/invites`, { email: t, role: s }, this.authHeaders());
|
|
348
435
|
}
|
|
349
|
-
async updateMemberRole(
|
|
350
|
-
await this.transport.patch(`/auth/orgs/${
|
|
436
|
+
async updateMemberRole(e, t, s) {
|
|
437
|
+
await this.transport.patch(`/auth/orgs/${e}/members/${t}`, { role: s }, this.authHeaders());
|
|
351
438
|
}
|
|
352
|
-
async removeMember(
|
|
353
|
-
await this.transport.del(`/auth/orgs/${
|
|
439
|
+
async removeMember(e, t) {
|
|
440
|
+
await this.transport.del(`/auth/orgs/${e}/members/${t}`, this.authHeaders());
|
|
354
441
|
}
|
|
355
|
-
async acceptInvite(
|
|
356
|
-
return this.transport.post(`/auth/invites/${
|
|
442
|
+
async acceptInvite(e) {
|
|
443
|
+
return this.transport.post(`/auth/invites/${e}/accept`, void 0, this.authHeaders());
|
|
357
444
|
}
|
|
358
445
|
// ---------------------------------------------------------------------------
|
|
359
446
|
// Internal
|
|
360
447
|
// ---------------------------------------------------------------------------
|
|
448
|
+
/** @internal Called when another tab logs out (via storage event). */
|
|
449
|
+
handleExternalLogout() {
|
|
450
|
+
this.cachedUser = null, this.emitter.emit("authStateChange", null);
|
|
451
|
+
}
|
|
361
452
|
/** @internal */
|
|
362
453
|
async performRefresh() {
|
|
363
454
|
var s;
|
|
364
|
-
const
|
|
365
|
-
if (!
|
|
366
|
-
const
|
|
455
|
+
const e = (s = this.tokenManager) == null ? void 0 : s.getRefreshToken();
|
|
456
|
+
if (!e) throw new Error("No refresh token");
|
|
457
|
+
const t = await this.transport.post(
|
|
367
458
|
"/auth/refresh",
|
|
368
|
-
{ refreshToken:
|
|
459
|
+
{ refreshToken: e }
|
|
369
460
|
);
|
|
370
|
-
if (this.tokenManager.setTokens(
|
|
461
|
+
if (this.tokenManager.setTokens(t.accessToken, t.refreshToken), !this.cachedUser)
|
|
371
462
|
try {
|
|
372
|
-
this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${
|
|
463
|
+
this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${t.accessToken}` }), this.emitter.emit("authStateChange", this.cachedUser);
|
|
373
464
|
} catch {
|
|
374
465
|
}
|
|
375
466
|
}
|
|
376
|
-
setSession(
|
|
377
|
-
var
|
|
378
|
-
(
|
|
467
|
+
setSession(e) {
|
|
468
|
+
var t;
|
|
469
|
+
(t = this.tokenManager) == null || t.setTokens(e.accessToken, e.refreshToken), this.cachedUser = e.user, this.emitter.emit("authStateChange", e.user);
|
|
379
470
|
}
|
|
380
471
|
clearSession() {
|
|
381
|
-
var
|
|
382
|
-
(
|
|
472
|
+
var e;
|
|
473
|
+
(e = this.tokenManager) == null || e.clearTokens(), this.cachedUser = null, this.emitter.emit("authStateChange", null);
|
|
383
474
|
}
|
|
384
475
|
authHeaders() {
|
|
385
|
-
var
|
|
386
|
-
const
|
|
387
|
-
return
|
|
476
|
+
var t;
|
|
477
|
+
const e = (t = this.tokenManager) == null ? void 0 : t.getAccessToken();
|
|
478
|
+
return e ? { Authorization: `Bearer ${e}` } : {};
|
|
388
479
|
}
|
|
389
480
|
}
|
|
390
|
-
class
|
|
391
|
-
constructor(
|
|
392
|
-
this.transport =
|
|
481
|
+
class R {
|
|
482
|
+
constructor(e) {
|
|
483
|
+
this.transport = e;
|
|
393
484
|
}
|
|
394
485
|
// --- Customer ---
|
|
395
|
-
async createCustomer(
|
|
396
|
-
return this.transport.post("/billing/customers",
|
|
486
|
+
async createCustomer(e) {
|
|
487
|
+
return this.transport.post("/billing/customers", e);
|
|
397
488
|
}
|
|
398
|
-
async getCustomer(
|
|
399
|
-
return this.transport.get(`/billing/customers/${
|
|
489
|
+
async getCustomer(e) {
|
|
490
|
+
return this.transport.get(`/billing/customers/${e}`);
|
|
400
491
|
}
|
|
401
|
-
async updateCustomer(
|
|
402
|
-
return this.transport.patch(`/billing/customers/${
|
|
492
|
+
async updateCustomer(e, t) {
|
|
493
|
+
return this.transport.patch(`/billing/customers/${e}`, t);
|
|
403
494
|
}
|
|
404
495
|
// --- Subscription ---
|
|
405
|
-
async subscribe(
|
|
406
|
-
return this.transport.post(`/billing/customers/${
|
|
496
|
+
async subscribe(e, t) {
|
|
497
|
+
return this.transport.post(`/billing/customers/${e}/subscribe`, { planId: t });
|
|
407
498
|
}
|
|
408
|
-
async changePlan(
|
|
409
|
-
return this.transport.patch(`/billing/customers/${
|
|
499
|
+
async changePlan(e, t) {
|
|
500
|
+
return this.transport.patch(`/billing/customers/${e}/subscription`, { planId: t });
|
|
410
501
|
}
|
|
411
|
-
async cancelSubscription(
|
|
412
|
-
return this.transport.del(`/billing/customers/${
|
|
502
|
+
async cancelSubscription(e) {
|
|
503
|
+
return this.transport.del(`/billing/customers/${e}/subscription`);
|
|
413
504
|
}
|
|
414
505
|
// --- Invoices ---
|
|
415
|
-
async getInvoices(
|
|
416
|
-
return this.transport.get(`/billing/customers/${
|
|
506
|
+
async getInvoices(e) {
|
|
507
|
+
return this.transport.get(`/billing/customers/${e}/invoices`);
|
|
417
508
|
}
|
|
418
509
|
// --- Usage ---
|
|
419
|
-
async ingestUsageEvent(
|
|
420
|
-
return this.transport.post("/billing/events",
|
|
510
|
+
async ingestUsageEvent(e) {
|
|
511
|
+
return this.transport.post("/billing/events", e);
|
|
421
512
|
}
|
|
422
|
-
async getCurrentUsage(
|
|
423
|
-
return this.transport.get(`/billing/customers/${
|
|
513
|
+
async getCurrentUsage(e) {
|
|
514
|
+
return this.transport.get(`/billing/customers/${e}/usage`);
|
|
424
515
|
}
|
|
425
516
|
// --- Portal ---
|
|
426
|
-
async createPortalToken(
|
|
427
|
-
return this.transport.post("/billing/portal-tokens", { customerId:
|
|
517
|
+
async createPortalToken(e, t) {
|
|
518
|
+
return this.transport.post("/billing/portal-tokens", { customerId: e, expiresIn: t });
|
|
428
519
|
}
|
|
429
520
|
// --- Coupon ---
|
|
430
|
-
async applyCoupon(
|
|
431
|
-
return this.transport.post(`/billing/customers/${
|
|
521
|
+
async applyCoupon(e, t) {
|
|
522
|
+
return this.transport.post(`/billing/customers/${e}/coupon`, { code: t });
|
|
432
523
|
}
|
|
433
524
|
}
|
|
434
525
|
class M {
|
|
435
|
-
constructor(
|
|
436
|
-
this.transport =
|
|
526
|
+
constructor(e) {
|
|
527
|
+
this.transport = e;
|
|
437
528
|
}
|
|
438
529
|
// --- Query ---
|
|
439
|
-
async executeQuery(
|
|
440
|
-
return this.transport.post("/report/query",
|
|
530
|
+
async executeQuery(e) {
|
|
531
|
+
return this.transport.post("/report/query", e);
|
|
441
532
|
}
|
|
442
533
|
// --- Saved Queries ---
|
|
443
|
-
async listQueries(
|
|
444
|
-
const
|
|
445
|
-
return this.transport.get(`/report/queries${
|
|
534
|
+
async listQueries(e) {
|
|
535
|
+
const t = e ? this.toQueryString(e) : "";
|
|
536
|
+
return this.transport.get(`/report/queries${t}`);
|
|
446
537
|
}
|
|
447
|
-
async saveQuery(
|
|
448
|
-
return this.transport.post("/report/queries",
|
|
538
|
+
async saveQuery(e) {
|
|
539
|
+
return this.transport.post("/report/queries", e);
|
|
449
540
|
}
|
|
450
|
-
async updateQuery(
|
|
451
|
-
return this.transport.patch(`/report/queries/${
|
|
541
|
+
async updateQuery(e, t) {
|
|
542
|
+
return this.transport.patch(`/report/queries/${e}`, t);
|
|
452
543
|
}
|
|
453
|
-
async deleteQuery(
|
|
454
|
-
await this.transport.del(`/report/queries/${
|
|
544
|
+
async deleteQuery(e) {
|
|
545
|
+
await this.transport.del(`/report/queries/${e}`);
|
|
455
546
|
}
|
|
456
547
|
// --- Dashboards ---
|
|
457
|
-
async listDashboards(
|
|
458
|
-
const
|
|
459
|
-
return this.transport.get(`/report/dashboards${
|
|
548
|
+
async listDashboards(e) {
|
|
549
|
+
const t = e ? this.toQueryString(e) : "";
|
|
550
|
+
return this.transport.get(`/report/dashboards${t}`);
|
|
460
551
|
}
|
|
461
|
-
async createDashboard(
|
|
462
|
-
return this.transport.post("/report/dashboards",
|
|
552
|
+
async createDashboard(e) {
|
|
553
|
+
return this.transport.post("/report/dashboards", e);
|
|
463
554
|
}
|
|
464
|
-
async getDashboard(
|
|
465
|
-
return this.transport.get(`/report/dashboards/${
|
|
555
|
+
async getDashboard(e) {
|
|
556
|
+
return this.transport.get(`/report/dashboards/${e}`);
|
|
466
557
|
}
|
|
467
|
-
async updateDashboard(
|
|
468
|
-
return this.transport.patch(`/report/dashboards/${
|
|
558
|
+
async updateDashboard(e, t) {
|
|
559
|
+
return this.transport.patch(`/report/dashboards/${e}`, t);
|
|
469
560
|
}
|
|
470
|
-
async deleteDashboard(
|
|
471
|
-
await this.transport.del(`/report/dashboards/${
|
|
561
|
+
async deleteDashboard(e) {
|
|
562
|
+
await this.transport.del(`/report/dashboards/${e}`);
|
|
472
563
|
}
|
|
473
564
|
// --- Embed Tokens ---
|
|
474
|
-
async createEmbedToken(
|
|
475
|
-
return this.transport.post("/report/embed-tokens",
|
|
565
|
+
async createEmbedToken(e) {
|
|
566
|
+
return this.transport.post("/report/embed-tokens", e);
|
|
476
567
|
}
|
|
477
568
|
async listEmbedTokens() {
|
|
478
569
|
return this.transport.get("/report/embed-tokens");
|
|
479
570
|
}
|
|
480
|
-
async revokeEmbedToken(
|
|
481
|
-
await this.transport.del(`/report/embed-tokens/${
|
|
571
|
+
async revokeEmbedToken(e) {
|
|
572
|
+
await this.transport.del(`/report/embed-tokens/${e}`);
|
|
482
573
|
}
|
|
483
|
-
toQueryString(
|
|
484
|
-
const
|
|
485
|
-
return
|
|
574
|
+
toQueryString(e) {
|
|
575
|
+
const t = Object.entries(e).filter(([, s]) => s != null && s !== "");
|
|
576
|
+
return t.length === 0 ? "" : "?" + t.map(([s, r]) => `${s}=${encodeURIComponent(String(r))}`).join("&");
|
|
486
577
|
}
|
|
487
578
|
}
|
|
488
579
|
const v = "https://api.saas-support.com/v1";
|
|
489
|
-
class
|
|
490
|
-
constructor(
|
|
491
|
-
if (this.tokenManager = null, this.loaded = !1, !
|
|
580
|
+
class H {
|
|
581
|
+
constructor(e) {
|
|
582
|
+
if (this.tokenManager = null, this.loaded = !1, !e.publishableKey && !e.apiKey)
|
|
492
583
|
throw new Error("SaaSSupport: either publishableKey or apiKey is required");
|
|
493
|
-
const
|
|
584
|
+
const t = e.baseUrl ?? v;
|
|
494
585
|
this.emitter = new S();
|
|
495
|
-
const s =
|
|
496
|
-
|
|
586
|
+
const s = e.publishableKey ? new k(t, { type: "publishableKey", key: e.publishableKey }) : null, r = e.apiKey ? new k(t, { type: "apiKey", key: e.apiKey }) : null;
|
|
587
|
+
e.publishableKey && (this.tokenManager = new b(e.publishableKey)), this.auth = new $(
|
|
497
588
|
s ?? r,
|
|
498
589
|
this.tokenManager,
|
|
499
590
|
this.emitter,
|
|
500
|
-
|
|
501
|
-
), this.billing = new
|
|
591
|
+
t
|
|
592
|
+
), this.billing = new R(r ?? s), this.report = new M(r ?? s), this.tokenManager && (this.tokenManager.setRefreshCallback(() => this.auth.performRefresh()), this.tokenManager.setTokensChangedCallback(() => {
|
|
593
|
+
this.tokenManager.hasRefreshToken() || this.auth.handleExternalLogout();
|
|
594
|
+
})), this.tokenManager && s && s.setUnauthorizedHandler(async () => {
|
|
595
|
+
try {
|
|
596
|
+
return await this.tokenManager.refreshOnce(), this.tokenManager.getAccessToken();
|
|
597
|
+
} catch {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
});
|
|
502
601
|
}
|
|
503
602
|
async load() {
|
|
504
603
|
this.loaded || (await this.auth.load(), this.loaded = !0);
|
|
@@ -506,23 +605,23 @@ class E {
|
|
|
506
605
|
isLoaded() {
|
|
507
606
|
return this.loaded;
|
|
508
607
|
}
|
|
509
|
-
onError(
|
|
510
|
-
return this.emitter.on("error",
|
|
608
|
+
onError(e) {
|
|
609
|
+
return this.emitter.on("error", e);
|
|
511
610
|
}
|
|
512
611
|
destroy() {
|
|
513
|
-
var
|
|
514
|
-
(
|
|
612
|
+
var e;
|
|
613
|
+
(e = this.tokenManager) == null || e.destroy(), this.emitter.removeAll();
|
|
515
614
|
}
|
|
516
615
|
}
|
|
517
|
-
function
|
|
518
|
-
return "mfaRequired" in
|
|
616
|
+
function E(o) {
|
|
617
|
+
return "mfaRequired" in o && o.mfaRequired === !0;
|
|
519
618
|
}
|
|
520
619
|
export {
|
|
521
|
-
|
|
522
|
-
|
|
620
|
+
$ as AuthClient,
|
|
621
|
+
R as BillingClient,
|
|
523
622
|
M as ReportClient,
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
623
|
+
g as SaaSError,
|
|
624
|
+
H as SaaSSupport,
|
|
625
|
+
k as Transport,
|
|
626
|
+
E as isMfaRequired
|
|
528
627
|
};
|