codevdesign 1.0.76 → 1.0.78

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.
@@ -1,294 +1,310 @@
1
- class RafraichisseurToken {
2
- private intervalleEnSecondes = 15
3
- private secondesAvantExpirationTokenPourRafraichir = 20
4
- private skewSeconds = 5 // marge anti-derives d'horloge
5
- private popupAffiche = false
6
- private timerId: number | null = null
7
- private nomTemoin = 'csqc_jeton_secure_expiration'
8
- private urlPortailSeConnecter = ''
9
- private urlPortailRafraichir = ''
10
- private urlPortailDeconnecte = ''
11
- private refreshPromise: Promise<void> | null = null
12
- public loggerTrace = false
13
-
14
- // Lance une seule fois
15
- public async demarrer(
16
- nomTemoin: string,
17
- urlPortailSeConnecter: string,
18
- urlPortailRafraichir: string,
19
- urlPortailDeconnecte: string,
20
- ): Promise<void> {
21
- window.rafraichisseurToken = this
22
- if (this.loggerTrace) {
23
- console.log('Nom témoin', nomTemoin)
24
- console.log('URL portail se connecter', urlPortailSeConnecter)
25
- console.log('URL portail rafraichir', urlPortailRafraichir)
26
- console.log('URL portail déconnecte', urlPortailDeconnecte)
27
- }
28
-
29
- if (nomTemoin == null || nomTemoin === '') {
30
- console.warn('[RafraichisseurToken] nomTemoin invalide, rafraichisseur de token désactivé')
31
- return
32
- }
33
- if (urlPortailSeConnecter == null || urlPortailSeConnecter === '') {
34
- console.warn('[RafraichisseurToken] urlPortailSeConnecter invalide, rafraichisseur de token désactivé')
35
- return
36
- }
37
- if (urlPortailRafraichir == null || urlPortailRafraichir === '') {
38
- console.warn('[RafraichisseurToken] urlPortailRafraichir invalide, rafraichisseur de token désactivé')
39
- return
40
- }
41
- if (urlPortailDeconnecte == null || urlPortailDeconnecte === '') {
42
- console.warn('[RafraichisseurToken] urlPortailDeconnecte invalide, rafraichisseur de token désactivé')
43
- return
44
- }
45
- this.nomTemoin = nomTemoin
46
- this.urlPortailSeConnecter = urlPortailSeConnecter.replace(/\/+$/, '')
47
- this.urlPortailRafraichir = urlPortailRafraichir.replace(/\/+$/, '')
48
- this.urlPortailDeconnecte = urlPortailDeconnecte.replace(/\/+$/, '')
49
-
50
- await this.verifierJeton()
51
- if (this.timerId != null) return
52
- this.timerId = window.setInterval(() => {
53
- this.verifierJeton()
54
- }, this.intervalleEnSecondes * 1000)
55
- }
56
-
57
- public attendreRefreshSiNecessaire(force = false): Promise<void> {
58
- if (this.refreshPromise) return this.refreshPromise
59
- if (force || !this.estJetonValide()) {
60
- this.refreshPromise = this.rafraichir().finally(() => {
61
- this.refreshPromise = null
62
- })
63
- return this.refreshPromise
64
- }
65
- return Promise.resolve()
66
- }
67
-
68
- // Permet d'arrêter le timer (ex: au logout / destroy)
69
- public arreter(): void {
70
- if (this.timerId != null) {
71
- clearInterval(this.timerId)
72
- this.timerId = null
73
- }
74
- }
75
-
76
- public existeJeton = () => {
77
- return this.estJetonValide()
78
- }
79
-
80
- private async verifierJeton(): Promise<void> {
81
- if (this.loggerTrace) console.log('[RafraichisseurToken] Vérification du jeton...')
82
- if (this.popupAffiche) {
83
- if (this.loggerTrace) console.log('[RafraichisseurToken] Popup affiché, vérification du jeton ignorée')
84
- return
85
- }
86
-
87
- if (!this.estJetonValide()) {
88
- if (!this.refreshPromise) {
89
- this.refreshPromise = this.rafraichir().finally(() => {
90
- this.refreshPromise = null
91
- })
92
- }
93
- }
94
- }
95
-
96
- private estJetonValide(): boolean {
97
- if (this.popupAffiche) return true //On fait semblant que c'est valide pour ne pas provoquer un autre affichage du popup.
98
-
99
- const tokenEncode = this.lireCookie()
100
- if (!tokenEncode) {
101
- return false
102
- }
103
-
104
- let token: any
105
- try {
106
- token = this.parseJwt(tokenEncode)
107
- } catch {
108
- return false
109
- }
110
-
111
- const exp = Number(token?.exp)
112
- if (!Number.isFinite(exp)) {
113
- return false
114
- }
115
-
116
- const now = Math.floor(Date.now() / 1000)
117
- const refreshAt = exp - this.secondesAvantExpirationTokenPourRafraichir - this.skewSeconds
118
- if (now >= refreshAt) {
119
- return false
120
- }
121
-
122
- return true
123
- }
124
-
125
- private async rafraichir(): Promise<void> {
126
- if (this.loggerTrace) console.log('[RafraichisseurToken] rafraichir() appelé')
127
-
128
- this.popupAffiche = true // bloquer tout nouveau tick pendant le refresh
129
- const url = this.getRefreshUrl()
130
- const controller = new AbortController()
131
- const timeout = setTimeout(() => controller.abort(), 10_000)
132
-
133
- try {
134
- //Première tentative sans iframe, pour la majorité des cas.
135
- const resp = await fetch(url, {
136
- method: 'POST',
137
- credentials: 'include',
138
- headers: { Accept: 'application/json' },
139
- redirect: 'manual',
140
- signal: controller.signal,
141
- })
142
-
143
- // redirection vers Azure AD → session expirée, l'iframe ne peut pas compléter le flow OIDC cross-site
144
- if (this.loggerTrace) console.log('[RafraichisseurToken] fetch réponse type:', resp.type, 'status:', resp.status)
145
- if (resp.type === 'opaqueredirect' || resp.status === 302) {
146
- this.estDeconnecteAzure()
147
- return
148
- }
149
-
150
- // OK ou No Content: le cookie devrait être réécrit
151
- if (resp.status === 200 || resp.status === 204) {
152
- const jeton = this.lireCookie()
153
- if (!jeton) {
154
- if (this.loggerTrace) console.log('[RafraichisseurToken] 200 mais cookie absent → iframe')
155
- this.rafraichirParIframe()
156
- } else {
157
- this.popupAffiche = false // succès, reprendre la surveillance
158
- }
159
- return
160
- }
161
-
162
- // non auth / expiré (401, 419) + IIS timeout (440)
163
- if (resp.status === 401 || resp.status === 419 || resp.status === 440) {
164
- if (this.loggerTrace) console.log('[RafraichisseurToken] statut auth → iframe')
165
- this.rafraichirParIframe()
166
- return
167
- }
168
-
169
- console.warn('Rafraichisseur token: statut inattendu', resp.status)
170
- this.popupAffiche = false // statut inattendu, permettre un retry
171
- } catch (err: any) {
172
- if (err?.name === 'AbortError') console.warn('RafraichisseurToken timeout')
173
- else console.error('Erreur rafraichisseur de token', err)
174
- this.reloadSiErreurReseau()
175
- } finally {
176
- clearTimeout(timeout)
177
- }
178
- }
179
-
180
- private rafraichirParIframe(): void {
181
- this.popupAffiche = true // bloquer le timer pendant que l'iframe tente l'auth
182
- let iframe = document.createElement('iframe')
183
- const url = this.getRefreshUrl()
184
- iframe.src = `${url}?urlRetour=${encodeURI(window.location.href)}`
185
- iframe.id = 'idRafrToken'
186
- iframe.style.display = 'none'
187
- document.body.appendChild(iframe)
188
-
189
- const iframeTimeout = setTimeout(() => {
190
- iframe.onload = null
191
- iframe.src = 'about:blank'
192
- iframe.remove()
193
- this.popupAffiche = false
194
- console.warn('[RafraichisseurToken] iframe timeout — popupAffiche réinitialisé')
195
- }, 15_000)
196
-
197
- iframe.onload = () => {
198
- if (this.loggerTrace) console.log('[RafraichisseurToken] iframe onload, vérification du jeton...')
199
- clearTimeout(iframeTimeout)
200
- iframe.onload = null // empêcher les re-entrées sur les redirects OIDC internes
201
-
202
- const jeton = this.lireCookie()
203
- if (jeton == null || jeton === '') {
204
- if (this.loggerTrace)
205
- console.log('[RafraichisseurToken] iframe onload, jeton toujours absent → déconnecté Azure')
206
- this.estDeconnecteAzure()
207
- } else {
208
- this.popupAffiche = false // cookie recréé avec succès, reprendre la surveillance
209
- }
210
-
211
- iframe.src = 'about:blank' // stopper les navigations internes
212
- iframe.remove()
213
- }
214
- }
215
-
216
- private estDeconnecteAzure(): void {
217
- //on envoie au portail, pas le choix
218
- const retour = encodeURI(window.location.href)
219
-
220
- window.open(`${this.urlPortailSeConnecter}?urlRetour=${retour}`, '_self')
221
- return
222
- }
223
-
224
- public deconnecterPortail(): void {
225
- window.location.replace(`${this.urlPortailDeconnecte}?urlRetour=${encodeURIComponent(window.location.href)}`)
226
- }
227
-
228
- private lireCookie(): string | null {
229
- if (!document.cookie) return null
230
- const cookies = document.cookie.split(';').map(c => c.trim())
231
- for (const cookie of cookies) {
232
- if (cookie.startsWith(`${this.nomTemoin}=`)) {
233
- try {
234
- return decodeURIComponent(cookie.substring(this.nomTemoin.length + 1))
235
- } catch {
236
- return cookie.substring(this.nomTemoin.length + 1)
237
- }
238
- }
239
- }
240
- return null
241
- }
242
-
243
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
- private parseJwt(token: string): any {
245
- const parts = token.split('.')
246
- const base64Url = parts[1]
247
- if (!base64Url) throw new Error('Invalid JWT format')
248
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
249
- const jsonPayload = decodeURIComponent(
250
- atob(base64)
251
- .split('')
252
- .map(c => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
253
- .join(''),
254
- )
255
- return JSON.parse(jsonPayload)
256
- }
257
-
258
- private reloadSiErreurReseau(): void {
259
- const CLE = 'rafraichisseur_dernierReload'
260
- if (this.loggerTrace) console.log('[RafraichisseurToken] reload si erreur')
261
- const maintenant = Date.now()
262
- const dernierReload = Number(sessionStorage.getItem(CLE) ?? 0)
263
- if (maintenant - dernierReload > 30_000) {
264
- if (this.loggerTrace) console.log('[RafraichisseurToken] Écriture sessionStorage:', CLE, maintenant)
265
- sessionStorage.setItem(CLE, String(maintenant))
266
-
267
- globalThis.location.reload()
268
- } else {
269
- this.popupAffiche = false
270
- }
271
- }
272
-
273
- // URL refresh selon env (dev = proxy Vite ; prod = portail)
274
- private getRefreshUrl(): string {
275
- if (import.meta.env.MODE === 'development') return '/portail-refresh'
276
- return this.urlPortailRafraichir
277
- }
278
- }
279
-
280
- declare global {
281
- interface Window {
282
- rafraichisseurToken: RafraichisseurToken
283
- }
284
- }
285
-
286
- // Instance
287
- const rafraichisseurToken = new RafraichisseurToken()
288
- export default rafraichisseurToken
289
-
290
- declare global {
291
- interface Window {
292
- rafraichisseurToken: RafraichisseurToken
293
- }
294
- }
1
+ class RafraichisseurToken {
2
+ private intervalleEnSecondes = 15
3
+ private secondesAvantExpirationTokenPourRafraichir = 20
4
+ private skewSeconds = 5 // marge anti-derives d'horloge
5
+ private popupAffiche = false
6
+ private timerId: number | null = null
7
+ private nomTemoin = 'csqc_jeton_secure_expiration'
8
+ private urlPortailSeConnecter = ''
9
+ private urlPortailRafraichir = ''
10
+ private urlPortailDeconnecte = ''
11
+ private refreshPromise: Promise<void> | null = null
12
+ public loggerTrace = false
13
+
14
+ // Lance une seule fois
15
+ public async demarrer(
16
+ nomTemoin: string,
17
+ urlPortailSeConnecter: string,
18
+ urlPortailRafraichir: string,
19
+ urlPortailDeconnecte: string,
20
+ ): Promise<void> {
21
+ window.rafraichisseurToken = this
22
+ if (this.loggerTrace) {
23
+ console.log('Nom témoin', nomTemoin)
24
+ console.log('URL portail se connecter', urlPortailSeConnecter)
25
+ console.log('URL portail rafraichir', urlPortailRafraichir)
26
+ console.log('URL portail déconnecte', urlPortailDeconnecte)
27
+ }
28
+
29
+ if (nomTemoin == null || nomTemoin === '') {
30
+ console.warn('[RafraichisseurToken] nomTemoin invalide, rafraichisseur de token désactivé')
31
+ return
32
+ }
33
+ if (urlPortailSeConnecter == null || urlPortailSeConnecter === '') {
34
+ console.warn('[RafraichisseurToken] urlPortailSeConnecter invalide, rafraichisseur de token désactivé')
35
+ return
36
+ }
37
+ if (urlPortailRafraichir == null || urlPortailRafraichir === '') {
38
+ console.warn('[RafraichisseurToken] urlPortailRafraichir invalide, rafraichisseur de token désactivé')
39
+ return
40
+ }
41
+ if (urlPortailDeconnecte == null || urlPortailDeconnecte === '') {
42
+ console.warn('[RafraichisseurToken] urlPortailDeconnecte invalide, rafraichisseur de token désactivé')
43
+ return
44
+ }
45
+ this.nomTemoin = nomTemoin
46
+ this.urlPortailSeConnecter = urlPortailSeConnecter.replace(/\/+$/, '')
47
+ this.urlPortailRafraichir = urlPortailRafraichir.replace(/\/+$/, '')
48
+ this.urlPortailDeconnecte = urlPortailDeconnecte.replace(/\/+$/, '')
49
+
50
+ await this.verifierJeton()
51
+ if (this.timerId != null) return
52
+ this.timerId = window.setInterval(() => {
53
+ this.verifierJeton()
54
+ }, this.intervalleEnSecondes * 1000)
55
+ }
56
+
57
+ public attendreRefreshSiNecessaire(force = false): Promise<void> {
58
+ if (this.urlPortailSeConnecter == null || this.urlPortailSeConnecter === '') {
59
+ if (this.loggerTrace)
60
+ console.warn(
61
+ '[RafraichisseurToken] attendreRefreshSiNecessaire appelé alors que aucun url se connecter a été configuré.',
62
+ )
63
+ return Promise.resolve()
64
+ }
65
+
66
+ if (this.urlPortailRafraichir == null || this.urlPortailRafraichir === '') {
67
+ if (this.loggerTrace)
68
+ console.warn(
69
+ '[RafraichisseurToken] attendreRefreshSiNecessaire appelé alors que aucun url rafraichir a été configuré.',
70
+ )
71
+ return Promise.resolve()
72
+ }
73
+
74
+ if (this.refreshPromise) return this.refreshPromise
75
+ if (force || !this.estJetonValide()) {
76
+ this.refreshPromise = this.rafraichir().finally(() => {
77
+ this.refreshPromise = null
78
+ })
79
+ return this.refreshPromise
80
+ }
81
+ return Promise.resolve()
82
+ }
83
+
84
+ // Permet d'arrêter le timer (ex: au logout / destroy)
85
+ public arreter(): void {
86
+ if (this.timerId != null) {
87
+ clearInterval(this.timerId)
88
+ this.timerId = null
89
+ }
90
+ }
91
+
92
+ public existeJeton = () => {
93
+ return this.estJetonValide()
94
+ }
95
+
96
+ private async verifierJeton(): Promise<void> {
97
+ if (this.loggerTrace) console.log('[RafraichisseurToken] Vérification du jeton...')
98
+ if (this.popupAffiche) {
99
+ if (this.loggerTrace) console.log('[RafraichisseurToken] Popup affiché, vérification du jeton ignorée')
100
+ return
101
+ }
102
+
103
+ if (!this.estJetonValide()) {
104
+ if (!this.refreshPromise) {
105
+ this.refreshPromise = this.rafraichir().finally(() => {
106
+ this.refreshPromise = null
107
+ })
108
+ }
109
+ }
110
+ }
111
+
112
+ private estJetonValide(): boolean {
113
+ if (this.popupAffiche) return true //On fait semblant que c'est valide pour ne pas provoquer un autre affichage du popup.
114
+
115
+ const tokenEncode = this.lireCookie()
116
+ if (!tokenEncode) {
117
+ return false
118
+ }
119
+
120
+ let token: any
121
+ try {
122
+ token = this.parseJwt(tokenEncode)
123
+ } catch {
124
+ return false
125
+ }
126
+
127
+ const exp = Number(token?.exp)
128
+ if (!Number.isFinite(exp)) {
129
+ return false
130
+ }
131
+
132
+ const now = Math.floor(Date.now() / 1000)
133
+ const refreshAt = exp - this.secondesAvantExpirationTokenPourRafraichir - this.skewSeconds
134
+ if (now >= refreshAt) {
135
+ return false
136
+ }
137
+
138
+ return true
139
+ }
140
+
141
+ private async rafraichir(): Promise<void> {
142
+ if (this.loggerTrace) console.log('[RafraichisseurToken] rafraichir() appelé')
143
+
144
+ this.popupAffiche = true // bloquer tout nouveau tick pendant le refresh
145
+ const url = this.getRefreshUrl()
146
+ const controller = new AbortController()
147
+ const timeout = setTimeout(() => controller.abort(), 10_000)
148
+
149
+ try {
150
+ //Première tentative sans iframe, pour la majorité des cas.
151
+ const resp = await fetch(url, {
152
+ method: 'POST',
153
+ credentials: 'include',
154
+ headers: { Accept: 'application/json' },
155
+ redirect: 'manual',
156
+ signal: controller.signal,
157
+ })
158
+
159
+ // redirection vers Azure AD → session expirée, l'iframe ne peut pas compléter le flow OIDC cross-site
160
+ if (this.loggerTrace) console.log('[RafraichisseurToken] fetch réponse type:', resp.type, 'status:', resp.status)
161
+ if (resp.type === 'opaqueredirect' || resp.status === 302) {
162
+ this.estDeconnecteAzure()
163
+ return
164
+ }
165
+
166
+ // OK ou No Content: le cookie devrait être réécrit
167
+ if (resp.status === 200 || resp.status === 204) {
168
+ const jeton = this.lireCookie()
169
+ if (!jeton) {
170
+ if (this.loggerTrace) console.log('[RafraichisseurToken] 200 mais cookie absent iframe')
171
+ this.rafraichirParIframe()
172
+ } else {
173
+ this.popupAffiche = false // succès, reprendre la surveillance
174
+ }
175
+ return
176
+ }
177
+
178
+ // non auth / expiré (401, 419) + IIS timeout (440)
179
+ if (resp.status === 401 || resp.status === 419 || resp.status === 440) {
180
+ if (this.loggerTrace) console.log('[RafraichisseurToken] statut auth → iframe')
181
+ this.rafraichirParIframe()
182
+ return
183
+ }
184
+
185
+ console.warn('Rafraichisseur token: statut inattendu', resp.status)
186
+ this.popupAffiche = false // statut inattendu, permettre un retry
187
+ } catch (err: any) {
188
+ if (err?.name === 'AbortError') console.warn('RafraichisseurToken timeout')
189
+ else console.error('Erreur rafraichisseur de token', err)
190
+ this.reloadSiErreurReseau()
191
+ } finally {
192
+ clearTimeout(timeout)
193
+ }
194
+ }
195
+
196
+ private rafraichirParIframe(): void {
197
+ this.popupAffiche = true // bloquer le timer pendant que l'iframe tente l'auth
198
+ let iframe = document.createElement('iframe')
199
+ const url = this.getRefreshUrl()
200
+ iframe.src = `${url}?urlRetour=${encodeURI(window.location.href)}`
201
+ iframe.id = 'idRafrToken'
202
+ iframe.style.display = 'none'
203
+ document.body.appendChild(iframe)
204
+
205
+ const iframeTimeout = setTimeout(() => {
206
+ iframe.onload = null
207
+ iframe.src = 'about:blank'
208
+ iframe.remove()
209
+ this.popupAffiche = false
210
+ console.warn('[RafraichisseurToken] iframe timeout — popupAffiche réinitialisé')
211
+ }, 15_000)
212
+
213
+ iframe.onload = () => {
214
+ if (this.loggerTrace) console.log('[RafraichisseurToken] iframe onload, vérification du jeton...')
215
+ clearTimeout(iframeTimeout)
216
+ iframe.onload = null // empêcher les re-entrées sur les redirects OIDC internes
217
+
218
+ const jeton = this.lireCookie()
219
+ if (jeton == null || jeton === '') {
220
+ if (this.loggerTrace)
221
+ console.log('[RafraichisseurToken] iframe onload, jeton toujours absent → déconnecté Azure')
222
+ this.estDeconnecteAzure()
223
+ } else {
224
+ this.popupAffiche = false // cookie recréé avec succès, reprendre la surveillance
225
+ }
226
+
227
+ iframe.src = 'about:blank' // stopper les navigations internes
228
+ iframe.remove()
229
+ }
230
+ }
231
+
232
+ private estDeconnecteAzure(): void {
233
+ //on envoie au portail, pas le choix
234
+ const retour = encodeURI(window.location.href)
235
+
236
+ window.open(`${this.urlPortailSeConnecter}?urlRetour=${retour}`, '_self')
237
+ return
238
+ }
239
+
240
+ public deconnecterPortail(): void {
241
+ window.location.replace(`${this.urlPortailDeconnecte}?urlRetour=${encodeURIComponent(window.location.href)}`)
242
+ }
243
+
244
+ private lireCookie(): string | null {
245
+ if (!document.cookie) return null
246
+ const cookies = document.cookie.split(';').map(c => c.trim())
247
+ for (const cookie of cookies) {
248
+ if (cookie.startsWith(`${this.nomTemoin}=`)) {
249
+ try {
250
+ return decodeURIComponent(cookie.substring(this.nomTemoin.length + 1))
251
+ } catch {
252
+ return cookie.substring(this.nomTemoin.length + 1)
253
+ }
254
+ }
255
+ }
256
+ return null
257
+ }
258
+
259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
+ private parseJwt(token: string): any {
261
+ const parts = token.split('.')
262
+ const base64Url = parts[1]
263
+ if (!base64Url) throw new Error('Invalid JWT format')
264
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
265
+ const jsonPayload = decodeURIComponent(
266
+ atob(base64)
267
+ .split('')
268
+ .map(c => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
269
+ .join(''),
270
+ )
271
+ return JSON.parse(jsonPayload)
272
+ }
273
+
274
+ private reloadSiErreurReseau(): void {
275
+ const CLE = 'rafraichisseur_dernierReload'
276
+ if (this.loggerTrace) console.log('[RafraichisseurToken] reload si erreur')
277
+ const maintenant = Date.now()
278
+ const dernierReload = Number(sessionStorage.getItem(CLE) ?? 0)
279
+ if (maintenant - dernierReload > 30_000) {
280
+ if (this.loggerTrace) console.log('[RafraichisseurToken] Écriture sessionStorage:', CLE, maintenant)
281
+ sessionStorage.setItem(CLE, String(maintenant))
282
+
283
+ globalThis.location.reload()
284
+ } else {
285
+ this.popupAffiche = false
286
+ }
287
+ }
288
+
289
+ // URL refresh selon env (dev = proxy Vite ; prod = portail)
290
+ private getRefreshUrl(): string {
291
+ if (import.meta.env.MODE === 'development') return '/portail-refresh'
292
+ return this.urlPortailRafraichir
293
+ }
294
+ }
295
+
296
+ declare global {
297
+ interface Window {
298
+ rafraichisseurToken: RafraichisseurToken
299
+ }
300
+ }
301
+
302
+ // Instance
303
+ const rafraichisseurToken = new RafraichisseurToken()
304
+ export default rafraichisseurToken
305
+
306
+ declare global {
307
+ interface Window {
308
+ rafraichisseurToken: RafraichisseurToken
309
+ }
310
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codevdesign",
3
- "version": "1.0.76",
3
+ "version": "1.0.78",
4
4
  "description": "Composants Vuetify 3 pour les projets Codev",
5
5
  "files": [
6
6
  "./**/*.vue",