iconograph-ui 2.0.0 → 2.0.2

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/index.js CHANGED
@@ -1,32 +1,32 @@
1
- import FormButton from "./lib/form/FormButton.svelte";
2
- import Form from "./lib/form/Form.svelte"
3
- import Input from "./lib/form/Input.svelte"
4
- import SexeChoiceInput from "./lib/form/SexeChoiceInput.svelte";
5
- import BodySection from "./lib/layout/BodySection.svelte"
6
- import HeadSection from "./lib/layout/HeadSection.svelte"
7
- import Card from "./lib/layout/Card.svelte"
8
- import MainMenu from "./lib/navigation/MainMenu.svelte"
9
- import Button from "./lib/navigation/Button.svelte"
10
- import Notification from "./lib/notification/Notification.svelte";
11
- import NotificationWrapper from "./lib/notification/NotificationWrapper.svelte";
12
- import { addNotification } from "./lib/notification/NotificationWrapper.svelte";
13
- import Table from "./lib/table/Table.svelte"
14
- import CellLink from "./lib/table/CellLink.svelte"
15
- import UserPicture from "./lib/user/UserPicture.svelte";
16
- import NavBar from "./lib/navigation/NavBar.svelte";
17
- import SelectUserInput from "./lib/user/SelectUserInput.svelte";
18
- import Modal from "./lib/layout/Modal.svelte"
19
- import FlexForm from "./lib/form/FlexForm.svelte";
20
- import Editor from "./lib/inputs/Editor.svelte";
21
- import SegmentedSwitchInput from "./lib/form/SegmentedSwitchInput.svelte"
22
- import DateStr from "./lib/display/DateStr.svelte";
23
- import Field from "./lib/display/Field.svelte";
24
- import Link from "./lib/display/Link.svelte";
25
- import ActionButton from "./lib/form/ActionButton.svelte";
26
- import MultiSelect from "./lib/form/MultiSelect.svelte";
27
- import PasswordInput from "./lib/inputs/PasswordInput.svelte";
28
- import SearchSelect from "./lib/inputs/SearchSelect.svelte"
29
- import Checkbox from "./lib/form/Checkbox.svelte"
1
+ import FormButton from "./lib/components/form/FormButton.svelte";
2
+ import Form from "./lib/components/form/Form.svelte"
3
+ import Input from "./lib/components/form/Input.svelte"
4
+ import SexeChoiceInput from "./lib/components/form/SexeChoiceInput.svelte";
5
+ import BodySection from "./lib/components/layout/BodySection.svelte"
6
+ import HeadSection from "./lib/components/layout/HeadSection.svelte"
7
+ import Card from "./lib/components/layout/Card.svelte"
8
+ import MainMenu from "./lib/components/navigation/MainMenu.svelte"
9
+ import Button from "./lib/components/navigation/Button.svelte"
10
+ import Notification from "./lib/components/notification/Notification.svelte";
11
+ import NotificationWrapper from "./lib/components/notification/NotificationWrapper.svelte";
12
+ import { addNotification } from "./lib/components/notification/NotificationWrapper.svelte";
13
+ import Table from "./lib/components/table/Table.svelte"
14
+ import CellLink from "./lib/components/table/CellLink.svelte"
15
+ import UserPicture from "./lib/components/user/UserPicture.svelte";
16
+ import NavBar from "./lib/components/navigation/NavBar.svelte";
17
+ import SelectUserInput from "./lib/components/user/SelectUserInput.svelte";
18
+ import Modal from "./lib/components/layout/Modal.svelte"
19
+ import FlexForm from "./lib/components/form/FlexForm.svelte";
20
+ import Editor from "./lib/components/inputs/Editor.svelte";
21
+ import SegmentedSwitchInput from "./lib/components/form/SegmentedSwitchInput.svelte"
22
+ import DateStr from "./lib/components/display/DateStr.svelte";
23
+ import Field from "./lib/components/display/Field.svelte";
24
+ import Link from "./lib/components/display/Link.svelte";
25
+ import ActionButton from "./lib/components/form/ActionButton.svelte";
26
+ import MultiSelect from "./lib/components/form/MultiSelect.svelte";
27
+ import PasswordInput from "./lib/components/inputs/PasswordInput.svelte";
28
+ import SearchSelect from "./lib/components/inputs/SearchSelect.svelte"
29
+ import Checkbox from "./lib/components/form/Checkbox.svelte"
30
30
 
