@communecter/cocolight-api-client 1.0.51 → 1.0.54

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.
Files changed (51) hide show
  1. package/dist/123.cocolight-api-client.browser.js +1 -1
  2. package/dist/123.cocolight-api-client.cjs +1 -1
  3. package/dist/774.cocolight-api-client.mjs.js +1 -1
  4. package/dist/cocolight-api-client.browser.js +1 -1
  5. package/dist/cocolight-api-client.cjs +1 -1
  6. package/dist/cocolight-api-client.mjs.js +1 -1
  7. package/dist/cocolight-api-client.vite.mjs.js +1 -1
  8. package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
  9. package/package.json +15 -9
  10. package/src/Api.js +41 -23
  11. package/src/ApiClient.js +33 -18
  12. package/src/api/EndpointApi.js +95 -95
  13. package/src/error.js +63 -10
  14. package/src/index.js +65 -2
  15. package/src/utils/FileStorageStrategy.node.js +28 -2
  16. package/src/utils/MultiServerFileStorageStrategy.node.js +24 -2
  17. package/src/utils/MultiServerTokenStorageStrategy.js +84 -27
  18. package/src/utils/TokenStorage.js +79 -19
  19. package/src/utils/createDefaultMultiServerTokenStorageStrategy.js +33 -27
  20. package/src/utils/createDefaultTokenStorageStrategy.js +29 -23
  21. package/types/Api.d.ts +131 -0
  22. package/types/ApiClient.d.ts +377 -0
  23. package/types/EJSONType.d.ts +27 -0
  24. package/types/api/Badge.d.ts +24 -0
  25. package/types/api/BaseEntity.d.ts +1017 -0
  26. package/types/api/EndpointApi.d.ts +933 -0
  27. package/types/api/EntityRegistry.d.ts +22 -0
  28. package/types/api/Event.d.ts +38 -0
  29. package/types/api/News.d.ts +45 -0
  30. package/types/api/Organization.d.ts +87 -0
  31. package/types/api/Poi.d.ts +25 -0
  32. package/types/api/Project.d.ts +81 -0
  33. package/types/api/User.d.ts +203 -0
  34. package/types/api/UserApi.d.ts +13 -0
  35. package/types/endpoints.module.d.ts +14852 -0
  36. package/types/error.d.ts +80 -0
  37. package/types/index.d.ts +52 -0
  38. package/types/mixin/UserMixin.d.ts +1 -0
  39. package/types/utils/FileOfflineStorageStrategy.node.d.ts +10 -0
  40. package/types/utils/FileStorageStrategy.node.d.ts +25 -0
  41. package/types/utils/MultiServerFileStorageStrategy.node.d.ts +22 -0
  42. package/types/utils/MultiServerTokenStorageStrategy.d.ts +65 -0
  43. package/types/utils/OfflineClientManager.d.ts +94 -0
  44. package/types/utils/OfflineQueueStorageStrategy.d.ts +13 -0
  45. package/types/utils/TokenStorage.d.ts +76 -0
  46. package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts +11 -0
  47. package/types/utils/createDefaultOfflineStrategy.d.ts +3 -0
  48. package/types/utils/createDefaultTokenStorageStrategy.d.ts +12 -0
  49. package/types/utils/reactive.d.ts +60 -0
  50. package/types/utils/stream-utils.node.d.ts +2 -0
  51. /package/{src → types}/api/EndpointApi.types.d.ts +0 -0
package/src/error.js CHANGED
@@ -1,68 +1,121 @@
1
+ /**
2
+ * @typedef {Record<string, any>} ApiErrorDetails
3
+ */
1
4
 
2
5
  /**
3
6
  * Classe de base pour toutes les erreurs de l'API.
7
+ * @extends Error
4
8
  */
