@communecter/cocolight-api-client 1.0.54 → 1.0.56
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/401.cocolight-api-client.browser.js +1 -0
- package/dist/401.cocolight-api-client.cjs +1 -0
- package/dist/401.cocolight-api-client.mjs.js +1 -0
- package/dist/588.cocolight-api-client.browser.js +1 -0
- package/dist/588.cocolight-api-client.cjs +1 -0
- package/dist/588.cocolight-api-client.mjs.js +1 -0
- package/dist/593.cocolight-api-client.browser.js +1 -0
- package/dist/593.cocolight-api-client.cjs +1 -0
- package/dist/593.cocolight-api-client.mjs.js +1 -0
- package/dist/839.cocolight-api-client.browser.js +1 -0
- package/dist/839.cocolight-api-client.cjs +1 -0
- package/dist/839.cocolight-api-client.mjs.js +1 -0
- package/dist/cocolight-api-client.browser.js +3 -3
- package/dist/cocolight-api-client.cjs +1 -1
- package/dist/cocolight-api-client.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js +1 -1
- package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
- package/package.json +29 -17
- package/src/{Api.js → Api.ts} +85 -95
- package/src/{ApiClient.js → ApiClient.ts} +436 -247
- package/src/EJSONType.ts +103 -0
- package/src/api/{Badge.js → Badge.ts} +56 -45
- package/src/api/BaseEntity.ts +3890 -0
- package/src/api/Comment.ts +200 -0
- package/src/api/{EndpointApi.js → EndpointApi.ts} +363 -297
- package/src/api/EndpointApi.types.ts +4609 -0
- package/src/api/EntityRegistry.ts +203 -0
- package/src/api/Event.ts +332 -0
- package/src/api/News.ts +331 -0
- package/src/api/{Organization.js → Organization.ts} +155 -119
- package/src/api/{Poi.js → Poi.ts} +68 -60
- package/src/api/{Project.js → Project.ts} +150 -127
- package/src/api/{User.js → User.ts} +321 -256
- package/src/api/UserApi.ts +148 -0
- package/src/api/serverDataType/Comment.ts +88 -0
- package/src/api/serverDataType/Event.ts +80 -0
- package/src/api/serverDataType/News.ts +138 -0
- package/src/api/serverDataType/Organization.ts +80 -0
- package/src/api/serverDataType/Project.ts +71 -0
- package/src/api/serverDataType/User.ts +103 -0
- package/src/api/serverDataType/common.ts +80 -0
- package/src/endpoints.module.ts +2621 -0
- package/src/error.ts +86 -0
- package/src/index.ts +86 -0
- package/src/mixin/UserMixin.ts +4 -0
- package/src/types/api-responses.ts +217 -0
- package/src/types/entities.ts +22 -0
- package/src/types/error-guards.ts +230 -0
- package/src/types/index.ts +39 -0
- package/src/types/payloads.ts +21 -0
- package/src/types/transforms.ts +110 -0
- package/src/utils/{FileOfflineStorageStrategy.node.js → FileOfflineStorageStrategy.node.ts} +15 -12
- package/src/utils/{FileStorageStrategy.node.js → FileStorageStrategy.node.ts} +16 -39
- package/src/utils/MultiServerFileStorageStrategy.node.ts +67 -0
- package/src/utils/MultiServerTokenStorageStrategy.ts +139 -0
- package/src/utils/{OfflineClientManager.js → OfflineClientManager.ts} +82 -86
- package/src/utils/OfflineQueueStorageStrategy.ts +47 -0
- package/src/utils/TokenStorage.ts +77 -0
- package/src/utils/compat.ts +12 -0
- package/src/utils/createDefaultMultiServerTokenStorageStrategy.ts +35 -0
- package/src/utils/{createDefaultOfflineStrategy.js → createDefaultOfflineStrategy.ts} +8 -3
- package/src/utils/createDefaultTokenStorageStrategy.ts +33 -0
- package/src/utils/{reactive.js → reactive.ts} +49 -40
- package/src/utils/stream-utils.node.ts +12 -0
- package/types/Api.d.ts +38 -82
- package/types/Api.d.ts.map +1 -0
- package/types/ApiClient.d.ts +244 -184
- package/types/ApiClient.d.ts.map +1 -0
- package/types/EJSONType.d.ts +48 -22
- package/types/EJSONType.d.ts.map +1 -0
- package/types/api/Badge.d.ts +20 -20
- package/types/api/Badge.d.ts.map +1 -0
- package/types/api/BaseEntity.d.ts +751 -446
- package/types/api/BaseEntity.d.ts.map +1 -0
- package/types/api/Comment.d.ts +36 -0
- package/types/api/EndpointApi.d.ts +347 -295
- package/types/api/EndpointApi.d.ts.map +1 -0
- package/types/api/EndpointApi.types.d.ts +3914 -4133
- package/types/api/EntityRegistry.d.ts +18 -16
- package/types/api/EntityRegistry.d.ts.map +1 -0
- package/types/api/Event.d.ts +119 -35
- package/types/api/Event.d.ts.map +1 -0
- package/types/api/News.d.ts +52 -20
- package/types/api/News.d.ts.map +1 -0
- package/types/api/Organization.d.ts +165 -49
- package/types/api/Organization.d.ts.map +1 -0
- package/types/api/Poi.d.ts +51 -22
- package/types/api/Poi.d.ts.map +1 -0
- package/types/api/Project.d.ts +151 -52
- package/types/api/Project.d.ts.map +1 -0
- package/types/api/User.d.ts +222 -93
- package/types/api/User.d.ts.map +1 -0
- package/types/api/UserApi.d.ts +60 -9
- package/types/api/UserApi.d.ts.map +1 -0
- package/types/api/serverDataType/Comment.d.ts +83 -0
- package/types/api/serverDataType/Event.d.ts +67 -0
- package/types/api/serverDataType/News.d.ts +130 -0
- package/types/api/serverDataType/Organization.d.ts +65 -0
- package/types/api/serverDataType/Organization.d.ts.map +1 -0
- package/types/api/serverDataType/Project.d.ts +58 -0
- package/types/api/serverDataType/Project.d.ts.map +1 -0
- package/types/api/serverDataType/User.d.ts +86 -0
- package/types/api/serverDataType/User.d.ts.map +1 -0
- package/types/api/serverDataType/common.d.ts +71 -0
- package/types/api/serverDataType/common.d.ts.map +1 -0
- package/types/endpoints.module.d.ts +6922 -1215
- package/types/endpoints.module.d.ts.map +1 -0
- package/types/error.d.ts +25 -51
- package/types/error.d.ts.map +1 -0
- package/types/index.d.ts +55 -48
- package/types/index.d.ts.map +1 -0
- package/types/mixin/UserMixin.d.ts +1 -1
- package/types/mixin/UserMixin.d.ts.map +1 -0
- package/types/types/api-responses.d.ts +190 -0
- package/types/types/api-responses.d.ts.map +1 -0
- package/types/types/entities.d.ts +17 -0
- package/types/types/entities.d.ts.map +1 -0
- package/types/types/error-guards.d.ts +99 -0
- package/types/types/error-guards.d.ts.map +1 -0
- package/types/types/index.d.ts +7 -0
- package/types/types/payloads.d.ts +17 -0
- package/types/types/payloads.d.ts.map +1 -0
- package/types/types/transforms.d.ts +79 -0
- package/types/types/transforms.d.ts.map +1 -0
- package/types/utils/FileOfflineStorageStrategy.node.d.ts +10 -9
- package/types/utils/FileOfflineStorageStrategy.node.d.ts.map +1 -0
- package/types/utils/FileStorageStrategy.node.d.ts +9 -20
- package/types/utils/FileStorageStrategy.node.d.ts.map +1 -0
- package/types/utils/MultiServerFileStorageStrategy.node.d.ts +13 -18
- package/types/utils/MultiServerFileStorageStrategy.node.d.ts.map +1 -0
- package/types/utils/MultiServerTokenStorageStrategy.d.ts +30 -51
- package/types/utils/MultiServerTokenStorageStrategy.d.ts.map +1 -0
- package/types/utils/OfflineClientManager.d.ts +52 -88
- package/types/utils/OfflineClientManager.d.ts.map +1 -0
- package/types/utils/OfflineQueueStorageStrategy.d.ts +12 -9
- package/types/utils/OfflineQueueStorageStrategy.d.ts.map +1 -0
- package/types/utils/TokenStorage.d.ts +20 -70
- package/types/utils/TokenStorage.d.ts.map +1 -0
- package/types/utils/compat.d.ts +4 -0
- package/types/utils/compat.d.ts.map +1 -0
- package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts +2 -11
- package/types/utils/createDefaultMultiServerTokenStorageStrategy.d.ts.map +1 -0
- package/types/utils/createDefaultOfflineStrategy.d.ts +2 -3
- package/types/utils/createDefaultOfflineStrategy.d.ts.map +1 -0
- package/types/utils/createDefaultTokenStorageStrategy.d.ts +2 -12
- package/types/utils/createDefaultTokenStorageStrategy.d.ts.map +1 -0
- package/types/utils/reactive.d.ts +10 -16
- package/types/utils/reactive.d.ts.map +1 -0
- package/types/utils/stream-utils.node.d.ts +3 -2
- package/types/utils/stream-utils.node.d.ts.map +1 -0
- package/dist/123.cocolight-api-client.browser.js +0 -1
- package/dist/123.cocolight-api-client.cjs +0 -1
- package/dist/22.cocolight-api-client.mjs.js +0 -1
- package/dist/339.cocolight-api-client.mjs.js +0 -1
- package/dist/394.cocolight-api-client.browser.js +0 -1
- package/dist/394.cocolight-api-client.cjs +0 -1
- package/dist/405.cocolight-api-client.browser.js +0 -1
- package/dist/405.cocolight-api-client.cjs +0 -1
- package/dist/774.cocolight-api-client.mjs.js +0 -1
- package/dist/790.cocolight-api-client.mjs.js +0 -1
- package/dist/931.cocolight-api-client.browser.js +0 -1
- package/dist/931.cocolight-api-client.cjs +0 -1
- package/src/EJSONType.js +0 -53
- package/src/api/BaseEntity.js +0 -2828
- package/src/api/EntityRegistry.js +0 -152
- package/src/api/Event.js +0 -226
- package/src/api/News.js +0 -244
- package/src/api/UserApi.js +0 -81
- package/src/endpoints.module.js +0 -5
- package/src/error.js +0 -121
- package/src/index.js +0 -97
- package/src/mixin/UserMixin.js +0 -8
- package/src/utils/MultiServerFileStorageStrategy.node.js +0 -87
- package/src/utils/MultiServerTokenStorageStrategy.js +0 -188
- package/src/utils/OfflineQueueStorageStrategy.js +0 -51
- package/src/utils/TokenStorage.js +0 -153
- package/src/utils/createDefaultMultiServerTokenStorageStrategy.js +0 -51
- package/src/utils/createDefaultTokenStorageStrategy.js +0 -49
- package/src/utils/stream-utils.node.js +0 -10
|
@@ -1,31 +1,70 @@
|
|
|
1
|
-
// OfflineClientManager.
|
|
1
|
+
// OfflineClientManager.ts
|
|
2
2
|
import ApiClient from "../ApiClient.js";
|
|
3
3
|
import { createDefaultOfflineStrategy } from "./createDefaultOfflineStrategy.js";
|
|
4
|
+
import { OfflineQueueStorageStrategy } from "./OfflineQueueStorageStrategy.js";
|
|
5
|
+
|
|
6
|
+
type OfflineQueueStorageStrategyAction = import("./OfflineQueueStorageStrategy.js").OfflineQueueStorageStrategy<OfflineAction>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Métadonnées nécessaires pour rejouer une action offline sur le bon serveur.
|
|
10
|
+
*/
|
|
11
|
+
interface OfflineActionMeta {
|
|
12
|
+
baseURL: string;
|
|
13
|
+
accessToken?: string | null;
|
|
14
|
+
refreshToken?: string | null;
|
|
15
|
+
timestamp: number; // Epoch ms de l'enfilement.
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options passées à l'appel de l'endpoint lors du replay.
|
|
20
|
+
*/
|
|
21
|
+
interface OfflineActionOptions {
|
|
22
|
+
transformResponseData?: boolean | ((...args: any[]) => any);
|
|
23
|
+
validateResponseSchema?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Élément de la file d'attente offline.
|
|
28
|
+
*/
|
|
29
|
+
export interface OfflineAction {
|
|
30
|
+
constant: string; // Nom de l'endpoint à appeler.
|
|
31
|
+
data: unknown; // Payload d'entrée (forme dépendante de l'endpoint).
|
|
32
|
+
options?: OfflineActionOptions;
|
|
33
|
+
meta: OfflineActionMeta;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options d'attache du gestionnaire offline.
|
|
38
|
+
*/
|
|
39
|
+
interface AttachOptions {
|
|
40
|
+
offlineStorageStrategy?: "auto" | "memory" | "localStorage" | "file";
|
|
41
|
+
disableOfflineMonitoring?: boolean;
|
|
42
|
+
interval?: number; // Intervalle de ping réseau (ms), relayé à startOfflineMonitoring.
|
|
43
|
+
}
|
|
4
44
|
|
|
5
45
|
export class OfflineClientManager {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
46
|
+
private _clients: Map<string, any>;
|
|
47
|
+
private _client: any;
|
|
48
|
+
private _offlineMode: boolean | undefined;
|
|
49
|
+
private _pingInterval: ReturnType<typeof setInterval> | undefined;
|
|
50
|
+
private _offlineQueueStorage: OfflineQueueStorageStrategyAction | undefined;
|
|
51
|
+
private _offlineQueue: OfflineAction[];
|
|
52
|
+
private _monitoringStarted: boolean | undefined;
|
|
53
|
+
|
|
54
|
+
constructor(apiClient: any) {
|
|
55
|
+
if (!apiClient || !(apiClient instanceof ApiClient)) {
|
|
13
56
|
throw new Error("[OfflineClientManager] apiClient doit être une instance de ApiClient");
|
|
14
57
|
}
|
|
15
58
|
this._clients = new Map();
|
|
16
59
|
this._client = apiClient;
|
|
60
|
+
this._offlineMode = undefined;
|
|
61
|
+
this._pingInterval = undefined;
|
|
62
|
+
this._offlineQueueStorage = undefined;
|
|
63
|
+
this._offlineQueue = [];
|
|
64
|
+
this._monitoringStarted = undefined;
|
|
17
65
|
}
|
|
18
66
|
|
|
19
|
-
|
|
20
|
-
* Attache le gestionnaire offline à une instance ApiClient.
|
|
21
|
-
* Configure la file d'attente offline, le monitoring réseau, et le ping serveur.
|
|
22
|
-
*
|
|
23
|
-
* @param {ApiClient} apiClient - L'instance du client à enrichir.
|
|
24
|
-
* @param {Object} options
|
|
25
|
-
* @param {Object} [options.offlineStorageStrategy] - Stratégie de stockage offline.
|
|
26
|
-
* @param {boolean} [options.disableOfflineMonitoring] - Si true, désactive le monitoring réseau.
|
|
27
|
-
*/
|
|
28
|
-
static async attachTo(apiClient, options = {}) {
|
|
67
|
+
static async attachTo(apiClient: any, options: AttachOptions = {}): Promise<any> {
|
|
29
68
|
apiClient._offlineClientManager = new OfflineClientManager(apiClient);
|
|
30
69
|
const strategy = await createDefaultOfflineStrategy(options?.offlineStorageStrategy || "auto");
|
|
31
70
|
await apiClient._offlineClientManager._initOfflineQueue(strategy);
|
|
@@ -34,14 +73,7 @@ export class OfflineClientManager {
|
|
|
34
73
|
}
|
|
35
74
|
}
|
|
36
75
|
|
|
37
|
-
|
|
38
|
-
* Retourne un ApiClient configuré avec les bons tokens/baseURL pour rejouer une action offline.
|
|
39
|
-
* Met en cache l'instance pour éviter les recréations.
|
|
40
|
-
*
|
|
41
|
-
* @param {Object} meta - Métadonnées contenant baseURL et tokens.
|
|
42
|
-
* @returns {Promise<ApiClient>}
|
|
43
|
-
*/
|
|
44
|
-
async getClient(meta) {
|
|
76
|
+
async getClient(meta: OfflineActionMeta): Promise<any> {
|
|
45
77
|
const key = `${meta.baseURL}|${meta.accessToken}`;
|
|
46
78
|
if (this._clients.has(key)) return this._clients.get(key);
|
|
47
79
|
|
|
@@ -56,16 +88,9 @@ export class OfflineClientManager {
|
|
|
56
88
|
return client;
|
|
57
89
|
}
|
|
58
90
|
|
|
59
|
-
|
|
60
|
-
* Rejoue une action unique depuis la file offline.
|
|
61
|
-
*
|
|
62
|
-
* @param {Object} action - Objet contenant constant, data, options et meta.
|
|
63
|
-
* @returns {Promise<any>}
|
|
64
|
-
*/
|
|
65
|
-
async replayAction(action) {
|
|
91
|
+
async replayAction(action: OfflineAction): Promise<any> {
|
|
66
92
|
const client = await this.getClient(action.meta);
|
|
67
93
|
try {
|
|
68
|
-
|
|
69
94
|
const result = await client.callEndpoint(
|
|
70
95
|
action.constant,
|
|
71
96
|
action.data,
|
|
@@ -75,20 +100,12 @@ export class OfflineClientManager {
|
|
|
75
100
|
this._client._logger.info(`[OfflineReplay] Succès ${action.constant} sur ${action.meta.baseURL}`);
|
|
76
101
|
return result;
|
|
77
102
|
} catch (err) {
|
|
78
|
-
this._client._logger.error(`[OfflineReplay] Échec ${action.constant}`, err.message);
|
|
103
|
+
this._client._logger.error(`[OfflineReplay] Échec ${action.constant}`, (err as Error).message);
|
|
79
104
|
throw err;
|
|
80
105
|
}
|
|
81
106
|
}
|
|
82
107
|
|
|
83
|
-
|
|
84
|
-
* Démarre la surveillance de l'état réseau.
|
|
85
|
-
* Basé sur navigator.onLine + un ping serveur régulier.
|
|
86
|
-
*
|
|
87
|
-
* @param {Object} [options]
|
|
88
|
-
* @param {number} [options.interval=30000] - Intervalle de vérification en ms.
|
|
89
|
-
* @param {boolean} [options.disableOfflineMonitoring=false] - Pour désactiver.
|
|
90
|
-
*/
|
|
91
|
-
startOfflineMonitoring({ interval = 30000, disableOfflineMonitoring = false } = {}) {
|
|
108
|
+
startOfflineMonitoring({ interval = 30000, disableOfflineMonitoring = false }: Pick<AttachOptions, "interval" | "disableOfflineMonitoring"> = {}) {
|
|
92
109
|
if (disableOfflineMonitoring || this._monitoringStarted) return;
|
|
93
110
|
|
|
94
111
|
this._monitoringStarted = true;
|
|
@@ -102,27 +119,20 @@ export class OfflineClientManager {
|
|
|
102
119
|
this._pingInterval = setInterval(async () => {
|
|
103
120
|
const wasOffline = this._offlineMode;
|
|
104
121
|
try {
|
|
105
|
-
await this._client._client.head("/api/cocolight/infoserver", { timeout: 5000 });
|
|
122
|
+
await this._client._client.head("/api/cocolight/infoserver", { timeout: 5000 });
|
|
106
123
|
if (wasOffline) this.setOfflineMode(false);
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
} catch (err) {
|
|
125
|
+
} catch {
|
|
110
126
|
this._client._logger.error("[OfflineMonitor] ping() échoué → passage en offline");
|
|
111
127
|
this.setOfflineMode(true);
|
|
112
128
|
}
|
|
113
129
|
}, interval);
|
|
114
130
|
}
|
|
115
131
|
|
|
116
|
-
|
|
117
|
-
* Change l'état offline et émet un événement si l'état change.
|
|
118
|
-
* Déclenche la relecture de la file si on revient online.
|
|
119
|
-
*
|
|
120
|
-
* @param {boolean} isOffline
|
|
121
|
-
*/
|
|
122
|
-
setOfflineMode(isOffline) {
|
|
132
|
+
setOfflineMode(isOffline: boolean) {
|
|
123
133
|
if (this._offlineMode === isOffline) return;
|
|
124
134
|
this._offlineMode = isOffline;
|
|
125
|
-
if (typeof this.
|
|
135
|
+
if (typeof this._client.emit === "function") {
|
|
126
136
|
this._client.emit("offlineModeChanged", isOffline);
|
|
127
137
|
}
|
|
128
138
|
this._client._logger.info(`[ApiClient] Mode offline : ${isOffline} (set depuis monitoring)`);
|
|
@@ -132,33 +142,23 @@ export class OfflineClientManager {
|
|
|
132
142
|
}
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
* Indique si le client est actuellement en mode offline.
|
|
137
|
-
*
|
|
138
|
-
* @returns {boolean}
|
|
139
|
-
*/
|
|
140
|
-
isOffline() {
|
|
145
|
+
isOffline(): boolean {
|
|
141
146
|
return !!this._offlineMode;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* @param {string} params.constant - Le nom de l'endpoint.
|
|
149
|
-
* @param {Object} params.data - Données à envoyer.
|
|
150
|
-
* @param {Object} [params.options] - Options de transformation/validation.
|
|
151
|
-
*/
|
|
152
|
-
async _enqueueOfflineAction({ constant, data, options = {} }) {
|
|
149
|
+
async _enqueueOfflineAction({ constant, data, options = {} }: { constant: string; data: unknown; options?: OfflineActionOptions }): Promise<any> {
|
|
150
|
+
if (!this._offlineQueueStorage) {
|
|
151
|
+
throw new Error("[OfflineClientManager] _offlineQueueStorage non initialisé");
|
|
152
|
+
}
|
|
153
153
|
const hasToken = this._client.getToken() || this._client.getRefreshToken();
|
|
154
|
-
const endpoint = this._client._endpoints.find(e => e.constant === constant);
|
|
154
|
+
const endpoint = this._client._endpoints.find((e: any) => e.constant === constant);
|
|
155
155
|
const authPolicy = endpoint?.authPolicy || (endpoint?.auth === "none" ? "none" : "required");
|
|
156
156
|
if (authPolicy === "required" && !hasToken) {
|
|
157
157
|
this._client._logger.warn(`[Offline] Action ignorée : token requis mais absent (${constant})`);
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
const action = {
|
|
161
|
+
const action: OfflineAction = {
|
|
162
162
|
constant,
|
|
163
163
|
data,
|
|
164
164
|
options,
|
|
@@ -175,17 +175,16 @@ export class OfflineClientManager {
|
|
|
175
175
|
this._client._logger.info(`[Offline] Action mise en file : ${constant}`);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
async _replayOfflineQueue() {
|
|
178
|
+
async _replayOfflineQueue(): Promise<any> {
|
|
179
|
+
if (!this._offlineQueueStorage) {
|
|
180
|
+
throw new Error("[OfflineClientManager] _offlineQueueStorage non initialisé");
|
|
181
|
+
}
|
|
183
182
|
const queueCopy = [...this._offlineQueue];
|
|
184
183
|
this._offlineQueue = [];
|
|
185
184
|
await this._offlineQueueStorage.saveQueue([]);
|
|
186
185
|
|
|
187
186
|
for (const action of queueCopy) {
|
|
188
|
-
const endpoint = this._client._endpoints.find(e => e.constant === action.constant);
|
|
187
|
+
const endpoint = this._client._endpoints.find((e: any) => e.constant === action.constant);
|
|
189
188
|
const authPolicy = endpoint?.authPolicy || (endpoint?.auth === "none" ? "none" : "required");
|
|
190
189
|
const hasToken = action.meta.accessToken || action.meta.refreshToken;
|
|
191
190
|
|
|
@@ -196,9 +195,8 @@ export class OfflineClientManager {
|
|
|
196
195
|
|
|
197
196
|
try {
|
|
198
197
|
await this.replayAction(action);
|
|
199
|
-
|
|
200
198
|
} catch (e) {
|
|
201
|
-
this._client._logger.error("[Offline] Relecture échouée :", e.message);
|
|
199
|
+
this._client._logger.error("[Offline] Relecture échouée :", (e as Error).message);
|
|
202
200
|
this._offlineQueue.push(action); // requeue si erreur
|
|
203
201
|
}
|
|
204
202
|
}
|
|
@@ -206,12 +204,10 @@ export class OfflineClientManager {
|
|
|
206
204
|
await this._offlineQueueStorage.saveQueue(this._offlineQueue);
|
|
207
205
|
}
|
|
208
206
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
*/
|
|
214
|
-
async _initOfflineQueue(storageStrategy) {
|
|
207
|
+
async _initOfflineQueue(storageStrategy: OfflineQueueStorageStrategyAction): Promise<any> {
|
|
208
|
+
if(!storageStrategy || !(storageStrategy instanceof OfflineQueueStorageStrategy)) {
|
|
209
|
+
throw new Error("[OfflineClientManager] storageStrategy doit être une instance de OfflineQueueStorageStrategy");
|
|
210
|
+
}
|
|
215
211
|
this._offlineQueueStorage = storageStrategy;
|
|
216
212
|
this._offlineQueue = await storageStrategy.loadQueue();
|
|
217
213
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export abstract class OfflineQueueStorageStrategy<T = any> {
|
|
2
|
+
abstract loadQueue(): Promise<T[]>;
|
|
3
|
+
abstract saveQueue(queue: T[]): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class MemoryOfflineStorage<T = any> extends OfflineQueueStorageStrategy<T> {
|
|
7
|
+
private _queue: T[] = [];
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async loadQueue(): Promise<T[]> {
|
|
14
|
+
return this._queue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async saveQueue(queue: T[]): Promise<void> {
|
|
18
|
+
this._queue = [...queue];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class LocalStorageOfflineStorage<T = any> extends OfflineQueueStorageStrategy<T> {
|
|
23
|
+
public readonly key: string;
|
|
24
|
+
|
|
25
|
+
constructor(key: string = "cocolight-api-offline-queue") {
|
|
26
|
+
super();
|
|
27
|
+
this.key = key;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async loadQueue(): Promise<T[]> {
|
|
31
|
+
try {
|
|
32
|
+
const raw = localStorage.getItem(this.key);
|
|
33
|
+
const parsed: T[] | null = raw ? JSON.parse(raw) : null;
|
|
34
|
+
return parsed ?? [];
|
|
35
|
+
} catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async saveQueue(queue: T[]): Promise<void> {
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(this.key, JSON.stringify(queue));
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.warn("[Offline] Échec du stockage localStorage", e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export abstract class TokenStorageStrategy {
|
|
2
|
+
abstract getAccessToken(): string | null;
|
|
3
|
+
abstract setAccessToken(token: string | null): void;
|
|
4
|
+
abstract getRefreshToken(): string | null;
|
|
5
|
+
abstract setRefreshToken(token: string | null): void;
|
|
6
|
+
abstract clear(): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class MemoryStorageStrategy extends TokenStorageStrategy {
|
|
10
|
+
private _accessToken: string | null = null;
|
|
11
|
+
private _refreshToken: string | null = null;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getAccessToken(): string | null {
|
|
18
|
+
return this._accessToken;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setAccessToken(token: string | null): void {
|
|
22
|
+
this._accessToken = token;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getRefreshToken(): string | null {
|
|
26
|
+
return this._refreshToken;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setRefreshToken(token: string | null): void {
|
|
30
|
+
this._refreshToken = token;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clear(): void {
|
|
34
|
+
this._accessToken = null;
|
|
35
|
+
this._refreshToken = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class LocalStorageStrategy extends TokenStorageStrategy {
|
|
40
|
+
public readonly prefix: string;
|
|
41
|
+
|
|
42
|
+
constructor(prefix: string = "cocolight") {
|
|
43
|
+
super();
|
|
44
|
+
this.prefix = prefix;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getAccessToken(): string | null {
|
|
48
|
+
return typeof localStorage !== "undefined"
|
|
49
|
+
? localStorage.getItem(`${this.prefix}_accessToken`)
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setAccessToken(token: string | null): void {
|
|
54
|
+
if (typeof localStorage !== "undefined") {
|
|
55
|
+
localStorage.setItem(`${this.prefix}_accessToken`, token ?? "");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getRefreshToken(): string | null {
|
|
60
|
+
return typeof localStorage !== "undefined"
|
|
61
|
+
? localStorage.getItem(`${this.prefix}_refreshToken`)
|
|
62
|
+
: null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setRefreshToken(token: string | null): void {
|
|
66
|
+
if (typeof localStorage !== "undefined") {
|
|
67
|
+
localStorage.setItem(`${this.prefix}_refreshToken`, token ?? "");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
clear(): void {
|
|
72
|
+
if (typeof localStorage !== "undefined") {
|
|
73
|
+
localStorage.removeItem(`${this.prefix}_accessToken`);
|
|
74
|
+
localStorage.removeItem(`${this.prefix}_refreshToken`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// utils/compat.ts - Compatibility layer for default imports
|
|
2
|
+
const _def = <T>(m: T & { default?: any }): any => (m && m.default) ? m.default : m;
|
|
3
|
+
|
|
4
|
+
import * as AjvNS from "ajv";
|
|
5
|
+
import * as addFormatsNS from "ajv-formats";
|
|
6
|
+
import * as EJSONNS from "ejson";
|
|
7
|
+
import * as pinoNS from "pino";
|
|
8
|
+
|
|
9
|
+
export const Ajv = _def(AjvNS); // constructible
|
|
10
|
+
export const addFormats = _def(addFormatsNS); // callable
|
|
11
|
+
export const pino = _def(pinoNS); // callable
|
|
12
|
+
export const EJSON = _def(EJSONNS); // default safe
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MultiServerMemoryStorageStrategy,
|
|
3
|
+
MultiServerLocalStorageStrategy,
|
|
4
|
+
type MultiServerTokenStorageStrategy,
|
|
5
|
+
} from "./MultiServerTokenStorageStrategy.js";
|
|
6
|
+
|
|
7
|
+
export async function createDefaultMultiServerTokenStorageStrategy(
|
|
8
|
+
strategyType: "memory" | "localStorage" | "file" | "auto" = "auto"
|
|
9
|
+
): Promise<MultiServerTokenStorageStrategy> {
|
|
10
|
+
if (strategyType === "memory") {
|
|
11
|
+
return new MultiServerMemoryStorageStrategy();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (strategyType === "localStorage") {
|
|
15
|
+
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
|
16
|
+
return new MultiServerLocalStorageStrategy();
|
|
17
|
+
}
|
|
18
|
+
throw new Error("localStorage n'est pas disponible dans cet environnement.");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (strategyType === "file") {
|
|
22
|
+
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
|
23
|
+
throw new Error("Le stockage fichier n'est pas disponible côté navigateur.");
|
|
24
|
+
}
|
|
25
|
+
const { MultiServerFileStorageStrategy } = await import("./MultiServerFileStorageStrategy.node.js");
|
|
26
|
+
return new MultiServerFileStorageStrategy();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// auto
|
|
30
|
+
if (typeof window !== "undefined" && typeof window.localStorage !== "undefined") {
|
|
31
|
+
return new MultiServerLocalStorageStrategy();
|
|
32
|
+
}
|
|
33
|
+
const { MultiServerFileStorageStrategy } = await import("./MultiServerFileStorageStrategy.node.js");
|
|
34
|
+
return new MultiServerFileStorageStrategy();
|
|
35
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { LocalStorageOfflineStorage, MemoryOfflineStorage } from "./OfflineQueueStorageStrategy.js";
|
|
1
|
+
import { LocalStorageOfflineStorage, MemoryOfflineStorage, type OfflineQueueStorageStrategy } from "./OfflineQueueStorageStrategy.js";
|
|
2
2
|
|
|
3
|
-
export async function createDefaultOfflineStrategy
|
|
3
|
+
export async function createDefaultOfflineStrategy<T = any>(
|
|
4
|
+
offlineStorageStrategy: "auto" | "memory" | "localStorage" | "file" = "auto"
|
|
5
|
+
): Promise<OfflineQueueStorageStrategy<T>> {
|
|
4
6
|
if (offlineStorageStrategy === "memory") {
|
|
5
7
|
return new MemoryOfflineStorage();
|
|
6
8
|
}
|
|
@@ -26,4 +28,7 @@ export async function createDefaultOfflineStrategy(offlineStorageStrategy = "aut
|
|
|
26
28
|
return new FileOfflineStorage();
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
|
|
31
|
+
|
|
32
|
+
// Handle unexpected strategy values
|
|
33
|
+
throw new Error(`Unsupported offline storage strategy: ${offlineStorageStrategy}`);
|
|
34
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LocalStorageStrategy, MemoryStorageStrategy } from "./TokenStorage.js";
|
|
2
|
+
|
|
3
|
+
import type { TokenStorageStrategy } from "./TokenStorage.js";
|
|
4
|
+
|
|
5
|
+
export async function createDefaultTokenStorageStrategy(
|
|
6
|
+
tokenStorageStrategy: "memory" | "localStorage" | "file" | "auto" = "auto"
|
|
7
|
+
): Promise<TokenStorageStrategy> {
|
|
8
|
+
if (tokenStorageStrategy === "memory") {
|
|
9
|
+
return new MemoryStorageStrategy();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (tokenStorageStrategy === "localStorage") {
|
|
13
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
14
|
+
return new LocalStorageStrategy();
|
|
15
|
+
}
|
|
16
|
+
throw new Error("localStorage is not available in this environment.");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (tokenStorageStrategy === "file") {
|
|
20
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
21
|
+
throw new Error("file storage is not available in this environment.");
|
|
22
|
+
}
|
|
23
|
+
const { FileStorageStrategy } = await import("./FileStorageStrategy.node.js");
|
|
24
|
+
return new FileStorageStrategy();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 'auto'
|
|
28
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
29
|
+
return new LocalStorageStrategy();
|
|
30
|
+
}
|
|
31
|
+
const { FileStorageStrategy } = await import("./FileStorageStrategy.node.js");
|
|
32
|
+
return new FileStorageStrategy();
|
|
33
|
+
}
|