31
31
  export {
32
32
  Button,
@@ -0,0 +1,202 @@
1
+ // @ts-nocheck
2
+ import { error, redirect } from '@sveltejs/kit';
3
+ import { dev } from '$app/environment';
4
+ import { APIError } from './APIErrors';
5
+
6
+ const API_DATA_HOST = process.env.API_DATA_HOST ?? 'srvc-data';
7
+ const API_DATA_PORT = process.env.API_DATA_PORT ?? '3030';
8
+ const API_BASE_URL = `http://${API_DATA_HOST}:${API_DATA_PORT}`;
9
+
10
+ const ACCESS_COOKIE = 'accessToken';
11
+ const REFRESH_COOKIE = 'refreshToken';
12
+
13
+ export class APIClient {
14
+ constructor(cookies) {
15
+ this.cookies = cookies;
16
+ }
17
+
18
+ setAuthCookies(accessToken, refreshToken) {
19
+ this.cookies.set(ACCESS_COOKIE, accessToken, {
20
+ httpOnly: true,
21
+ secure: !dev,
22
+ sameSite: 'lax',
23
+ path: '/',
24
+ maxAge: 15 * 60 // 15 minutes
25
+ });
26
+
27
+ this.cookies.set(REFRESH_COOKIE, refreshToken, {
28
+ httpOnly: true,
29
+ secure: !dev,
30
+ sameSite: 'strict',
31
+ path: '/',
32
+ maxAge: 30 * 24 * 60 * 60 // 30 days
33
+ });
34
+ }
35
+
36
+ clearAuthCookies() {
37
+ this.cookies.delete(ACCESS_COOKIE, { path: '/' });
38
+ this.cookies.delete(REFRESH_COOKIE, { path: '/' });
39
+ }
40
+
41
+ getAccessToken() {
42
+ return this.cookies.get(ACCESS_COOKIE);
43
+ }
44
+
45
+ async parseJSONSafe(response, resource) {
46
+ const text = await response.text();
47
+
48
+ try {
49
+ return JSON.parse(text);
50
+ }
51
+ catch {
52
+ throw new APIError('Invalid JSON received from API', 502, resource);
53
+ }
54
+ }
55
+
56
+ isUnauthenticated(json) {
57
+ const err = json?.errors?.[0];
58
+ if (!err) return false;
59
+
60
+ return (
61
+ err.extensions?.code === 'UNAUTHENTICATED' ||
62
+ err.message === 'Not authenticated'
63
+ );
64
+ }
65
+
66
+ async refreshTokens() {
67
+ const refreshToken = this.cookies.get(REFRESH_COOKIE);
68
+ if (!refreshToken) return false;
69
+
70
+ const response = await fetch(`${API_BASE_URL}/graphql`, {
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json' },
73
+ body: JSON.stringify({
74
+ query: `
75
+ mutation Refresh($refreshToken: String!) {
76
+ refreshTokens(refreshToken: $refreshToken) {
77
+ accessToken
78
+ refreshToken
79
+ }
80
+ }
81
+ `,
82
+ variables: { refreshToken }
83
+ })
84
+ });
85
+
86
+ const json = await this.parseJSONSafe(response, '/graphql');
87
+
88
+ if (!response.ok || json.errors?.length || !json.data?.refreshTokens) {
89
+ this.clearAuthCookies();
90
+ return false;
91
+ }
92
+
93
+ const tokens = json.data.refreshTokens;
94
+ this.setAuthCookies(tokens.accessToken, tokens.refreshToken);
95
+ return true;
96
+ }
97
+
98
+ async fetchWithAutoRefresh(uri, options, retry = true) {
99
+ const response = await fetch(`${API_BASE_URL}${uri}`, options);
100
+ const json = await this.parseJSONSafe(response, uri);
101
+
102
+ if (this.isUnauthenticated(json) && retry) {
103
+ const refreshed = await this.refreshTokens();
104
+
105
+ if (!refreshed)
106
+ throw new APIError('Not authenticated', 401, uri);
107
+
108
+ const newJwt = this.getAccessToken();
109
+
110
+ return this.fetchWithAutoRefresh(uri, {
111
+ ...options,
112
+ headers: {
113
+ ...(options.headers ?? {}),
114
+ Authorization: `Bearer ${newJwt}`
115
+ }
116
+ },
117
+ false
118
+ );
119
+ }
120
+
121
+ return { response, json };
122
+ }
123
+
124
+ async request(method, uri, body = null) {
125
+ const jwt = this.getAccessToken();
126
+
127
+ const headers = {
128
+ Accept: 'application/json',
129
+ 'Content-Type': 'application/json',
130
+ ...(jwt ? { Authorization: `Bearer ${jwt}` } : {})
131
+ };
132
+
133
+ try {
134
+ const { json } = await this.fetchWithAutoRefresh(uri, {
135
+ method,
136
+ headers,
137
+ body: method === 'GET' || method === 'HEAD' ? undefined : JSON.stringify(body)
138
+ });
139
+
140
+ if (json.errors?.length) {
141
+ const err = json.errors[0];
142
+ const code = err.extensions?.code;
143
+
144
+ switch (code ?? err.message) {
145
+ case 'UNAUTHENTICATED':
146
+ case 'Not authenticated':
147
+ throw new APIError(err.message, 401, uri);
148
+ case 'FORBIDDEN':
149
+ case 'Unauthorized':
150
+ throw new APIError(err.message, 403, uri);
151
+ case 'Invalid credentials':
152
+ throw new APIError('Identifiants non valides', 401, uri);
153
+ default:
154
+ throw new APIError(err.message, 500, uri);
155
+ }
156
+ }
157
+
158
+ return json;
159
+ }
160
+ catch (err) {
161
+ if (err instanceof APIError)
162
+ throw err;
163
+
164
+ if (err?.message === 'fetch failed') {
165
+ throw error(503, {
166
+ message: "Le serveur de données n'est pas disponible (__SRVC_DATACORE_DOWN__)"
167
+ });
168
+ }
169
+
170
+ throw error(500, { message: 'Unexpected server error' });
171
+ }
172
+ }
173
+
174
+ async requestApi(method, uri, body = null) {
175
+ try {
176
+ return await this.request(method, uri, body);
177
+ } catch (err) {
178
+ if (err instanceof APIError) {
179
+ err.logError();
180
+ throw error(err.statusCode, { message: err.message });
181
+ }
182
+ throw err;
183
+ }
184
+ }
185
+
186
+ async loadApi(method, uri, body = null) {
187
+ try {
188
+ return await this.request(method, uri, body);
189
+ } catch (err) {
190
+ if (err instanceof APIError) {
191
+ err.logError();
192
+
193
+ if (err.statusCode === 401) {
194
+ throw redirect(307, '/auth/signout?reason=Expired');
195
+ }
196
+
197
+ throw error(err.statusCode, { message: err.message });
198
+ }
199
+ throw err;
200
+ }
201
+ }
202
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iconograph-ui",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "A Svelte Kit components library",
5
5
  "main": "./index.js",
6
6
  "svelte": "./index.js",