@eminuckan/spine 0.3.0 → 0.3.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/CHANGELOG.md +12 -0
- package/README.md +116 -98
- package/SECURITY.md +5 -15
- package/dist/access/index.mjs +2 -0
- package/dist/{access-CGrsDMwu.js → access-BHy1v5sr.mjs} +6 -8
- package/dist/access-BHy1v5sr.mjs.map +1 -0
- package/dist/api-client/index.mjs +3 -0
- package/dist/api-client/server.mjs +4 -0
- package/dist/{api-client-CgZUMukO.js → api-client-CB7XZaMg.mjs} +8 -10
- package/dist/api-client-CB7XZaMg.mjs.map +1 -0
- package/dist/auth/auth.server.d.ts +1 -0
- package/dist/auth/auth.server.d.ts.map +1 -1
- package/dist/auth/index.mjs +1 -0
- package/dist/auth/route-protection.server.d.ts.map +1 -1
- package/dist/auth/server.mjs +3 -0
- package/dist/{identity-C5un-xAs.js → identity/index.mjs} +24 -21
- package/dist/identity/index.mjs.map +1 -0
- package/dist/{server-Dxsww9v9.js → identity/server.mjs} +7 -8
- package/dist/identity/server.mjs.map +1 -0
- package/dist/{index.js → index.mjs} +11 -13
- package/dist/{logging-DviiAkLx.js → logging/index.mjs} +22 -24
- package/dist/logging/index.mjs.map +1 -0
- package/dist/{permission-service-CvGpwI0d.js → permission-service-DUyKomU3.mjs} +6 -10
- package/dist/permission-service-DUyKomU3.mjs.map +1 -0
- package/dist/permissions/index.mjs +3 -0
- package/dist/{permissions-_n1hIG7Q.js → permissions-C5dTP9y4.mjs} +23 -15
- package/dist/permissions-C5dTP9y4.mjs.map +1 -0
- package/dist/query-client/index.mjs +2 -0
- package/dist/{query-client-Bh3TyB8W.js → query-client-_xB300gc.mjs} +49 -15
- package/dist/query-client-_xB300gc.mjs.map +1 -0
- package/dist/react-router/{index.js → index.mjs} +11 -13
- package/dist/react-router/server.mjs +17 -0
- package/dist/{redis-session-storage.server-v2VcpFsQ.js → redis-session-storage.server-DGgQcJ9B.mjs} +30 -42
- package/dist/redis-session-storage.server-DGgQcJ9B.mjs.map +1 -0
- package/dist/{server-C96NMFBy.js → server-ByScGRXA.mjs} +39 -36
- package/dist/server-ByScGRXA.mjs.map +1 -0
- package/dist/{auth.server-D5oiz9aF.js → server-CqX2yCa4.mjs} +388 -119
- package/dist/server-CqX2yCa4.mjs.map +1 -0
- package/dist/server.mjs +90 -0
- package/dist/server.mjs.map +1 -0
- package/dist/signalr/index.mjs +2 -0
- package/dist/{signalr-k-70VHKo.js → signalr-C-TA8nEE.mjs} +11 -6
- package/dist/signalr-C-TA8nEE.mjs.map +1 -0
- package/dist/{tenant-DfLemb6n.js → tenant/index.mjs} +19 -12
- package/dist/tenant/index.mjs.map +1 -0
- package/dist/{server-Ca-3DJf2.js → tenant/server.mjs} +12 -12
- package/dist/tenant/server.mjs.map +1 -0
- package/dist/{types-BaaE6wjY.js → types-DqCXinAk.mjs} +3 -3
- package/dist/types-DqCXinAk.mjs.map +1 -0
- package/docs/codex-for-oss-application.md +14 -2
- package/package.json +14 -2
- package/dist/access/index.js +0 -4
- package/dist/access-CGrsDMwu.js.map +0 -1
- package/dist/api-client/index.js +0 -5
- package/dist/api-client/server.js +0 -6
- package/dist/api-client-CgZUMukO.js.map +0 -1
- package/dist/auth/index.js +0 -0
- package/dist/auth/server.js +0 -6
- package/dist/auth.server-D5oiz9aF.js.map +0 -1
- package/dist/auth.server-lC94lmlx.js +0 -5
- package/dist/identity/index.js +0 -3
- package/dist/identity/server.js +0 -5
- package/dist/identity-C5un-xAs.js.map +0 -1
- package/dist/logging/index.js +0 -3
- package/dist/logging-DviiAkLx.js.map +0 -1
- package/dist/permission-service-CvGpwI0d.js.map +0 -1
- package/dist/permissions/index.js +0 -4
- package/dist/permissions-_n1hIG7Q.js.map +0 -1
- package/dist/query-client/index.js +0 -3
- package/dist/query-client-Bh3TyB8W.js.map +0 -1
- package/dist/react-router/server.js +0 -20
- package/dist/redis-session-storage.server-v2VcpFsQ.js.map +0 -1
- package/dist/server-B6PCxc-T.js +0 -304
- package/dist/server-B6PCxc-T.js.map +0 -1
- package/dist/server-C96NMFBy.js.map +0 -1
- package/dist/server-Ca-3DJf2.js.map +0 -1
- package/dist/server-Dxsww9v9.js.map +0 -1
- package/dist/server-hGrElJMG.js +0 -78
- package/dist/server-hGrElJMG.js.map +0 -1
- package/dist/server.js +0 -20
- package/dist/signalr/index.js +0 -3
- package/dist/signalr-k-70VHKo.js.map +0 -1
- package/dist/src-B9rDUgSb.js +0 -0
- package/dist/tenant/index.js +0 -3
- package/dist/tenant/server.js +0 -5
- package/dist/tenant-DfLemb6n.js.map +0 -1
- package/dist/types-BaaE6wjY.js.map +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
-
|
|
5
4
|
//#region src/identity/identity-store.ts
|
|
5
|
+
/**
|
|
6
|
+
* Identity Store (Zustand)
|
|
7
|
+
*
|
|
8
|
+
* Centralized state management for identity context.
|
|
9
|
+
*/
|
|
6
10
|
const initialIdentityContext = {
|
|
7
11
|
hasAnyMembership: false,
|
|
8
12
|
hasEntitlement: false,
|
|
@@ -33,6 +37,8 @@ const DEFAULT_IDENTITY_STORE_CONFIG = {
|
|
|
33
37
|
};
|
|
34
38
|
let identityStoreConfig = { ...DEFAULT_IDENTITY_STORE_CONFIG };
|
|
35
39
|
var IdentityUnauthorizedError = class extends Error {
|
|
40
|
+
reason;
|
|
41
|
+
response;
|
|
36
42
|
constructor(reason, response) {
|
|
37
43
|
super(reason);
|
|
38
44
|
this.reason = reason;
|
|
@@ -51,8 +57,7 @@ function isRecord(value) {
|
|
|
51
57
|
}
|
|
52
58
|
function appendRefreshParam(endpoint, forceRefresh) {
|
|
53
59
|
if (!forceRefresh) return endpoint;
|
|
54
|
-
|
|
55
|
-
return `${endpoint}${separator}refresh=true`;
|
|
60
|
+
return `${endpoint}${endpoint.includes("?") ? "&" : "?"}refresh=true`;
|
|
56
61
|
}
|
|
57
62
|
async function readJsonResponse(response) {
|
|
58
63
|
const text = await response.text();
|
|
@@ -135,7 +140,7 @@ const useIdentityStore = create((set, get) => ({
|
|
|
135
140
|
...state.context,
|
|
136
141
|
...newContext,
|
|
137
142
|
isLoading: false,
|
|
138
|
-
lastUpdated: new Date()
|
|
143
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
139
144
|
} }));
|
|
140
145
|
},
|
|
141
146
|
refreshContext: async () => {
|
|
@@ -145,8 +150,7 @@ const useIdentityStore = create((set, get) => ({
|
|
|
145
150
|
...state.context,
|
|
146
151
|
isLoading: true
|
|
147
152
|
} }));
|
|
148
|
-
const
|
|
149
|
-
const newContext = await fetchContext({
|
|
153
|
+
const newContext = await (config.fetchContext ?? defaultFetchIdentityContext)({
|
|
150
154
|
endpoint: config.contextEndpoint,
|
|
151
155
|
fetch: config.fetcher,
|
|
152
156
|
forceRefresh: true,
|
|
@@ -157,7 +161,7 @@ const useIdentityStore = create((set, get) => ({
|
|
|
157
161
|
...get().context,
|
|
158
162
|
...newContext,
|
|
159
163
|
isLoading: false,
|
|
160
|
-
lastUpdated: new Date()
|
|
164
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
161
165
|
} });
|
|
162
166
|
console.log("Identity context refreshed:", newContext);
|
|
163
167
|
} else set((state) => ({ context: {
|
|
@@ -183,8 +187,7 @@ const useIdentityStore = create((set, get) => ({
|
|
|
183
187
|
try {
|
|
184
188
|
const config = getIdentityStoreConfig();
|
|
185
189
|
console.log("Refreshing permissions...");
|
|
186
|
-
const
|
|
187
|
-
const permissions = await fetchPermissions({
|
|
190
|
+
const permissions = await (config.fetchPermissions ?? defaultFetchIdentityPermissions)({
|
|
188
191
|
endpoint: config.permissionsEndpoint,
|
|
189
192
|
fetch: config.fetcher,
|
|
190
193
|
currentContext: get().context
|
|
@@ -192,7 +195,7 @@ const useIdentityStore = create((set, get) => ({
|
|
|
192
195
|
set((state) => ({ context: {
|
|
193
196
|
...state.context,
|
|
194
197
|
permissions,
|
|
195
|
-
lastUpdated: new Date()
|
|
198
|
+
lastUpdated: /* @__PURE__ */ new Date()
|
|
196
199
|
} }));
|
|
197
200
|
console.log("Permissions refreshed:", { count: permissions.length });
|
|
198
201
|
} catch (error) {
|
|
@@ -220,18 +223,19 @@ const useUserTenants = () => useIdentityStore((state) => ({
|
|
|
220
223
|
}));
|
|
221
224
|
const useUserPermissions = () => useIdentityStore((state) => state.context.permissions);
|
|
222
225
|
const useIdentityContext = () => {
|
|
223
|
-
const context = useIdentityStore((state) => state.context);
|
|
224
|
-
const refreshContext = useIdentityStore((state) => state.refreshContext);
|
|
225
|
-
const refreshPermissions = useIdentityStore((state) => state.refreshPermissions);
|
|
226
226
|
return {
|
|
227
|
-
context,
|
|
228
|
-
refreshContext,
|
|
229
|
-
refreshPermissions
|
|
227
|
+
context: useIdentityStore((state) => state.context),
|
|
228
|
+
refreshContext: useIdentityStore((state) => state.refreshContext),
|
|
229
|
+
refreshPermissions: useIdentityStore((state) => state.refreshPermissions)
|
|
230
230
|
};
|
|
231
231
|
};
|
|
232
|
-
|
|
233
232
|
//#endregion
|
|
234
233
|
//#region src/identity/identity-context-provider.tsx
|
|
234
|
+
/**
|
|
235
|
+
* Identity Context Provider
|
|
236
|
+
*
|
|
237
|
+
* Initializes the identity store and listens to SignalR events.
|
|
238
|
+
*/
|
|
235
239
|
function IdentityContextProvider({ children, initialContext, accessToken, signalRClient }) {
|
|
236
240
|
const setContext = useIdentityStore((state) => state.setContext);
|
|
237
241
|
const refreshContext = useIdentityStore((state) => state.refreshContext);
|
|
@@ -252,8 +256,7 @@ function IdentityContextProvider({ children, initialContext, accessToken, signal
|
|
|
252
256
|
} else refreshContext();
|
|
253
257
|
}
|
|
254
258
|
};
|
|
255
|
-
|
|
256
|
-
return unsubscribe;
|
|
259
|
+
return signalRClient.onIdentityContextChanged(handleContextChange);
|
|
257
260
|
}, [
|
|
258
261
|
accessToken,
|
|
259
262
|
contextVersion,
|
|
@@ -273,7 +276,7 @@ function IdentityContextProvider({ children, initialContext, accessToken, signal
|
|
|
273
276
|
}, [signalRClient]);
|
|
274
277
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
275
278
|
}
|
|
276
|
-
|
|
277
279
|
//#endregion
|
|
278
280
|
export { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants };
|
|
279
|
-
|
|
281
|
+
|
|
282
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/identity/identity-store.ts","../../src/identity/identity-context-provider.tsx"],"sourcesContent":["/**\n * Identity Store (Zustand)\n * \n * Centralized state management for identity context.\n */\n\nimport { create } from 'zustand';\nimport type { IdentityContextData } from './types';\n\nexport const initialIdentityContext: IdentityContextData = {\n hasAnyMembership: false,\n hasEntitlement: false,\n hasSubscription: false,\n isOnboarded: false,\n tenants: [],\n currentTenant: undefined,\n permissions: [],\n contextVersion: 0,\n isLoading: true,\n userId: undefined,\n email: undefined,\n firstName: undefined,\n lastName: undefined,\n displayName: undefined,\n profileImageUrl: undefined,\n phoneNumber: undefined,\n timeZone: undefined,\n addresses: undefined,\n hasOwnerMembership: false,\n memberships: undefined,\n};\n\nexport interface IdentityContextFetchContext {\n endpoint: string;\n fetch: typeof fetch;\n forceRefresh: boolean;\n currentContext: IdentityContextData;\n}\n\nexport interface IdentityPermissionsFetchContext {\n endpoint: string;\n fetch: typeof fetch;\n currentContext: IdentityContextData;\n}\n\nexport interface IdentityState {\n context: IdentityContextData;\n\n // Actions\n setContext: (context: Partial<IdentityContextData>) => void;\n refreshContext: () => Promise<void>;\n refreshPermissions: () => Promise<void>;\n reset: () => void;\n}\n\nexport interface IdentityUnauthorizedDetails {\n reason: 'current-user-missing' | 'unauthorized';\n logoutPath: string;\n response?: Response;\n}\n\nexport interface IdentityStoreConfig {\n contextEndpoint?: string;\n permissionsEndpoint?: string;\n logoutPath?: string;\n fetcher?: typeof fetch;\n fetchContext?: (context: IdentityContextFetchContext) => Promise<Partial<IdentityContextData> | null>;\n fetchPermissions?: (context: IdentityPermissionsFetchContext) => Promise<string[]>;\n onUnauthorized?: (details: IdentityUnauthorizedDetails) => void;\n}\n\nconst DEFAULT_IDENTITY_STORE_CONFIG: Required<\n Pick<IdentityStoreConfig, 'contextEndpoint' | 'permissionsEndpoint' | 'logoutPath' | 'fetcher'>\n> = {\n contextEndpoint: '/api/identity/context',\n permissionsEndpoint: '/api/identity/permissions',\n logoutPath: '/auth/logout',\n fetcher: (input, init) => fetch(input, init),\n};\n\nlet identityStoreConfig: IdentityStoreConfig = {\n ...DEFAULT_IDENTITY_STORE_CONFIG,\n};\n\nclass IdentityUnauthorizedError extends Error {\n constructor(\n readonly reason: IdentityUnauthorizedDetails['reason'],\n readonly response?: Response\n ) {\n super(reason);\n this.name = 'IdentityUnauthorizedError';\n }\n}\n\nfunction getIdentityStoreConfig(): IdentityStoreConfig & typeof DEFAULT_IDENTITY_STORE_CONFIG {\n return {\n ...DEFAULT_IDENTITY_STORE_CONFIG,\n ...identityStoreConfig,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction appendRefreshParam(endpoint: string, forceRefresh: boolean): string {\n if (!forceRefresh) {\n return endpoint;\n }\n\n const separator = endpoint.includes('?') ? '&' : '?';\n return `${endpoint}${separator}refresh=true`;\n}\n\nasync function readJsonResponse(response: Response): Promise<unknown> {\n const text = await response.text();\n return text.trim().length > 0 ? JSON.parse(text) : null;\n}\n\nexport function normalizeIdentityContextPayload(payload: unknown): Partial<IdentityContextData> | null {\n if (!isRecord(payload)) {\n return null;\n }\n\n if (payload.success === false) {\n return null;\n }\n\n const nestedContext = payload.context ?? payload.data ?? payload.identity ?? payload.user;\n if (isRecord(nestedContext)) {\n return nestedContext as Partial<IdentityContextData>;\n }\n\n return payload as Partial<IdentityContextData>;\n}\n\nexport function normalizeIdentityPermissionsPayload(payload: unknown): string[] {\n if (Array.isArray(payload)) {\n return payload.filter((permission): permission is string => typeof permission === 'string');\n }\n\n if (!isRecord(payload)) {\n return [];\n }\n\n const nestedPermissions =\n payload.permissions ??\n (isRecord(payload.data) ? payload.data.permissions : payload.data) ??\n (isRecord(payload.context) ? payload.context.permissions : undefined);\n\n return Array.isArray(nestedPermissions)\n ? nestedPermissions.filter((permission): permission is string => typeof permission === 'string')\n : [];\n}\n\nasync function defaultFetchIdentityContext({\n endpoint,\n fetch: fetchFn,\n forceRefresh,\n}: IdentityContextFetchContext): Promise<Partial<IdentityContextData> | null> {\n const response = await fetchFn(appendRefreshParam(endpoint, forceRefresh), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (response.ok) {\n return normalizeIdentityContextPayload(await readJsonResponse(response));\n }\n\n if (response.status === 404) {\n const errorData = await readJsonResponse(response).catch(() => ({}));\n if (\n isRecord(errorData) &&\n (errorData.code === 'CurrentUserMissing' || errorData.title === 'CurrentUserMissing')\n ) {\n throw new IdentityUnauthorizedError('current-user-missing', response);\n }\n }\n\n if (response.status === 401) {\n throw new IdentityUnauthorizedError('unauthorized', response);\n }\n\n console.error('Failed to refresh identity context:', response.status);\n return null;\n}\n\nasync function defaultFetchIdentityPermissions({\n endpoint,\n fetch: fetchFn,\n}: IdentityPermissionsFetchContext): Promise<string[]> {\n const response = await fetchFn(endpoint, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (response.ok) {\n return normalizeIdentityPermissionsPayload(await readJsonResponse(response));\n }\n\n if (response.status === 401) {\n throw new IdentityUnauthorizedError('unauthorized', response);\n }\n\n console.error('Failed to refresh permissions:', response.status);\n return [];\n}\n\nfunction handleUnauthorized(details: Omit<IdentityUnauthorizedDetails, 'logoutPath'>): void {\n const config = getIdentityStoreConfig();\n const unauthorizedDetails: IdentityUnauthorizedDetails = {\n ...details,\n logoutPath: config.logoutPath,\n };\n\n if (config.onUnauthorized) {\n config.onUnauthorized(unauthorizedDetails);\n return;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = config.logoutPath;\n }\n}\n\n/**\n * Configure the identity store for different app/back-end conventions.\n */\nexport function configureIdentityStore(config: IdentityStoreConfig): void {\n identityStoreConfig = {\n ...identityStoreConfig,\n ...config,\n };\n}\n\n/**\n * Reset identity store configuration to the built-in defaults.\n */\nexport function resetIdentityStoreConfig(): void {\n identityStoreConfig = {\n ...DEFAULT_IDENTITY_STORE_CONFIG,\n };\n}\n\n/**\n * Backward-compatible endpoint-only configuration helper.\n */\nexport function configureIdentityEndpoints(config: {\n contextEndpoint?: string;\n permissionsEndpoint?: string;\n}): void {\n configureIdentityStore(config);\n}\n\nexport const useIdentityStore = create<IdentityState>((set, get) => ({\n context: initialIdentityContext,\n\n setContext: (newContext) => {\n set((state) => ({\n context: {\n ...state.context,\n ...newContext,\n isLoading: false,\n lastUpdated: new Date(),\n },\n }));\n },\n\n refreshContext: async () => {\n try {\n const config = getIdentityStoreConfig();\n\n set((state) => ({\n context: { ...state.context, isLoading: true },\n }));\n\n const fetchContext = config.fetchContext ?? defaultFetchIdentityContext;\n const newContext = await fetchContext({\n endpoint: config.contextEndpoint,\n fetch: config.fetcher,\n forceRefresh: true,\n currentContext: get().context,\n });\n\n if (newContext) {\n set({\n context: {\n ...get().context,\n ...newContext,\n isLoading: false,\n lastUpdated: new Date(),\n },\n });\n console.log('Identity context refreshed:', newContext);\n } else {\n set((state) => ({\n context: { ...state.context, isLoading: false },\n }));\n }\n } catch (error) {\n if (error instanceof IdentityUnauthorizedError) {\n handleUnauthorized({\n reason: error.reason,\n response: error.response,\n });\n return;\n }\n\n console.error('Error refreshing identity context:', error);\n set((state) => ({\n context: { ...state.context, isLoading: false },\n }));\n }\n },\n\n refreshPermissions: async () => {\n try {\n const config = getIdentityStoreConfig();\n\n console.log('Refreshing permissions...');\n\n const fetchPermissions = config.fetchPermissions ?? defaultFetchIdentityPermissions;\n const permissions = await fetchPermissions({\n endpoint: config.permissionsEndpoint,\n fetch: config.fetcher,\n currentContext: get().context,\n });\n\n set((state) => ({\n context: {\n ...state.context,\n permissions,\n lastUpdated: new Date(),\n },\n }));\n\n console.log('Permissions refreshed:', {\n count: permissions.length,\n });\n } catch (error) {\n if (error instanceof IdentityUnauthorizedError) {\n handleUnauthorized({\n reason: error.reason,\n response: error.response,\n });\n return;\n }\n\n console.error('Error refreshing permissions:', error);\n }\n },\n\n reset: () => {\n set({ context: initialIdentityContext });\n },\n}));\n\n// Convenience selectors\nexport const useIsOnboarded = () => useIdentityStore((state) => state.context.isOnboarded);\nexport const useHasEntitlement = () => useIdentityStore((state) => state.context.hasEntitlement ?? state.context.hasSubscription);\n/** @deprecated Use useHasEntitlement or an app-specific selector instead. */\nexport const useHasSubscription = () => useIdentityStore((state) => state.context.hasSubscription);\nexport const useUserTenants = () =>\n useIdentityStore((state) => ({\n tenants: state.context.tenants,\n currentTenant: state.context.currentTenant,\n }));\nexport const useUserPermissions = () => useIdentityStore((state) => state.context.permissions);\nexport const useIdentityContext = () => {\n const context = useIdentityStore((state) => state.context);\n const refreshContext = useIdentityStore((state) => state.refreshContext);\n const refreshPermissions = useIdentityStore((state) => state.refreshPermissions);\n\n return { context, refreshContext, refreshPermissions };\n};\n","/**\n * Identity Context Provider\n * \n * Initializes the identity store and listens to SignalR events.\n */\n\nimport { useEffect, type ReactNode } from 'react';\nimport { useIdentityStore } from './identity-store';\nimport type { IdentityContextData, IdentityContextChangedEvent } from './types';\n\ninterface IdentityContextProviderProps {\n children: ReactNode;\n initialContext?: Partial<IdentityContextData>;\n accessToken?: string | null;\n signalRClient?: {\n onIdentityContextChanged: (handler: (event: IdentityContextChangedEvent) => void) => () => void;\n disconnect: () => Promise<void>;\n };\n}\n\nexport function IdentityContextProvider({\n children,\n initialContext,\n accessToken,\n signalRClient,\n}: IdentityContextProviderProps) {\n const setContext = useIdentityStore((state) => state.setContext);\n const refreshContext = useIdentityStore((state) => state.refreshContext);\n const refreshPermissions = useIdentityStore((state) => state.refreshPermissions);\n const contextVersion = useIdentityStore((state) => state.context.contextVersion);\n\n // Initialize store with initial context from server\n useEffect(() => {\n if (initialContext) {\n setContext(initialContext);\n }\n }, [initialContext, setContext]);\n\n // Handle SignalR identity context changes\n useEffect(() => {\n if (!signalRClient) return;\n\n const handleContextChange = (event: IdentityContextChangedEvent) => {\n console.log('Identity context changed via SignalR:', event);\n\n if (event.contextVersion > (contextVersion ?? 0)) {\n console.log('Refreshing context due to SignalR event...');\n\n if (event.reason === 'PermissionsChanged' || event.reason === 'RoleAssignmentChanged') {\n console.log('Permission-specific change detected, refreshing permissions only...');\n refreshPermissions();\n } else {\n refreshContext();\n }\n }\n };\n\n const unsubscribe = signalRClient.onIdentityContextChanged(handleContextChange);\n\n return unsubscribe;\n }, [accessToken, contextVersion, refreshContext, refreshPermissions, signalRClient]);\n\n // Initial context load if not provided\n useEffect(() => {\n if (!initialContext) {\n refreshContext();\n }\n }, [initialContext, refreshContext]);\n\n // Cleanup SignalR on unmount\n useEffect(() => {\n return () => {\n if (signalRClient) {\n signalRClient.disconnect().catch((error) => {\n console.error('Error disconnecting SignalR:', error);\n });\n }\n };\n }, [signalRClient]);\n\n return <>{children}</>;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { logger } from "
|
|
2
|
-
import { isApiClientError } from "
|
|
3
|
-
|
|
1
|
+
import { logger } from "../logging/index.mjs";
|
|
2
|
+
import { t as isApiClientError } from "../types-DqCXinAk.mjs";
|
|
4
3
|
//#region src/identity/identity-context.server.ts
|
|
5
4
|
let identityAPIFetcher = null;
|
|
6
5
|
/**
|
|
@@ -9,8 +8,8 @@ let identityAPIFetcher = null;
|
|
|
9
8
|
function configureIdentityAPIFetcher(fetcher) {
|
|
10
9
|
identityAPIFetcher = fetcher;
|
|
11
10
|
}
|
|
12
|
-
const contextCache = new Map();
|
|
13
|
-
const CONTEXT_CACHE_TTL =
|
|
11
|
+
const contextCache = /* @__PURE__ */ new Map();
|
|
12
|
+
const CONTEXT_CACHE_TTL = 300 * 1e3;
|
|
14
13
|
/**
|
|
15
14
|
* Fetch identity context from backend
|
|
16
15
|
*/
|
|
@@ -30,7 +29,7 @@ async function fetchIdentityContext(request) {
|
|
|
30
29
|
logger.error("Failed to fetch identity context", error instanceof Error ? error : void 0);
|
|
31
30
|
if (isApiClientError(error) && error.response?.status === 404 && error.problemDetails?.title === "CurrentUserMissing") {
|
|
32
31
|
logger.error("CurrentUserMissing error detected - session is invalid");
|
|
33
|
-
const invalidSessionError = new Error("INVALID_SESSION");
|
|
32
|
+
const invalidSessionError = /* @__PURE__ */ new Error("INVALID_SESSION");
|
|
34
33
|
invalidSessionError.isInvalidSession = true;
|
|
35
34
|
throw invalidSessionError;
|
|
36
35
|
}
|
|
@@ -141,7 +140,7 @@ function updateContextVersion(userId, newVersion) {
|
|
|
141
140
|
version: newVersion
|
|
142
141
|
});
|
|
143
142
|
}
|
|
144
|
-
|
|
145
143
|
//#endregion
|
|
146
144
|
export { clearIdentityContextCache, configureIdentityAPIFetcher, configurePermissionFetcher, contextToUserInfo, fetchIdentityContext, fetchUserPermissions, getIdentityContext, hasContextVersionChanged, updateContextVersion };
|
|
147
|
-
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/identity/identity-context.server.ts"],"sourcesContent":["/**\n * Identity Context Server Module\n * \n * Server-side identity context management.\n */\n\nimport type { AddressData, IdentityContextData, TenantMembership } from './types';\nimport { isApiClientError } from '../api-client/types';\nimport { logger } from '../logging';\n\n/**\n * Identity API fetcher type (to avoid coupling with specific API client)\n */\nexport type IdentityAPIFetcher = (request: Request) => Promise<IdentityContextData>;\n\nlet identityAPIFetcher: IdentityAPIFetcher | null = null;\n\n/**\n * Configure identity API fetcher\n */\nexport function configureIdentityAPIFetcher(fetcher: IdentityAPIFetcher): void {\n identityAPIFetcher = fetcher;\n}\n\n// In-memory cache for identity context\nconst contextCache = new Map<\n string,\n {\n context: IdentityContextData;\n timestamp: number;\n version: number;\n }\n>();\n\nconst CONTEXT_CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Fetch identity context from backend\n */\nexport async function fetchIdentityContext(request: Request): Promise<IdentityContextData> {\n if (!identityAPIFetcher) {\n throw new Error('Identity API fetcher not configured. Call configureIdentityAPIFetcher first.');\n }\n\n try {\n logger.info('Fetching identity context');\n const context = await identityAPIFetcher(request);\n \n logger.debug('Identity context fetched', {\n hasAnyMembership: context.hasAnyMembership,\n hasEntitlement: context.hasEntitlement ?? context.hasSubscription,\n isOnboarded: context.isOnboarded,\n membershipCount: context.memberships?.length || 0,\n });\n\n return context;\n } catch (error: unknown) {\n logger.error('Failed to fetch identity context', error instanceof Error ? error : undefined);\n\n // Check if this is a CurrentUserMissing error\n if (isApiClientError(error) && error.response?.status === 404 && error.problemDetails?.title === 'CurrentUserMissing') {\n logger.error('CurrentUserMissing error detected - session is invalid');\n const invalidSessionError = new Error('INVALID_SESSION');\n (invalidSessionError as Error & { isInvalidSession: boolean }).isInvalidSession = true;\n throw invalidSessionError;\n }\n\n throw error;\n }\n}\n\n/**\n * Get cached identity context or fetch fresh if needed\n */\nexport async function getIdentityContext(\n request: Request,\n userId: string,\n forceRefresh = false\n): Promise<IdentityContextData> {\n const cacheKey = userId;\n const cached = contextCache.get(cacheKey);\n const now = Date.now();\n\n // Return cached if valid and not forcing refresh\n if (!forceRefresh && cached && now - cached.timestamp < CONTEXT_CACHE_TTL) {\n logger.debug('Using cached identity context for user', { userId });\n return cached.context;\n }\n\n // Fetch fresh context\n const context = await fetchIdentityContext(request);\n\n // Cache the result\n contextCache.set(cacheKey, {\n context,\n timestamp: now,\n version: context.contextVersion ?? 0,\n });\n\n logger.debug('Cached fresh identity context for user', { userId });\n return context;\n}\n\n/**\n * Clear identity context cache for user\n */\nexport function clearIdentityContextCache(userId?: string): void {\n if (userId) {\n contextCache.delete(userId);\n logger.debug('Cleared identity context cache for user', { userId });\n } else {\n contextCache.clear();\n logger.debug('Cleared all identity context cache');\n }\n}\n\n/**\n * Permission fetcher type\n */\nexport type PermissionFetcher = (request: Request, tenantId: string) => Promise<string[]>;\n\nlet permissionFetcher: PermissionFetcher | null = null;\n\n/**\n * Configure permission fetcher\n */\nexport function configurePermissionFetcher(fetcher: PermissionFetcher): void {\n permissionFetcher = fetcher;\n}\n\n/**\n * Fetch user permissions for a specific tenant\n */\nexport async function fetchUserPermissions(request: Request, tenantId: string): Promise<string[]> {\n if (!tenantId) {\n logger.warn('No tenant ID provided, skipping permission fetch');\n return [];\n }\n\n if (!permissionFetcher) {\n throw new Error('Permission fetcher not configured. Call configurePermissionFetcher first.');\n }\n\n try {\n return await permissionFetcher(request, tenantId);\n } catch (error) {\n logger.error('Failed to fetch user permissions', error instanceof Error ? error : undefined);\n return [];\n }\n}\n\nexport interface UserContextInfo {\n tenants: string[];\n memberships: TenantMembership[];\n currentTenant?: string;\n isOnboarded: boolean;\n permissions: string[];\n hasEntitlement: boolean;\n /** @deprecated Use hasEntitlement or app-specific fields instead. */\n hasSubscription: boolean;\n userId?: string;\n email?: string;\n firstName?: string | null;\n lastName?: string | null;\n displayName?: string | null;\n profileImageUrl?: string | null;\n phoneNumber?: string | null;\n timeZone?: string;\n addresses?: AddressData[];\n hasOwnerMembership: boolean;\n}\n\nexport interface ContextToUserInfoOptions {\n currentTenant?: string | null;\n request?: Request;\n}\n\n/**\n * Convert raw identity context into a tenant-aware user info shape.\n */\nexport async function contextToUserInfo(\n context: IdentityContextData,\n options: ContextToUserInfoOptions = {}\n): Promise<UserContextInfo> {\n const memberships = context.memberships || [];\n const tenants = memberships.map((membership) => membership.tenantId);\n const resolvedCurrentTenant =\n options.currentTenant && tenants.includes(options.currentTenant)\n ? options.currentTenant\n : tenants[0] || undefined;\n\n let permissions: string[] = [];\n if (resolvedCurrentTenant && options.request) {\n permissions = await fetchUserPermissions(options.request, resolvedCurrentTenant);\n }\n\n return {\n tenants,\n memberships,\n currentTenant: resolvedCurrentTenant,\n isOnboarded: context.isOnboarded ?? false,\n permissions,\n hasEntitlement: context.hasEntitlement ?? context.hasSubscription ?? false,\n hasSubscription: context.hasSubscription ?? context.hasEntitlement ?? false,\n userId: context.userId,\n email: context.email,\n firstName: context.firstName,\n lastName: context.lastName,\n displayName: context.displayName,\n profileImageUrl: context.profileImageUrl,\n phoneNumber: context.phoneNumber,\n timeZone: context.timeZone,\n addresses: context.addresses,\n hasOwnerMembership: context.hasOwnerMembership ?? false,\n };\n}\n\n/**\n * Check if a cached context version differs from the latest version.\n */\nexport function hasContextVersionChanged(userId: string, newVersion: number): boolean {\n const cached = contextCache.get(userId);\n return !cached || cached.version !== newVersion;\n}\n\n/**\n * Mark a cached identity context as stale after a version change.\n */\nexport function updateContextVersion(userId: string, newVersion: number): void {\n const cached = contextCache.get(userId);\n if (!cached) return;\n\n cached.version = newVersion;\n cached.timestamp = 0;\n logger.debug('Marked identity context as stale for user', {\n userId,\n version: newVersion,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { LOG_LEVEL_PRIORITY, LogLevel, Logger, createLogger, defaultLoggerConfig, logger } from "./logging
|
|
2
|
-
import { PermissionChecker } from "./permission-service-
|
|
3
|
-
import {
|
|
4
|
-
import { evaluateAccessRequirement } from "./access-
|
|
5
|
-
import { TenantProvider, configureTenantClient, configureTenantEndpoints, getTenantCookieName, initializeTenantStore, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetTenantClientConfig, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useOrganization, useTenant, useTenantData, useTenantStore, useTenantSwitcher } from "./tenant
|
|
6
|
-
import { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants } from "./identity
|
|
7
|
-
import { isApiClientError } from "./types-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export { ApiError, ErrorCodes, ErrorHandler, IdentityContextProvider, IdentitySignalRClient, LOG_LEVEL_PRIORITY, LogLevel, Logger, PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, SignalRClient, TenantProvider, cachePresets, calculateBackoffDelay, cleanupSignalR, configureIdentityEndpoints, configureIdentityStore, configureTenantClient, configureTenantEndpoints, createIdentitySignalRClient, createLogger, createQueryClient, createQueryKeyFactory, createRetryFetch, defaultLoggerConfig, evaluateAccessRequirement, fetchWithTimeout, getIdentitySignalRClient, getPermissionChecker, getPermissions, getTenantCookieName, handleApiError, initialIdentityContext, initializePermissions, initializeSignalR, initializeTenantStore, invalidationHelpers, isApiClientError, isRetryableError, logger, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetIdentityStoreConfig, resetTenantClientConfig, useAllPermissions, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useHasEntitlement, useHasModulePermission, useHasPermission, useHasPermissions, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useModulePermission, useOrganization, usePermission, usePermissionChecker, usePermissionStore, usePermissions, useTenant, useTenantData, useTenantStore, useTenantSwitcher, useUserPermissions, useUserTenants, withRetry };
|
|
1
|
+
import { LOG_LEVEL_PRIORITY, LogLevel, Logger, createLogger, defaultLoggerConfig, logger } from "./logging/index.mjs";
|
|
2
|
+
import { t as PermissionChecker } from "./permission-service-DUyKomU3.mjs";
|
|
3
|
+
import { _ as initializePermissions, a as ProtectedSection, c as useHasPermission, d as usePermission, f as usePermissionChecker, g as getPermissions, h as getPermissionChecker, i as ProtectedLink, l as useHasPermissions, m as PermissionInitializer, n as ProtectedAction, o as useAllPermissions, p as usePermissions, r as ProtectedButton, s as useHasModulePermission, t as PERMISSION_MODULES, u as useModulePermission, v as usePermissionStore } from "./permissions-C5dTP9y4.mjs";
|
|
4
|
+
import { t as evaluateAccessRequirement } from "./access-BHy1v5sr.mjs";
|
|
5
|
+
import { TenantProvider, configureTenantClient, configureTenantEndpoints, getTenantCookieName, initializeTenantStore, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetTenantClientConfig, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useOrganization, useTenant, useTenantData, useTenantStore, useTenantSwitcher } from "./tenant/index.mjs";
|
|
6
|
+
import { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants } from "./identity/index.mjs";
|
|
7
|
+
import { t as isApiClientError } from "./types-DqCXinAk.mjs";
|
|
8
|
+
import { a as withRetry, c as handleApiError, i as isRetryableError, l as ErrorCodes, n as createRetryFetch, o as ApiError, r as fetchWithTimeout, s as ErrorHandler, t as calculateBackoffDelay } from "./api-client-CB7XZaMg.mjs";
|
|
9
|
+
import { a as getIdentitySignalRClient, i as createIdentitySignalRClient, n as SignalRClient, o as initializeSignalR, r as cleanupSignalR, t as IdentitySignalRClient } from "./signalr-C-TA8nEE.mjs";
|
|
10
|
+
import { i as invalidationHelpers, n as createQueryClient, r as createQueryKeyFactory, t as cachePresets } from "./query-client-_xB300gc.mjs";
|
|
11
|
+
export { ApiError, ErrorCodes, ErrorHandler, IdentityContextProvider, IdentitySignalRClient, LOG_LEVEL_PRIORITY, LogLevel, Logger, PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, SignalRClient, TenantProvider, cachePresets, calculateBackoffDelay, cleanupSignalR, configureIdentityEndpoints, configureIdentityStore, configureTenantClient, configureTenantEndpoints, createIdentitySignalRClient, createLogger, createQueryClient, createQueryKeyFactory, createRetryFetch, defaultLoggerConfig, evaluateAccessRequirement, fetchWithTimeout, getIdentitySignalRClient, getPermissionChecker, getPermissions, getTenantCookieName, handleApiError, initialIdentityContext, initializePermissions, initializeSignalR, initializeTenantStore, invalidationHelpers, isApiClientError, isRetryableError, logger, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetIdentityStoreConfig, resetTenantClientConfig, useAllPermissions, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useHasEntitlement, useHasModulePermission, useHasPermission, useHasPermissions, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useModulePermission, useOrganization, usePermission, usePermissionChecker, usePermissionStore, usePermissions, useTenant, useTenantData, useTenantStore, useTenantSwitcher, useUserPermissions, useUserTenants, withRetry };
|
|
@@ -2,29 +2,27 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Logging Types
|
|
4
4
|
*/
|
|
5
|
-
let LogLevel = /* @__PURE__ */ function(LogLevel
|
|
6
|
-
LogLevel
|
|
7
|
-
LogLevel
|
|
8
|
-
LogLevel
|
|
9
|
-
LogLevel
|
|
10
|
-
return LogLevel
|
|
5
|
+
let LogLevel = /* @__PURE__ */ function(LogLevel) {
|
|
6
|
+
LogLevel["DEBUG"] = "debug";
|
|
7
|
+
LogLevel["INFO"] = "info";
|
|
8
|
+
LogLevel["WARN"] = "warn";
|
|
9
|
+
LogLevel["ERROR"] = "error";
|
|
10
|
+
return LogLevel;
|
|
11
11
|
}({});
|
|
12
|
-
|
|
13
12
|
//#endregion
|
|
14
13
|
//#region src/logging/config.ts
|
|
15
14
|
const LOG_LEVEL_PRIORITY = {
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
15
|
+
["debug"]: 0,
|
|
16
|
+
["info"]: 1,
|
|
17
|
+
["warn"]: 2,
|
|
18
|
+
["error"]: 3
|
|
20
19
|
};
|
|
21
20
|
const defaultLoggerConfig = {
|
|
22
|
-
minLevel: process.env.NODE_ENV === "production" ?
|
|
21
|
+
minLevel: process.env.NODE_ENV === "production" ? "info" : "debug",
|
|
23
22
|
enableConsole: true,
|
|
24
23
|
enableRemote: false,
|
|
25
24
|
serviceName: "spine"
|
|
26
25
|
};
|
|
27
|
-
|
|
28
26
|
//#endregion
|
|
29
27
|
//#region src/logging/logger.ts
|
|
30
28
|
var Logger = class {
|
|
@@ -36,21 +34,21 @@ var Logger = class {
|
|
|
36
34
|
};
|
|
37
35
|
}
|
|
38
36
|
debug(message, context) {
|
|
39
|
-
this.log(
|
|
37
|
+
this.log("debug", message, context);
|
|
40
38
|
}
|
|
41
39
|
info(message, context) {
|
|
42
|
-
this.log(
|
|
40
|
+
this.log("info", message, context);
|
|
43
41
|
}
|
|
44
42
|
warn(message, context) {
|
|
45
|
-
this.log(
|
|
43
|
+
this.log("warn", message, context);
|
|
46
44
|
}
|
|
47
45
|
error(message, error, context) {
|
|
48
|
-
this.log(
|
|
46
|
+
this.log("error", message, context, error);
|
|
49
47
|
}
|
|
50
48
|
log(level, message, context, error) {
|
|
51
49
|
if (!this.shouldLog(level)) return;
|
|
52
50
|
const entry = {
|
|
53
|
-
timestamp: new Date().toISOString(),
|
|
51
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
54
52
|
level,
|
|
55
53
|
message,
|
|
56
54
|
context,
|
|
@@ -67,16 +65,16 @@ var Logger = class {
|
|
|
67
65
|
logToConsole(entry) {
|
|
68
66
|
const formatted = this.formatEntry(entry);
|
|
69
67
|
switch (entry.level) {
|
|
70
|
-
case
|
|
68
|
+
case "debug":
|
|
71
69
|
console.debug(formatted);
|
|
72
70
|
break;
|
|
73
|
-
case
|
|
71
|
+
case "info":
|
|
74
72
|
console.info(formatted);
|
|
75
73
|
break;
|
|
76
|
-
case
|
|
74
|
+
case "warn":
|
|
77
75
|
console.warn(formatted);
|
|
78
76
|
break;
|
|
79
|
-
case
|
|
77
|
+
case "error":
|
|
80
78
|
console.error(formatted);
|
|
81
79
|
if (entry.error) console.error(entry.error);
|
|
82
80
|
break;
|
|
@@ -112,7 +110,7 @@ const logger = new Logger();
|
|
|
112
110
|
function createLogger(config) {
|
|
113
111
|
return new Logger(config);
|
|
114
112
|
}
|
|
115
|
-
|
|
116
113
|
//#endregion
|
|
117
114
|
export { LOG_LEVEL_PRIORITY, LogLevel, Logger, createLogger, defaultLoggerConfig, logger };
|
|
118
|
-
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/logging/types.ts","../../src/logging/config.ts","../../src/logging/logger.ts"],"sourcesContent":["/**\n * Logging Types\n */\n\nexport enum LogLevel {\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n}\n\nexport interface LogContext {\n [key: string]: unknown;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n context?: LogContext;\n error?: Error;\n}\n\nexport interface LoggerConfig {\n minLevel: LogLevel;\n enableConsole: boolean;\n enableRemote: boolean;\n remoteEndpoint?: string;\n serviceName?: string;\n}\n","/**\n * Logger Configuration\n */\n\nimport { LogLevel, type LoggerConfig } from './types';\n\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n [LogLevel.DEBUG]: 0,\n [LogLevel.INFO]: 1,\n [LogLevel.WARN]: 2,\n [LogLevel.ERROR]: 3,\n};\n\nexport const defaultLoggerConfig: LoggerConfig = {\n minLevel: process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG,\n enableConsole: true,\n enableRemote: false,\n serviceName: 'spine',\n};\n","/**\n * Logger Service\n * \n * Centralized logging implementation with structured logging format.\n */\n\nimport { LogLevel, type LogContext, type LogEntry, type LoggerConfig } from './types';\nimport { defaultLoggerConfig, LOG_LEVEL_PRIORITY } from './config';\n\nexport class Logger {\n private config: LoggerConfig;\n\n constructor(config?: Partial<LoggerConfig>) {\n this.config = {\n ...defaultLoggerConfig,\n ...config,\n };\n }\n\n debug(message: string, context?: LogContext): void {\n this.log(LogLevel.DEBUG, message, context);\n }\n\n info(message: string, context?: LogContext): void {\n this.log(LogLevel.INFO, message, context);\n }\n\n warn(message: string, context?: LogContext): void {\n this.log(LogLevel.WARN, message, context);\n }\n\n error(message: string, error?: Error, context?: LogContext): void {\n this.log(LogLevel.ERROR, message, context, error);\n }\n\n private log(\n level: LogLevel,\n message: string,\n context?: LogContext,\n error?: Error\n ): void {\n if (!this.shouldLog(level)) {\n return;\n }\n\n const entry: LogEntry = {\n timestamp: new Date().toISOString(),\n level,\n message,\n context,\n error,\n };\n\n if (this.config.enableConsole) {\n this.logToConsole(entry);\n }\n\n if (this.config.enableRemote && this.config.remoteEndpoint) {\n this.sendToRemote(entry).catch((err) => {\n console.error('Failed to send log to remote endpoint:', err);\n });\n }\n }\n\n private shouldLog(level: LogLevel): boolean {\n return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.config.minLevel];\n }\n\n private logToConsole(entry: LogEntry): void {\n const formatted = this.formatEntry(entry);\n\n switch (entry.level) {\n case LogLevel.DEBUG:\n console.debug(formatted);\n break;\n case LogLevel.INFO:\n console.info(formatted);\n break;\n case LogLevel.WARN:\n console.warn(formatted);\n break;\n case LogLevel.ERROR:\n console.error(formatted);\n if (entry.error) {\n console.error(entry.error);\n }\n break;\n }\n }\n\n private formatEntry(entry: LogEntry): string {\n const parts = [\n `[${entry.timestamp}]`,\n `[${entry.level.toUpperCase()}]`,\n entry.message,\n ];\n\n if (entry.context && Object.keys(entry.context).length > 0) {\n parts.push(JSON.stringify(entry.context));\n }\n\n return parts.join(' ');\n }\n\n private async sendToRemote(entry: LogEntry): Promise<void> {\n if (!this.config.remoteEndpoint) return;\n\n await fetch(this.config.remoteEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...entry,\n serviceName: this.config.serviceName,\n error: entry.error\n ? {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n }\n : undefined,\n }),\n });\n }\n}\n\n// Default logger instance\nexport const logger = new Logger();\n\n// Create logger with custom config\nexport function createLogger(config?: Partial<LoggerConfig>): Logger {\n return new Logger(config);\n}\n"],"mappings":";;;;AAIA,IAAY,WAAL,yBAAA,UAAA;CACL,SAAA,WAAA;CACA,SAAA,UAAA;CACA,SAAA,UAAA;CACA,SAAA,WAAA;;AACF,EAAA,CAAA,CAAA;;;ACHA,MAAa,qBAA+C;YACxC;WACD;WACA;YACC;AACpB;AAEA,MAAa,sBAAoC;CAC/C,UAAU,QAAQ,IAAI,aAAa,eAAA,SAAA;CACnC,eAAe;CACf,cAAc;CACd,aAAa;AACf;;;ACTA,IAAa,SAAb,MAAoB;CAClB;CAEA,YAAY,QAAgC;EAC1C,KAAK,SAAS;GACZ,GAAG;GACH,GAAG;EACL;CACF;CAEA,MAAM,SAAiB,SAA4B;EACjD,KAAK,IAAA,SAAoB,SAAS,OAAO;CAC3C;CAEA,KAAK,SAAiB,SAA4B;EAChD,KAAK,IAAA,QAAmB,SAAS,OAAO;CAC1C;CAEA,KAAK,SAAiB,SAA4B;EAChD,KAAK,IAAA,QAAmB,SAAS,OAAO;CAC1C;CAEA,MAAM,SAAiB,OAAe,SAA4B;EAChE,KAAK,IAAA,SAAoB,SAAS,SAAS,KAAK;CAClD;CAEA,IACE,OACA,SACA,SACA,OACM;EACN,IAAI,CAAC,KAAK,UAAU,KAAK,GACvB;EAGF,MAAM,QAAkB;GACtB,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GAClC;GACA;GACA;GACA;EACF;EAEA,IAAI,KAAK,OAAO,eACd,KAAK,aAAa,KAAK;EAGzB,IAAI,KAAK,OAAO,gBAAgB,KAAK,OAAO,gBAC1C,KAAK,aAAa,KAAK,CAAC,CAAC,OAAO,QAAQ;GACtC,QAAQ,MAAM,0CAA0C,GAAG;EAC7D,CAAC;CAEL;CAEA,UAAkB,OAA0B;EAC1C,OAAO,mBAAmB,UAAU,mBAAmB,KAAK,OAAO;CACrE;CAEA,aAAqB,OAAuB;EAC1C,MAAM,YAAY,KAAK,YAAY,KAAK;EAExC,QAAQ,MAAM,OAAd;GACE,KAAA;IACE,QAAQ,MAAM,SAAS;IACvB;GACF,KAAA;IACE,QAAQ,KAAK,SAAS;IACtB;GACF,KAAA;IACE,QAAQ,KAAK,SAAS;IACtB;GACF,KAAA;IACE,QAAQ,MAAM,SAAS;IACvB,IAAI,MAAM,OACR,QAAQ,MAAM,MAAM,KAAK;IAE3B;EACJ;CACF;CAEA,YAAoB,OAAyB;EAC3C,MAAM,QAAQ;GACZ,IAAI,MAAM,UAAU;GACpB,IAAI,MAAM,MAAM,YAAY,EAAE;GAC9B,MAAM;EACR;EAEA,IAAI,MAAM,WAAW,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,SAAS,GACvD,MAAM,KAAK,KAAK,UAAU,MAAM,OAAO,CAAC;EAG1C,OAAO,MAAM,KAAK,GAAG;CACvB;CAEA,MAAc,aAAa,OAAgC;EACzD,IAAI,CAAC,KAAK,OAAO,gBAAgB;EAEjC,MAAM,MAAM,KAAK,OAAO,gBAAgB;GACtC,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU;IACnB,GAAG;IACH,aAAa,KAAK,OAAO;IACzB,OAAO,MAAM,QACT;KACE,MAAM,MAAM,MAAM;KAClB,SAAS,MAAM,MAAM;KACrB,OAAO,MAAM,MAAM;IACrB,IACA,KAAA;GACN,CAAC;EACH,CAAC;CACH;AACF;AAGA,MAAa,SAAS,IAAI,OAAO;AAGjC,SAAgB,aAAa,QAAwC;CACnE,OAAO,IAAI,OAAO,MAAM;AAC1B"}
|
|
@@ -24,14 +24,10 @@ var PermissionChecker = class {
|
|
|
24
24
|
const { operator = "OR", wildcard = false } = options;
|
|
25
25
|
const matchedPermissions = [];
|
|
26
26
|
const missingPermissions = [];
|
|
27
|
-
for (const permission of permissions)
|
|
28
|
-
|
|
29
|
-
if (hasIt) matchedPermissions.push(permission);
|
|
30
|
-
else missingPermissions.push(permission);
|
|
31
|
-
}
|
|
32
|
-
const hasPermission = operator === "AND" ? missingPermissions.length === 0 : matchedPermissions.length > 0;
|
|
27
|
+
for (const permission of permissions) if (wildcard ? this.hasWildcardPermission(permission) : this.hasPermission(permission)) matchedPermissions.push(permission);
|
|
28
|
+
else missingPermissions.push(permission);
|
|
33
29
|
return {
|
|
34
|
-
hasPermission,
|
|
30
|
+
hasPermission: operator === "AND" ? missingPermissions.length === 0 : matchedPermissions.length > 0,
|
|
35
31
|
matchedPermissions,
|
|
36
32
|
missingPermissions
|
|
37
33
|
};
|
|
@@ -89,7 +85,7 @@ var PermissionChecker = class {
|
|
|
89
85
|
return this.permissionArray.length;
|
|
90
86
|
}
|
|
91
87
|
};
|
|
92
|
-
|
|
93
88
|
//#endregion
|
|
94
|
-
export { PermissionChecker };
|
|
95
|
-
|
|
89
|
+
export { PermissionChecker as t };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=permission-service-DUyKomU3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-service-DUyKomU3.mjs","names":[],"sources":["../src/permissions/permission-service.ts"],"sourcesContent":["/**\n * Permission Service\n * \n * Core permission checking logic.\n */\n\nimport type {\n PermissionCode,\n PermissionModule,\n PermissionCheckOptions,\n PermissionCheckResult,\n FieldPermissionResult,\n} from './types';\n\n/**\n * Permission Checker Class\n * \n * Provides methods for checking user permissions.\n */\nexport class PermissionChecker {\n private permissions: Set<PermissionCode>;\n private permissionArray: PermissionCode[];\n\n constructor(permissions: PermissionCode[]) {\n this.permissionArray = permissions;\n this.permissions = new Set(permissions);\n }\n\n /**\n * Check if user has a single permission\n */\n hasPermission(permission: PermissionCode): boolean {\n return this.permissions.has(permission);\n }\n\n /**\n * Check multiple permissions with AND/OR logic\n */\n hasPermissions(\n permissions: PermissionCode[],\n options: PermissionCheckOptions = {}\n ): PermissionCheckResult {\n const { operator = 'OR', wildcard = false } = options;\n\n const matchedPermissions: PermissionCode[] = [];\n const missingPermissions: PermissionCode[] = [];\n\n for (const permission of permissions) {\n const hasIt = wildcard\n ? this.hasWildcardPermission(permission)\n : this.hasPermission(permission);\n\n if (hasIt) {\n matchedPermissions.push(permission);\n } else {\n missingPermissions.push(permission);\n }\n }\n\n const hasPermission =\n operator === 'AND'\n ? missingPermissions.length === 0\n : matchedPermissions.length > 0;\n\n return {\n hasPermission,\n matchedPermissions,\n missingPermissions,\n };\n }\n\n /**\n * Check wildcard permission pattern\n */\n private hasWildcardPermission(pattern: PermissionCode): boolean {\n if (!pattern.includes('*')) {\n return this.hasPermission(pattern);\n }\n\n const regex = new RegExp(\n '^' + pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '.*') + '$'\n );\n\n return this.permissionArray.some((p) => regex.test(p));\n }\n\n /**\n * Check if user has any permission in a module\n */\n hasModulePermission(module: PermissionModule): boolean {\n const prefix = `${module}.`;\n return this.permissionArray.some((p) => p.startsWith(prefix));\n }\n\n /**\n * Get field-level permissions for an entity\n */\n getFieldPermissions(\n module: PermissionModule,\n entity: string\n ): FieldPermissionResult {\n const base = `${module}.${entity}`;\n\n return {\n canView: this.hasPermission(`${base}.View`),\n canEdit: this.hasPermission(`${base}.Edit`),\n canCreate: this.hasPermission(`${base}.Create`),\n canDelete: this.hasPermission(`${base}.Delete`),\n };\n }\n\n /**\n * Get all permissions\n */\n getAllPermissions(): readonly PermissionCode[] {\n return Object.freeze([...this.permissionArray]);\n }\n\n /**\n * Get permissions for a specific module\n */\n getModulePermissions(module: PermissionModule): PermissionCode[] {\n const prefix = `${module}.`;\n return this.permissionArray.filter((p) => p.startsWith(prefix));\n }\n\n /**\n * Check if user has any permissions\n */\n hasAnyPermissions(): boolean {\n return this.permissionArray.length > 0;\n }\n\n /**\n * Get permission count\n */\n getPermissionCount(): number {\n return this.permissionArray.length;\n }\n}\n"],"mappings":";;;;;;AAmBA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CAEA,YAAY,aAA+B;EACzC,KAAK,kBAAkB;EACvB,KAAK,cAAc,IAAI,IAAI,WAAW;CACxC;;;;CAKA,cAAc,YAAqC;EACjD,OAAO,KAAK,YAAY,IAAI,UAAU;CACxC;;;;CAKA,eACE,aACA,UAAkC,CAAC,GACZ;EACvB,MAAM,EAAE,WAAW,MAAM,WAAW,UAAU;EAE9C,MAAM,qBAAuC,CAAC;EAC9C,MAAM,qBAAuC,CAAC;EAE9C,KAAK,MAAM,cAAc,aAKvB,IAJc,WACV,KAAK,sBAAsB,UAAU,IACrC,KAAK,cAAc,UAAU,GAG/B,mBAAmB,KAAK,UAAU;OAElC,mBAAmB,KAAK,UAAU;EAStC,OAAO;GACL,eALA,aAAa,QACT,mBAAmB,WAAW,IAC9B,mBAAmB,SAAS;GAIhC;GACA;EACF;CACF;;;;CAKA,sBAA8B,SAAkC;EAC9D,IAAI,CAAC,QAAQ,SAAS,GAAG,GACvB,OAAO,KAAK,cAAc,OAAO;EAGnC,MAAM,QAAQ,IAAI,OAChB,MAAM,QAAQ,QAAQ,OAAO,KAAK,CAAC,CAAC,QAAQ,OAAO,IAAI,IAAI,GAC7D;EAEA,OAAO,KAAK,gBAAgB,MAAM,MAAM,MAAM,KAAK,CAAC,CAAC;CACvD;;;;CAKA,oBAAoB,QAAmC;EACrD,MAAM,SAAS,GAAG,OAAO;EACzB,OAAO,KAAK,gBAAgB,MAAM,MAAM,EAAE,WAAW,MAAM,CAAC;CAC9D;;;;CAKA,oBACE,QACA,QACuB;EACvB,MAAM,OAAO,GAAG,OAAO,GAAG;EAE1B,OAAO;GACL,SAAS,KAAK,cAAc,GAAG,KAAK,MAAM;GAC1C,SAAS,KAAK,cAAc,GAAG,KAAK,MAAM;GAC1C,WAAW,KAAK,cAAc,GAAG,KAAK,QAAQ;GAC9C,WAAW,KAAK,cAAc,GAAG,KAAK,QAAQ;EAChD;CACF;;;;CAKA,oBAA+C;EAC7C,OAAO,OAAO,OAAO,CAAC,GAAG,KAAK,eAAe,CAAC;CAChD;;;;CAKA,qBAAqB,QAA4C;EAC/D,MAAM,SAAS,GAAG,OAAO;EACzB,OAAO,KAAK,gBAAgB,QAAQ,MAAM,EAAE,WAAW,MAAM,CAAC;CAChE;;;;CAKA,oBAA6B;EAC3B,OAAO,KAAK,gBAAgB,SAAS;CACvC;;;;CAKA,qBAA6B;EAC3B,OAAO,KAAK,gBAAgB;CAC9B;AACF"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { t as PermissionChecker } from "../permission-service-DUyKomU3.mjs";
|
|
2
|
+
import { _ as initializePermissions, a as ProtectedSection, c as useHasPermission, d as usePermission, f as usePermissionChecker, g as getPermissions, h as getPermissionChecker, i as ProtectedLink, l as useHasPermissions, m as PermissionInitializer, n as ProtectedAction, o as useAllPermissions, p as usePermissions, r as ProtectedButton, s as useHasModulePermission, t as PERMISSION_MODULES, u as useModulePermission, v as usePermissionStore } from "../permissions-C5dTP9y4.mjs";
|
|
3
|
+
export { PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, getPermissionChecker, getPermissions, initializePermissions, useAllPermissions, useHasModulePermission, useHasPermission, useHasPermissions, useModulePermission, usePermission, usePermissionChecker, usePermissionStore, usePermissions };
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { PermissionChecker } from "./permission-service-
|
|
1
|
+
import { t as PermissionChecker } from "./permission-service-DUyKomU3.mjs";
|
|
2
2
|
import { create } from "zustand";
|
|
3
3
|
import { useCallback, useEffect, useMemo } from "react";
|
|
4
4
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
5
|
-
|
|
6
5
|
//#region src/permissions/permission-store.ts
|
|
7
6
|
/**
|
|
7
|
+
* Permission Store (Zustand)
|
|
8
|
+
*
|
|
9
|
+
* State management for permissions.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
8
12
|
* Selectors for focused store reads
|
|
9
13
|
*/
|
|
10
14
|
const selectChecker = (state) => state.checker;
|
|
@@ -53,10 +57,14 @@ function getPermissionChecker() {
|
|
|
53
57
|
function getPermissions() {
|
|
54
58
|
return usePermissionStore.getState().permissions;
|
|
55
59
|
}
|
|
56
|
-
|
|
57
60
|
//#endregion
|
|
58
61
|
//#region src/permissions/permission-initializer.tsx
|
|
59
62
|
/**
|
|
63
|
+
* Permission Initializer Component
|
|
64
|
+
*
|
|
65
|
+
* Initializes permission store from identity context.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
60
68
|
* Component that initializes permissions from props
|
|
61
69
|
*/
|
|
62
70
|
function PermissionInitializer({ permissions, isLoading = false, children }) {
|
|
@@ -65,10 +73,14 @@ function PermissionInitializer({ permissions, isLoading = false, children }) {
|
|
|
65
73
|
}, [permissions, isLoading]);
|
|
66
74
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
67
75
|
}
|
|
68
|
-
|
|
69
76
|
//#endregion
|
|
70
77
|
//#region src/permissions/hooks.ts
|
|
71
78
|
/**
|
|
79
|
+
* Permission Hooks
|
|
80
|
+
*
|
|
81
|
+
* React hooks for permission checks.
|
|
82
|
+
*/
|
|
83
|
+
/**
|
|
72
84
|
* Hook for checking a single permission
|
|
73
85
|
*/
|
|
74
86
|
function usePermission(permission) {
|
|
@@ -119,15 +131,13 @@ function useAllPermissions() {
|
|
|
119
131
|
const checker = usePermissionStore(selectChecker);
|
|
120
132
|
return useMemo(() => checker.getAllPermissions(), [checker]);
|
|
121
133
|
}
|
|
122
|
-
|
|
123
134
|
//#endregion
|
|
124
135
|
//#region src/permissions/components.tsx
|
|
125
136
|
/**
|
|
126
137
|
* Button that only renders if user has the required permission
|
|
127
138
|
*/
|
|
128
|
-
function ProtectedButton({ permission, fallback = null, children
|
|
129
|
-
|
|
130
|
-
if (!hasPermission) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
139
|
+
function ProtectedButton({ permission, fallback = null, children, ...props }) {
|
|
140
|
+
if (!usePermission(permission)) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
131
141
|
return /* @__PURE__ */ jsx("button", {
|
|
132
142
|
...props,
|
|
133
143
|
children
|
|
@@ -136,9 +146,8 @@ function ProtectedButton({ permission, fallback = null, children,...props }) {
|
|
|
136
146
|
/**
|
|
137
147
|
* Link that only renders if user has the required permission
|
|
138
148
|
*/
|
|
139
|
-
function ProtectedLink({ permission, fallback = null, children
|
|
140
|
-
|
|
141
|
-
if (!hasPermission) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
149
|
+
function ProtectedLink({ permission, fallback = null, children, ...props }) {
|
|
150
|
+
if (!usePermission(permission)) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
142
151
|
return /* @__PURE__ */ jsx("a", {
|
|
143
152
|
...props,
|
|
144
153
|
children
|
|
@@ -167,7 +176,6 @@ function ProtectedAction({ permission, onAction, children }) {
|
|
|
167
176
|
disabled: !hasPermission
|
|
168
177
|
}) });
|
|
169
178
|
}
|
|
170
|
-
|
|
171
179
|
//#endregion
|
|
172
180
|
//#region src/permissions/constants.ts
|
|
173
181
|
/**
|
|
@@ -177,7 +185,7 @@ function ProtectedAction({ permission, onAction, children }) {
|
|
|
177
185
|
* module constants in the consuming app instead.
|
|
178
186
|
*/
|
|
179
187
|
const PERMISSION_MODULES = [];
|
|
180
|
-
|
|
181
188
|
//#endregion
|
|
182
|
-
export {
|
|
183
|
-
|
|
189
|
+
export { initializePermissions as _, ProtectedSection as a, useHasPermission as c, usePermission as d, usePermissionChecker as f, getPermissions as g, getPermissionChecker as h, ProtectedLink as i, useHasPermissions as l, PermissionInitializer as m, ProtectedAction as n, useAllPermissions as o, usePermissions as p, ProtectedButton as r, useHasModulePermission as s, PERMISSION_MODULES as t, useModulePermission as u, usePermissionStore as v };
|
|
190
|
+
|
|
191
|
+
//# sourceMappingURL=permissions-C5dTP9y4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-C5dTP9y4.mjs","names":[],"sources":["../src/permissions/permission-store.ts","../src/permissions/permission-initializer.tsx","../src/permissions/hooks.ts","../src/permissions/components.tsx","../src/permissions/constants.ts"],"sourcesContent":["/**\n * Permission Store (Zustand)\n * \n * State management for permissions.\n */\n\nimport { create } from 'zustand';\nimport { PermissionChecker } from './permission-service';\nimport type { PermissionCode } from './types';\n\n/**\n * Permission store state interface\n */\ninterface PermissionState {\n checker: PermissionChecker;\n permissions: PermissionCode[];\n isLoading: boolean;\n setPermissions: (permissions: PermissionCode[]) => void;\n setLoading: (isLoading: boolean) => void;\n reset: () => void;\n}\n\n/**\n * Selectors for focused store reads\n */\nexport const selectChecker = (state: PermissionState) => state.checker;\nexport const selectPermissions = (state: PermissionState) => state.permissions;\nexport const selectIsLoading = (state: PermissionState) => state.isLoading;\n\n/**\n * Initial state\n */\nconst initialState = {\n checker: new PermissionChecker([]),\n permissions: [] as PermissionCode[],\n isLoading: false,\n};\n\n/**\n * Permission store\n */\nexport const usePermissionStore = create<PermissionState>((set) => ({\n ...initialState,\n\n setPermissions: (permissions: PermissionCode[]) => {\n set({\n permissions,\n checker: new PermissionChecker(permissions),\n });\n },\n\n setLoading: (isLoading: boolean) => {\n set({ isLoading });\n },\n\n reset: () => {\n set(initialState);\n },\n}));\n\n/**\n * Initialize permission store\n */\nexport function initializePermissions(\n permissions: PermissionCode[],\n isLoading: boolean = false\n): void {\n usePermissionStore.getState().setPermissions(permissions);\n usePermissionStore.getState().setLoading(isLoading);\n}\n\n/**\n * Get permission checker outside React components\n */\nexport function getPermissionChecker(): PermissionChecker {\n return usePermissionStore.getState().checker;\n}\n\n/**\n * Get all permissions outside React components\n */\nexport function getPermissions(): PermissionCode[] {\n return usePermissionStore.getState().permissions;\n}\n","/**\n * Permission Initializer Component\n * \n * Initializes permission store from identity context.\n */\n\nimport { useEffect, type ReactNode } from 'react';\nimport { initializePermissions } from './permission-store';\nimport type { PermissionCode } from './types';\n\ninterface PermissionInitializerProps {\n permissions: PermissionCode[];\n isLoading?: boolean;\n children: ReactNode;\n}\n\n/**\n * Component that initializes permissions from props\n */\nexport function PermissionInitializer({\n permissions,\n isLoading = false,\n children,\n}: PermissionInitializerProps) {\n useEffect(() => {\n initializePermissions(permissions, isLoading);\n }, [permissions, isLoading]);\n\n return <>{children}</>;\n}\n","/**\n * Permission Hooks\n * \n * React hooks for permission checks.\n */\n\nimport { useMemo, useCallback } from 'react';\nimport { usePermissionStore, selectChecker } from './permission-store';\nimport type { PermissionCode, PermissionCheckOptions, PermissionModule } from './types';\n\n/**\n * Hook for checking a single permission\n */\nexport function usePermission(permission: PermissionCode): boolean {\n const checker = usePermissionStore(selectChecker);\n return useMemo(() => checker.hasPermission(permission), [checker, permission]);\n}\n\n/**\n * Alias for usePermission\n */\nexport const useHasPermission = usePermission;\n\n/**\n * Hook for checking multiple permissions\n */\nexport function usePermissions(\n permissions: PermissionCode[],\n options?: PermissionCheckOptions\n) {\n const checker = usePermissionStore(selectChecker);\n return useMemo(\n () => checker.hasPermissions(permissions, options),\n [checker, permissions, options]\n );\n}\n\n/**\n * Alias for usePermissions\n */\nexport const useHasPermissions = usePermissions;\n\n/**\n * Hook for checking module-level permissions\n */\nexport function useModulePermission(module: PermissionModule): boolean {\n const checker = usePermissionStore(selectChecker);\n return useMemo(() => checker.hasModulePermission(module), [checker, module]);\n}\n\n/**\n * Alias for useModulePermission\n */\nexport const useHasModulePermission = useModulePermission;\n\n/**\n * Hook for getting a memoized permission checker function\n */\nexport function usePermissionChecker() {\n const checker = usePermissionStore(selectChecker);\n return useCallback(\n (permission: PermissionCode) => checker.hasPermission(permission),\n [checker]\n );\n}\n\n/**\n * Hook for getting all user permissions\n */\nexport function useAllPermissions(): readonly PermissionCode[] {\n const checker = usePermissionStore(selectChecker);\n return useMemo(() => checker.getAllPermissions(), [checker]);\n}\n\n/**\n * Hook for field-level permissions\n */\nexport function useFieldPermissions(module: PermissionModule, entity: string) {\n const checker = usePermissionStore(selectChecker);\n return useMemo(\n () => checker.getFieldPermissions(module, entity),\n [checker, module, entity]\n );\n}\n\n/**\n * Hook for permission loading state\n */\nexport function usePermissionLoading(): boolean {\n return usePermissionStore((state) => state.isLoading);\n}\n","/**\n * Permission Components\n * \n * Protected UI components that conditionally render based on permissions.\n */\n\nimport React, { type ReactNode, type ComponentProps } from 'react';\nimport { usePermission, usePermissions } from './hooks';\nimport type { PermissionCode, PermissionCheckOptions } from './types';\n\n// ============================================================================\n// ProtectedButton\n// ============================================================================\n\ninterface ProtectedButtonProps extends ComponentProps<'button'> {\n permission: PermissionCode;\n fallback?: ReactNode;\n}\n\n/**\n * Button that only renders if user has the required permission\n */\nexport function ProtectedButton({\n permission,\n fallback = null,\n children,\n ...props\n}: ProtectedButtonProps) {\n const hasPermission = usePermission(permission);\n\n if (!hasPermission) {\n return <>{fallback}</>;\n }\n\n return <button {...props}>{children}</button>;\n}\n\n// ============================================================================\n// ProtectedLink\n// ============================================================================\n\ninterface ProtectedLinkProps extends ComponentProps<'a'> {\n permission: PermissionCode;\n fallback?: ReactNode;\n}\n\n/**\n * Link that only renders if user has the required permission\n */\nexport function ProtectedLink({\n permission,\n fallback = null,\n children,\n ...props\n}: ProtectedLinkProps) {\n const hasPermission = usePermission(permission);\n\n if (!hasPermission) {\n return <>{fallback}</>;\n }\n\n return <a {...props}>{children}</a>;\n}\n\n// ============================================================================\n// ProtectedSection\n// ============================================================================\n\ninterface ProtectedSectionProps {\n permission?: PermissionCode;\n permissions?: PermissionCode[];\n options?: PermissionCheckOptions;\n fallback?: ReactNode;\n children: ReactNode;\n}\n\n/**\n * Section that only renders if user has the required permission(s)\n */\nexport function ProtectedSection({\n permission,\n permissions,\n options,\n fallback = null,\n children,\n}: ProtectedSectionProps) {\n // Single permission check\n const singlePermissionResult = usePermission(permission || '');\n \n // Multiple permissions check\n const multiplePermissionsResult = usePermissions(permissions || [], options);\n\n // Determine if we should render\n let hasPermission = false;\n \n if (permission) {\n hasPermission = singlePermissionResult;\n } else if (permissions && permissions.length > 0) {\n hasPermission = multiplePermissionsResult.hasPermission;\n } else {\n // No permission specified, render by default\n hasPermission = true;\n }\n\n if (!hasPermission) {\n return <>{fallback}</>;\n }\n\n return <>{children}</>;\n}\n\n// ============================================================================\n// ProtectedAction\n// ============================================================================\n\ninterface ProtectedActionProps {\n permission: PermissionCode;\n onAction: () => void;\n children: (props: { onClick: () => void; disabled: boolean }) => ReactNode;\n}\n\n/**\n * Render prop component for protected actions\n */\nexport function ProtectedAction({\n permission,\n onAction,\n children,\n}: ProtectedActionProps) {\n const hasPermission = usePermission(permission);\n\n return (\n <>\n {children({\n onClick: hasPermission ? onAction : () => {},\n disabled: !hasPermission,\n })}\n </>\n );\n}\n","/**\n * Permission Constants\n */\n\nimport type { PermissionModule } from './types';\n\n/**\n * Available permission modules\n *\n * @deprecated Spine does not own an application's permission taxonomy. Define\n * module constants in the consuming app instead.\n */\nexport const PERMISSION_MODULES: PermissionModule[] = [];\n"],"mappings":"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"}
|