@inwink-mcp/backoffice-extension-helpers 1.0.1 → 1.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/dist/cli.js CHANGED
@@ -252,7 +252,8 @@ function bootstrap(opts) {
252
252
  error(`La cible existe et n'est pas un dossier : ${destDir}`);
253
253
  return 1;
254
254
  }
255
- const entries = fs3.readdirSync(destDir);
255
+ const IGNORED_DIRS = /* @__PURE__ */ new Set([".claude", ".github", ".git", ".vscode", ".idea", ".cursor"]);
256
+ const entries = fs3.readdirSync(destDir).filter((e) => !IGNORED_DIRS.has(e));
256
257
  if (entries.length > 0 && !opts.force) {
257
258
  error(`Le dossier cible n'est pas vide : ${destDir}`);
258
259
  info("Utilisez --force pour \xE9crire malgr\xE9 tout, ou --dir vers un dossier vide.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inwink-mcp/backoffice-extension-helpers",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "author": "inwink",
5
5
  "license": "ISC",
6
6
  "description": "CLI pour bootstrapper, packager et vérifier les extensions backoffice inwink.",
@@ -1,5 +1,5 @@
1
1
  // src/auth/AuthProvider.tsx
2
- import { createContext, useEffect, useState, type PropsWithChildren } from 'react';
2
+ import { createContext, useCallback, useEffect, useMemo, useRef, useState, type PropsWithChildren } from 'react';
3
3
  import type { User } from 'oidc-client-ts';
4
4
  import { userManager } from './config';
5
5
 
@@ -24,6 +24,8 @@ function isOidcCallback(): boolean {
24
24
  export function AuthProvider({ children }: PropsWithChildren) {
25
25
  const [user, setUser] = useState<User | null>(null);
26
26
  const [isLoading, setIsLoading] = useState(true);
27
+ const userRef = useRef(user);
28
+ useEffect(() => { userRef.current = user; }, [user]);
27
29
 
28
30
  useEffect(() => {
29
31
  let isMounted = true;
@@ -56,24 +58,40 @@ export function AuthProvider({ children }: PropsWithChildren) {
56
58
  };
57
59
  }, []);
58
60
 
61
+ const login = useCallback(async () => {
62
+ setIsLoading(true);
63
+ await userManager.signinRedirect();
64
+ }, []);
65
+
66
+ const logout = useCallback(async () => {
67
+ await userManager.removeUser();
68
+ setUser(null);
69
+ }, []);
70
+
71
+ const getAccessToken = useCallback(() => userRef.current?.access_token ?? null, []);
72
+
73
+ const renewToken = useCallback(async () => {
74
+ try {
75
+ const renewed = await userManager.signinSilent();
76
+ setUser(renewed);
77
+ return renewed?.access_token ?? null;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }, []);
82
+
83
+ const value = useMemo(() => ({
84
+ isAuthenticated: Boolean(user && !user.expired),
85
+ isLoading,
86
+ login,
87
+ logout,
88
+ user,
89
+ getAccessToken,
90
+ renewToken,
91
+ }), [user, isLoading]);
92
+
59
93
  return (
60
- <AuthContext.Provider value={{
61
- isAuthenticated: Boolean(user && !user.expired),
62
- isLoading,
63
- login: async () => { setIsLoading(true); await userManager.signinRedirect(); },
64
- logout: async () => { await userManager.removeUser(); setUser(null); },
65
- user,
66
- getAccessToken: () => user?.access_token ?? null,
67
- renewToken: async () => {
68
- try {
69
- const renewed = await userManager.signinSilent();
70
- setUser(renewed);
71
- return renewed?.access_token ?? null;
72
- } catch {
73
- return null;
74
- }
75
- },
76
- }}>
94
+ <AuthContext.Provider value={value}>
77
95
  {children}
78
96
  </AuthContext.Provider>
79
97
  );
@@ -1,3 +1,4 @@
1
+ import { useMemo } from 'react';
1
2
  import { useAuth } from './useAuth';
2
3
 
3
4
  /** Erreur levée lorsqu'une réponse API n'est pas OK (hors 401 géré par renouvellement). */
@@ -38,64 +39,66 @@ async function safeBody(response: Response): Promise<unknown> {
38
39
  export function useInwinkApi(baseUrl: string): InwinkApi {
39
40
  const { getAccessToken, renewToken, login } = useAuth();
40
41
 
41
- async function doFetch(path: string, token: string, init?: RequestInit): Promise<Response> {
42
- return fetch(`${baseUrl}${path}`, {
43
- ...init,
44
- headers: {
45
- Authorization: `Bearer ${token}`,
46
- 'Content-Type': 'application/json',
47
- ...(init?.headers as Record<string, string> | undefined),
48
- },
49
- });
50
- }
51
-
52
- async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {
53
- let token = getAccessToken();
54
- if (!token) {
55
- await login();
56
- throw new InwinkApiError(401, 'Non authentifié.');
42
+ return useMemo(() => {
43
+ async function doFetch(path: string, token: string, init?: RequestInit): Promise<Response> {
44
+ return fetch(`${baseUrl}${path}`, {
45
+ ...init,
46
+ headers: {
47
+ Authorization: `Bearer ${token}`,
48
+ 'Content-Type': 'application/json',
49
+ ...(init?.headers as Record<string, string> | undefined),
50
+ },
51
+ });
57
52
  }
58
53
 
59
- let response = await doFetch(path, token, init);
60
-
61
- if (response.status === 401) {
62
- const renewed = await renewToken();
63
- if (renewed) {
64
- token = renewed;
65
- response = await doFetch(path, token, init);
54
+ async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {
55
+ let token = getAccessToken();
56
+ if (!token) {
57
+ await login();
58
+ throw new InwinkApiError(401, 'Non authentifié.');
66
59
  }
60
+
61
+ let response = await doFetch(path, token, init);
62
+
67
63
  if (response.status === 401) {
68
- await login();
69
- throw new InwinkApiError(401, 'Session expirée, reconnexion requise.');
64
+ const renewed = await renewToken();
65
+ if (renewed) {
66
+ token = renewed;
67
+ response = await doFetch(path, token, init);
68
+ }
69
+ if (response.status === 401) {
70
+ await login();
71
+ throw new InwinkApiError(401, 'Session expirée, reconnexion requise.');
72
+ }
70
73
  }
71
- }
72
74
 
73
- if (!response.ok) {
74
- throw new InwinkApiError(response.status, await safeBody(response));
75
- }
75
+ if (!response.ok) {
76
+ throw new InwinkApiError(response.status, await safeBody(response));
77
+ }
76
78
 
77
- if (response.status === 204) {
78
- return undefined as T;
79
+ if (response.status === 204) {
80
+ return undefined as T;
81
+ }
82
+ return (await response.json()) as T;
79
83
  }
80
- return (await response.json()) as T;
81
- }
82
84
 
83
- const withBody =
84
- (method: string) =>
85
- <T = unknown>(path: string, body?: unknown, init?: RequestInit): Promise<T> =>
86
- request<T>(path, {
87
- ...init,
88
- method,
89
- body: body === undefined ? init?.body : JSON.stringify(body),
90
- });
85
+ const withBody =
86
+ (method: string) =>
87
+ <T = unknown>(path: string, body?: unknown, init?: RequestInit): Promise<T> =>
88
+ request<T>(path, {
89
+ ...init,
90
+ method,
91
+ body: body === undefined ? init?.body : JSON.stringify(body),
92
+ });
91
93
 
92
- return {
93
- request,
94
- get: <T = unknown>(path: string, init?: RequestInit) =>
95
- request<T>(path, { ...init, method: 'GET' }),
96
- post: withBody('POST'),
97
- put: withBody('PUT'),
98
- del: <T = unknown>(path: string, init?: RequestInit) =>
99
- request<T>(path, { ...init, method: 'DELETE' }),
100
- };
94
+ return {
95
+ request,
96
+ get: <T = unknown>(path: string, init?: RequestInit) =>
97
+ request<T>(path, { ...init, method: 'GET' }),
98
+ post: withBody('POST'),
99
+ put: withBody('PUT'),
100
+ del: <T = unknown>(path: string, init?: RequestInit) =>
101
+ request<T>(path, { ...init, method: 'DELETE' }),
102
+ };
103
+ }, [baseUrl, getAccessToken, renewToken, login]);
101
104
  }