@famgia/omnify-client-sso-react 1.0.0

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/index.mjs ADDED
@@ -0,0 +1,611 @@
1
+ // src/context/SsoContext.tsx
2
+ import { createContext, useContext } from "react";
3
+ var SsoContext = createContext(null);
4
+ function useSsoContext() {
5
+ const context = useContext(SsoContext);
6
+ if (!context) {
7
+ throw new Error("useSsoContext must be used within a SsoProvider");
8
+ }
9
+ return context;
10
+ }
11
+
12
+ // src/context/SsoProvider.tsx
13
+ import { useCallback, useEffect, useMemo, useState } from "react";
14
+ import { jsx } from "react/jsx-runtime";
15
+ function transformUser(data) {
16
+ return {
17
+ id: data.id,
18
+ consoleUserId: data.console_user_id,
19
+ email: data.email,
20
+ name: data.name
21
+ };
22
+ }
23
+ function transformOrganizations(data) {
24
+ return data.map((org) => ({
25
+ id: org.organization_id,
26
+ slug: org.organization_slug,
27
+ name: org.organization_name,
28
+ orgRole: org.org_role,
29
+ serviceRole: org.service_role
30
+ }));
31
+ }
32
+ function getStorage(type) {
33
+ if (typeof window === "undefined") return null;
34
+ return type === "localStorage" ? window.localStorage : window.sessionStorage;
35
+ }
36
+ function getXsrfToken() {
37
+ if (typeof document === "undefined") return void 0;
38
+ return document.cookie.split("; ").find((row) => row.startsWith("XSRF-TOKEN="))?.split("=")[1];
39
+ }
40
+ function SsoProvider({ children, config, onAuthChange }) {
41
+ const [user, setUser] = useState(null);
42
+ const [organizations, setOrganizations] = useState([]);
43
+ const [currentOrg, setCurrentOrg] = useState(null);
44
+ const [isLoading, setIsLoading] = useState(true);
45
+ const storageKey = config.storageKey ?? "sso_selected_org";
46
+ const storage = getStorage(config.storage ?? "localStorage");
47
+ const loadSelectedOrg = useCallback(
48
+ (orgs) => {
49
+ if (!storage || orgs.length === 0) return null;
50
+ const savedSlug = storage.getItem(storageKey);
51
+ if (savedSlug) {
52
+ const found = orgs.find((o) => o.slug === savedSlug);
53
+ if (found) return found;
54
+ }
55
+ return orgs[0];
56
+ },
57
+ [storage, storageKey]
58
+ );
59
+ const saveSelectedOrg = useCallback(
60
+ (org) => {
61
+ if (!storage) return;
62
+ if (org) {
63
+ storage.setItem(storageKey, org.slug);
64
+ } else {
65
+ storage.removeItem(storageKey);
66
+ }
67
+ },
68
+ [storage, storageKey]
69
+ );
70
+ const fetchUser = useCallback(async () => {
71
+ try {
72
+ const xsrfToken = getXsrfToken();
73
+ const headers = {
74
+ "Accept": "application/json"
75
+ };
76
+ if (xsrfToken) {
77
+ headers["X-XSRF-TOKEN"] = decodeURIComponent(xsrfToken);
78
+ }
79
+ const response = await fetch(`${config.apiUrl}/api/sso/user`, {
80
+ headers,
81
+ credentials: "include"
82
+ });
83
+ if (!response.ok) {
84
+ return null;
85
+ }
86
+ const data = await response.json();
87
+ const transformedUser = transformUser(data.user);
88
+ const transformedOrgs = transformOrganizations(data.organizations);
89
+ return { user: transformedUser, organizations: transformedOrgs };
90
+ } catch {
91
+ return null;
92
+ }
93
+ }, [config.apiUrl]);
94
+ useEffect(() => {
95
+ let mounted = true;
96
+ const init = async () => {
97
+ setIsLoading(true);
98
+ const result = await fetchUser();
99
+ if (!mounted) return;
100
+ if (result) {
101
+ setUser(result.user);
102
+ setOrganizations(result.organizations);
103
+ const selectedOrg = loadSelectedOrg(result.organizations);
104
+ setCurrentOrg(selectedOrg);
105
+ onAuthChange?.(true, result.user);
106
+ } else {
107
+ setUser(null);
108
+ setOrganizations([]);
109
+ setCurrentOrg(null);
110
+ onAuthChange?.(false, null);
111
+ }
112
+ setIsLoading(false);
113
+ };
114
+ init();
115
+ return () => {
116
+ mounted = false;
117
+ };
118
+ }, [fetchUser, loadSelectedOrg, onAuthChange]);
119
+ const login = useCallback(
120
+ (redirectTo) => {
121
+ const callbackUrl = new URL("/sso/callback", window.location.origin);
122
+ if (redirectTo) {
123
+ callbackUrl.searchParams.set("redirect", redirectTo);
124
+ }
125
+ const loginUrl = new URL("/sso/authorize", config.consoleUrl);
126
+ loginUrl.searchParams.set("service", config.serviceSlug);
127
+ loginUrl.searchParams.set("redirect_uri", callbackUrl.toString());
128
+ window.location.href = loginUrl.toString();
129
+ },
130
+ [config.consoleUrl, config.serviceSlug]
131
+ );
132
+ const logout = useCallback(async () => {
133
+ try {
134
+ const xsrfToken = getXsrfToken();
135
+ const headers = {};
136
+ if (xsrfToken) {
137
+ headers["X-XSRF-TOKEN"] = decodeURIComponent(xsrfToken);
138
+ }
139
+ await fetch(`${config.apiUrl}/api/sso/logout`, {
140
+ method: "POST",
141
+ headers,
142
+ credentials: "include"
143
+ });
144
+ } catch {
145
+ }
146
+ setUser(null);
147
+ setOrganizations([]);
148
+ setCurrentOrg(null);
149
+ saveSelectedOrg(null);
150
+ onAuthChange?.(false, null);
151
+ }, [config.apiUrl, saveSelectedOrg, onAuthChange]);
152
+ const globalLogout = useCallback(
153
+ async (redirectTo) => {
154
+ await logout();
155
+ const redirectUri = redirectTo ?? window.location.origin;
156
+ const logoutUrl = new URL("/sso/logout", config.consoleUrl);
157
+ logoutUrl.searchParams.set("redirect_uri", redirectUri);
158
+ window.location.href = logoutUrl.toString();
159
+ },
160
+ [logout, config.consoleUrl]
161
+ );
162
+ const switchOrg = useCallback(
163
+ (orgSlug) => {
164
+ const org = organizations.find((o) => o.slug === orgSlug);
165
+ if (org) {
166
+ setCurrentOrg(org);
167
+ saveSelectedOrg(org);
168
+ }
169
+ },
170
+ [organizations, saveSelectedOrg]
171
+ );
172
+ const refreshUser = useCallback(async () => {
173
+ const result = await fetchUser();
174
+ if (result) {
175
+ setUser(result.user);
176
+ setOrganizations(result.organizations);
177
+ if (currentOrg) {
178
+ const stillValid = result.organizations.find((o) => o.slug === currentOrg.slug);
179
+ if (!stillValid) {
180
+ const newOrg = loadSelectedOrg(result.organizations);
181
+ setCurrentOrg(newOrg);
182
+ }
183
+ }
184
+ }
185
+ }, [fetchUser, currentOrg, loadSelectedOrg]);
186
+ const getHeaders = useCallback(() => {
187
+ const headers = {};
188
+ if (currentOrg) {
189
+ headers["X-Org-Id"] = currentOrg.slug;
190
+ }
191
+ return headers;
192
+ }, [currentOrg]);
193
+ const value = useMemo(
194
+ () => ({
195
+ user,
196
+ organizations,
197
+ currentOrg,
198
+ isLoading,
199
+ isAuthenticated: !!user,
200
+ config,
201
+ login,
202
+ logout,
203
+ globalLogout,
204
+ switchOrg,
205
+ refreshUser,
206
+ getHeaders
207
+ }),
208
+ [
209
+ user,
210
+ organizations,
211
+ currentOrg,
212
+ isLoading,
213
+ config,
214
+ login,
215
+ logout,
216
+ globalLogout,
217
+ switchOrg,
218
+ refreshUser,
219
+ getHeaders
220
+ ]
221
+ );
222
+ return /* @__PURE__ */ jsx(SsoContext.Provider, { value, children });
223
+ }
224
+
225
+ // src/hooks/useAuth.ts
226
+ import { useCallback as useCallback2 } from "react";
227
+ function useAuth() {
228
+ const { user, isLoading, isAuthenticated, login, logout, globalLogout, refreshUser } = useSsoContext();
229
+ const handleLogin = useCallback2(
230
+ (redirectTo) => {
231
+ login(redirectTo);
232
+ },
233
+ [login]
234
+ );
235
+ const handleLogout = useCallback2(async () => {
236
+ await logout();
237
+ }, [logout]);
238
+ const handleGlobalLogout = useCallback2(
239
+ (redirectTo) => {
240
+ globalLogout(redirectTo);
241
+ },
242
+ [globalLogout]
243
+ );
244
+ return {
245
+ user,
246
+ isLoading,
247
+ isAuthenticated,
248
+ login: handleLogin,
249
+ logout: handleLogout,
250
+ globalLogout: handleGlobalLogout,
251
+ refreshUser
252
+ };
253
+ }
254
+
255
+ // src/hooks/useOrganization.ts
256
+ import { useCallback as useCallback3, useMemo as useMemo2 } from "react";
257
+ var ROLE_LEVELS = {
258
+ admin: 100,
259
+ manager: 50,
260
+ member: 10
261
+ };
262
+ function useOrganization() {
263
+ const { organizations, currentOrg, switchOrg } = useSsoContext();
264
+ const hasMultipleOrgs = organizations.length > 1;
265
+ const currentRole = currentOrg?.serviceRole ?? null;
266
+ const hasRole = useCallback3(
267
+ (role) => {
268
+ if (!currentRole) return false;
269
+ const requiredLevel = ROLE_LEVELS[role] ?? 0;
270
+ const userLevel = ROLE_LEVELS[currentRole] ?? 0;
271
+ return userLevel >= requiredLevel;
272
+ },
273
+ [currentRole]
274
+ );
275
+ const handleSwitchOrg = useCallback3(
276
+ (orgSlug) => {
277
+ switchOrg(orgSlug);
278
+ },
279
+ [switchOrg]
280
+ );
281
+ return useMemo2(
282
+ () => ({
283
+ organizations,
284
+ currentOrg,
285
+ hasMultipleOrgs,
286
+ switchOrg: handleSwitchOrg,
287
+ currentRole,
288
+ hasRole
289
+ }),
290
+ [organizations, currentOrg, hasMultipleOrgs, handleSwitchOrg, currentRole, hasRole]
291
+ );
292
+ }
293
+
294
+ // src/hooks/useSso.ts
295
+ import { useMemo as useMemo3 } from "react";
296
+ function useSso() {
297
+ const context = useSsoContext();
298
+ return useMemo3(
299
+ () => ({
300
+ // Auth
301
+ user: context.user,
302
+ isLoading: context.isLoading,
303
+ isAuthenticated: context.isAuthenticated,
304
+ login: context.login,
305
+ logout: context.logout,
306
+ globalLogout: context.globalLogout,
307
+ refreshUser: context.refreshUser,
308
+ // Organization
309
+ organizations: context.organizations,
310
+ currentOrg: context.currentOrg,
311
+ hasMultipleOrgs: context.organizations.length > 1,
312
+ switchOrg: context.switchOrg,
313
+ // Utilities
314
+ getHeaders: context.getHeaders,
315
+ config: context.config
316
+ }),
317
+ [context]
318
+ );
319
+ }
320
+
321
+ // src/components/SsoCallback.tsx
322
+ import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
323
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
324
+ function transformUser2(data) {
325
+ return {
326
+ id: data.id,
327
+ consoleUserId: data.console_user_id,
328
+ email: data.email,
329
+ name: data.name
330
+ };
331
+ }
332
+ function transformOrganizations2(data) {
333
+ return data.map((org) => ({
334
+ id: org.organization_id,
335
+ slug: org.organization_slug,
336
+ name: org.organization_name,
337
+ orgRole: org.org_role,
338
+ serviceRole: org.service_role
339
+ }));
340
+ }
341
+ function DefaultLoading() {
342
+ return /* @__PURE__ */ jsx2("div", { style: {
343
+ display: "flex",
344
+ justifyContent: "center",
345
+ alignItems: "center",
346
+ minHeight: "200px"
347
+ }, children: /* @__PURE__ */ jsx2("div", { children: "Authenticating..." }) });
348
+ }
349
+ function DefaultError({ error }) {
350
+ return /* @__PURE__ */ jsxs("div", { style: {
351
+ display: "flex",
352
+ flexDirection: "column",
353
+ justifyContent: "center",
354
+ alignItems: "center",
355
+ minHeight: "200px",
356
+ color: "red"
357
+ }, children: [
358
+ /* @__PURE__ */ jsx2("div", { children: "Authentication Error" }),
359
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: "0.875rem", marginTop: "0.5rem" }, children: error.message })
360
+ ] });
361
+ }
362
+ function SsoCallback({
363
+ onSuccess,
364
+ onError,
365
+ redirectTo = "/",
366
+ loadingComponent,
367
+ errorComponent
368
+ }) {
369
+ const { config, refreshUser } = useSsoContext();
370
+ const [error, setError] = useState2(null);
371
+ const [isProcessing, setIsProcessing] = useState2(true);
372
+ const isProcessingRef = useRef(false);
373
+ useEffect2(() => {
374
+ if (isProcessingRef.current) {
375
+ return;
376
+ }
377
+ isProcessingRef.current = true;
378
+ const processCallback = async () => {
379
+ try {
380
+ const urlParams = new URLSearchParams(window.location.search);
381
+ const code = urlParams.get("code");
382
+ const redirectParam = urlParams.get("redirect");
383
+ if (!code) {
384
+ throw new Error("No authorization code received");
385
+ }
386
+ await fetch(`${config.apiUrl}/sanctum/csrf-cookie`, {
387
+ credentials: "include"
388
+ });
389
+ const xsrfToken = document.cookie.split("; ").find((row) => row.startsWith("XSRF-TOKEN="))?.split("=")[1];
390
+ const response = await fetch(`${config.apiUrl}/api/sso/callback`, {
391
+ method: "POST",
392
+ headers: {
393
+ "Content-Type": "application/json",
394
+ "Accept": "application/json",
395
+ ...xsrfToken ? { "X-XSRF-TOKEN": decodeURIComponent(xsrfToken) } : {}
396
+ },
397
+ credentials: "include",
398
+ body: JSON.stringify({ code })
399
+ });
400
+ if (!response.ok) {
401
+ const errorData = await response.json().catch(() => ({}));
402
+ throw new Error(errorData.message || "Failed to authenticate");
403
+ }
404
+ const data = await response.json();
405
+ const user = transformUser2(data.user);
406
+ const organizations = transformOrganizations2(data.organizations);
407
+ await refreshUser();
408
+ onSuccess?.(user, organizations);
409
+ const finalRedirect = redirectParam || redirectTo;
410
+ window.location.href = finalRedirect;
411
+ } catch (err) {
412
+ const error2 = err instanceof Error ? err : new Error("Authentication failed");
413
+ setError(error2);
414
+ onError?.(error2);
415
+ isProcessingRef.current = false;
416
+ } finally {
417
+ setIsProcessing(false);
418
+ }
419
+ };
420
+ processCallback();
421
+ }, []);
422
+ if (error) {
423
+ if (errorComponent) {
424
+ return /* @__PURE__ */ jsx2(Fragment, { children: errorComponent(error) });
425
+ }
426
+ return /* @__PURE__ */ jsx2(DefaultError, { error });
427
+ }
428
+ if (isProcessing) {
429
+ if (loadingComponent) {
430
+ return /* @__PURE__ */ jsx2(Fragment, { children: loadingComponent });
431
+ }
432
+ return /* @__PURE__ */ jsx2(DefaultLoading, {});
433
+ }
434
+ return null;
435
+ }
436
+
437
+ // src/components/OrganizationSwitcher.tsx
438
+ import React3, { useCallback as useCallback4, useMemo as useMemo4 } from "react";
439
+ import { Dropdown, Button, Space, Typography, Badge } from "antd";
440
+ import { SwapOutlined, CheckOutlined } from "@ant-design/icons";
441
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
442
+ var { Text } = Typography;
443
+ function OrganizationSwitcher({
444
+ className,
445
+ renderTrigger,
446
+ renderOption,
447
+ onChange
448
+ }) {
449
+ const { organizations, currentOrg, hasMultipleOrgs, switchOrg } = useOrganization();
450
+ const [isOpen, setIsOpen] = React3.useState(false);
451
+ const handleSelect = useCallback4(
452
+ (org) => {
453
+ switchOrg(org.slug);
454
+ setIsOpen(false);
455
+ onChange?.(org);
456
+ },
457
+ [switchOrg, onChange]
458
+ );
459
+ const menuItems = useMemo4(() => {
460
+ return organizations.map((org) => {
461
+ const isSelected = currentOrg?.slug === org.slug;
462
+ if (renderOption) {
463
+ return {
464
+ key: org.slug,
465
+ label: /* @__PURE__ */ jsx3("div", { onClick: () => handleSelect(org), children: renderOption(org, isSelected) })
466
+ };
467
+ }
468
+ return {
469
+ key: org.slug,
470
+ label: /* @__PURE__ */ jsxs2(Space, { style: { width: "100%", justifyContent: "space-between" }, children: [
471
+ /* @__PURE__ */ jsxs2(Space, { direction: "vertical", size: 0, children: [
472
+ /* @__PURE__ */ jsx3(Text, { strong: isSelected, children: org.name }),
473
+ org.serviceRole && /* @__PURE__ */ jsx3(Text, { type: "secondary", style: { fontSize: 12 }, children: org.serviceRole })
474
+ ] }),
475
+ isSelected && /* @__PURE__ */ jsx3(CheckOutlined, { style: { color: "#1890ff" } })
476
+ ] }),
477
+ onClick: () => handleSelect(org)
478
+ };
479
+ });
480
+ }, [organizations, currentOrg, renderOption, handleSelect]);
481
+ if (!hasMultipleOrgs) {
482
+ return null;
483
+ }
484
+ if (renderTrigger) {
485
+ return /* @__PURE__ */ jsx3(
486
+ Dropdown,
487
+ {
488
+ menu: { items: menuItems },
489
+ trigger: ["click"],
490
+ open: isOpen,
491
+ onOpenChange: setIsOpen,
492
+ className,
493
+ children: /* @__PURE__ */ jsx3("div", { style: { cursor: "pointer" }, children: renderTrigger(currentOrg, isOpen) })
494
+ }
495
+ );
496
+ }
497
+ return /* @__PURE__ */ jsx3(
498
+ Dropdown,
499
+ {
500
+ menu: { items: menuItems },
501
+ trigger: ["click"],
502
+ open: isOpen,
503
+ onOpenChange: setIsOpen,
504
+ className,
505
+ children: /* @__PURE__ */ jsx3(Button, { children: /* @__PURE__ */ jsxs2(Space, { children: [
506
+ /* @__PURE__ */ jsx3(Badge, { status: "success" }),
507
+ /* @__PURE__ */ jsx3("span", { children: currentOrg?.name ?? "Select Organization" }),
508
+ /* @__PURE__ */ jsx3(SwapOutlined, {})
509
+ ] }) })
510
+ }
511
+ );
512
+ }
513
+
514
+ // src/components/ProtectedRoute.tsx
515
+ import { useEffect as useEffect3 } from "react";
516
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
517
+ function DefaultLoading2() {
518
+ return /* @__PURE__ */ jsx4("div", { style: {
519
+ display: "flex",
520
+ justifyContent: "center",
521
+ alignItems: "center",
522
+ minHeight: "200px"
523
+ }, children: /* @__PURE__ */ jsx4("div", { children: "Loading..." }) });
524
+ }
525
+ function DefaultLoginFallback({ login }) {
526
+ return /* @__PURE__ */ jsxs3("div", { style: {
527
+ display: "flex",
528
+ flexDirection: "column",
529
+ justifyContent: "center",
530
+ alignItems: "center",
531
+ minHeight: "200px",
532
+ gap: "1rem"
533
+ }, children: [
534
+ /* @__PURE__ */ jsx4("div", { children: "Please log in to continue" }),
535
+ /* @__PURE__ */ jsx4(
536
+ "button",
537
+ {
538
+ onClick: login,
539
+ style: {
540
+ padding: "0.5rem 1rem",
541
+ background: "#0070f3",
542
+ color: "white",
543
+ border: "none",
544
+ borderRadius: "0.375rem",
545
+ cursor: "pointer"
546
+ },
547
+ children: "Log In"
548
+ }
549
+ )
550
+ ] });
551
+ }
552
+ function DefaultAccessDenied({ reason }) {
553
+ return /* @__PURE__ */ jsxs3("div", { style: {
554
+ display: "flex",
555
+ flexDirection: "column",
556
+ justifyContent: "center",
557
+ alignItems: "center",
558
+ minHeight: "200px",
559
+ color: "#dc2626"
560
+ }, children: [
561
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "1.5rem", fontWeight: 600 }, children: "Access Denied" }),
562
+ /* @__PURE__ */ jsx4("div", { style: { marginTop: "0.5rem" }, children: reason })
563
+ ] });
564
+ }
565
+ function ProtectedRoute({
566
+ children,
567
+ fallback,
568
+ loginFallback,
569
+ requiredRole,
570
+ requiredPermission,
571
+ onAccessDenied
572
+ }) {
573
+ const { user, isLoading, isAuthenticated, login } = useAuth();
574
+ const { hasRole, currentOrg } = useOrganization();
575
+ useEffect3(() => {
576
+ if (isLoading) return;
577
+ if (!isAuthenticated) {
578
+ onAccessDenied?.("unauthenticated");
579
+ } else if (requiredRole && !hasRole(requiredRole)) {
580
+ onAccessDenied?.("insufficient_role");
581
+ }
582
+ }, [isLoading, isAuthenticated, requiredRole, hasRole, onAccessDenied]);
583
+ if (isLoading) {
584
+ return /* @__PURE__ */ jsx4(Fragment2, { children: fallback ?? /* @__PURE__ */ jsx4(DefaultLoading2, {}) });
585
+ }
586
+ if (!isAuthenticated) {
587
+ if (loginFallback) {
588
+ return /* @__PURE__ */ jsx4(Fragment2, { children: loginFallback });
589
+ }
590
+ return /* @__PURE__ */ jsx4(DefaultLoginFallback, { login: () => login() });
591
+ }
592
+ if (requiredRole && !hasRole(requiredRole)) {
593
+ return /* @__PURE__ */ jsx4(
594
+ DefaultAccessDenied,
595
+ {
596
+ reason: `This page requires ${requiredRole} role. Your role: ${currentOrg?.serviceRole ?? "none"}`
597
+ }
598
+ );
599
+ }
600
+ return /* @__PURE__ */ jsx4(Fragment2, { children });
601
+ }
602
+ export {
603
+ OrganizationSwitcher,
604
+ ProtectedRoute,
605
+ SsoCallback,
606
+ SsoContext,
607
+ SsoProvider,
608
+ useAuth,
609
+ useOrganization,
610
+ useSso
611
+ };
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@famgia/omnify-client-sso-react",
3
+ "version": "1.0.0",
4
+ "description": "React components and hooks for Omnify SSO integration",
5
+ "keywords": [
6
+ "react",
7
+ "sso",
8
+ "authentication",
9
+ "omnify",
10
+ "laravel"
11
+ ],
12
+ "author": "Omnify Team <dev@omnify.jp>",
13
+ "license": "MIT",
14
+ "main": "dist/index.js",
15
+ "module": "dist/index.mjs",
16
+ "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "sideEffects": false,
28
+ "scripts": {
29
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
30
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
31
+ "lint": "eslint src/",
32
+ "typecheck": "tsc --noEmit",
33
+ "test": "vitest",
34
+ "test:run": "vitest run",
35
+ "test:coverage": "vitest run --coverage",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "peerDependencies": {
39
+ "react": "^18.0.0 || ^19.0.0",
40
+ "react-dom": "^18.0.0 || ^19.0.0",
41
+ "antd": "^5.0.0 || ^6.0.0",
42
+ "@ant-design/icons": "^5.0.0 || ^6.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@ant-design/icons": "^6.0.0",
46
+ "@testing-library/jest-dom": "^6.4.0",
47
+ "@testing-library/react": "^14.2.0",
48
+ "@types/react": "^18.2.0",
49
+ "@types/react-dom": "^18.2.0",
50
+ "@vitejs/plugin-react": "^4.2.0",
51
+ "antd": "^6.0.0",
52
+ "happy-dom": "^13.0.0",
53
+ "react": "^18.2.0",
54
+ "react-dom": "^18.2.0",
55
+ "tsup": "^8.0.0",
56
+ "typescript": "^5.3.0",
57
+ "vitest": "^1.2.0"
58
+ },
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/omnifyjp/omnify-client-sso-react.git"
62
+ },
63
+ "bugs": {
64
+ "url": "https://github.com/omnifyjp/omnify-client-sso-react/issues"
65
+ },
66
+ "homepage": "https://github.com/omnifyjp/omnify-client-sso-react#readme",
67
+ "publishConfig": {
68
+ "access": "public"
69
+ }
70
+ }