@eminuckan/spine 0.2.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/CHANGELOG.md +47 -0
- package/LICENSE +21 -0
- package/README.md +425 -0
- package/dist/api-client/index.d.ts +3 -0
- package/dist/api-client/index.js +4 -0
- package/dist/api-client/server.d.ts +4 -0
- package/dist/api-client/server.js +5 -0
- package/dist/api-client-Ftxqm3Ep.js +212 -0
- package/dist/api-client-Ftxqm3Ep.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +0 -0
- package/dist/auth/server.d.ts +3 -0
- package/dist/auth/server.js +6 -0
- package/dist/auth.server-C979TR6W.js +724 -0
- package/dist/auth.server-C979TR6W.js.map +1 -0
- package/dist/auth.server-DU-x2LPA.js +5 -0
- package/dist/identity/index.d.ts +4 -0
- package/dist/identity/index.js +3 -0
- package/dist/identity/server.d.ts +4 -0
- package/dist/identity/server.js +4 -0
- package/dist/identity-CFa1txoj.js +235 -0
- package/dist/identity-CFa1txoj.js.map +1 -0
- package/dist/index-BIJOC8Fb.d.ts +272 -0
- package/dist/index-BIJOC8Fb.d.ts.map +1 -0
- package/dist/index-Bt9XE6gj.d.ts +104 -0
- package/dist/index-Bt9XE6gj.d.ts.map +1 -0
- package/dist/index-C2hGpzGr.d.ts +74 -0
- package/dist/index-C2hGpzGr.d.ts.map +1 -0
- package/dist/index-CfQsb3l_.d.ts +170 -0
- package/dist/index-CfQsb3l_.d.ts.map +1 -0
- package/dist/index-D6RK_5vm.d.ts +199 -0
- package/dist/index-D6RK_5vm.d.ts.map +1 -0
- package/dist/index-DrttYPAj.d.ts +30 -0
- package/dist/index-DrttYPAj.d.ts.map +1 -0
- package/dist/index-geY2ynnN.d.ts +141 -0
- package/dist/index-geY2ynnN.d.ts.map +1 -0
- package/dist/index-kr8Wk7v5.d.ts +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/logging/index.d.ts +3 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging-_h2mi3L9.js +118 -0
- package/dist/logging-_h2mi3L9.js.map +1 -0
- package/dist/permissions/index.d.ts +2 -0
- package/dist/permissions/index.js +3 -0
- package/dist/permissions-D5ScGGnd.js +281 -0
- package/dist/permissions-D5ScGGnd.js.map +1 -0
- package/dist/query-client/index.d.ts +2 -0
- package/dist/query-client/index.js +3 -0
- package/dist/query-client-DRPQIKHX.js +174 -0
- package/dist/query-client-DRPQIKHX.js.map +1 -0
- package/dist/react-router/index.d.ts +12 -0
- package/dist/react-router/index.js +10 -0
- package/dist/react-router/server.d.ts +18 -0
- package/dist/react-router/server.js +17 -0
- package/dist/redis-session-storage.server-DhufyPjd.js +250 -0
- package/dist/redis-session-storage.server-DhufyPjd.js.map +1 -0
- package/dist/server-BoeUiXSh.d.ts +40 -0
- package/dist/server-BoeUiXSh.d.ts.map +1 -0
- package/dist/server-C-DiVP9e.js +185 -0
- package/dist/server-C-DiVP9e.js.map +1 -0
- package/dist/server-CLbksnvg.d.ts +144 -0
- package/dist/server-CLbksnvg.d.ts.map +1 -0
- package/dist/server-CNp6gjFE.js +78 -0
- package/dist/server-CNp6gjFE.js.map +1 -0
- package/dist/server-CXGaRaiS.d.ts +256 -0
- package/dist/server-CXGaRaiS.d.ts.map +1 -0
- package/dist/server-CiSbg-9C.js +352 -0
- package/dist/server-CiSbg-9C.js.map +1 -0
- package/dist/server-DLgH3FRR.js +312 -0
- package/dist/server-DLgH3FRR.js.map +1 -0
- package/dist/server-DazqdM63.d.ts +93 -0
- package/dist/server-DazqdM63.d.ts.map +1 -0
- package/dist/server-iWm3oeCM.js +145 -0
- package/dist/server-iWm3oeCM.js.map +1 -0
- package/dist/server-zEKMDpP7.d.ts +80 -0
- package/dist/server-zEKMDpP7.d.ts.map +1 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.js +17 -0
- package/dist/signalr/index.d.ts +4 -0
- package/dist/signalr/index.js +3 -0
- package/dist/signalr-BJGcXc0m.js +274 -0
- package/dist/signalr-BJGcXc0m.js.map +1 -0
- package/dist/src-D7mh9MKA.js +0 -0
- package/dist/tenant/index.d.ts +3 -0
- package/dist/tenant/index.js +3 -0
- package/dist/tenant/server.d.ts +3 -0
- package/dist/tenant/server.js +5 -0
- package/dist/tenant-C6KPaSCX.js +256 -0
- package/dist/tenant-C6KPaSCX.js.map +1 -0
- package/dist/types-BTjFzIhv.d.ts +31 -0
- package/dist/types-BTjFzIhv.d.ts.map +1 -0
- package/dist/types-DoIfDhl6.d.ts +55 -0
- package/dist/types-DoIfDhl6.d.ts.map +1 -0
- package/dist/types-DufLRY7A.d.ts +164 -0
- package/dist/types-DufLRY7A.d.ts.map +1 -0
- package/dist/types-uDrDMkCq.d.ts +93 -0
- package/dist/types-uDrDMkCq.d.ts.map +1 -0
- package/package.json +147 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { create } from "zustand";
|
|
2
|
+
import { createContext, useContext, useEffect } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { devtools } from "zustand/middleware";
|
|
5
|
+
|
|
6
|
+
//#region src/tenant/tenant-store.ts
|
|
7
|
+
/**
|
|
8
|
+
* Organization fetch endpoint (configurable)
|
|
9
|
+
*/
|
|
10
|
+
let organizationEndpoint = "/api/organization/get";
|
|
11
|
+
let tenantSwitchEndpoint = "/api/tenant/switch";
|
|
12
|
+
/**
|
|
13
|
+
* Configure tenant store endpoints
|
|
14
|
+
*/
|
|
15
|
+
function configureTenantEndpoints(config) {
|
|
16
|
+
if (config.organizationEndpoint) organizationEndpoint = config.organizationEndpoint;
|
|
17
|
+
if (config.tenantSwitchEndpoint) tenantSwitchEndpoint = config.tenantSwitchEndpoint;
|
|
18
|
+
}
|
|
19
|
+
const useTenantStore = create()(devtools((set, get) => ({
|
|
20
|
+
currentTenant: null,
|
|
21
|
+
availableTenants: [],
|
|
22
|
+
memberships: new Map(),
|
|
23
|
+
organizations: new Map(),
|
|
24
|
+
isLoading: false,
|
|
25
|
+
isSwitching: false,
|
|
26
|
+
setCurrentTenant: (tenantId) => set({ currentTenant: tenantId }),
|
|
27
|
+
setAvailableTenants: (tenants) => {
|
|
28
|
+
set({ availableTenants: tenants });
|
|
29
|
+
const { organizations, fetchOrganization } = get();
|
|
30
|
+
tenants.forEach((tenantId) => {
|
|
31
|
+
if (!organizations.has(tenantId)) fetchOrganization(tenantId);
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
setMemberships: (memberships) => {
|
|
35
|
+
const membershipMap = new Map();
|
|
36
|
+
memberships.forEach((membership) => {
|
|
37
|
+
membershipMap.set(membership.tenantId, membership);
|
|
38
|
+
});
|
|
39
|
+
set({ memberships: membershipMap });
|
|
40
|
+
},
|
|
41
|
+
setOrganization: (tenantId, data) => set((state) => ({ organizations: new Map(state.organizations).set(tenantId, data) })),
|
|
42
|
+
addOrganization: (tenantId, data) => {
|
|
43
|
+
set((state) => {
|
|
44
|
+
const newOrganizations = new Map(state.organizations);
|
|
45
|
+
newOrganizations.set(tenantId, data);
|
|
46
|
+
return { organizations: newOrganizations };
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
setLoading: (loading) => set({ isLoading: loading }),
|
|
50
|
+
setSwitching: (switching) => set({ isSwitching: switching }),
|
|
51
|
+
fetchOrganization: async (tenantId) => {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`${organizationEndpoint}/${tenantId}`);
|
|
54
|
+
if (response.ok) {
|
|
55
|
+
const result = await response.json();
|
|
56
|
+
if (result.success && result.data) get().setOrganization(tenantId, result.data);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Failed to fetch organization data for tenant:", tenantId, error);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
switchTenant: async (tenantId) => {
|
|
63
|
+
const { availableTenants } = get();
|
|
64
|
+
if (!availableTenants.includes(tenantId)) {
|
|
65
|
+
console.error("Tenant not available:", tenantId);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
set({ isSwitching: true });
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(tenantSwitchEndpoint, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "Content-Type": "application/json" },
|
|
73
|
+
body: JSON.stringify({ tenantId })
|
|
74
|
+
});
|
|
75
|
+
if (response.ok) {
|
|
76
|
+
const result = await response.json();
|
|
77
|
+
if (result.success) {
|
|
78
|
+
set({ currentTenant: tenantId });
|
|
79
|
+
const { organizations, fetchOrganization } = get();
|
|
80
|
+
if (!organizations.has(tenantId)) await fetchOrganization(tenantId);
|
|
81
|
+
window.location.reload();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error("Failed to switch tenant:", error);
|
|
86
|
+
set({ isSwitching: false });
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
getMembership: (tenantId) => {
|
|
90
|
+
const { memberships } = get();
|
|
91
|
+
return memberships.get(tenantId) || null;
|
|
92
|
+
},
|
|
93
|
+
getCurrentMembership: () => {
|
|
94
|
+
const { currentTenant, memberships } = get();
|
|
95
|
+
if (!currentTenant) return null;
|
|
96
|
+
return memberships.get(currentTenant) || null;
|
|
97
|
+
},
|
|
98
|
+
getTenantName: (tenantId) => {
|
|
99
|
+
const { memberships, organizations } = get();
|
|
100
|
+
const membership = memberships.get(tenantId);
|
|
101
|
+
if (membership?.tenantName) return membership.tenantName;
|
|
102
|
+
if (membership?.organizationName) return membership.organizationName;
|
|
103
|
+
return organizations.get(tenantId)?.name || tenantId;
|
|
104
|
+
},
|
|
105
|
+
getOrganization: (tenantId) => {
|
|
106
|
+
const { organizations } = get();
|
|
107
|
+
return organizations.get(tenantId) || null;
|
|
108
|
+
},
|
|
109
|
+
getCurrentOrganization: () => {
|
|
110
|
+
const { currentTenant, organizations } = get();
|
|
111
|
+
if (!currentTenant) return null;
|
|
112
|
+
return organizations.get(currentTenant) || null;
|
|
113
|
+
}
|
|
114
|
+
}), { name: "TenantStore" }));
|
|
115
|
+
/**
|
|
116
|
+
* Initialize store with server data
|
|
117
|
+
*/
|
|
118
|
+
function initializeTenantStore(currentTenant, availableTenants, memberships) {
|
|
119
|
+
const store = useTenantStore.getState();
|
|
120
|
+
store.setCurrentTenant(currentTenant);
|
|
121
|
+
store.setAvailableTenants(availableTenants);
|
|
122
|
+
if (memberships) store.setMemberships(memberships);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/tenant/tenant-context.tsx
|
|
127
|
+
const TenantContext = createContext(null);
|
|
128
|
+
function TenantProvider({ children, initialTenant = null, initialTenants = [], initialMemberships = [], onTenantChange }) {
|
|
129
|
+
const currentTenant = useTenantStore((state) => state.currentTenant);
|
|
130
|
+
const availableTenants = useTenantStore((state) => state.availableTenants);
|
|
131
|
+
const memberships = useTenantStore((state) => state.memberships);
|
|
132
|
+
const organizations = useTenantStore((state) => state.organizations);
|
|
133
|
+
const isLoading = useTenantStore((state) => state.isLoading || state.isSwitching);
|
|
134
|
+
const currentOrganization = useTenantStore((state) => state.getCurrentOrganization());
|
|
135
|
+
const currentMembership = useTenantStore((state) => state.getCurrentMembership());
|
|
136
|
+
const setCurrentTenant = useTenantStore((state) => state.setCurrentTenant);
|
|
137
|
+
const setAvailableTenants = useTenantStore((state) => state.setAvailableTenants);
|
|
138
|
+
const setMemberships = useTenantStore((state) => state.setMemberships);
|
|
139
|
+
const switchTenantAction = useTenantStore((state) => state.switchTenant);
|
|
140
|
+
const fetchOrganization = useTenantStore((state) => state.fetchOrganization);
|
|
141
|
+
const getOrganization = useTenantStore((state) => state.getOrganization);
|
|
142
|
+
const getMembership = useTenantStore((state) => state.getMembership);
|
|
143
|
+
const getTenantName = useTenantStore((state) => state.getTenantName);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (initialTenant) setCurrentTenant(initialTenant);
|
|
146
|
+
if (initialTenants.length > 0) setAvailableTenants(initialTenants);
|
|
147
|
+
if (initialMemberships.length > 0) setMemberships(initialMemberships);
|
|
148
|
+
}, [
|
|
149
|
+
initialTenant,
|
|
150
|
+
initialTenants.join(","),
|
|
151
|
+
initialMemberships.map((membership) => membership.tenantId).join(","),
|
|
152
|
+
setCurrentTenant,
|
|
153
|
+
setAvailableTenants,
|
|
154
|
+
setMemberships
|
|
155
|
+
]);
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
const getCookieTenant = () => {
|
|
158
|
+
const cookies = document.cookie.split(";");
|
|
159
|
+
const tenantCookie = cookies.find((cookie) => cookie.trim().startsWith("__active-org="));
|
|
160
|
+
if (tenantCookie) {
|
|
161
|
+
const value$1 = tenantCookie.split("=")[1];
|
|
162
|
+
return decodeURIComponent(value$1);
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
};
|
|
166
|
+
if (!currentTenant) {
|
|
167
|
+
const cookieTenant = getCookieTenant();
|
|
168
|
+
if (cookieTenant && availableTenants.includes(cookieTenant)) setCurrentTenant(cookieTenant);
|
|
169
|
+
}
|
|
170
|
+
}, [
|
|
171
|
+
currentTenant,
|
|
172
|
+
availableTenants,
|
|
173
|
+
setCurrentTenant
|
|
174
|
+
]);
|
|
175
|
+
const switchTenant = (tenantId) => {
|
|
176
|
+
switchTenantAction(tenantId).then(() => {
|
|
177
|
+
if (onTenantChange) onTenantChange();
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
const refreshTenants = () => {
|
|
181
|
+
console.log("Tenant refresh requested - should be handled by identity context");
|
|
182
|
+
};
|
|
183
|
+
const refreshOrganization = () => {
|
|
184
|
+
if (currentTenant) fetchOrganization(currentTenant);
|
|
185
|
+
};
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (currentTenant && !currentOrganization) refreshOrganization();
|
|
188
|
+
}, [currentTenant, currentOrganization]);
|
|
189
|
+
const value = {
|
|
190
|
+
currentTenant,
|
|
191
|
+
availableTenants,
|
|
192
|
+
memberships,
|
|
193
|
+
currentOrganization,
|
|
194
|
+
currentMembership,
|
|
195
|
+
organizations,
|
|
196
|
+
isLoading,
|
|
197
|
+
switchTenant,
|
|
198
|
+
refreshTenants,
|
|
199
|
+
refreshOrganization,
|
|
200
|
+
getOrganization,
|
|
201
|
+
getMembership,
|
|
202
|
+
getTenantName
|
|
203
|
+
};
|
|
204
|
+
return /* @__PURE__ */ jsx(TenantContext.Provider, {
|
|
205
|
+
value,
|
|
206
|
+
children
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Hook for using tenant context
|
|
211
|
+
*/
|
|
212
|
+
function useTenant() {
|
|
213
|
+
const context = useContext(TenantContext);
|
|
214
|
+
if (!context) throw new Error("useTenant must be used within a TenantProvider");
|
|
215
|
+
return context;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Hook for current tenant only
|
|
219
|
+
*/
|
|
220
|
+
function useCurrentTenant() {
|
|
221
|
+
const { currentTenant } = useTenant();
|
|
222
|
+
return currentTenant;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Hook for tenant switching
|
|
226
|
+
*/
|
|
227
|
+
function useTenantSwitcher() {
|
|
228
|
+
const { switchTenant, isLoading, availableTenants } = useTenant();
|
|
229
|
+
return {
|
|
230
|
+
switchTenant,
|
|
231
|
+
isLoading,
|
|
232
|
+
availableTenants
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Hook for current organization
|
|
237
|
+
*/
|
|
238
|
+
function useCurrentOrganization() {
|
|
239
|
+
const { currentOrganization } = useTenant();
|
|
240
|
+
return currentOrganization;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Hook for organization operations
|
|
244
|
+
*/
|
|
245
|
+
function useOrganization() {
|
|
246
|
+
const { currentOrganization, refreshOrganization, isLoading } = useTenant();
|
|
247
|
+
return {
|
|
248
|
+
organization: currentOrganization,
|
|
249
|
+
refreshOrganization,
|
|
250
|
+
isLoading
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
//#endregion
|
|
255
|
+
export { TenantProvider, configureTenantEndpoints, initializeTenantStore, useCurrentOrganization, useCurrentTenant, useOrganization, useTenant, useTenantStore, useTenantSwitcher };
|
|
256
|
+
//# sourceMappingURL=tenant-C6KPaSCX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-C6KPaSCX.js","names":["config: {\n organizationEndpoint?: string;\n tenantSwitchEndpoint?: string;\n}","currentTenant: string | null","availableTenants: string[]","memberships?: TenantMembership[]","value","tenantId: string","value: TenantContextType"],"sources":["../src/tenant/tenant-store.ts","../src/tenant/tenant-context.tsx"],"sourcesContent":["/**\n * Tenant Store (Zustand)\n * \n * Client-side state management for tenant and organization data.\n */\n\nimport { create } from 'zustand';\nimport { devtools } from 'zustand/middleware';\nimport type { OrganizationData, TenantMembership } from './types';\n\ninterface TenantState {\n // State\n currentTenant: string | null;\n availableTenants: string[];\n memberships: Map<string, TenantMembership>;\n organizations: Map<string, OrganizationData>;\n isLoading: boolean;\n isSwitching: boolean;\n\n // Actions\n setCurrentTenant: (tenantId: string | null) => void;\n setAvailableTenants: (tenants: string[]) => void;\n setMemberships: (memberships: TenantMembership[]) => void;\n setOrganization: (tenantId: string, data: OrganizationData) => void;\n addOrganization: (tenantId: string, data: OrganizationData) => void;\n setLoading: (loading: boolean) => void;\n setSwitching: (switching: boolean) => void;\n\n // Async actions\n fetchOrganization: (tenantId: string) => Promise<void>;\n switchTenant: (tenantId: string) => Promise<void>;\n\n // Getters\n getMembership: (tenantId: string) => TenantMembership | null;\n getCurrentMembership: () => TenantMembership | null;\n getTenantName: (tenantId: string) => string;\n getOrganization: (tenantId: string) => OrganizationData | null;\n getCurrentOrganization: () => OrganizationData | null;\n}\n\n/**\n * Organization fetch endpoint (configurable)\n */\nlet organizationEndpoint = '/api/organization/get';\nlet tenantSwitchEndpoint = '/api/tenant/switch';\n\n/**\n * Configure tenant store endpoints\n */\nexport function configureTenantEndpoints(config: {\n organizationEndpoint?: string;\n tenantSwitchEndpoint?: string;\n}) {\n if (config.organizationEndpoint) {\n organizationEndpoint = config.organizationEndpoint;\n }\n if (config.tenantSwitchEndpoint) {\n tenantSwitchEndpoint = config.tenantSwitchEndpoint;\n }\n}\n\nexport const useTenantStore = create<TenantState>()(\n devtools(\n (set, get) => ({\n // Initial state\n currentTenant: null,\n availableTenants: [],\n memberships: new Map(),\n organizations: new Map(),\n isLoading: false,\n isSwitching: false,\n\n // Setters\n setCurrentTenant: (tenantId) => set({ currentTenant: tenantId }),\n\n setAvailableTenants: (tenants) => {\n set({ availableTenants: tenants });\n\n // Fetch organization data for all tenants\n const { organizations, fetchOrganization } = get();\n tenants.forEach((tenantId) => {\n if (!organizations.has(tenantId)) {\n fetchOrganization(tenantId);\n }\n });\n },\n\n setMemberships: (memberships) => {\n const membershipMap = new Map<string, TenantMembership>();\n memberships.forEach((membership) => {\n membershipMap.set(membership.tenantId, membership);\n });\n set({ memberships: membershipMap });\n },\n\n setOrganization: (tenantId, data) =>\n set((state) => ({\n organizations: new Map(state.organizations).set(tenantId, data),\n })),\n\n addOrganization: (tenantId, data) => {\n set((state) => {\n const newOrganizations = new Map(state.organizations);\n newOrganizations.set(tenantId, data);\n return { organizations: newOrganizations };\n });\n },\n\n setLoading: (loading) => set({ isLoading: loading }),\n setSwitching: (switching) => set({ isSwitching: switching }),\n\n // Async actions\n fetchOrganization: async (tenantId) => {\n try {\n const response = await fetch(`${organizationEndpoint}/${tenantId}`);\n if (response.ok) {\n const result = await response.json();\n if (result.success && result.data) {\n get().setOrganization(tenantId, result.data);\n }\n }\n } catch (error) {\n console.error('Failed to fetch organization data for tenant:', tenantId, error);\n }\n },\n\n switchTenant: async (tenantId) => {\n const { availableTenants } = get();\n\n if (!availableTenants.includes(tenantId)) {\n console.error('Tenant not available:', tenantId);\n return;\n }\n\n set({ isSwitching: true });\n\n try {\n const response = await fetch(tenantSwitchEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tenantId }),\n });\n\n if (response.ok) {\n const result = await response.json();\n if (result.success) {\n set({ currentTenant: tenantId });\n\n // Fetch organization data if not already loaded\n const { organizations, fetchOrganization } = get();\n if (!organizations.has(tenantId)) {\n await fetchOrganization(tenantId);\n }\n\n // Reload the page to refresh all data with new tenant context\n window.location.reload();\n }\n }\n } catch (error) {\n console.error('Failed to switch tenant:', error);\n set({ isSwitching: false });\n }\n },\n\n // Getters\n getMembership: (tenantId) => {\n const { memberships } = get();\n return memberships.get(tenantId) || null;\n },\n\n getCurrentMembership: () => {\n const { currentTenant, memberships } = get();\n if (!currentTenant) return null;\n return memberships.get(currentTenant) || null;\n },\n\n getTenantName: (tenantId) => {\n const { memberships, organizations } = get();\n const membership = memberships.get(tenantId);\n if (membership?.tenantName) {\n return membership.tenantName;\n }\n if (membership?.organizationName) {\n return membership.organizationName;\n }\n return organizations.get(tenantId)?.name || tenantId;\n },\n\n getOrganization: (tenantId) => {\n const { organizations } = get();\n return organizations.get(tenantId) || null;\n },\n\n getCurrentOrganization: () => {\n const { currentTenant, organizations } = get();\n if (!currentTenant) return null;\n return organizations.get(currentTenant) || null;\n },\n }),\n { name: 'TenantStore' }\n )\n);\n\n/**\n * Initialize store with server data\n */\nexport function initializeTenantStore(\n currentTenant: string | null,\n availableTenants: string[],\n memberships?: TenantMembership[]\n) {\n const store = useTenantStore.getState();\n store.setCurrentTenant(currentTenant);\n store.setAvailableTenants(availableTenants);\n if (memberships) {\n store.setMemberships(memberships);\n }\n}\n","/**\n * Tenant Context Provider\n * \n * React context for tenant management.\n */\n\nimport { createContext, useContext, useEffect, type ReactNode } from 'react';\nimport { useTenantStore } from './tenant-store';\nimport type { OrganizationData, TenantContextType, TenantMembership } from './types';\n\nconst TenantContext = createContext<TenantContextType | null>(null);\n\ninterface TenantProviderProps {\n children: ReactNode;\n initialTenant?: string | null;\n initialTenants?: string[];\n initialMemberships?: TenantMembership[];\n onTenantChange?: () => void;\n}\n\nexport function TenantProvider({\n children,\n initialTenant = null,\n initialTenants = [],\n initialMemberships = [],\n onTenantChange,\n}: TenantProviderProps) {\n const currentTenant = useTenantStore((state) => state.currentTenant);\n const availableTenants = useTenantStore((state) => state.availableTenants);\n const memberships = useTenantStore((state) => state.memberships);\n const organizations = useTenantStore((state) => state.organizations);\n const isLoading = useTenantStore((state) => state.isLoading || state.isSwitching);\n const currentOrganization = useTenantStore((state) => state.getCurrentOrganization());\n const currentMembership = useTenantStore((state) => state.getCurrentMembership());\n\n const setCurrentTenant = useTenantStore((state) => state.setCurrentTenant);\n const setAvailableTenants = useTenantStore((state) => state.setAvailableTenants);\n const setMemberships = useTenantStore((state) => state.setMemberships);\n const switchTenantAction = useTenantStore((state) => state.switchTenant);\n const fetchOrganization = useTenantStore((state) => state.fetchOrganization);\n const getOrganization = useTenantStore((state) => state.getOrganization);\n const getMembership = useTenantStore((state) => state.getMembership);\n const getTenantName = useTenantStore((state) => state.getTenantName);\n\n // Initialize store with initial values\n useEffect(() => {\n if (initialTenant) {\n setCurrentTenant(initialTenant);\n }\n if (initialTenants.length > 0) {\n setAvailableTenants(initialTenants);\n }\n if (initialMemberships.length > 0) {\n setMemberships(initialMemberships);\n }\n }, [\n initialTenant,\n initialTenants.join(','),\n initialMemberships.map((membership) => membership.tenantId).join(','),\n setCurrentTenant,\n setAvailableTenants,\n setMemberships,\n ]);\n\n // Read tenant from cookie (client-side)\n useEffect(() => {\n const getCookieTenant = () => {\n const cookies = document.cookie.split(';');\n const tenantCookie = cookies.find((cookie) =>\n cookie.trim().startsWith('__active-org=')\n );\n\n if (tenantCookie) {\n const value = tenantCookie.split('=')[1];\n return decodeURIComponent(value);\n }\n\n return null;\n };\n\n if (!currentTenant) {\n const cookieTenant = getCookieTenant();\n if (cookieTenant && availableTenants.includes(cookieTenant)) {\n setCurrentTenant(cookieTenant);\n }\n }\n }, [currentTenant, availableTenants, setCurrentTenant]);\n\n // Switch tenant handler\n const switchTenant = (tenantId: string) => {\n switchTenantAction(tenantId).then(() => {\n if (onTenantChange) {\n onTenantChange();\n }\n });\n };\n\n // Refresh tenants (handled by identity context)\n const refreshTenants = () => {\n console.log('Tenant refresh requested - should be handled by identity context');\n };\n\n // Refresh organization\n const refreshOrganization = () => {\n if (currentTenant) {\n fetchOrganization(currentTenant);\n }\n };\n\n // Load organization data when tenant is set\n useEffect(() => {\n if (currentTenant && !currentOrganization) {\n refreshOrganization();\n }\n }, [currentTenant, currentOrganization]);\n\n const value: TenantContextType = {\n currentTenant,\n availableTenants,\n memberships,\n currentOrganization,\n currentMembership,\n organizations,\n isLoading,\n switchTenant,\n refreshTenants,\n refreshOrganization,\n getOrganization,\n getMembership,\n getTenantName,\n };\n\n return (\n <TenantContext.Provider value={value}>\n {children}\n </TenantContext.Provider>\n );\n}\n\n/**\n * Hook for using tenant context\n */\nexport function useTenant(): TenantContextType {\n const context = useContext(TenantContext);\n\n if (!context) {\n throw new Error('useTenant must be used within a TenantProvider');\n }\n\n return context;\n}\n\n/**\n * Hook for current tenant only\n */\nexport function useCurrentTenant(): string | null {\n const { currentTenant } = useTenant();\n return currentTenant;\n}\n\n/**\n * Hook for tenant switching\n */\nexport function useTenantSwitcher() {\n const { switchTenant, isLoading, availableTenants } = useTenant();\n return { switchTenant, isLoading, availableTenants };\n}\n\n/**\n * Hook for current organization\n */\nexport function useCurrentOrganization(): OrganizationData | null {\n const { currentOrganization } = useTenant();\n return currentOrganization;\n}\n\n/**\n * Hook for organization operations\n */\nexport function useOrganization() {\n const { currentOrganization, refreshOrganization, isLoading } = useTenant();\n\n return {\n organization: currentOrganization,\n refreshOrganization,\n isLoading,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/logging/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Logging Types
|
|
4
|
+
*/
|
|
5
|
+
declare enum LogLevel {
|
|
6
|
+
DEBUG = "debug",
|
|
7
|
+
INFO = "info",
|
|
8
|
+
WARN = "warn",
|
|
9
|
+
ERROR = "error",
|
|
10
|
+
}
|
|
11
|
+
interface LogContext {
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface LogEntry {
|
|
15
|
+
timestamp: string;
|
|
16
|
+
level: LogLevel;
|
|
17
|
+
message: string;
|
|
18
|
+
context?: LogContext;
|
|
19
|
+
error?: Error;
|
|
20
|
+
}
|
|
21
|
+
interface LoggerConfig {
|
|
22
|
+
minLevel: LogLevel;
|
|
23
|
+
enableConsole: boolean;
|
|
24
|
+
enableRemote: boolean;
|
|
25
|
+
remoteEndpoint?: string;
|
|
26
|
+
serviceName?: string;
|
|
27
|
+
} //#endregion
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
30
|
+
export { LogContext, LogEntry, LogLevel as LogLevel$1, LoggerConfig };
|
|
31
|
+
//# sourceMappingURL=types-BTjFzIhv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BTjFzIhv.d.ts","names":[],"sources":["../src/logging/types.ts"],"sourcesContent":null,"mappings":";;;;aAIY,QAAA;EAAA,KAAA,GAAA,OAAQ;EAOH,IAAA,GAAA,MAAU;EAIV,IAAA,GAAA,MAAQ;EAAA,KAAA,GAAA,OAAA;;AAIb,UARK,UAAA,CAQL;EAAU,CAAA,GACZ,EAAA,MAAA,CAAA,EAAA,OAAA;AAAK;AAGE,UARA,QAAA,CAQY;;SANpB;;YAEG;UACF;;UAGO,YAAA;YACL"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/tenant/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Tenant Types
|
|
4
|
+
*/
|
|
5
|
+
interface TenantInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
role?: string;
|
|
9
|
+
}
|
|
10
|
+
interface TenantMembership {
|
|
11
|
+
tenantId: string;
|
|
12
|
+
tenantName?: string;
|
|
13
|
+
tenantSlug?: string | null;
|
|
14
|
+
organizationName?: string;
|
|
15
|
+
role?: string;
|
|
16
|
+
roles?: string[];
|
|
17
|
+
ownerUserId?: string | null;
|
|
18
|
+
isOwner?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface OrganizationBranding {
|
|
21
|
+
logoUrl?: string | null;
|
|
22
|
+
themePrimary?: string | null;
|
|
23
|
+
themeMode?: string | null;
|
|
24
|
+
emailBranding?: string | null;
|
|
25
|
+
}
|
|
26
|
+
interface OrganizationData {
|
|
27
|
+
organizationId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
slug?: string | null;
|
|
30
|
+
timeZone?: string | null;
|
|
31
|
+
currency?: string | null;
|
|
32
|
+
branding?: OrganizationBranding | null;
|
|
33
|
+
ownerUserId?: string | null;
|
|
34
|
+
ownerAssignedAtUtc?: string | null;
|
|
35
|
+
defaultOperatingAccountId?: string | null;
|
|
36
|
+
}
|
|
37
|
+
interface TenantContextType {
|
|
38
|
+
currentTenant: string | null;
|
|
39
|
+
availableTenants: string[];
|
|
40
|
+
memberships: Map<string, TenantMembership>;
|
|
41
|
+
currentOrganization: OrganizationData | null;
|
|
42
|
+
currentMembership: TenantMembership | null;
|
|
43
|
+
organizations: Map<string, OrganizationData>;
|
|
44
|
+
isLoading: boolean;
|
|
45
|
+
switchTenant: (tenantId: string) => void;
|
|
46
|
+
refreshTenants: () => void;
|
|
47
|
+
refreshOrganization: () => void;
|
|
48
|
+
getOrganization: (tenantId: string) => OrganizationData | null;
|
|
49
|
+
getMembership: (tenantId: string) => TenantMembership | null;
|
|
50
|
+
getTenantName: (tenantId: string) => string;
|
|
51
|
+
} //#endregion
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
54
|
+
export { OrganizationBranding, OrganizationData, TenantContextType, TenantInfo, TenantMembership };
|
|
55
|
+
//# sourceMappingURL=types-DoIfDhl6.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DoIfDhl6.d.ts","names":[],"sources":["../src/tenant/types.ts"],"sourcesContent":null,"mappings":";;;;UAIiB,UAAA;EAAA,EAAA,EAAA,MAAA;EAMA,IAAA,CAAA,EAAA,MAAA;EAWA,IAAA,CAAA,EAAA,MAAA;AAOjB;AAYiB,UA9BA,gBAAA,CA8BiB;EAAA,QAAA,EAAA,MAAA;EAAA,UAGP,CAAA,EAAA,MAAA;EAAgB,UAA5B,CAAA,EAAA,MAAA,GAAA,IAAA;EAAG,gBACK,CAAA,EAAA,MAAA;EAAgB,IAClB,CAAA,EAAA,MAAA;EAAgB,KACR,CAAA,EAAA,MAAA,EAAA;EAAgB,WAA5B,CAAA,EAAA,MAAA,GAAA,IAAA;EAAG,OAKqB,CAAA,EAAA,OAAA;;AACc,UA/BtC,oBAAA,CA+BsC;;;;;;UAxBtC,gBAAA;;;;;;aAMJ;;;;;UAMI,iBAAA;;;eAGF,YAAY;uBACJ;qBACF;iBACJ,YAAY;;;;;yCAKY;uCACF"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
//#region src/auth/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Auth Types
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Application Type
|
|
7
|
+
* Determines logout behavior and error handling
|
|
8
|
+
*/
|
|
9
|
+
type ApplicationType = 'no-landing-page' | 'landing-page' | 'dashboard' | 'tenant-app' | 'custom';
|
|
10
|
+
/**
|
|
11
|
+
* OAuth Configuration
|
|
12
|
+
*/
|
|
13
|
+
interface AuthConfig {
|
|
14
|
+
authority: string;
|
|
15
|
+
clientId: string;
|
|
16
|
+
clientSecret?: string;
|
|
17
|
+
redirectUri: string;
|
|
18
|
+
postLogoutRedirectUri?: string;
|
|
19
|
+
scope: string;
|
|
20
|
+
/**
|
|
21
|
+
* Application type - determines logout behavior
|
|
22
|
+
* - 'no-landing-page': No landing page, defaults to full SSO logout
|
|
23
|
+
* - 'landing-page': Has a landing page, defaults to app-specific logout
|
|
24
|
+
* - 'dashboard': Legacy alias for 'no-landing-page'
|
|
25
|
+
* - 'tenant-app': Legacy alias for 'landing-page'
|
|
26
|
+
* - 'custom': Use explicit ssoLogout setting
|
|
27
|
+
* @default 'custom'
|
|
28
|
+
*/
|
|
29
|
+
applicationType?: ApplicationType;
|
|
30
|
+
/**
|
|
31
|
+
* Whether to perform full SSO logout (clear identity cookie) or app-specific logout
|
|
32
|
+
* - true: Full logout - clears identity cookie, revokes all tokens
|
|
33
|
+
* - false: App-specific logout - keeps identity cookie, revokes only this app's tokens
|
|
34
|
+
*
|
|
35
|
+
* If not set, determined by applicationType:
|
|
36
|
+
* - 'no-landing-page': true (full logout)
|
|
37
|
+
* - 'landing-page': false (app-specific)
|
|
38
|
+
* - 'dashboard': true (legacy alias)
|
|
39
|
+
* - 'tenant-app': false (legacy alias)
|
|
40
|
+
* - 'custom': false (default)
|
|
41
|
+
*/
|
|
42
|
+
ssoLogout?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the application has a landing page
|
|
45
|
+
* - true: Has landing page - OAuth errors redirect to home, user sees landing
|
|
46
|
+
* - false: No landing page - Must handle OAuth errors specially to avoid loops
|
|
47
|
+
*
|
|
48
|
+
* If not set, determined by applicationType:
|
|
49
|
+
* - 'no-landing-page': false
|
|
50
|
+
* - 'landing-page': true
|
|
51
|
+
* - 'dashboard': false (legacy alias)
|
|
52
|
+
* - 'tenant-app': true (legacy alias)
|
|
53
|
+
* - 'custom': true (default)
|
|
54
|
+
*/
|
|
55
|
+
hasLandingPage?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Configurable claim mapping for adapting auth/session extraction
|
|
59
|
+
* to different identity providers and backend conventions.
|
|
60
|
+
*/
|
|
61
|
+
interface AuthClaimMapping {
|
|
62
|
+
subject?: string[];
|
|
63
|
+
name?: string[];
|
|
64
|
+
email?: string[];
|
|
65
|
+
givenName?: string[];
|
|
66
|
+
familyName?: string[];
|
|
67
|
+
picture?: string[];
|
|
68
|
+
locale?: string[];
|
|
69
|
+
zoneinfo?: string[];
|
|
70
|
+
updatedAt?: string[];
|
|
71
|
+
tenantIds?: string[];
|
|
72
|
+
tenantRoles?: string[];
|
|
73
|
+
permissions?: string[];
|
|
74
|
+
isOnboarded?: string[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* User Info from OAuth provider
|
|
78
|
+
*/
|
|
79
|
+
interface UserInfo {
|
|
80
|
+
sub: string;
|
|
81
|
+
name?: string;
|
|
82
|
+
email?: string;
|
|
83
|
+
givenName?: string;
|
|
84
|
+
familyName?: string;
|
|
85
|
+
picture?: string;
|
|
86
|
+
locale?: string;
|
|
87
|
+
zoneinfo?: string;
|
|
88
|
+
updated_at?: number;
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* OAuth State for PKCE flow
|
|
93
|
+
*/
|
|
94
|
+
interface OAuthState {
|
|
95
|
+
state: string;
|
|
96
|
+
codeVerifier: string;
|
|
97
|
+
nonce?: string;
|
|
98
|
+
returnUrl?: string;
|
|
99
|
+
createdAt: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Session Data stored in Redis
|
|
103
|
+
*/
|
|
104
|
+
interface SessionData {
|
|
105
|
+
userId?: string;
|
|
106
|
+
accessToken?: string;
|
|
107
|
+
refreshToken?: string;
|
|
108
|
+
idToken?: string;
|
|
109
|
+
expiresAt?: number;
|
|
110
|
+
user?: UserInfo;
|
|
111
|
+
lastActivity?: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Session Flash Data for temporary messages
|
|
115
|
+
*/
|
|
116
|
+
interface SessionFlashData {
|
|
117
|
+
error: string;
|
|
118
|
+
success: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Token refresh result
|
|
122
|
+
*/
|
|
123
|
+
interface TokenRefreshResult {
|
|
124
|
+
success: boolean;
|
|
125
|
+
newAccessToken?: string;
|
|
126
|
+
tokens?: {
|
|
127
|
+
access_token: string;
|
|
128
|
+
refresh_token?: string;
|
|
129
|
+
id_token?: string;
|
|
130
|
+
expires_in?: number;
|
|
131
|
+
};
|
|
132
|
+
error?: string;
|
|
133
|
+
shouldLogout?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Route protection levels
|
|
137
|
+
*/
|
|
138
|
+
type ProtectionLevel = 'public' | 'auth' | 'onboarding-required' | 'subscription-required';
|
|
139
|
+
/**
|
|
140
|
+
* Protected route loader function type
|
|
141
|
+
*/
|
|
142
|
+
type ProtectedLoaderFn<T> = (user?: UserInfo) => Promise<T> | T;
|
|
143
|
+
/**
|
|
144
|
+
* Login options
|
|
145
|
+
*/
|
|
146
|
+
interface LoginOptions {
|
|
147
|
+
/** Return URL after successful login */
|
|
148
|
+
returnUrl?: string;
|
|
149
|
+
/** OAuth prompt parameter (none, login, consent, select_account) */
|
|
150
|
+
prompt?: 'none' | 'login' | 'consent' | 'select_account';
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Auth error information from OAuth callback
|
|
154
|
+
*/
|
|
155
|
+
interface AuthError {
|
|
156
|
+
/** Error code (e.g., 'access_denied', 'login_required') */
|
|
157
|
+
error: string;
|
|
158
|
+
/** Human-readable error description */
|
|
159
|
+
description?: string;
|
|
160
|
+
} //#endregion
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=types.d.ts.map
|
|
163
|
+
export { ApplicationType, AuthClaimMapping, AuthConfig, AuthError, LoginOptions, OAuthState, ProtectedLoaderFn, ProtectionLevel, SessionData, SessionFlashData, TokenRefreshResult as TokenRefreshResult$1, UserInfo };
|
|
164
|
+
//# sourceMappingURL=types-DufLRY7A.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DufLRY7A.d.ts","names":[],"sources":["../src/auth/types.ts"],"sourcesContent":null,"mappings":";;;;;;;;KAQY,eAAA;AAAZ;;;UAUiB,UAAA;EAAA,SAAA,EAAA,MAAU;;;;;EAoDV,KAAA,EAAA,MAAA;;;;AAmBjB;;;;AAgBA;;oBAtEoB;;AAiFpB;;;;AAaA;;;;AAQA;;;;EAgBY;;;;AASZ;;;;;;AAAsE;;;;AAKtE;;;;AAUiB,UA3GA,gBAAA,CA2GS;;;;;;;;;;;;;;;;;;UAxFT,QAAA;;;;;;;;;;;;;;;UAgBA,UAAA;;;;;;;;;;UAWA,WAAA;;;;;;SAMR;;;;;;UAOQ,gBAAA;;;;;;;UAQA,kBAAA;;;;;;;;;;;;;;;KAgBL,eAAA;;;;KASA,+BAA+B,aAAa,QAAQ,KAAK;;;;UAKpD,YAAA;;;;;;;;;UAUA,SAAA"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { TenantMembership } from "./types-DoIfDhl6.js";
|
|
2
|
+
|
|
3
|
+
//#region src/identity/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Address data for user profile
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Address data for user profile
|
|
10
|
+
*/
|
|
11
|
+
interface AddressData {
|
|
12
|
+
addressId?: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
line1: string;
|
|
15
|
+
line2?: string | null;
|
|
16
|
+
city: string;
|
|
17
|
+
state: string;
|
|
18
|
+
postalCode: string;
|
|
19
|
+
country: string;
|
|
20
|
+
isPrimary?: boolean;
|
|
21
|
+
attention?: string | null;
|
|
22
|
+
region?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Core identity context data
|
|
26
|
+
*
|
|
27
|
+
* This interface is designed to be flexible and work with various backend implementations.
|
|
28
|
+
* Only the fields your backend returns need to be populated.
|
|
29
|
+
*/
|
|
30
|
+
interface IdentityContextData {
|
|
31
|
+
/** Unique user identifier */
|
|
32
|
+
userId?: string;
|
|
33
|
+
/** User's email address */
|
|
34
|
+
email?: string;
|
|
35
|
+
/** User's first name */
|
|
36
|
+
firstName?: string | null;
|
|
37
|
+
/** User's last name */
|
|
38
|
+
lastName?: string | null;
|
|
39
|
+
/** User's display name (computed or custom) */
|
|
40
|
+
displayName?: string | null;
|
|
41
|
+
/** URL to user's profile image/avatar */
|
|
42
|
+
profileImageUrl?: string | null;
|
|
43
|
+
/** User's phone number */
|
|
44
|
+
phoneNumber?: string | null;
|
|
45
|
+
/** User's timezone (e.g., "America/New_York") */
|
|
46
|
+
timeZone?: string;
|
|
47
|
+
/** User's preferred locale (e.g., "en-US") */
|
|
48
|
+
locale?: string;
|
|
49
|
+
/** User's addresses */
|
|
50
|
+
addresses?: AddressData[];
|
|
51
|
+
/** Whether user has any tenant membership */
|
|
52
|
+
hasAnyMembership?: boolean;
|
|
53
|
+
/** Whether user owns any tenant */
|
|
54
|
+
hasOwnerMembership?: boolean;
|
|
55
|
+
/** List of tenant IDs user belongs to */
|
|
56
|
+
tenants?: string[];
|
|
57
|
+
/** Currently active tenant ID */
|
|
58
|
+
currentTenant?: string;
|
|
59
|
+
/** Detailed membership information */
|
|
60
|
+
memberships?: TenantMembership[];
|
|
61
|
+
/** Whether user has completed onboarding */
|
|
62
|
+
isOnboarded?: boolean;
|
|
63
|
+
/** Whether user has an active subscription */
|
|
64
|
+
hasSubscription?: boolean;
|
|
65
|
+
/** Whether user's email is verified */
|
|
66
|
+
emailVerified?: boolean;
|
|
67
|
+
/** Whether 2FA is enabled */
|
|
68
|
+
twoFactorEnabled?: boolean;
|
|
69
|
+
/** User's permissions (for current tenant or global) */
|
|
70
|
+
permissions?: string[];
|
|
71
|
+
/** User's global roles */
|
|
72
|
+
globalRoles?: string[];
|
|
73
|
+
/** Version number for cache invalidation */
|
|
74
|
+
contextVersion?: number;
|
|
75
|
+
/** Loading state indicator */
|
|
76
|
+
isLoading?: boolean;
|
|
77
|
+
/** Last time context was updated */
|
|
78
|
+
lastUpdated?: Date;
|
|
79
|
+
/** Additional custom data from backend */
|
|
80
|
+
metadata?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
interface IdentityContextChangedEvent {
|
|
83
|
+
reason: string;
|
|
84
|
+
tenantId?: string;
|
|
85
|
+
triggeredByUserId?: string;
|
|
86
|
+
contextVersion: number;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
}
|
|
89
|
+
type IdentityContextChangeHandler = (event: IdentityContextChangedEvent) => void; //#endregion
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
92
|
+
export { AddressData, IdentityContextChangeHandler, IdentityContextChangedEvent, IdentityContextData };
|
|
93
|
+
//# sourceMappingURL=types-uDrDMkCq.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-uDrDMkCq.d.ts","names":[],"sources":["../src/identity/types.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;UAciB,WAAA;;EAoBA,IAAA,CAAA,EAAA,MAAA;EAAmB,KAAA,EAAA,MAAA;EAAA,KAuBtB,CAAA,EAAA,MAAA,GAAA,IAAA;EAAW,IAYT,EAAA,MAAA;EAAgB,KAwBhB,EAAA,MAAA;EAAI,UAIP,EAAA,MAAA;EAAM,OAAA,EAAA,MAAA;EAGF,SAAA,CAAA,EAAA,OAAA;EAQL,SAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;;UA1EK,mBAAA;;;;;;;;;;;;;;;;;;;;cAuBH;;;;;;;;;;gBAYE;;;;;;;;;;;;;;;;;;gBAwBA;;aAIH;;UAGI,2BAAA;;;;;;;KAQL,4BAAA,WAAuC"}
|