@alliumcloud/avatar-sso-internal 2.5.5-dev.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/main.js ADDED
@@ -0,0 +1,1109 @@
1
+ import * as c from "./config";
2
+ import CustomStorage from "./lib/CustomStorage";
3
+ import { isBrowser } from "./lib/utils";
4
+ export { default as CustomStorage } from "./lib/CustomStorage";
5
+ export default class SSO {
6
+ constructor(config) {
7
+ this.config = config;
8
+ this.serviceType = "AVATAR_SSO";
9
+ this.ssoClientUrl = c.ssoClientUrl;
10
+ this.ssoServerUrl = c.ssoServerUrl;
11
+ this.dbId = "local-user";
12
+ this.callbacks = new Set();
13
+ this.cb = (payload) => {
14
+ for (const cb of this.callbacks)
15
+ cb(payload);
16
+ };
17
+ this.register = async (payload) => {
18
+ const { redirectUri, type, ...body } = payload;
19
+ const queryParams = this.emailVerificationQuery({ redirectUri, type });
20
+ const res = await fetch(`${this.ssoServerUrl}/users/signup${queryParams}`, {
21
+ method: "POST",
22
+ body: JSON.stringify(body),
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ "x-sdk-key": this.sdkKey,
26
+ },
27
+ });
28
+ const json = (await res.json());
29
+ if (!res?.ok || !json.success)
30
+ throw new Error(json.message ?? "SSO-SDK: Local register failed.");
31
+ this.cb({
32
+ type: c.actions.local_register,
33
+ payload: json.data,
34
+ });
35
+ return json.data;
36
+ };
37
+ this.localLogin = async (payload) => {
38
+ if (!(payload instanceof Object))
39
+ return;
40
+ const { redirectUri, type, ...body } = payload;
41
+ const queryParams = this.emailVerificationQuery({ redirectUri, type });
42
+ const res = await fetch(`${this.ssoServerUrl}/users/signin${queryParams}`, {
43
+ method: "POST",
44
+ body: JSON.stringify(body),
45
+ headers: {
46
+ "Content-Type": "application/json",
47
+ "x-sdk-key": this.sdkKey,
48
+ },
49
+ });
50
+ const json = await res.json();
51
+ if (!res?.ok || !json.success)
52
+ throw new Error(json.message ?? "SSO-SDK: Local login failed.");
53
+ this.cb({
54
+ type: c.actions.local_login,
55
+ payload: null,
56
+ });
57
+ };
58
+ this.ssoLogin = async (redirectUrl) => {
59
+ if (redirectUrl instanceof Object)
60
+ return;
61
+ this.throwIfNotBrowser();
62
+ const r = `${this.ssoClientUrl}/sso?sdkKey=${this.sdkKey}&redirectUri=${redirectUrl}`;
63
+ window.location.href = r;
64
+ };
65
+ /**
66
+ * Login function that triggers both local and SSO login flows. If the payload is a string, it will trigger the SSO login flow with the payload as the redirect URL. If the payload is an object, it will trigger the local login flow with the payload as the login data.
67
+ * @param payload just redirect URL string for SSO login or LocalLoginPayload for local login
68
+ * @returns
69
+ */
70
+ this.login = async (payload) => {
71
+ await Promise.all([this.localLogin(payload), this.ssoLogin(payload)]);
72
+ };
73
+ this.decodeJwtPayload = (token) => {
74
+ try {
75
+ const payload = token.split(".")[1];
76
+ if (!payload)
77
+ return null;
78
+ const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
79
+ return JSON.parse(atob(base64));
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ };
85
+ /**
86
+ * - it throws.
87
+ */
88
+ this.autoRefresh = async () => {
89
+ const existingUser = await this.getLocalUser().catch(() => null);
90
+ if (!existingUser)
91
+ return null;
92
+ const { accessToken, refreshToken } = existingUser.tokens;
93
+ const decodedRT = this.decodeJwtPayload(refreshToken);
94
+ const decodedAT = this.decodeJwtPayload(accessToken);
95
+ const date = new Date();
96
+ const isValidRefToken = decodedRT?.exp && decodedAT?.exp && date.getTime() < decodedRT.exp * 1000;
97
+ if (!isValidRefToken) {
98
+ await this.db.reset();
99
+ throw new Error(`SSO-SDK: Session cleared. Please login again.`);
100
+ }
101
+ else {
102
+ date.setMinutes(date.getMinutes() + 5); // check if it expires in the next 5 minutes
103
+ const shouldRefresh = decodedAT?.exp && date.getTime() > decodedAT.exp * 1000;
104
+ if (!shouldRefresh)
105
+ return existingUser.tokens;
106
+ return await this.refresh();
107
+ }
108
+ };
109
+ this.localExchange = async (email, token) => {
110
+ if (!email || !token)
111
+ return;
112
+ const res = await fetch(`${this.ssoServerUrl}/users/verify`, {
113
+ method: "POST",
114
+ body: JSON.stringify({
115
+ token,
116
+ email,
117
+ }),
118
+ headers: {
119
+ "Content-Type": "application/json",
120
+ "x-sdk-key": this.sdkKey,
121
+ },
122
+ });
123
+ const verifyRes = await res.json();
124
+ if (!res.ok || !verifyRes.success)
125
+ throw new Error(verifyRes.message ?? `SSO-SDK: Local exchange failed.`);
126
+ const profileRes = await fetch(`${this.ssoServerUrl}/users/profile`, {
127
+ headers: {
128
+ "Content-Type": "application/json",
129
+ Authorization: `Bearer ${verifyRes.data.tokens.accessToken}`,
130
+ "x-sdk-key": this.sdkKey,
131
+ },
132
+ });
133
+ const profileJson = await profileRes.json();
134
+ if (!profileRes.ok || !profileJson.success)
135
+ throw new Error(profileJson.message ?? `SSO-SDK: Profile fetch failed.`);
136
+ await this.db.open();
137
+ await this.db.delete(this.dbId);
138
+ await this.db.create({
139
+ ...profileJson.data,
140
+ userId: profileJson.data.id,
141
+ tokens: verifyRes.data.tokens,
142
+ id: this.dbId,
143
+ });
144
+ this.cb({
145
+ type: c.actions.local_exchange,
146
+ payload: profileJson.data,
147
+ });
148
+ return { ...profileJson.data, tokens: verifyRes.data.tokens };
149
+ };
150
+ this.verifyOtp = async (payload) => {
151
+ await this.db.reset();
152
+ const email = payload.email.trim().toLowerCase();
153
+ const otpCode = payload.otpCode.trim();
154
+ if (!email || !otpCode)
155
+ throw new Error(`SSO-SDK: Invalid OTP payload.`);
156
+ const res = await this.localExchange(email, otpCode);
157
+ if (!res)
158
+ throw new Error(`SSO-SDK: OTP verification failed.`);
159
+ return res;
160
+ };
161
+ this.ssoExchange = async (email, token) => {
162
+ if (email || !token)
163
+ return;
164
+ const res = await fetch(`${this.ssoServerUrl}/sso/exchange`, {
165
+ method: "POST",
166
+ body: JSON.stringify({
167
+ token,
168
+ sdkKey: this.sdkKey,
169
+ }),
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ "x-sdk-key": this.sdkKey,
173
+ },
174
+ });
175
+ const data = await res.json();
176
+ if (!res.ok)
177
+ throw new Error(data.message ?? `SSO-SDK: SSO exchange failed.`);
178
+ const profileRes = await fetch(`${this.ssoServerUrl}/users/profile`, {
179
+ headers: {
180
+ "Content-Type": "application/json",
181
+ Authorization: `Bearer ${data.data.tokens.accessToken}`,
182
+ "x-sdk-key": this.sdkKey,
183
+ },
184
+ });
185
+ const profileJson = await profileRes.json();
186
+ if (!profileRes.ok || !profileJson.success)
187
+ throw new Error(profileJson.message ?? `SSO-SDK: Profile fetch failed.`);
188
+ await this.db.open();
189
+ await this.db.delete(this.dbId);
190
+ await this.db.create({
191
+ ...profileJson.data,
192
+ userId: profileJson.data.id,
193
+ tokens: data.data.tokens,
194
+ id: this.dbId,
195
+ });
196
+ this.cb({
197
+ type: c.actions.sso_exchange,
198
+ payload: profileJson.data,
199
+ });
200
+ return { ...profileJson.data, tokens: data.data.tokens };
201
+ };
202
+ /**
203
+ * - this will just skip the exchange returning null if the query params are not correct and won't throw an error.
204
+ */
205
+ this.exchange = async () => {
206
+ this.throwIfNotBrowser();
207
+ const url = new URL(window.location.href);
208
+ const email = url.searchParams.get("email");
209
+ const token = url.searchParams.get("token");
210
+ const isExchanging = !!(email && token);
211
+ if (isExchanging) {
212
+ url.searchParams.delete("email");
213
+ url.searchParams.delete("token");
214
+ window.history.replaceState({}, "", url);
215
+ }
216
+ const tokenReused = await this.autoRefresh().catch((e) => {
217
+ if (/session cleared/i.test(e?.message ?? "") && !isExchanging) {
218
+ this.cb({
219
+ type: c.actions.session_cleared,
220
+ payload: null,
221
+ });
222
+ }
223
+ if (!isExchanging)
224
+ throw new Error(e?.message ?? `SSO-SDK: Auto refresh failed.`);
225
+ return null;
226
+ });
227
+ if (tokenReused && isExchanging)
228
+ throw new Error(`SSO-SDK: User already logged in.`);
229
+ if (!isExchanging)
230
+ return null;
231
+ await this.db.reset(); // reset the db before exchange to avoid conflicts
232
+ const sanitizedEmail = email
233
+ ? email.trim().toLowerCase().replace(/\s+/g, "+")
234
+ : null;
235
+ const res = await Promise.all([
236
+ this.localExchange(sanitizedEmail, token),
237
+ this.ssoExchange(sanitizedEmail, token),
238
+ ]).then((r) => r.find((r2) => !!r2));
239
+ if (!res)
240
+ throw new Error(`SSO-SDK: Exchange failed.`);
241
+ return res;
242
+ };
243
+ this.fetchProfile = async () => {
244
+ const res = await this.withRefresh(async (aT) => {
245
+ return await fetch(`${this.ssoServerUrl}/users/profile`, {
246
+ headers: {
247
+ "Content-Type": "application/json",
248
+ Authorization: `Bearer ${aT}`,
249
+ "x-sdk-key": this.sdkKey,
250
+ },
251
+ });
252
+ });
253
+ const pRes = await res.json();
254
+ if (!res?.ok || !pRes.success)
255
+ throw new Error(pRes.message ?? `SSO-SDK: Profile fetch failed.`);
256
+ this.cb({
257
+ type: c.actions.profile_fetch,
258
+ payload: pRes.data,
259
+ });
260
+ return pRes.data;
261
+ };
262
+ this.refresh = async () => {
263
+ const dbUser = (await this.db.getById(this.dbId));
264
+ if (!dbUser)
265
+ throw new Error(`SSO-SDK: No user found in IndexedDB.`);
266
+ const res = await fetch(`${this.ssoServerUrl}/users/refresh`, {
267
+ method: "POST",
268
+ body: JSON.stringify({
269
+ token: dbUser.tokens.refreshToken,
270
+ }),
271
+ headers: {
272
+ "Content-Type": "application/json",
273
+ "x-sdk-key": this.sdkKey,
274
+ },
275
+ });
276
+ const refTokeRes = await res.json();
277
+ if (!res.ok || !refTokeRes.success) {
278
+ this.db.delete(this.dbId);
279
+ this.cb({
280
+ type: c.actions.session_cleared,
281
+ payload: null,
282
+ });
283
+ throw new Error(refTokeRes.message ?? `SSO-SDK: Refresh failed.`);
284
+ }
285
+ await this.db.update(this.dbId, {
286
+ ...dbUser,
287
+ tokens: {
288
+ accessToken: refTokeRes.data.accessToken,
289
+ refreshToken: refTokeRes.data.refreshToken,
290
+ },
291
+ });
292
+ this.cb({
293
+ type: c.actions.refresh,
294
+ payload: refTokeRes.data,
295
+ });
296
+ return refTokeRes.data;
297
+ };
298
+ this.localLogout = async (redirectUrl) => {
299
+ if (redirectUrl)
300
+ return;
301
+ const res = await this.withRefresh(async (aT) => {
302
+ return await fetch(`${this.ssoServerUrl}/users/logout`, {
303
+ method: "DELETE",
304
+ headers: {
305
+ "Content-Type": "application/json",
306
+ Authorization: `Bearer ${aT}`,
307
+ "x-sdk-key": this.sdkKey,
308
+ },
309
+ });
310
+ });
311
+ const json = await res.json();
312
+ if (!res?.ok || !json.success)
313
+ throw new Error(json.message ?? `SSO-SDK: Local logout failed.`);
314
+ await this.db.delete(this.dbId);
315
+ this.cb({
316
+ type: c.actions.local_logout,
317
+ payload: null,
318
+ });
319
+ return true;
320
+ };
321
+ this.ssoLogout = async (redirectUrl) => {
322
+ if (!redirectUrl)
323
+ return;
324
+ this.throwIfNotBrowser();
325
+ const url = this.validatedUrl(redirectUrl);
326
+ const res = await this.withRefresh(async (aT) => {
327
+ return await fetch(`${this.ssoServerUrl}/users/logout`, {
328
+ method: "DELETE",
329
+ headers: {
330
+ "Content-Type": "application/json",
331
+ Authorization: `Bearer ${aT}`,
332
+ "x-sdk-key": this.sdkKey,
333
+ },
334
+ });
335
+ });
336
+ const json = await res.json();
337
+ if (!res?.ok || !json.success)
338
+ throw new Error(json.message ?? `SSO-SDK: Logout failed.`);
339
+ await this.db.delete(this.dbId);
340
+ const goToUrl = `${this.ssoClientUrl}/logout?redirectUri=${url}`;
341
+ location.href = goToUrl;
342
+ this.cb({
343
+ type: c.actions.logout,
344
+ payload: null,
345
+ });
346
+ return true;
347
+ };
348
+ /**
349
+ * - If a redirect URL is provided, it will trigger the SSO logout flow and redirect the user to the provided URL after logout.
350
+ * - If no redirect URL is provided, it will trigger the local logout flow and clear the user data from IndexedDB.
351
+ */
352
+ this.logout = async (redirectUrl) => {
353
+ const res = await Promise.all([
354
+ this.localLogout(redirectUrl),
355
+ this.ssoLogout(redirectUrl),
356
+ ]).then((r) => r.find((r) => r !== undefined));
357
+ if (!res)
358
+ throw new Error("SSO-SDK: Logout failed.");
359
+ this.cb({
360
+ type: c.actions.session_cleared,
361
+ payload: null,
362
+ });
363
+ return res;
364
+ };
365
+ this.onEvents = (cb) => {
366
+ this.callbacks.add(cb);
367
+ return () => this.callbacks.delete(cb);
368
+ };
369
+ this.withRefresh = async (api) => {
370
+ await this.db.open();
371
+ const dbUser = (await this.db.getById(this.dbId));
372
+ if (!dbUser)
373
+ throw new Error(`SSO-SDK: No user found in IndexedDB.`);
374
+ const r = await api(dbUser.tokens.accessToken);
375
+ if (r.status !== 401)
376
+ return r;
377
+ const resToken = await this.refresh();
378
+ const nR = await api(resToken.accessToken);
379
+ return nR;
380
+ };
381
+ this.profileUpdate = async (payload) => {
382
+ const res = await this.withRefresh(async (aT) => {
383
+ return await fetch(`${this.ssoServerUrl}/users/profile`, {
384
+ method: "PATCH",
385
+ body: JSON.stringify(payload),
386
+ headers: {
387
+ "Content-Type": "application/json",
388
+ Authorization: `Bearer ${aT}`,
389
+ "x-sdk-key": this.sdkKey,
390
+ },
391
+ });
392
+ });
393
+ const pRes = await res.json();
394
+ if (!res?.ok || !pRes.success)
395
+ throw new Error(pRes.message ?? `SSO-SDK: Profile update failed.`);
396
+ const { id, ...rest } = pRes.data;
397
+ await this.db.update(this.dbId, {
398
+ ...rest,
399
+ userId: id,
400
+ });
401
+ this.cb({
402
+ type: c.actions.profile_update,
403
+ payload: pRes.data,
404
+ });
405
+ return pRes.data;
406
+ };
407
+ this.avatarUpdate = async (file) => {
408
+ const photoURL = await this.fileUpload(file);
409
+ if (!photoURL)
410
+ throw new Error(`SSO-SDK: File upload failed.`);
411
+ const res = await this.withRefresh(async (aT) => {
412
+ return await fetch(`${this.ssoServerUrl}/users/profile/avatar`, {
413
+ method: "PUT",
414
+ body: JSON.stringify({
415
+ photoURL,
416
+ }),
417
+ headers: {
418
+ "Content-Type": "application/json",
419
+ Authorization: `Bearer ${aT}`,
420
+ "x-sdk-key": this.sdkKey,
421
+ },
422
+ });
423
+ });
424
+ const pRes = await res.json();
425
+ if (!res?.ok || !pRes.success)
426
+ throw new Error(pRes.message ?? `SSO-SDK: Profile update failed.`);
427
+ const { id, ...rest } = pRes.data;
428
+ await this.db.update(this.dbId, {
429
+ ...rest,
430
+ userId: id,
431
+ });
432
+ this.cb({
433
+ type: c.actions.profile_photo_update,
434
+ payload: pRes.data,
435
+ });
436
+ return pRes.data;
437
+ };
438
+ this.fileUpload = async (file) => {
439
+ const { type } = file;
440
+ const url = await this.withRefresh(async (aT) => {
441
+ return await fetch(`${this.ssoServerUrl}/uploads/url?fileType=${type}`, {
442
+ headers: {
443
+ "Content-Type": "application/json",
444
+ Authorization: `Bearer ${aT}`,
445
+ "x-sdk-key": this.sdkKey,
446
+ },
447
+ });
448
+ });
449
+ const json = await url.json();
450
+ if (!url?.ok || !json.success)
451
+ throw new Error(json.message ?? `SSO-SDK: could not get upload url.`);
452
+ const upload = await fetch(json.data.url, {
453
+ method: json.data.method,
454
+ body: file,
455
+ headers: json.data.headers,
456
+ });
457
+ if (!upload.ok)
458
+ throw new Error(`SSO-SDK: could not upload file.`);
459
+ const { origin, pathname } = new URL(json.data.url);
460
+ return `${origin}${pathname}`;
461
+ };
462
+ this.clothAdd = async (payload) => {
463
+ const thumb = await this.fileUpload(payload.thumb);
464
+ if (!thumb)
465
+ throw new Error(`SSO-SDK: File upload failed.`);
466
+ const jsonPayload = {
467
+ ...payload,
468
+ thumb,
469
+ };
470
+ await this.db.open();
471
+ const dbUser = (await this.db.getById(this.dbId));
472
+ if (!dbUser)
473
+ throw new Error(`SSO-SDK: No user found in IndexedDB.`);
474
+ const res = await this.withRefresh(async (aT) => {
475
+ return await fetch(`${this.ssoServerUrl}/clothings`, {
476
+ method: "POST",
477
+ body: JSON.stringify(jsonPayload),
478
+ headers: {
479
+ "Content-Type": "application/json",
480
+ Authorization: `Bearer ${aT || dbUser.tokens.accessToken}`,
481
+ "x-sdk-key": this.sdkKey,
482
+ },
483
+ });
484
+ });
485
+ const bodyRes = await res.json();
486
+ if (!res?.ok || !bodyRes.success)
487
+ throw new Error(bodyRes.message ?? `SSO-SDK: Cloth add failed.`);
488
+ const { data } = bodyRes;
489
+ this.cb({
490
+ type: c.actions.cloth_add,
491
+ payload: data,
492
+ });
493
+ return data;
494
+ };
495
+ this.clothGetAll = async (options) => {
496
+ const queryParams = new URLSearchParams({
497
+ skip: Math.ceil(options?.skip ?? 0).toString(),
498
+ limit: Math.ceil(options?.limit ?? 10).toString(),
499
+ sort: options?.sort ?? "asc",
500
+ });
501
+ const res = await this.withRefresh(async (aT) => {
502
+ return await fetch(`${this.ssoServerUrl}/clothings?${queryParams.toString()}`, {
503
+ headers: {
504
+ "Content-Type": "application/json",
505
+ Authorization: `Bearer ${aT}`,
506
+ "x-sdk-key": this.sdkKey,
507
+ },
508
+ });
509
+ });
510
+ const clothRes = await res.json();
511
+ if (!res?.ok || !clothRes.success)
512
+ throw new Error(clothRes.message ?? `SSO-SDK: Get all cloths failed.`);
513
+ this.cb({
514
+ type: c.actions.cloth_fetch_all,
515
+ payload: clothRes.data,
516
+ });
517
+ return clothRes.data;
518
+ };
519
+ this.clothGetById = async (clothId) => {
520
+ const res = await this.withRefresh(async (aT) => {
521
+ return await fetch(`${this.ssoServerUrl}/clothings/${clothId}`, {
522
+ headers: {
523
+ "Content-Type": "application/json",
524
+ Authorization: `Bearer ${aT}`,
525
+ "x-sdk-key": this.sdkKey,
526
+ },
527
+ });
528
+ });
529
+ const clothRes = await res.json();
530
+ if (!res?.ok || !clothRes.success)
531
+ throw new Error(clothRes.message ?? `SSO-SDK: Get cloth by id failed.`);
532
+ this.cb({
533
+ type: c.actions.cloth_fetch,
534
+ payload: clothRes.data,
535
+ });
536
+ return clothRes.data;
537
+ };
538
+ this.clothUpdate = async (clothId, payload) => {
539
+ const res = await this.withRefresh(async (aT) => {
540
+ return await fetch(`${this.ssoServerUrl}/clothings/${clothId}`, {
541
+ method: "PATCH",
542
+ body: JSON.stringify(payload),
543
+ headers: {
544
+ "Content-Type": "application/json",
545
+ Authorization: `Bearer ${aT}`,
546
+ "x-sdk-key": this.sdkKey,
547
+ },
548
+ });
549
+ });
550
+ const clothRes = await res.json();
551
+ if (!res?.ok || !clothRes.success)
552
+ throw new Error(clothRes.message ?? `SSO-SDK: Cloth update failed.`);
553
+ const { data } = clothRes;
554
+ this.cb({
555
+ type: c.actions.cloth_update,
556
+ payload: data,
557
+ });
558
+ return data;
559
+ };
560
+ this.clothDelete = async (clothId) => {
561
+ await this.db.open();
562
+ const dbUser = (await this.db.getById(this.dbId));
563
+ if (!dbUser)
564
+ throw new Error(`SSO-SDK: No user found in IndexedDB.`);
565
+ const res = await this.withRefresh(async (aT) => {
566
+ return await fetch(`${this.ssoServerUrl}/clothings/${clothId}`, {
567
+ method: "DELETE",
568
+ headers: {
569
+ "Content-Type": "application/json",
570
+ Authorization: `Bearer ${aT || dbUser.tokens.accessToken}`,
571
+ "x-sdk-key": this.sdkKey,
572
+ },
573
+ });
574
+ });
575
+ const clothRes = await res.json();
576
+ if (!res?.ok || !clothRes.success)
577
+ throw new Error(clothRes.message ?? `SSO-SDK: Cloth delete failed.`);
578
+ this.cb({
579
+ type: c.actions.cloth_delete,
580
+ payload: clothRes.data,
581
+ });
582
+ return clothRes.data;
583
+ };
584
+ // materials
585
+ this.materialAdd = async (payload) => {
586
+ const thumb = await this.fileUpload(payload.thumb);
587
+ if (!thumb)
588
+ throw new Error(`SSO-SDK: File upload failed.`);
589
+ const jsonPayload = {
590
+ ...payload,
591
+ thumb: thumb,
592
+ };
593
+ const res = await this.withRefresh(async (aT) => {
594
+ return await fetch(`${this.ssoServerUrl}/clothings/materials`, {
595
+ method: "POST",
596
+ body: JSON.stringify(jsonPayload),
597
+ headers: {
598
+ "Content-Type": "application/json",
599
+ Authorization: `Bearer ${aT}`,
600
+ "x-sdk-key": this.sdkKey,
601
+ },
602
+ });
603
+ });
604
+ const materialRes = await res.json();
605
+ if (!res?.ok || !materialRes.success)
606
+ throw new Error(materialRes.message ?? `SSO-SDK: Material add failed.`);
607
+ const { data } = materialRes;
608
+ this.cb({
609
+ type: c.actions.material_add,
610
+ payload: data,
611
+ });
612
+ return data;
613
+ };
614
+ this.materialGetAll = async (options) => {
615
+ const params = new URLSearchParams({
616
+ skip: options?.skip?.toString() || "0",
617
+ limit: options?.limit?.toString() || "10",
618
+ sort: options?.sort || "asc",
619
+ });
620
+ const res = await this.withRefresh(async (aT) => {
621
+ return await fetch(`${this.ssoServerUrl}/clothings/materials?${params}`, {
622
+ method: "GET",
623
+ headers: {
624
+ "Content-Type": "application/json",
625
+ Authorization: `Bearer ${aT}`,
626
+ "x-sdk-key": this.sdkKey,
627
+ },
628
+ });
629
+ });
630
+ const materialRes = await res.json();
631
+ if (!res?.ok || !materialRes.success)
632
+ throw new Error(materialRes.message ?? `SSO-SDK: Material fetch all failed.`);
633
+ const { data } = materialRes;
634
+ this.cb({
635
+ type: c.actions.material_fetch_all,
636
+ payload: data,
637
+ });
638
+ return data;
639
+ };
640
+ this.materialGetById = async (materialId) => {
641
+ const res = await this.withRefresh(async (aT) => {
642
+ return await fetch(`${this.ssoServerUrl}/clothings/materials/${materialId}`, {
643
+ method: "GET",
644
+ headers: {
645
+ "Content-Type": "application/json",
646
+ Authorization: `Bearer ${aT}`,
647
+ "x-sdk-key": this.sdkKey,
648
+ },
649
+ });
650
+ });
651
+ const materialRes = await res.json();
652
+ if (!res?.ok || !materialRes.success)
653
+ throw new Error(materialRes.message ?? `SSO-SDK: Material fetch by id failed.`);
654
+ const { data } = materialRes;
655
+ this.cb({
656
+ type: c.actions.material_fetch,
657
+ payload: data,
658
+ });
659
+ return data;
660
+ };
661
+ this.materialUpdate = async (materialId, payload) => {
662
+ const res = await this.withRefresh(async (aT) => {
663
+ return await fetch(`${this.ssoServerUrl}/clothings/materials/${materialId}`, {
664
+ method: "PATCH",
665
+ body: JSON.stringify(payload),
666
+ headers: {
667
+ "Content-Type": "application/json",
668
+ Authorization: `Bearer ${aT}`,
669
+ "x-sdk-key": this.sdkKey,
670
+ },
671
+ });
672
+ });
673
+ const materialRes = await res.json();
674
+ if (!res?.ok || !materialRes.success)
675
+ throw new Error(materialRes.message ?? `SSO-SDK: Material update failed.`);
676
+ const { data } = materialRes;
677
+ this.cb({
678
+ type: c.actions.material_update,
679
+ payload: data,
680
+ });
681
+ return data;
682
+ };
683
+ this.materialDelete = async (materialId) => {
684
+ const res = await this.withRefresh(async (aT) => {
685
+ return await fetch(`${this.ssoServerUrl}/clothings/materials/${materialId}`, {
686
+ method: "DELETE",
687
+ headers: {
688
+ "Content-Type": "application/json",
689
+ Authorization: `Bearer ${aT}`,
690
+ "x-sdk-key": this.sdkKey,
691
+ },
692
+ });
693
+ });
694
+ const materialRes = await res.json();
695
+ if (!res?.ok || !materialRes.success)
696
+ throw new Error(materialRes.message ?? `SSO-SDK: Material delete failed.`);
697
+ const { data } = materialRes;
698
+ this.cb({
699
+ type: c.actions.material_delete,
700
+ payload: data,
701
+ });
702
+ return data;
703
+ };
704
+ // skulls
705
+ this.skullAdd = async (payload) => {
706
+ const preview = await this.fileUpload(payload.filePreview);
707
+ const model = await this.fileUpload(payload.file3d);
708
+ if (!preview || !model)
709
+ throw new Error(`SSO-SDK: File upload failed.`);
710
+ const jsonPayload = {
711
+ ...payload,
712
+ previewUrl: preview,
713
+ url3d: model,
714
+ };
715
+ const res = await this.withRefresh(async (aT) => {
716
+ return await fetch(`${this.ssoServerUrl}/skulls`, {
717
+ method: "POST",
718
+ body: JSON.stringify(jsonPayload),
719
+ headers: {
720
+ "Content-Type": "application/json",
721
+ Authorization: `Bearer ${aT}`,
722
+ "x-sdk-key": this.sdkKey,
723
+ },
724
+ });
725
+ });
726
+ const skullRes = await res.json();
727
+ if (!res?.ok || !skullRes.success)
728
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull add failed.");
729
+ const { data } = skullRes;
730
+ this.cb({
731
+ type: c.actions.skull_add,
732
+ payload: data,
733
+ });
734
+ return data;
735
+ };
736
+ this.skullUpdate = async (skullId, payload) => {
737
+ const res = await this.withRefresh(async (aT) => {
738
+ return await fetch(`${this.ssoServerUrl}/skulls/${skullId}`, {
739
+ method: "PATCH",
740
+ body: JSON.stringify(payload),
741
+ headers: {
742
+ "Content-Type": "application/json",
743
+ Authorization: `Bearer ${aT}`,
744
+ "x-sdk-key": this.sdkKey,
745
+ },
746
+ });
747
+ });
748
+ const skullRes = (await res.json());
749
+ if (!res?.ok || !skullRes.success)
750
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull update failed.");
751
+ const { data } = skullRes;
752
+ this.cb({
753
+ type: c.actions.skull_update,
754
+ payload: data,
755
+ });
756
+ return data;
757
+ };
758
+ this.skullGetAll = async () => {
759
+ const res = await this.withRefresh(async (aT) => {
760
+ return await fetch(`${this.ssoServerUrl}/skulls`, {
761
+ method: "GET",
762
+ headers: {
763
+ "Content-Type": "application/json",
764
+ Authorization: `Bearer ${aT}`,
765
+ "x-sdk-key": this.sdkKey,
766
+ },
767
+ });
768
+ });
769
+ const skullRes = await res.json();
770
+ if (!res?.ok || !skullRes.success)
771
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull get all failed.");
772
+ const { data } = skullRes;
773
+ this.cb({
774
+ type: c.actions.skull_fetch_all,
775
+ payload: data,
776
+ });
777
+ return data;
778
+ };
779
+ this.skullGetById = async (skullId) => {
780
+ const res = await this.withRefresh(async (aT) => {
781
+ return await fetch(`${this.ssoServerUrl}/skulls/${skullId}`, {
782
+ method: "GET",
783
+ headers: {
784
+ "Content-Type": "application/json",
785
+ Authorization: `Bearer ${aT}`,
786
+ "x-sdk-key": this.sdkKey,
787
+ },
788
+ });
789
+ });
790
+ const skullRes = await res.json();
791
+ if (!res?.ok || !skullRes.success)
792
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull get by id failed.");
793
+ const { data } = skullRes;
794
+ this.cb({
795
+ type: c.actions.skull_fetch,
796
+ payload: data,
797
+ });
798
+ return data;
799
+ };
800
+ this.skullDelete = async (skullId) => {
801
+ const res = await this.withRefresh(async (aT) => {
802
+ return await fetch(`${this.ssoServerUrl}/skulls/${skullId}`, {
803
+ method: "DELETE",
804
+ headers: {
805
+ "Content-Type": "application/json",
806
+ Authorization: `Bearer ${aT}`,
807
+ "x-sdk-key": this.sdkKey,
808
+ },
809
+ });
810
+ });
811
+ const skullRes = await res.json();
812
+ if (!res?.ok || !skullRes.success)
813
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull delete failed.");
814
+ const { data } = skullRes;
815
+ this.cb({
816
+ type: c.actions.skull_delete,
817
+ payload: data,
818
+ });
819
+ return data;
820
+ };
821
+ // skull material
822
+ this.skullMaterialAdd = async (payload) => {
823
+ const { file3d, previewFile, ...rest } = payload;
824
+ const preview = await this.fileUpload(previewFile);
825
+ const model = await this.fileUpload(file3d);
826
+ if (!preview || !model)
827
+ throw new Error(`SSO-SDK: File upload failed.`);
828
+ const jsonPayload = {
829
+ ...rest,
830
+ previewUrl: preview,
831
+ url3d: model,
832
+ };
833
+ const res = await this.withRefresh(async (aT) => {
834
+ return await fetch(`${this.ssoServerUrl}/skulls/materials`, {
835
+ method: "POST",
836
+ body: JSON.stringify(jsonPayload),
837
+ headers: {
838
+ "Content-Type": "application/json",
839
+ Authorization: `Bearer ${aT}`,
840
+ "x-sdk-key": this.sdkKey,
841
+ },
842
+ });
843
+ });
844
+ const skullRes = await res.json();
845
+ if (!res?.ok || !skullRes.success)
846
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull material add failed.");
847
+ const { data } = skullRes;
848
+ this.cb({
849
+ type: c.actions.skull_material_add,
850
+ payload: data,
851
+ });
852
+ return data;
853
+ };
854
+ this.skullMaterialGetAll = async () => {
855
+ const res = await this.withRefresh(async (aT) => {
856
+ return await fetch(`${this.ssoServerUrl}/skulls/materials`, {
857
+ method: "GET",
858
+ headers: {
859
+ "Content-Type": "application/json",
860
+ Authorization: `Bearer ${aT}`,
861
+ "x-sdk-key": this.sdkKey,
862
+ },
863
+ });
864
+ });
865
+ const skullRes = await res.json();
866
+ if (!res?.ok || !skullRes.success)
867
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull materials get all failed.");
868
+ const { data } = skullRes;
869
+ this.cb({
870
+ type: c.actions.skull_material_fetch_all,
871
+ payload: data,
872
+ });
873
+ return data;
874
+ };
875
+ this.skullMaterialGetById = async (id) => {
876
+ const res = await this.withRefresh(async (aT) => {
877
+ return await fetch(`${this.ssoServerUrl}/skulls/materials/${id}`, {
878
+ method: "GET",
879
+ headers: {
880
+ "Content-Type": "application/json",
881
+ Authorization: `Bearer ${aT}`,
882
+ "x-sdk-key": this.sdkKey,
883
+ },
884
+ });
885
+ });
886
+ const skullRes = await res.json();
887
+ if (!res?.ok || !skullRes.success)
888
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull material get by id failed.");
889
+ const { data } = skullRes;
890
+ this.cb({
891
+ type: c.actions.skull_material_fetch,
892
+ payload: data,
893
+ });
894
+ return data;
895
+ };
896
+ this.skullMaterialUpdate = async (id, payload) => {
897
+ const { file3d, previewFile, ...rest } = payload;
898
+ const preview = previewFile && (await this.fileUpload(previewFile));
899
+ const model = file3d && (await this.fileUpload(file3d));
900
+ const jsonPayload = {
901
+ ...rest,
902
+ previewUrl: preview,
903
+ url3d: model,
904
+ };
905
+ const res = await this.withRefresh(async (aT) => {
906
+ return await fetch(`${this.ssoServerUrl}/skulls/materials/${id}`, {
907
+ method: "PATCH",
908
+ body: JSON.stringify(jsonPayload),
909
+ headers: {
910
+ "Content-Type": "application/json",
911
+ Authorization: `Bearer ${aT}`,
912
+ "x-sdk-key": this.sdkKey,
913
+ },
914
+ });
915
+ });
916
+ const skullRes = await res.json();
917
+ if (!res?.ok || !skullRes.success)
918
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull material update failed.");
919
+ const { data } = skullRes;
920
+ this.cb({
921
+ type: c.actions.skull_material_update,
922
+ payload: data,
923
+ });
924
+ return data;
925
+ };
926
+ this.skullMaterialDelete = async (id) => {
927
+ const res = await this.withRefresh(async (aT) => {
928
+ return await fetch(`${this.ssoServerUrl}/skulls/materials/${id}`, {
929
+ method: "DELETE",
930
+ headers: {
931
+ "Content-Type": "application/json",
932
+ Authorization: `Bearer ${aT}`,
933
+ "x-sdk-key": this.sdkKey,
934
+ },
935
+ });
936
+ });
937
+ const skullRes = await res.json();
938
+ if (!res?.ok || !skullRes.success)
939
+ throw new Error(skullRes.message ?? "SSO-SDK: Skull material delete failed.");
940
+ const { data } = skullRes;
941
+ this.cb({
942
+ type: c.actions.skull_material_delete,
943
+ payload: data,
944
+ });
945
+ return data;
946
+ };
947
+ // body
948
+ this.bodyAdd = async (payload) => {
949
+ const { file3d, previewFile, ...rest } = payload;
950
+ const preview = await this.fileUpload(previewFile);
951
+ const model = await this.fileUpload(file3d);
952
+ if (!preview || !model)
953
+ throw new Error(`SSO-SDK: File upload failed.`);
954
+ const jsonPayload = {
955
+ ...rest,
956
+ previewUrl: preview,
957
+ url3d: model,
958
+ };
959
+ const res = await this.withRefresh(async (aT) => {
960
+ return await fetch(`${this.ssoServerUrl}/bodies`, {
961
+ method: "POST",
962
+ body: JSON.stringify(jsonPayload),
963
+ headers: {
964
+ "Content-Type": "application/json",
965
+ Authorization: `Bearer ${aT}`,
966
+ "x-sdk-key": this.sdkKey,
967
+ },
968
+ });
969
+ });
970
+ const bodyRes = await res.json();
971
+ if (!res?.ok || !bodyRes.success)
972
+ throw new Error(bodyRes.message ?? "SSO-SDK: Body add failed.");
973
+ const { data } = bodyRes;
974
+ this.cb({
975
+ type: c.actions.body_add,
976
+ payload: data,
977
+ });
978
+ return data;
979
+ };
980
+ this.bodyGetAll = async () => {
981
+ const res = await this.withRefresh(async (aT) => {
982
+ return await fetch(`${this.ssoServerUrl}/bodies`, {
983
+ method: "GET",
984
+ headers: {
985
+ "Content-Type": "application/json",
986
+ Authorization: `Bearer ${aT}`,
987
+ "x-sdk-key": this.sdkKey,
988
+ },
989
+ });
990
+ });
991
+ const bodyRes = await res.json();
992
+ if (!res?.ok || !bodyRes.success)
993
+ throw new Error(bodyRes.message ?? "SSO-SDK: Body add failed.");
994
+ const { data } = bodyRes;
995
+ this.cb({
996
+ type: c.actions.body_fetch_all,
997
+ payload: data,
998
+ });
999
+ return data;
1000
+ };
1001
+ this.bodyGetById = async (id) => {
1002
+ const res = await this.withRefresh(async (aT) => {
1003
+ return await fetch(`${this.ssoServerUrl}/bodies/${id}`, {
1004
+ method: "GET",
1005
+ headers: {
1006
+ "Content-Type": "application/json",
1007
+ Authorization: `Bearer ${aT}`,
1008
+ "x-sdk-key": this.sdkKey,
1009
+ },
1010
+ });
1011
+ });
1012
+ const bodyRes = await res.json();
1013
+ if (!res?.ok || !bodyRes.success)
1014
+ throw new Error(bodyRes.message ?? "SSO-SDK: Body add failed.");
1015
+ const { data } = bodyRes;
1016
+ this.cb({
1017
+ type: c.actions.body_fetch,
1018
+ payload: data,
1019
+ });
1020
+ return data;
1021
+ };
1022
+ this.bodyUpdate = async (id, payload) => {
1023
+ const { file3d, previewFile, ...rest } = payload;
1024
+ const preview = previewFile && (await this.fileUpload(previewFile));
1025
+ const model = file3d && (await this.fileUpload(file3d));
1026
+ const jsonPayload = {
1027
+ ...rest,
1028
+ previewUrl: preview,
1029
+ url3d: model,
1030
+ };
1031
+ const res = await this.withRefresh(async (aT) => {
1032
+ return await fetch(`${this.ssoServerUrl}/bodies/${id}`, {
1033
+ method: "PATCH",
1034
+ body: JSON.stringify(jsonPayload),
1035
+ headers: {
1036
+ "Content-Type": "application/json",
1037
+ Authorization: `Bearer ${aT}`,
1038
+ "x-sdk-key": this.sdkKey,
1039
+ },
1040
+ });
1041
+ });
1042
+ const bodyRes = await res.json();
1043
+ if (!res?.ok || !bodyRes.success)
1044
+ throw new Error(bodyRes.message ?? "SSO-SDK: Body add failed.");
1045
+ const { data } = bodyRes;
1046
+ this.cb({
1047
+ type: c.actions.body_update,
1048
+ payload: data,
1049
+ });
1050
+ return data;
1051
+ };
1052
+ this.bodyDelete = async (id) => {
1053
+ const res = await this.withRefresh(async (aT) => {
1054
+ return await fetch(`${this.ssoServerUrl}/bodies/${id}`, {
1055
+ method: "DELETE",
1056
+ headers: {
1057
+ "Content-Type": "application/json",
1058
+ Authorization: `Bearer ${aT}`,
1059
+ "x-sdk-key": this.sdkKey,
1060
+ },
1061
+ });
1062
+ });
1063
+ const bodyRes = await res.json();
1064
+ if (!res?.ok || !bodyRes.success)
1065
+ throw new Error(bodyRes.message ?? "SSO-SDK: Body add failed.");
1066
+ const { data } = bodyRes;
1067
+ this.cb({
1068
+ type: c.actions.body_delete,
1069
+ payload: data,
1070
+ });
1071
+ return data;
1072
+ };
1073
+ this.validatedUrl = (url) => {
1074
+ try {
1075
+ return new URL(url).href;
1076
+ }
1077
+ catch {
1078
+ throw new Error(`SSO-SDK: Invalid URL.`);
1079
+ }
1080
+ };
1081
+ this.emailVerificationQuery = ({ redirectUri, type, }) => {
1082
+ const params = new URLSearchParams();
1083
+ if (redirectUri)
1084
+ params.set("redirectUri", this.validatedUrl(redirectUri));
1085
+ if (type)
1086
+ params.set("type", type);
1087
+ const query = params.toString();
1088
+ return query ? `?${query}` : "";
1089
+ };
1090
+ this.throwIfNotBrowser = () => {
1091
+ if (!isBrowser())
1092
+ throw new Error("SSO-SDK: Not in browser.");
1093
+ };
1094
+ /**
1095
+ * get user from indexedDB without validating tokens.
1096
+ */
1097
+ this.getLocalUser = async () => {
1098
+ const dbUser = (await this.db.getById(this.dbId));
1099
+ if (!dbUser)
1100
+ throw new Error(`SSO-SDK: No user found in IndexedDB.`);
1101
+ return dbUser;
1102
+ };
1103
+ this.sdkKey = config.sdkKey ?? "no-sdk-key";
1104
+ this.db = config.storage instanceof CustomStorage ? config.storage : c.db;
1105
+ this.ssoClientUrl = config.clientUrl ?? c.ssoClientUrl;
1106
+ this.ssoServerUrl = config.serverUrl ?? c.ssoServerUrl;
1107
+ }
1108
+ }
1109
+ //# sourceMappingURL=main.js.map