5
9
  export class ApiError extends Error {
10
+ /**
11
+ * @param {string} message - Message d'erreur.
12
+ * @param {number} status - Code HTTP (ex: 400, 401, 500, 503).
13
+ * @param {ApiErrorDetails} [details] - Détails supplémentaires.
14
+ */
6
15
  constructor(message, status, details) {
7
16
  super(message);
17
+ /** @type {string} */
8
18
  this.name = "ApiError";
19
+ /** @type {number} */
9
20
  this.status = status;
21
+ /** @type {ApiErrorDetails|undefined} */
10
22
  this.details = details;
11
23
  }
12
24
  }
13
25
 
14
26
  /**
15
- * Erreur levée par le client API lorsqu'un appel échoue.
27
+ * Erreur levée par le client API lorsqu'un appel échoue (réseau, config, etc.).
28
+ * @extends ApiError
16
29
  */
17
30
  export class ApiClientError extends ApiError {
31
+ /**
32
+ * @param {string} message - Message d'erreur.
33
+ * @param {number} status - Code HTTP.
34
+ * @param {ApiErrorDetails} [details] - Détails supplémentaires.
35
+ */
18
36
  constructor(message, status, details) {
19
37
  super(message, status, details);
38
+ /** @type {string} */
20
39
  this.name = "ApiClientError";
21
40
  }
22
41
  }
23
42
 
43
+ /**
44
+ * Erreur de validation AJV (schemas de requête/réponse).
45
+ * @extends Error
46
+ */
24
47
  export class ApiValidationError extends Error {
48
+ /**
49
+ * @param {string} message - Message d'erreur.
50
+ * @param {number} status - Code HTTP logique (souvent 400).
51
+ * @param {string[]} messages - Messages humains dérivés des erreurs AJV.
52
+ * @param {any} [details=null] - Erreurs AJV brutes ou métadonnées.
53
+ */
25
54
  constructor(message, status, messages, details = null) {
26
55
  super(message);
56
+ /** @type {string} */
27
57
  this.name = "ApiValidationError";
58
+ /** @type {number} */
28
59
  this.status = status;
60
+ /** @type {string[]} */
29
61
  this.messages = messages;
62
+ /** @type {any} */
30
63
  this.details = details;
31
64
  }
32
65
  }
33
66
 
34
-
35
67
  /**
36
- * CircuitBreakerError : levée lorsqu'on bloque l'accès à l'API (trop d'erreurs).
68
+ * Levée lorsqu'on bloque l'accès à l'API (circuit breaker).
69
+ * @extends ApiError
37
70
  */
38
71
  export class CircuitBreakerError extends ApiError {
72
+ /**
73
+ * @param {string} message - Message d'erreur.
74
+ */
39
75
  constructor(message) {
40
76
  super(message, 503);
77
+ /** @type {string} */
41
78
  this.name = "CircuitBreakerError";
42
79
  }
43
80
  }
44
-
81
+
82
+ /**
83
+ * Erreur "métier" retour de l'API (ex: result=false) même si HTTP=200.
84
+ * @extends Error
85
+ */
45
86
  export class ApiResponseError extends Error {
46
87
  /**
47
- * Crée une instance d'ApiResponseError.
48
- *
49
88
  * @param {string} message - Message d'erreur.
50
- * @param {number} status - Le code HTTP (souvent 200 quand c'est une erreur logique).
51
- * @param {Object} responseData - Les données renvoyées par l'API.
52
- * @param {any} details - Informations supplémentaires (facultatif).
89
+ * @param {number} status - Code HTTP (peut être 200 pour erreurs logiques).
90
+ * @param {object} responseData - Payload renvoyé par l'API.
91
+ * @param {any} [details=null] - Infos supplémentaires (optionnel).
53
92
  */
54
93
  constructor(message, status, responseData, details = null) {
55
94
  super(message);
95
+ /** @type {string} */
56
96
  this.name = "ApiResponseError";
97
+ /** @type {number} */
57
98
  this.status = status;
99
+ /** @type {object} */
58
100
  this.responseData = responseData;
101
+ /** @type {any} */
59
102
  this.details = details;
60
103
  }
61
104
  }
