@data-fair/lib-vue 0.1.0 → 1.0.1

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/session.js CHANGED
@@ -1,289 +1,257 @@
1
- import { reactive, computed, watch, inject } from 'vue';
2
- import { ofetch } from 'ofetch';
3
- import { jwtDecode } from 'jwt-decode';
4
- import cookiesModule from 'universal-cookie';
5
- import Debug from 'debug';
6
- const Cookies = cookiesModule;
7
- const debug = Debug('session');
8
- debug.log = console.log.bind(console);
9
- function jwtDecodeAlive(jwt) {
10
- if (!jwt)
11
- return;
12
- const decoded = jwtDecode(jwt);
13
- if (!decoded)
14
- return;
15
- const now = Math.ceil(Date.now().valueOf() / 1000);
16
- if (typeof decoded.exp !== 'undefined' && decoded.exp < now) {
17
- console.error(`token expired: ${decoded.exp}<${now}, ${JSON.stringify(decoded)}`);
18
- return;
19
- }
20
- if (typeof decoded.nbf !== 'undefined' && decoded.nbf > now) {
21
- console.warn(`token not yet valid: ${decoded.nbf}>${now}, ${JSON.stringify(decoded)}`);
22
- // do not return null here, this is probably a false flag due to a slightly mismatched clock
23
- // return null
24
- }
25
- return decoded;
1
+ import { reactive, computed, watch, inject } from 'vue'
2
+ import { ofetch } from 'ofetch'
3
+ import { jwtDecode } from 'jwt-decode'
4
+ import cookiesModule from 'universal-cookie'
5
+ import Debug from 'debug'
6
+ const Cookies = cookiesModule
7
+ const debug = Debug('session')
8
+ debug.log = console.log.bind(console)
9
+ function jwtDecodeAlive (jwt) {
10
+ if (!jwt) { return }
11
+ const decoded = jwtDecode(jwt)
12
+ if (!decoded) { return }
13
+ const now = Math.ceil(Date.now().valueOf() / 1000)
14
+ if (typeof decoded.exp !== 'undefined' && decoded.exp < now) {
15
+ console.error(`token expired: ${decoded.exp}<${now}, ${JSON.stringify(decoded)}`)
16
+ return
17
+ }
18
+ if (typeof decoded.nbf !== 'undefined' && decoded.nbf > now) {
19
+ console.warn(`token not yet valid: ${decoded.nbf}>${now}, ${JSON.stringify(decoded)}`)
20
+ // do not return null here, this is probably a false flag due to a slightly mismatched clock
21
+ // return null
22
+ }
23
+ return decoded
26
24
  }
27
25
  const getTopLocation = () => {
28
- try {
29
- return window.top ? window.top.location : window.location;
26
+ try {
27
+ return window.top ? window.top.location : window.location
28
+ } catch (err) {
29
+ return window.location
30
+ }
31
+ }
32
+ const goTo = (url) => {
33
+ const topLocation = getTopLocation()
34
+ if (topLocation == null) {
35
+ throw new TypeError('session.goTo was called without access to the window object or its location')
36
+ }
37
+ if (url) { topLocation.href = url } else { topLocation.reload() }
38
+ }
39
+ const defaultOptions = { directoryUrl: '/simple-directory', sitePath: '' }
40
+ export async function getSession (initOptions) {
41
+ const options = { ...defaultOptions, ...initOptions }
42
+ const cookiesPath = options.sitePath + '/'
43
+ debug(`init directoryUrl=${options.directoryUrl}, cookiesPath=${cookiesPath}`)
44
+ const ssr = !!options.req
45
+ if (ssr) { debug('run in SSR context') }
46
+ const customFetch = initOptions?.customFetch ?? ofetch
47
+ // use vue-router to detect page change and maintain a reference to the current page location
48
+ // top page if we are in iframe context
49
+ const topLocation = computed(() => {
50
+ if (ssr) { return undefined }
51
+ if (options.route?.fullPath) { /* empty */ } // adds reactivity
52
+ const location = getTopLocation()
53
+ debug('update location based on route change', location)
54
+ return location
55
+ })
56
+ // the core state of the session that is filled by reading cookies
57
+ const state = reactive({})
58
+ // cookies are the source of truth and this information is transformed into the state reactive object
59
+ const cookies = initOptions?.cookies ?? new Cookies(options.req?.headers.cookie)
60
+ const readCookies = () => {
61
+ const darkCookie = cookies.get('theme_dark')
62
+ state.dark = darkCookie === '1' || darkCookie === 'true'
63
+ const langCookie = cookies.get('i18n_lang')
64
+ if (langCookie) { state.lang = langCookie } else { delete state.lang }
65
+ const idToken = cookies.get('id_token')
66
+ const user = jwtDecodeAlive(idToken)
67
+ if (!user) {
68
+ delete state.user
69
+ delete state.organization
70
+ delete state.account
71
+ delete state.accountRole
72
+ return
30
73
  }
31
- catch (err) {
32
- return window.location;
74
+ // this is to prevent null values that are put by SD versions that do not strictly respect their schema
75
+ for (const org of user.organizations) {
76
+ if (!org.department) {
77
+ delete org.department
78
+ delete org.departmentName
79
+ }
33
80
  }
34
- };
35
- const goTo = (url) => {
36
- const topLocation = getTopLocation();
37
- if (topLocation == null) {
38
- throw new TypeError('session.goTo was called without access to the window object or its location');
81
+ state.user = user
82
+ const organizationId = cookies.get('id_token_org')
83
+ const departmentId = cookies.get('id_token_dep')
84
+ if (organizationId) {
85
+ if (departmentId) {
86
+ state.organization = state.user.organizations.find(o => o.id === organizationId && o.department === departmentId)
87
+ } else {
88
+ state.organization = state.user.organizations.find(o => o.id === organizationId)
89
+ }
90
+ } else {
91
+ delete state.organization
92
+ }
93
+ if (state.organization) {
94
+ state.account = {
95
+ type: 'organization',
96
+ id: state.organization.id,
97
+ name: state.organization.name,
98
+ department: state.organization.department,
99
+ departmentName: state.organization.departmentName
100
+ }
101
+ state.accountRole = state.organization.role
102
+ } else {
103
+ state.account = {
104
+ type: 'user',
105
+ id: state.user.id,
106
+ name: state.user.name
107
+ }
108
+ state.accountRole = 'admin'
39
109
  }
40
- if (url)
41
- topLocation.href = url;
42
- else
43
- topLocation.reload();
44
- };
45
- const defaultOptions = { directoryUrl: '/simple-directory', sitePath: '' };
46
- export async function getSession(initOptions) {
47
- const options = { ...defaultOptions, ...initOptions };
48
- const cookiesPath = options.sitePath + '/';
49
- debug(`init directoryUrl=${options.directoryUrl}, cookiesPath=${cookiesPath}`);
50
- const ssr = !!options.req;
51
- if (ssr)
52
- debug('run in SSR context');
53
- const customFetch = initOptions?.customFetch ?? ofetch;
54
- // use vue-router to detect page change and maintain a reference to the current page location
55
- // top page if we are in iframe context
56
- const topLocation = computed(() => {
57
- if (ssr)
58
- return undefined;
59
- if (options.route?.fullPath) { /* empty */ } // adds reactivity
60
- const location = getTopLocation();
61
- debug('update location based on route change', location);
62
- return location;
63
- });
64
- // the core state of the session that is filled by reading cookies
65
- const state = reactive({});
66
- // cookies are the source of truth and this information is transformed into the state reactive object
67
- const cookies = initOptions?.cookies ?? new Cookies(options.req?.headers.cookie);
68
- const readCookies = () => {
69
- const darkCookie = cookies.get('theme_dark');
70
- state.dark = darkCookie === '1' || darkCookie === 'true';
71
- const langCookie = cookies.get('i18n_lang');
72
- if (langCookie)
73
- state.lang = langCookie;
74
- else
75
- delete state.lang;
76
- const idToken = cookies.get('id_token');
77
- const user = jwtDecodeAlive(idToken);
78
- if (!user) {
79
- delete state.user;
80
- delete state.organization;
81
- delete state.account;
82
- delete state.accountRole;
83
- return;
84
- }
85
- // this is to prevent null values that are put by SD versions that do not strictly respect their schema
86
- for (const org of user.organizations) {
87
- if (!org.department) {
88
- delete org.department;
89
- delete org.departmentName;
90
- }
91
- }
92
- state.user = user;
93
- const organizationId = cookies.get('id_token_org');
94
- const departmentId = cookies.get('id_token_dep');
95
- if (organizationId) {
96
- if (departmentId) {
97
- state.organization = state.user.organizations.find(o => o.id === organizationId && o.department === departmentId);
98
- }
99
- else {
100
- state.organization = state.user.organizations.find(o => o.id === organizationId);
101
- }
102
- }
103
- else {
104
- delete state.organization;
105
- }
106
- if (state.organization) {
107
- state.account = {
108
- type: 'organization',
109
- id: state.organization.id,
110
- name: state.organization.name,
111
- department: state.organization.department,
112
- departmentName: state.organization.departmentName
113
- };
114
- state.accountRole = state.organization.role;
115
- }
116
- else {
117
- state.account = {
118
- type: 'user',
119
- id: state.user.id,
120
- name: state.user.name
121
- };
122
- state.accountRole = 'admin';
123
- }
124
- };
125
- readCookies();
126
- debug('initial state', state);
127
- if (!ssr) {
128
- // sessionData is also stored in localStorage as a way to access it in simpler pages that do not require use-session
129
- // and in order to listen to storage event from other contexts and sync session info accross windows and tabs
130
- const storageListener = (event) => {
131
- if (event.key === 'sd-session' + options.sitePath)
132
- readCookies();
133
- };
134
- window.addEventListener('storage', storageListener);
135
- // we cannot use onUnmounted here or we get warnings "onUnmounted is called when there is no active component instance to be associated with. "
136
- // TODO: should we have another cleanup mechanism ?
137
- // onUnmounted(() => { window.removeEventListener('storage', storageListener) })
138
- // trigger some full page refresh when some key session elements are changed
139
- // the danger of simply using reactivity is too high, data must be re-fetched, etc.
140
- watch(() => state.account, (account, oldAccount) => {
141
- if (account?.type !== oldAccount?.type || account?.id !== oldAccount?.id || account?.department !== oldAccount?.department) {
142
- goTo(null);
143
- }
144
- });
145
- watch(() => state.lang, () => {
146
- goTo(null);
147
- });
148
- watch(() => state.dark, () => {
149
- goTo(null);
150
- });
151
- watch(state, (state) => {
152
- if (!ssr) {
153
- window.localStorage.setItem('sd-session' + options.sitePath, JSON.stringify(state));
154
- }
155
- debug('state changed', state);
156
- });
110
+ }
111
+ readCookies()
112
+ debug('initial state', state)
113
+ if (!ssr) {
114
+ // sessionData is also stored in localStorage as a way to access it in simpler pages that do not require use-session
115
+ // and in order to listen to storage event from other contexts and sync session info accross windows and tabs
116
+ const storageListener = (event) => {
117
+ if (event.key === 'sd-session' + options.sitePath) { readCookies() }
157
118
  }
158
- // login can be performed as a simple link (please use target=top) or as a function
159
- function loginUrl(redirect, extraParams = {}, immediateRedirect = true) {
160
- // login can also be used to redirect user immediately if he is already logged
161
- if (redirect && state.user && immediateRedirect)
162
- return redirect;
163
- if (!redirect && topLocation.value)
164
- redirect = topLocation.value.href;
165
- let url = `${options.directoryUrl}/login?redirect=${encodeURIComponent(redirect ?? '')}`;
166
- Object.keys(extraParams).filter(key => ![null, undefined, ''].includes(extraParams[key])).forEach((key) => {
167
- url += `&${key}=${encodeURIComponent(extraParams[key])}`;
168
- });
169
- return url;
119
+ window.addEventListener('storage', storageListener)
120
+ // we cannot use onUnmounted here or we get warnings "onUnmounted is called when there is no active component instance to be associated with. "
121
+ // TODO: should we have another cleanup mechanism ?
122
+ // onUnmounted(() => { window.removeEventListener('storage', storageListener) })
123
+ // trigger some full page refresh when some key session elements are changed
124
+ // the danger of simply using reactivity is too high, data must be re-fetched, etc.
125
+ watch(() => state.account, (account, oldAccount) => {
126
+ if (account?.type !== oldAccount?.type || account?.id !== oldAccount?.id || account?.department !== oldAccount?.department) {
127
+ goTo(null)
128
+ }
129
+ })
130
+ watch(() => state.lang, () => {
131
+ goTo(null)
132
+ })
133
+ watch(() => state.dark, () => {
134
+ goTo(null)
135
+ })
136
+ watch(state, (state) => {
137
+ if (!ssr) {
138
+ window.localStorage.setItem('sd-session' + options.sitePath, JSON.stringify(state))
139
+ }
140
+ debug('state changed', state)
141
+ })
142
+ }
143
+ // login can be performed as a simple link (please use target=top) or as a function
144
+ function loginUrl (redirect, extraParams = {}, immediateRedirect = true) {
145
+ // login can also be used to redirect user immediately if he is already logged
146
+ if (redirect && state.user && immediateRedirect) { return redirect }
147
+ if (!redirect && topLocation.value) { redirect = topLocation.value.href }
148
+ let url = `${options.directoryUrl}/login?redirect=${encodeURIComponent(redirect ?? '')}`
149
+ Object.keys(extraParams).filter(key => ![null, undefined, ''].includes(extraParams[key])).forEach((key) => {
150
+ url += `&${key}=${encodeURIComponent(extraParams[key])}`
151
+ })
152
+ return url
153
+ }
154
+ const login = (redirect, extraParams = {}, immediateRedirect = true) => {
155
+ goTo(loginUrl(redirect, extraParams, immediateRedirect))
156
+ }
157
+ const logout = async (redirect) => {
158
+ await customFetch(`${options.directoryUrl}/api/auth`, { method: 'DELETE' })
159
+ // sometimes server side cookie deletion is not applied immediately in browser local js context
160
+ // so we do it here to
161
+ cookies.remove('id_token')
162
+ cookies.remove('id_token_org')
163
+ cookies.remove('id_token_dep')
164
+ goTo(redirect ?? options.logoutRedirectUrl ?? null)
165
+ }
166
+ const switchOrganization = (org, dep) => {
167
+ if (org) { cookies.set('id_token_org', org, { path: cookiesPath }) } else { cookies.remove('id_token_org') }
168
+ if (dep) { cookies.set('id_token_dep', dep, { path: cookiesPath }) } else { cookies.remove('id_token_dep') }
169
+ readCookies()
170
+ }
171
+ const setAdminMode = async (adminMode, redirect) => {
172
+ if (adminMode) {
173
+ const params = { adminMode: 'true' }
174
+ if (state.user != null) { params.email = state.user.email }
175
+ const url = loginUrl(redirect, params, true)
176
+ goTo(url)
177
+ } else {
178
+ await customFetch(`${options.directoryUrl}/api/auth/adminmode`, { method: 'DELETE' })
179
+ goTo(redirect ?? null)
170
180
  }
171
- const login = (redirect, extraParams = {}, immediateRedirect = true) => {
172
- goTo(loginUrl(redirect, extraParams, immediateRedirect));
173
- };
174
- const logout = async (redirect) => {
175
- await customFetch(`${options.directoryUrl}/api/auth`, { method: 'DELETE' });
176
- // sometimes server side cookie deletion is not applied immediately in browser local js context
177
- // so we do it here to
178
- cookies.remove('id_token');
179
- cookies.remove('id_token_org');
180
- cookies.remove('id_token_dep');
181
- goTo(redirect ?? options.logoutRedirectUrl ?? null);
182
- };
183
- const switchOrganization = (org, dep) => {
184
- if (org)
185
- cookies.set('id_token_org', org, { path: cookiesPath });
186
- else
187
- cookies.remove('id_token_org');
188
- if (dep)
189
- cookies.set('id_token_dep', dep, { path: cookiesPath });
190
- else
191
- cookies.remove('id_token_dep');
192
- readCookies();
193
- };
194
- const setAdminMode = async (adminMode, redirect) => {
195
- if (adminMode) {
196
- const params = { adminMode: 'true' };
197
- if (state.user != null)
198
- params.email = state.user.email;
199
- const url = loginUrl(redirect, params, true);
200
- goTo(url);
201
- }
202
- else {
203
- await customFetch(`${options.directoryUrl}/api/auth/adminmode`, { method: 'DELETE' });
204
- goTo(redirect ?? null);
205
- }
206
- };
207
- const asAdmin = async (user) => {
208
- if (user) {
209
- await customFetch(`${options.directoryUrl}/api/auth/asadmin`, { method: 'POST', body: user });
210
- }
211
- else {
212
- await customFetch(`${options.directoryUrl}/api/auth/asadmin`, { method: 'DELETE' });
213
- }
214
- readCookies();
215
- };
216
- const cancelDeletion = async () => {
217
- if (state.user == null)
218
- return;
219
- await customFetch(`${options.directoryUrl}/api/users/${state.user.id}`, { method: 'PATCH', body: ({ plannedDeletion: null }) });
220
- readCookies();
221
- };
222
- const switchDark = (value) => {
223
- const maxAge = 60 * 60 * 24 * 365; // 1 year
224
- cookies.set('theme_dark', `${value}`, { maxAge, path: cookiesPath });
225
- readCookies();
226
- };
227
- const switchLang = (value) => {
228
- const maxAge = 60 * 60 * 24 * 365; // 1 year
229
- cookies.set('i18n_lang', value, { maxAge, path: cookiesPath });
230
- readCookies();
231
- };
232
- const keepalive = async () => {
233
- if (state.user == null)
234
- return;
235
- window.localStorage.setItem('sd-keepalive' + options.sitePath, `${new Date().getTime()}`);
236
- await customFetch(`${options.directoryUrl}/api/auth/keepalive`, { method: 'POST' });
237
- readCookies();
238
- };
239
- // immediately performs a keepalive, but only on top windows (not iframes or popups)
240
- // and only if it was not done very recently (maybe from a refreshed page next to this one)
241
- if (!ssr && window.top === window.self) {
242
- const lastKeepalive = window.localStorage.getItem('sd-keepalive' + options.sitePath);
243
- if (!lastKeepalive || (new Date().getTime() - Number(lastKeepalive)) > 10000) {
244
- await keepalive();
245
- }
181
+ }
182
+ const asAdmin = async (user) => {
183
+ if (user) {
184
+ await customFetch(`${options.directoryUrl}/api/auth/asadmin`, { method: 'POST', body: user })
185
+ } else {
186
+ await customFetch(`${options.directoryUrl}/api/auth/asadmin`, { method: 'DELETE' })
246
187
  }
247
- const session = {
248
- state,
249
- loginUrl,
250
- login,
251
- logout,
252
- switchOrganization,
253
- setAdminMode,
254
- asAdmin,
255
- cancelDeletion,
256
- keepalive,
257
- switchDark,
258
- switchLang,
259
- topLocation,
260
- options
261
- };
262
- return session;
188
+ readCookies()
189
+ }
190
+ const cancelDeletion = async () => {
191
+ if (state.user == null) { return }
192
+ await customFetch(`${options.directoryUrl}/api/users/${state.user.id}`, { method: 'PATCH', body: ({ plannedDeletion: null }) })
193
+ readCookies()
194
+ }
195
+ const switchDark = (value) => {
196
+ const maxAge = 60 * 60 * 24 * 365 // 1 year
197
+ cookies.set('theme_dark', `${value}`, { maxAge, path: cookiesPath })
198
+ readCookies()
199
+ }
200
+ const switchLang = (value) => {
201
+ const maxAge = 60 * 60 * 24 * 365 // 1 year
202
+ cookies.set('i18n_lang', value, { maxAge, path: cookiesPath })
203
+ readCookies()
204
+ }
205
+ const keepalive = async () => {
206
+ if (state.user == null) { return }
207
+ window.localStorage.setItem('sd-keepalive' + options.sitePath, `${new Date().getTime()}`)
208
+ await customFetch(`${options.directoryUrl}/api/auth/keepalive`, { method: 'POST' })
209
+ readCookies()
210
+ }
211
+ // immediately performs a keepalive, but only on top windows (not iframes or popups)
212
+ // and only if it was not done very recently (maybe from a refreshed page next to this one)
213
+ if (!ssr && window.top === window.self) {
214
+ const lastKeepalive = window.localStorage.getItem('sd-keepalive' + options.sitePath)
215
+ if (!lastKeepalive || (new Date().getTime() - Number(lastKeepalive)) > 10000) {
216
+ await keepalive()
217
+ }
218
+ }
219
+ const session = {
220
+ state,
221
+ loginUrl,
222
+ login,
223
+ logout,
224
+ switchOrganization,
225
+ setAdminMode,
226
+ asAdmin,
227
+ cancelDeletion,
228
+ keepalive,
229
+ switchDark,
230
+ switchLang,
231
+ topLocation,
232
+ options
233
+ }
234
+ return session
263
235
  }
264
236
  // uses pattern for SSR friendly plugin/composable, cf https://antfu.me/posts/composable-vue-vueday-2021#shared-state-ssr-friendly
265
- export const sessionKey = Symbol('session');
266
- export async function createSession(initOptions) {
267
- const session = await getSession(initOptions);
268
- return {
269
- install(app) { app.provide(sessionKey, session); },
270
- value: session
271
- };
237
+ export const sessionKey = Symbol('session')
238
+ export async function createSession (initOptions) {
239
+ const session = await getSession(initOptions)
240
+ return {
241
+ install (app) { app.provide(sessionKey, session) },
242
+ value: session
243
+ }
272
244
  }
273
- export function useSession() {
274
- const session = inject(sessionKey);
275
- if (!session)
276
- throw new Error('useSession requires using the plugin createSession');
277
- return session;
245
+ export function useSession () {
246
+ const session = inject(sessionKey)
247
+ if (!session) { throw new Error('useSession requires using the plugin createSession') }
248
+ return session
278
249
  }
279
- export function useSessionAuthenticated(errorBuilder) {
280
- const session = useSession();
281
- if (!session.state.user) {
282
- if (errorBuilder)
283
- throw errorBuilder();
284
- else
285
- throw new Error('useSessionAuthenticated requires a logged in user');
286
- }
287
- return session;
250
+ export function useSessionAuthenticated (errorBuilder) {
251
+ const session = useSession()
252
+ if (!session.state.user) {
253
+ if (errorBuilder) { throw errorBuilder() } else { throw new Error('useSessionAuthenticated requires a logged in user') }
254
+ }
255
+ return session
288
256
  }
289
- export default useSession;
257
+ export default useSession
@@ -1,7 +1,7 @@
1
1
  // same as use-ui-notif.js but in a module level singleton for convenience when not using SSR
2
- import { getUiNotif } from './ui-notif.js';
2
+ import { getUiNotif } from './ui-notif.js'
3
3
  // @ts-ignore
4
4
  if (import.meta.env?.SSR) {
5
- throw new Error('this module uses a module level singleton, it cannot be used in SSR mode');
5
+ throw new Error('this module uses a module level singleton, it cannot be used in SSR mode')
6
6
  }
7
- export default getUiNotif();
7
+ export default getUiNotif()
package/ui-notif.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Ref, App } from 'vue';
1
+ import type { App } from 'vue';
2
2
  export type UiNotif = UiNotifBase | UiNotifError;
3
3
  export interface PartialUiNotif {
4
4
  type?: 'default' | 'info' | 'success' | 'warning' | 'error';
@@ -17,7 +17,7 @@ interface UiNotifError {
17
17
  errorMsg: string;
18
18
  }
19
19
  export declare const getUiNotif: () => {
20
- current: Ref<{
20
+ current: import("vue").Ref<{
21
21
  type?: ("default" | "info" | "success" | "warning") | undefined;
22
22
  msg: string;
23
23
  } | {
package/ui-notif.js CHANGED
@@ -1,38 +1,35 @@
1
- import { ref, inject } from 'vue';
2
- import inIframe from '@data-fair/lib-utils/in-iframe.js';
3
- function getFullNotif(notif) {
4
- if (typeof notif === 'string')
5
- return { msg: notif, type: 'default' };
6
- if (notif.error) {
7
- notif.type = 'error';
8
- notif.errorMsg = (notif.error.response && (notif.error.response.data || notif.error.response.status)) || notif.error.message || notif.error;
9
- }
10
- notif.type = notif.type || 'default';
11
- if (inIframe) {
12
- window.top?.postMessage({ vIframe: true, uiNotification: notif }, '*');
13
- }
14
- else {
15
- console.log('notification', notif);
16
- }
17
- throw new Error('invalid UI notification');
1
+ import { ref, inject } from 'vue'
2
+ import inIframe from '@data-fair/lib-utils/in-iframe.js'
3
+ function getFullNotif (notif) {
4
+ if (typeof notif === 'string') { return { msg: notif, type: 'default' } }
5
+ if (notif.error) {
6
+ notif.type = 'error'
7
+ notif.errorMsg = (notif.error.response && (notif.error.response.data || notif.error.response.status)) || notif.error.message || notif.error
8
+ }
9
+ notif.type = notif.type || 'default'
10
+ if (inIframe) {
11
+ window.top?.postMessage({ vIframe: true, uiNotification: notif }, '*')
12
+ } else {
13
+ console.log('notification', notif)
14
+ }
15
+ throw new Error('invalid UI notification')
18
16
  }
19
17
  export const getUiNotif = () => {
20
- const current = ref(null);
21
- const send = (partialNotif) => {
22
- current.value = getFullNotif(partialNotif);
23
- };
24
- return { current, send };
25
- };
18
+ const current = ref(null)
19
+ const send = (partialNotif) => {
20
+ current.value = getFullNotif(partialNotif)
21
+ }
22
+ return { current, send }
23
+ }
26
24
  // uses pattern for SSR friendly plugin/composable, cf https://antfu.me/posts/composable-vue-vueday-2021#shared-state-ssr-friendly
27
- export const uiNotifKey = Symbol('uiNotif');
28
- export function createUiNotif() {
29
- const uiNotif = getUiNotif();
30
- return { install(app) { app.provide(uiNotifKey, uiNotif); } };
25
+ export const uiNotifKey = Symbol('uiNotif')
26
+ export function createUiNotif () {
27
+ const uiNotif = getUiNotif()
28
+ return { install (app) { app.provide(uiNotifKey, uiNotif) } }
31
29
  }
32
- export function useUiNotif() {
33
- const session = inject(uiNotifKey);
34
- if (!session)
35
- throw new Error('useUiNotif requires using the plugin createUiNotif');
36
- return session;
30
+ export function useUiNotif () {
31
+ const session = inject(uiNotifKey)
32
+ if (!session) { throw new Error('useUiNotif requires using the plugin createUiNotif') }
33
+ return session
37
34
  }
38
- export default useUiNotif;
35
+ export default useUiNotif