62
105
 
106
+ /**
107
+ * Erreur d'authentification (ex: 401/403).
108
+ * @extends ApiError
109
+ */
63
110
  export class ApiAuthenticationError extends ApiError {
111
+ /**
112
+ * @param {string} message - Message d'erreur.
113
+ * @param {number} status - Code HTTP (401/403).
114
+ * @param {ApiErrorDetails} [details] - Détails supplémentaires.
115
+ */
64
116
  constructor(message, status, details) {
65
117
  super(message, status, details);
118
+ /** @type {string} */
66
119
  this.name = "ApiAuthenticationError";
67
120
  }
68
- }
121
+ }
package/src/index.js CHANGED
@@ -9,26 +9,89 @@ import OfflineClientManager from "./utils/OfflineClientManager.js";
9
9
  import * as reactive from "./utils/reactive.js";
10
10
  import { TokenStorageStrategy } from "./utils/TokenStorage.js";
11
11
 
12
- export default {
12
+ /**
13
+ * @typedef {import("./Api.js").default} Api
14
+ * @typedef {import("./ApiClient.js").default} ApiClient
15
+ * @typedef {import("./error.js")} error
16
+ * @typedef {import("./utils/reactive.js")} reactive
17
+ * @typedef {import("./utils/TokenStorage.js").TokenStorageStrategy} TokenStorageStrategy
18
+ * @typedef {import("./utils/MultiServerTokenStorageStrategy.js").MultiServerTokenStorageStrategy} MultiServerTokenStorageStrategy
19
+ * @typedef {import("./utils/OfflineClientManager.js").default} OfflineClientManager
20
+ * @typedef {import("./api/User.js").User} User
21
+ * @typedef {import("./api/Organization.js").Organization} Organization
22
+ * @typedef {import("./api/Project.js").Project} Project
23
+ * @typedef {import("./api/Event.js").Event} Event
24
+ * @typedef {import("./api/Poi.js").Poi} Poi
25
+ * @typedef {import("./api/Badge.js").Badge} Badge
26
+ * @typedef {import("./api/News.js").News} News
27
+ * @typedef {import("./api/EndpointApi.js").default} EndpointApi
28
+ * @typedef {import("./api/UserApi.js").UserApi} UserApi
29
+ */
30
+
31
+ /**
32
+ * Client API complet de Cocolight.
33
+ */
34
+ const cocolightApiClient = {
35
+ /** @type {ApiClient} */
13
36
  ApiClient,
37
+
38
+ /** @type {Api} */
14
39
  Api,
40
+
41
+ /** @type {typeof import("./error.js")} */
15
42
  error,
16
43
 
17
44
  // Accès aux primitives réactives via .reactive
45
+ /** @type {typeof import("./utils/reactive.js")} */
18
46
  reactive,
19
47
 
20
48
  // Accès direct à chaque primitive (reactive, effect, watch...) en racine
21
- ...reactive,
49
+ /** @type {typeof import("./utils/reactive.js")["effect"]} */
50
+ effect: reactive.effect,
51
+
52
+ /** @type {typeof import("./utils/reactive.js")["batch"]} */
53
+ batch: reactive.batch,
54
+
55
+ /** @type {typeof import("./utils/reactive.js")["isSignal"]} */
56
+ isSignal: reactive.isSignal,
57
+
58
+ /** @type {typeof import("./utils/reactive.js")["reactive"]} */
59
+ reactiveObject: reactive.reactive, // évite le conflit de nom avec la clé "reactive" ci-dessus
60
+
61
+ /** @type {typeof import("./utils/reactive.js")["isReactive"]} */
62
+ isReactive: reactive.isReactive,
63
+
64
+ /** @type {typeof import("./utils/reactive.js")["computed"]} */
65
+ computed: reactive.computed,
66
+
67
+ /** @type {typeof import("./utils/reactive.js")["watch"]} */
68
+ watch: reactive.watch,
69
+
70
+ /** @type {typeof import("./utils/reactive.js")["getSignals"]} */
71
+ getSignals: reactive.getSignals,
72
+
73
+ /** @type {typeof import("./utils/reactive.js")["subscribeTo"]} */
74
+ subscribeTo: reactive.subscribeTo,
75
+
22
76
 
23
77
  tokenStorageStrategy: {
78
+ /** @type {() => import("./utils/TokenStorage.js").TokenStorageStrategy} */
24
79
  createDefaultTokenStorageStrategy,
80
+ /** @type {TokenStorageStrategy} */
25
81
  TokenStorageStrategy,
82
+ /** @type {() => MultiServerTokenStorageStrategy} */
26
83
  createDefaultMultiServerTokenStorageStrategy,
84
+ /** @type {MultiServerTokenStorageStrategy} */
27
85
  MultiServerTokenStorageStrategy
28
86
  },
87
+
29
88
  helper: {
89
+ /** @type {typeof fromEntityJSON} */
30
90
  fromEntityJSON
31
91
  },
32
92
 
93
+ /** @type {OfflineClientManager} */
33
94
  OfflineClientManager
34
95
  };
96
+
97
+ export default cocolightApiClient;
@@ -1,59 +1,85 @@
1
- // src/utils/FileStorageStrategy.node.js
2
1
  import fs from "fs";
3
2
  import os from "os";
4
3
  import path from "path";
5
4
 
6
5
  import { TokenStorageStrategy } from "./TokenStorage.js";
7
6
 
7
+ /**
8
+ * Stratégie de stockage des tokens basée sur un fichier (Node uniquement).
9
+ * @implements {TokenStorageStrategy}
10
+ */
8
11
  export class FileStorageStrategy extends TokenStorageStrategy {
12
+ /**
13
+ * @param {string} [filename="tokens.json"]
14
+ * @param {string} [dir=path.join(os.homedir(), ".config", "cocolight")]
15
+ */
9
16
  constructor(filename = "tokens.json", dir = path.join(os.homedir(), ".config", "cocolight")) {
10
17
  super();
18
+ /** @type {string} */
11
19
  this.dir = dir;
20
+ /** @type {string} */
12
21
  this.filePath = path.join(dir, filename);
13
22
  this._ensureDirectoryExists();
14
23
  }
15
24
 
25
+ /** @private */
16
26
  _ensureDirectoryExists() {
17
27
  if (!fs.existsSync(this.dir)) {
18
28
  fs.mkdirSync(this.dir, { recursive: true });
19
29
  }
20
30
  }
21
31
 
32
+ /** @private */
22
33
  _readFile() {
23
34
  if (!fs.existsSync(this.filePath)) return {};
24
35
  try {
25
36
  return JSON.parse(fs.readFileSync(this.filePath, "utf8"));
26
-
27
37
  } catch (e) {
38
+
28
39
  console.error("Error reading token file:", e);
29
40
  return {};
30
41
  }
31
42
  }
32
43
 
44
+ /**
45
+ * @private
46
+ * @param {Record<string, any>} data
47
+ */
33
48
  _writeFile(data) {
34
49
  fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf8");
35
50
  }
36
51
 
52
+ /** @returns {string|null} */
37
53
  getAccessToken() {
38
54
  return this._readFile().accessToken || null;
39
55
  }
40
56
 
57
+ /**
58
+ * @param {string|null} token
59
+ * @returns {void}
60
+ */
41
61
  setAccessToken(token) {
42
62
  const data = this._readFile();
43
63
  data.accessToken = token;
44
64
  this._writeFile(data);
45
65
  }
46
66
 
67
+ /** @returns {string|null} */
47
68
  getRefreshToken() {
48
69
  return this._readFile().refreshToken || null;
49
70
  }
50
71
 
72
+ /**
73
+ * @param {string|null} token
74
+ * @returns {void}
75
+ */
51
76
  setRefreshToken(token) {
52
77
  const data = this._readFile();
53
78
  data.refreshToken = token;
54
79
  this._writeFile(data);
55
80
  }
56
81
 
82
+ /** @returns {void} */
57
83
  clear() {
58
84
  this._writeFile({});
59
85
  }
@@ -4,13 +4,25 @@ import path from "path";
4
4
 
5
5
  import { MultiServerTokenStorageStrategy } from "./MultiServerTokenStorageStrategy.js";
6
6
 
7
+ /** @typedef {import("./MultiServerTokenStorageStrategy.js").MultiServerTokenStorageStrategy} MultiServerTokenStorageStrategy */
8
+
9
+ /**
10
+ * Stockage fichier (Node uniquement) multi-serveurs.
11
+ * @implements {MultiServerTokenStorageStrategy}
12
+ */
7
13
  export class MultiServerFileStorageStrategy extends MultiServerTokenStorageStrategy {
14
+ /**
15
+ * @param {string} [filename="tokens.json"]
16
+ * @param {string} [dir=path.join(os.homedir(), ".config", "cocolight")]
17
+ */
8
18
  constructor(filename = "tokens.json", dir = path.join(os.homedir(), ".config", "cocolight")) {
9
19
  super();
20
+ /** @type {string} */
10
21
  this.filePath = path.join(dir, filename);
11
22
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
12
23
  }
13
24
 
25
+ /** @private */
14
26
  _readFile() {
15
27
  if (!fs.existsSync(this.filePath)) return {};
16
28
  try {
@@ -20,15 +32,20 @@ export class MultiServerFileStorageStrategy extends MultiServerTokenStorageStrat
20
32
  }
21
33
  }
22
34
 
35
+ /** @private
36
+ * @param {Record<string, {accessToken?: string|null, refreshToken?: string|null}>} data
37
+ */
23
38
  _writeFile(data) {
24
39
  fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf8");
25
40
  }
26
41
 
42
+ /** @returns {string|null} */
27
43
  getAccessToken() {
28
44
  this._ensureContext();
29
- return this._readFile()[this._currentBaseURL]?.accessToken || null;
45
+ return this._readFile()[this._currentBaseURL]?.accessToken ?? null;
30
46
  }
31
47
 
48
+ /** @param {string|null} token */
32
49
  setAccessToken(token) {
33
50
  this._ensureContext();
34
51
  const all = this._readFile();
@@ -36,11 +53,13 @@ export class MultiServerFileStorageStrategy extends MultiServerTokenStorageStrat
36
53
  this._writeFile(all);
37
54
  }
38
55
 
56
+ /** @returns {string|null} */
39
57
  getRefreshToken() {
40
58
  this._ensureContext();
41
- return this._readFile()[this._currentBaseURL]?.refreshToken || null;
59
+ return this._readFile()[this._currentBaseURL]?.refreshToken ?? null;
42
60
  }
43
61
 
62
+ /** @param {string|null} token */
44
63
  setRefreshToken(token) {
45
64
  this._ensureContext();
46
65
  const all = this._readFile();
@@ -48,6 +67,7 @@ export class MultiServerFileStorageStrategy extends MultiServerTokenStorageStrat
48
67
  this._writeFile(all);
49
68
  }
50
69
 
70
+ /** @returns {void} */
51
71
  clear() {
52
72
  this._ensureContext();
53
73
  const all = this._readFile();
@@ -55,10 +75,12 @@ export class MultiServerFileStorageStrategy extends MultiServerTokenStorageStrat
55
75
  this._writeFile(all);
56
76
  }
57
77
 
78
+ /** @returns {string[]} */
58
79
  getServers() {
59
80
  return Object.keys(this._readFile());
60
81
  }
61
82
 
83
+ /** @returns {Record<string, {accessToken?: string|null, refreshToken?: string|null}>} */
62
84
  exportAll() {
63
85
  return this._readFile();
64
86
  }
@@ -1,131 +1,188 @@
1
- // MultiServerTokenStorageStrategy.js
2
1
  import { TokenStorageStrategy } from "./TokenStorage.js";
3
2
 
3
+ /** @typedef {import("./TokenStorage.js").TokenStorageStrategy} TokenStorageStrategy */
4
+
5
+ /**
6
+ * Stratégie de stockage multi-serveurs (abstraite).
7
+ * Permet de sélectionner un `baseURL` courant via `use()` puis d'opérer get/set/clear dessus.
8
+ * @abstract
9
+ * @extends TokenStorageStrategy
10
+ */
4
11
  export class MultiServerTokenStorageStrategy extends TokenStorageStrategy {
5
12
  constructor() {
6
13
  super();
14
+ /** @type {string|null} */
7
15
  this._currentBaseURL = null;
8
16
  }
9
17
 
18
+ /**
19
+ * Sélectionne le serveur courant (normalise en supprimant les `/` finaux).
20
+ * @param {string} baseURL
21
+ * @returns {void}
22
+ */
10
23
  use(baseURL) {
11
24
  if (!baseURL) throw new Error("baseURL est requis");
12
25
  this._currentBaseURL = baseURL.replace(/\/+$/, "");
13
26
  }
14
27
 
28
+ /** @private */
15
29
  _ensureContext() {
16
30
  if (!this._currentBaseURL) throw new Error("baseURL non défini. Appelez use(baseURL)");
17
31
  }
18
32
 
33
+ /**
34
+ * Retourne la liste des serveurs connus.
35
+ * @abstract
36
+ * @returns {string[]}
37
+ */
19
38
  getServers() {
20
39
  throw new Error("getServers() doit être implémenté");
21
40
  }
22
41
 
42
+ /**
43
+ * Exporte toutes les données stockées (par baseURL).
44
+ * @abstract
45
+ * @returns {Record<string, {accessToken?: string|null, refreshToken?: string|null}>}
46
+ */
23
47
  exportAll() {
24
48
  throw new Error("exportAll() doit être implémenté");
25
49
  }
26
50
  }
27
51
 
28
- // Mémoire
52
+ /**
53
+ * Stockage en mémoire (multi-serveurs).
54
+ * @implements {MultiServerTokenStorageStrategy}
55
+ */
29
56
  export class MultiServerMemoryStorageStrategy extends MultiServerTokenStorageStrategy {
30
57
  constructor() {
31
58
  super();
59
+ /** @type {Map<string, {accessToken?: string|null, refreshToken?: string|null}>} */
32
60
  this._storage = new Map();
33
61
  }
34
-
62
+
63
+ /** @returns {string|null} */
35
64
  getAccessToken() {
36
65
  this._ensureContext();
37
- return this._storage.get(this._currentBaseURL)?.accessToken || null;
66
+ return this._storage.get(this._currentBaseURL)?.accessToken ?? null;
38
67
  }
39
-
68
+
69
+ /** @param {string|null} token */
40
70
  setAccessToken(token) {
41
71
  this._ensureContext();
42
72
  const current = this._storage.get(this._currentBaseURL) || {};
43
73
  this._storage.set(this._currentBaseURL, { ...current, accessToken: token });
44
74
  }
45
-
75
+
76
+ /** @returns {string|null} */
46
77
  getRefreshToken() {
47
78
  this._ensureContext();
48
- return this._storage.get(this._currentBaseURL)?.refreshToken || null;
79
+ return this._storage.get(this._currentBaseURL)?.refreshToken ?? null;
49
80
  }
50
-
81
+
82
+ /** @param {string|null} token */
51
83
  setRefreshToken(token) {
52
84
  this._ensureContext();
53
85
  const current = this._storage.get(this._currentBaseURL) || {};
54
86
  this._storage.set(this._currentBaseURL, { ...current, refreshToken: token });
55
87
  }
56
-
88
+
89
+ /** @returns {void} */
57
90
  clear() {
58
91
  this._ensureContext();
59
92
  this._storage.delete(this._currentBaseURL);
60
93
  }
61
-
94
+
95
+ /** @returns {string[]} */
62
96
  getServers() {
63
97
  return [...this._storage.keys()];
64
98
  }
65
-
99
+
100
+ /** @returns {Record<string, {accessToken?: string|null, refreshToken?: string|null}>} */
66
101
  exportAll() {
67
102
  return Object.fromEntries(this._storage.entries());
68
103
  }
69
104
  }
70
-
71
- // Navigateur (localStorage)
105
+
106
+ /**
107
+ * Stockage navigateur (localStorage) multi-serveurs.
108
+ * @implements {MultiServerTokenStorageStrategy}
109
+ */
72
110
  export class MultiServerLocalStorageStrategy extends MultiServerTokenStorageStrategy {
111
+ /**
112
+ * @param {string} [prefix="cocolight"]
113
+ */
73
114
  constructor(prefix = "cocolight") {
74
115
  super();
116
+ /** @type {string} */
75
117
  this.prefix = prefix;
76
118
  }
77
-
119
+
120
+ /** @private */
78
121
  _key(baseURL) {
79
122
  return `${this.prefix}_tokens_${baseURL}`;
80
123
  }
81
-
124
+
125
+ /** @private */
82
126
  _getData() {
83
127
  this._ensureContext();
128
+ if (typeof localStorage === "undefined") return {};
84
129
  const raw = localStorage.getItem(this._key(this._currentBaseURL));
85
130
  return raw ? JSON.parse(raw) : {};
86
131
  }
87
-
132
+
133
+ /** @private */
88
134
  _setData(data) {
135
+ if (typeof localStorage === "undefined") return;
89
136
  localStorage.setItem(this._key(this._currentBaseURL), JSON.stringify(data));
90
137
  }
91
-
138
+
139
+ /** @returns {string|null} */
92
140
  getAccessToken() {
93
- return this._getData().accessToken || null;
141
+ return this._getData().accessToken ?? null;
94
142
  }
95
-
143
+
144
+ /** @param {string|null} token */
96
145
  setAccessToken(token) {
97
146
  const data = this._getData();
98
147
  data.accessToken = token;
99
148
  this._setData(data);
100
149
  }
101
-
150
+
151
+ /** @returns {string|null} */
102
152
  getRefreshToken() {
103
- return this._getData().refreshToken || null;
153
+ return this._getData().refreshToken ?? null;
104
154
  }
105
-
155
+
156
+ /** @param {string|null} token */
106
157
  setRefreshToken(token) {
107
158
  const data = this._getData();
108
159
  data.refreshToken = token;
109
160
  this._setData(data);
110
161
  }
111
-
162
+
163
+ /** @returns {void} */
112
164
  clear() {
165
+ if (typeof localStorage === "undefined") return;
113
166
  localStorage.removeItem(this._key(this._currentBaseURL));
114
167
  }
115
-
168
+
169
+ /** @returns {string[]} */
116
170
  getServers() {
171
+ if (typeof localStorage === "undefined") return [];
117
172
  const keys = Object.keys(localStorage);
118
173
  return keys
119
174
  .filter(k => k.startsWith(`${this.prefix}_tokens_`))
120
175
  .map(k => k.replace(`${this.prefix}_tokens_`, ""));
121
176
  }
122
-
177
+
178
+ /** @returns {Record<string, {accessToken?: string|null, refreshToken?: string|null}>} */
123
179
  exportAll() {
124
180
  const result = {};
125
181
  for (const baseURL of this.getServers()) {
126
- const raw = localStorage.getItem(this._key(baseURL));
182
+ const key = this._key(baseURL);
183
+ const raw = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
127
184
  if (raw) result[baseURL] = JSON.parse(raw);
128
185
  }
129
186
  return result;
130
187
  }
131
- }
188
+ }