@eminuckan/spine 0.3.1 → 0.3.3

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/SECURITY.md +5 -15
  3. package/dist/access/index.mjs +2 -0
  4. package/dist/{access-CGrsDMwu.js → access-BHy1v5sr.mjs} +6 -8
  5. package/dist/access-BHy1v5sr.mjs.map +1 -0
  6. package/dist/api-client/index.mjs +3 -0
  7. package/dist/api-client/server.mjs +4 -0
  8. package/dist/{api-client-CgZUMukO.js → api-client-CB7XZaMg.mjs} +8 -10
  9. package/dist/api-client-CB7XZaMg.mjs.map +1 -0
  10. package/dist/auth/auth.server.d.ts +1 -0
  11. package/dist/auth/auth.server.d.ts.map +1 -1
  12. package/dist/auth/index.mjs +1 -0
  13. package/dist/auth/route-protection.server.d.ts.map +1 -1
  14. package/dist/auth/server.mjs +3 -0
  15. package/dist/{identity-C5un-xAs.js → identity/index.mjs} +24 -21
  16. package/dist/identity/index.mjs.map +1 -0
  17. package/dist/{server-Dxsww9v9.js → identity/server.mjs} +7 -8
  18. package/dist/identity/server.mjs.map +1 -0
  19. package/dist/{index.js → index.mjs} +11 -13
  20. package/dist/{logging-DviiAkLx.js → logging/index.mjs} +22 -24
  21. package/dist/logging/index.mjs.map +1 -0
  22. package/dist/{permission-service-CvGpwI0d.js → permission-service-DUyKomU3.mjs} +6 -10
  23. package/dist/permission-service-DUyKomU3.mjs.map +1 -0
  24. package/dist/permissions/index.mjs +3 -0
  25. package/dist/{permissions-_n1hIG7Q.js → permissions-C5dTP9y4.mjs} +23 -15
  26. package/dist/permissions-C5dTP9y4.mjs.map +1 -0
  27. package/dist/query-client/index.mjs +2 -0
  28. package/dist/{query-client-Bh3TyB8W.js → query-client-_xB300gc.mjs} +49 -15
  29. package/dist/query-client-_xB300gc.mjs.map +1 -0
  30. package/dist/react-router/{index.js → index.mjs} +11 -13
  31. package/dist/react-router/server.mjs +17 -0
  32. package/dist/{redis-session-storage.server-v2VcpFsQ.js → redis-session-storage.server-DGgQcJ9B.mjs} +30 -42
  33. package/dist/redis-session-storage.server-DGgQcJ9B.mjs.map +1 -0
  34. package/dist/{server-C96NMFBy.js → server-ByScGRXA.mjs} +39 -36
  35. package/dist/server-ByScGRXA.mjs.map +1 -0
  36. package/dist/{auth.server-D5oiz9aF.js → server-CqX2yCa4.mjs} +388 -119
  37. package/dist/server-CqX2yCa4.mjs.map +1 -0
  38. package/dist/server.mjs +90 -0
  39. package/dist/server.mjs.map +1 -0
  40. package/dist/signalr/index.mjs +2 -0
  41. package/dist/{signalr-k-70VHKo.js → signalr-C-TA8nEE.mjs} +11 -6
  42. package/dist/signalr-C-TA8nEE.mjs.map +1 -0
  43. package/dist/{tenant-DfLemb6n.js → tenant/index.mjs} +19 -12
  44. package/dist/tenant/index.mjs.map +1 -0
  45. package/dist/{server-Ca-3DJf2.js → tenant/server.mjs} +12 -12
  46. package/dist/tenant/server.mjs.map +1 -0
  47. package/dist/{types-BaaE6wjY.js → types-DqCXinAk.mjs} +3 -3
  48. package/dist/types-DqCXinAk.mjs.map +1 -0
  49. package/docs/codex-for-oss-application.md +4 -0
  50. package/package.json +33 -21
  51. package/dist/access/index.js +0 -4
  52. package/dist/access-CGrsDMwu.js.map +0 -1
  53. package/dist/api-client/index.js +0 -5
  54. package/dist/api-client/server.js +0 -6
  55. package/dist/api-client-CgZUMukO.js.map +0 -1
  56. package/dist/auth/index.js +0 -0
  57. package/dist/auth/server.js +0 -6
  58. package/dist/auth.server-D5oiz9aF.js.map +0 -1
  59. package/dist/auth.server-lC94lmlx.js +0 -5
  60. package/dist/identity/index.js +0 -3
  61. package/dist/identity/server.js +0 -5
  62. package/dist/identity-C5un-xAs.js.map +0 -1
  63. package/dist/logging/index.js +0 -3
  64. package/dist/logging-DviiAkLx.js.map +0 -1
  65. package/dist/permission-service-CvGpwI0d.js.map +0 -1
  66. package/dist/permissions/index.js +0 -4
  67. package/dist/permissions-_n1hIG7Q.js.map +0 -1
  68. package/dist/query-client/index.js +0 -3
  69. package/dist/query-client-Bh3TyB8W.js.map +0 -1
  70. package/dist/react-router/server.js +0 -20
  71. package/dist/redis-session-storage.server-v2VcpFsQ.js.map +0 -1
  72. package/dist/server-B6PCxc-T.js +0 -304
  73. package/dist/server-B6PCxc-T.js.map +0 -1
  74. package/dist/server-C96NMFBy.js.map +0 -1
  75. package/dist/server-Ca-3DJf2.js.map +0 -1
  76. package/dist/server-Dxsww9v9.js.map +0 -1
  77. package/dist/server-hGrElJMG.js +0 -78
  78. package/dist/server-hGrElJMG.js.map +0 -1
  79. package/dist/server.js +0 -20
  80. package/dist/signalr/index.js +0 -3
  81. package/dist/signalr-k-70VHKo.js.map +0 -1
  82. package/dist/src-B9rDUgSb.js +0 -0
  83. package/dist/tenant/index.js +0 -3
  84. package/dist/tenant/server.js +0 -5
  85. package/dist/tenant-DfLemb6n.js.map +0 -1
  86. package/dist/types-BaaE6wjY.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  No unreleased changes.
8
8
 
9
+ ## [0.3.3] - 2026-07-11
10
+
11
+ ### Fixed
12
+
13
+ - Align package runtime exports with the emitted `.mjs` files and add an export-map regression test.
14
+
15
+ ## [0.3.2] - 2026-07-11
16
+
17
+ ### Fixed
18
+
19
+ - Use the configured public OIDC redirect URI during authorization-code exchange so callbacks remain valid behind TLS-terminating reverse proxies.
20
+
9
21
  ## [0.3.1] - 2026-06-01
10
22
 
11
23
  ### Changed
package/SECURITY.md CHANGED
@@ -2,27 +2,17 @@
2
2
 
3
3
  ## Supported Versions
4
4
 
5
- Security fixes are applied to the latest published release line.
5
+ Security fixes are applied to the latest published release line and the current `main` branch. Older releases are not supported.
6
6
 
7
7
  ## Reporting a Vulnerability
8
8
 
9
- Please do not open public GitHub issues for security problems.
9
+ Do not open a public issue or discussion for a suspected vulnerability.
10
10
 
11
- Instead, report vulnerabilities privately to:
11
+ Use [GitHub private vulnerability reporting](https://github.com/eminuckan/spine/security/advisories/new) from the repository's **Security** tab. Maintainers should coordinate investigation and remediation in a private draft security advisory.
12
12
 
13
- - Email: `eminuckan@gmail.com`
13
+ Include the affected package entry point or adapter, impact, reproduction steps, affected version or revision, and any suggested mitigation. Redact credentials, tokens, session material, and application data from the report.
14
14
 
15
- When possible, include:
16
-
17
- - A clear description of the issue
18
- - Affected versions
19
- - Reproduction steps
20
- - Potential impact
21
- - Any suggested mitigation
22
-
23
- ## Response Expectations
24
-
25
- The goal is to acknowledge reports quickly, validate impact, and coordinate a fix before public disclosure.
15
+ We will validate the report, prioritize remediation by impact and exploitability, and coordinate disclosure after a fix is available.
26
16
 
27
17
  ## Scope Notes
28
18
 
@@ -0,0 +1,2 @@
1
+ import { t as evaluateAccessRequirement } from "../access-BHy1v5sr.mjs";
2
+ export { evaluateAccessRequirement };
@@ -1,7 +1,6 @@
1
- import { PermissionChecker } from "./permission-service-CvGpwI0d.js";
2
-
1
+ import { t as PermissionChecker } from "./permission-service-DUyKomU3.mjs";
3
2
  //#region src/access/evaluator.ts
4
- const ACTIVE_ENTITLEMENT_STATUSES = new Set(["active", "trialing"]);
3
+ const ACTIVE_ENTITLEMENT_STATUSES = /* @__PURE__ */ new Set(["active", "trialing"]);
5
4
  const EMPTY_DECISION = Object.freeze({
6
5
  allowed: true,
7
6
  reasons: [],
@@ -34,8 +33,7 @@ function evaluateAccessRequirement(requirement, subject) {
34
33
  if (requirement.requireActiveEntitlement && !hasActiveEntitlement(entitlement)) reasons.push("inactive-entitlement");
35
34
  else if (requirement.requireActiveSubscription && !hasActiveEntitlement(entitlement)) reasons.push("inactive-subscription");
36
35
  if (permissions.length > 0) {
37
- const checker = new PermissionChecker([...subject.permissions ?? []]);
38
- const result = checker.hasPermissions([...permissions], {
36
+ const result = new PermissionChecker([...subject.permissions ?? []]).hasPermissions([...permissions], {
39
37
  operator: requirement.permissionOperator ?? "AND",
40
38
  wildcard: true
41
39
  });
@@ -55,7 +53,7 @@ function evaluateAccessRequirement(requirement, subject) {
55
53
  missingFeatures
56
54
  };
57
55
  }
58
-
59
56
  //#endregion
60
- export { evaluateAccessRequirement };
61
- //# sourceMappingURL=access-CGrsDMwu.js.map
57
+ export { evaluateAccessRequirement as t };
58
+
59
+ //# sourceMappingURL=access-BHy1v5sr.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-BHy1v5sr.mjs","names":[],"sources":["../src/access/evaluator.ts"],"sourcesContent":["import { PermissionChecker } from '../permissions/permission-service';\nimport type {\n AccessDecision,\n AccessDeniedReason,\n AccessOperator,\n AccessRequirement,\n AccessSubject,\n EntitlementAccessSnapshot,\n} from './types';\n\nconst ACTIVE_ENTITLEMENT_STATUSES = new Set(['active', 'trialing']);\n\nconst EMPTY_DECISION: AccessDecision = Object.freeze({\n allowed: true,\n reasons: [],\n missingPermissions: [],\n missingFeatures: [],\n});\n\nfunction normalizeFeature(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction hasActiveEntitlement(entitlement?: EntitlementAccessSnapshot | null): boolean {\n if (!entitlement?.exists) {\n return false;\n }\n\n return ACTIVE_ENTITLEMENT_STATUSES.has(entitlement.status?.toLowerCase() ?? '');\n}\n\nfunction checkFeatureSet(\n grantedFeatures: readonly string[],\n requiredFeatures: readonly string[],\n operator: AccessOperator,\n): string[] {\n if (requiredFeatures.length === 0) {\n return [];\n }\n\n const granted = new Set(grantedFeatures.map(normalizeFeature));\n const normalizedRequired = requiredFeatures.map(normalizeFeature).filter(Boolean);\n\n if (operator === 'OR') {\n return normalizedRequired.some((feature) => granted.has(feature)) ? [] : normalizedRequired;\n }\n\n return normalizedRequired.filter((feature) => !granted.has(feature));\n}\n\nexport function evaluateAccessRequirement(\n requirement: AccessRequirement | null | undefined,\n subject: AccessSubject,\n): AccessDecision {\n if (!requirement) {\n return EMPTY_DECISION;\n }\n\n const reasons: AccessDeniedReason[] = [];\n const permissions = requirement.permissions ?? [];\n const features = requirement.features ?? [];\n const entitlement = subject.entitlement ?? subject.subscription;\n let missingPermissions: string[] = [];\n let missingFeatures: string[] = [];\n\n if (requirement.internalOnly && !subject.isInternalUser) {\n reasons.push('internal-only');\n }\n\n if (requirement.requireActiveEntitlement && !hasActiveEntitlement(entitlement)) {\n reasons.push('inactive-entitlement');\n } else if (requirement.requireActiveSubscription && !hasActiveEntitlement(entitlement)) {\n reasons.push('inactive-subscription');\n }\n\n if (permissions.length > 0) {\n const checker = new PermissionChecker([...(subject.permissions ?? [])]);\n const result = checker.hasPermissions([...permissions], {\n operator: requirement.permissionOperator ?? 'AND',\n wildcard: true,\n });\n\n if (!result.hasPermission) {\n missingPermissions = result.missingPermissions;\n reasons.push('missing-permissions');\n }\n }\n\n if (features.length > 0) {\n missingFeatures = checkFeatureSet(\n entitlement?.featureKeys ?? [],\n features,\n requirement.featureOperator ?? 'AND',\n );\n\n if (missingFeatures.length > 0) {\n reasons.push('missing-features');\n }\n }\n\n return {\n allowed: reasons.length === 0,\n reasons,\n missingPermissions,\n missingFeatures,\n };\n}\n"],"mappings":";;AAUA,MAAM,8CAA8B,IAAI,IAAI,CAAC,UAAU,UAAU,CAAC;AAElE,MAAM,iBAAiC,OAAO,OAAO;CACnD,SAAS;CACT,SAAS,CAAC;CACV,oBAAoB,CAAC;CACrB,iBAAiB,CAAC;AACpB,CAAC;AAED,SAAS,iBAAiB,OAAuB;CAC/C,OAAO,MAAM,KAAK,CAAC,CAAC,YAAY;AAClC;AAEA,SAAS,qBAAqB,aAAyD;CACrF,IAAI,CAAC,aAAa,QAChB,OAAO;CAGT,OAAO,4BAA4B,IAAI,YAAY,QAAQ,YAAY,KAAK,EAAE;AAChF;AAEA,SAAS,gBACP,iBACA,kBACA,UACU;CACV,IAAI,iBAAiB,WAAW,GAC9B,OAAO,CAAC;CAGV,MAAM,UAAU,IAAI,IAAI,gBAAgB,IAAI,gBAAgB,CAAC;CAC7D,MAAM,qBAAqB,iBAAiB,IAAI,gBAAgB,CAAC,CAAC,OAAO,OAAO;CAEhF,IAAI,aAAa,MACf,OAAO,mBAAmB,MAAM,YAAY,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI;CAG3E,OAAO,mBAAmB,QAAQ,YAAY,CAAC,QAAQ,IAAI,OAAO,CAAC;AACrE;AAEA,SAAgB,0BACd,aACA,SACgB;CAChB,IAAI,CAAC,aACH,OAAO;CAGT,MAAM,UAAgC,CAAC;CACvC,MAAM,cAAc,YAAY,eAAe,CAAC;CAChD,MAAM,WAAW,YAAY,YAAY,CAAC;CAC1C,MAAM,cAAc,QAAQ,eAAe,QAAQ;CACnD,IAAI,qBAA+B,CAAC;CACpC,IAAI,kBAA4B,CAAC;CAEjC,IAAI,YAAY,gBAAgB,CAAC,QAAQ,gBACvC,QAAQ,KAAK,eAAe;CAG9B,IAAI,YAAY,4BAA4B,CAAC,qBAAqB,WAAW,GAC3E,QAAQ,KAAK,sBAAsB;MAC9B,IAAI,YAAY,6BAA6B,CAAC,qBAAqB,WAAW,GACnF,QAAQ,KAAK,uBAAuB;CAGtC,IAAI,YAAY,SAAS,GAAG;EAE1B,MAAM,SAAS,IADK,kBAAkB,CAAC,GAAI,QAAQ,eAAe,CAAC,CAAE,CAChD,CAAC,CAAC,eAAe,CAAC,GAAG,WAAW,GAAG;GACtD,UAAU,YAAY,sBAAsB;GAC5C,UAAU;EACZ,CAAC;EAED,IAAI,CAAC,OAAO,eAAe;GACzB,qBAAqB,OAAO;GAC5B,QAAQ,KAAK,qBAAqB;EACpC;CACF;CAEA,IAAI,SAAS,SAAS,GAAG;EACvB,kBAAkB,gBAChB,aAAa,eAAe,CAAC,GAC7B,UACA,YAAY,mBAAmB,KACjC;EAEA,IAAI,gBAAgB,SAAS,GAC3B,QAAQ,KAAK,kBAAkB;CAEnC;CAEA,OAAO;EACL,SAAS,QAAQ,WAAW;EAC5B;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,3 @@
1
+ import { t as isApiClientError } from "../types-DqCXinAk.mjs";
2
+ 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";
3
+ export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isApiClientError, isRetryableError, withRetry };
@@ -0,0 +1,4 @@
1
+ import { t as isApiClientError } from "../types-DqCXinAk.mjs";
2
+ 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";
3
+ import { n as createFetchMiddleware, r as createAPIConfigFactory, t as createApiClient } from "../server-ByScGRXA.mjs";
4
+ export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createAPIConfigFactory, createApiClient, createFetchMiddleware, createRetryFetch, fetchWithTimeout, handleApiError, isApiClientError, isRetryableError, withRetry };
@@ -1,5 +1,4 @@
1
- import { logger } from "./logging-DviiAkLx.js";
2
-
1
+ import { logger } from "./logging/index.mjs";
3
2
  //#region src/api-client/error-codes.ts
4
3
  /**
5
4
  * Error Codes
@@ -30,13 +29,14 @@ const ErrorCodes = {
30
29
  Forbidden: "Forbidden",
31
30
  InternalError: "InternalError"
32
31
  };
33
-
34
32
  //#endregion
35
33
  //#region src/api-client/error-handler.ts
36
34
  /**
37
35
  * API Error Class
38
36
  */
39
37
  var ApiError = class extends Error {
38
+ problemDetails;
39
+ response;
40
40
  constructor(problemDetails, response) {
41
41
  super(problemDetails.detail);
42
42
  this.problemDetails = problemDetails;
@@ -116,7 +116,6 @@ var ErrorHandler = class {
116
116
  this.showToast(message, "error");
117
117
  }
118
118
  };
119
-
120
119
  //#endregion
121
120
  //#region src/api-client/retry-handler.ts
122
121
  const DEFAULT_RETRY_CONFIG = {
@@ -168,15 +167,14 @@ async function withRetry(fn, config) {
168
167
  * Fetch with timeout
169
168
  */
170
169
  async function fetchWithTimeout(input, init) {
171
- const { timeout = 3e4,...fetchInit } = init || {};
170
+ const { timeout = 3e4, ...fetchInit } = init || {};
172
171
  const controller = new AbortController();
173
172
  const timeoutId = setTimeout(() => controller.abort(), timeout);
174
173
  try {
175
- const response = await fetch(input, {
174
+ return await fetch(input, {
176
175
  ...fetchInit,
177
176
  signal: controller.signal
178
177
  });
179
- return response;
180
178
  } finally {
181
179
  clearTimeout(timeoutId);
182
180
  }
@@ -189,7 +187,7 @@ function createRetryFetch(config) {
189
187
  return withRetry(() => fetch(input, init), config);
190
188
  };
191
189
  }
192
-
193
190
  //#endregion
194
- export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry };
195
- //# sourceMappingURL=api-client-CgZUMukO.js.map
191
+ export { withRetry as a, handleApiError as c, isRetryableError as i, ErrorCodes as l, createRetryFetch as n, ApiError as o, fetchWithTimeout as r, ErrorHandler as s, calculateBackoffDelay as t };
192
+
193
+ //# sourceMappingURL=api-client-CB7XZaMg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client-CB7XZaMg.mjs","names":[],"sources":["../src/api-client/error-codes.ts","../src/api-client/error-handler.ts","../src/api-client/retry-handler.ts"],"sourcesContent":["/**\n * Error Codes\n * \n * Generic error codes that apps can use as a starter vocabulary.\n * Domain-specific codes should live in the consuming app.\n */\n\nexport const ErrorCodes = {\n // Validation\n Validation: {\n Failed: 'ValidationFailed',\n },\n\n // Identity\n Identity: {\n InvalidCredentials: 'InvalidCredentials',\n UserInactive: 'UserInactive',\n TenantContextMissing: 'TenantContextMissing',\n InvalidCurrentPassword: 'InvalidCurrentPassword',\n OwnerCannotDisableMembership: 'OwnerCannotDisableMembership',\n CurrentUserMissing: 'CurrentUserMissing',\n UserNotFound: 'UserNotFound',\n EmailAlreadyExists: 'EmailAlreadyExists',\n },\n\n // Access and entitlement\n Access: {\n EntitlementRequired: 'EntitlementRequired',\n EntitlementExpired: 'EntitlementExpired',\n FeatureNotAvailable: 'FeatureNotAvailable',\n TenantContextMissing: 'TenantContextMissing',\n },\n\n // General\n NotFound: 'NotFound',\n Unauthorized: 'Unauthorized',\n Forbidden: 'Forbidden',\n InternalError: 'InternalError',\n} as const;\n","/**\n * Error Handler\n * \n * Centralized error handling with ProblemDetails parsing.\n */\n\nimport type { ProblemDetails, ValidationError } from './types';\nimport { ErrorCodes } from './error-codes';\nimport { logger } from '../logging';\n\n/**\n * API Error Class\n */\nexport class ApiError extends Error {\n constructor(\n public problemDetails: ProblemDetails,\n public response: Response\n ) {\n super(problemDetails.detail);\n this.name = 'ApiError';\n }\n\n get code(): string {\n return this.problemDetails.title;\n }\n\n get status(): number {\n return this.problemDetails.status;\n }\n\n get isValidationError(): boolean {\n return this.code === ErrorCodes.Validation.Failed;\n }\n\n get validationErrors(): ValidationError[] {\n return this.problemDetails.meta?.errors ?? [];\n }\n\n get traceId(): string {\n return this.problemDetails.traceId;\n }\n}\n\n/**\n * Parse API error response\n */\nexport async function handleApiError(response: Response): Promise<never> {\n let problemDetails: ProblemDetails;\n\n try {\n problemDetails = await response.json();\n } catch {\n problemDetails = {\n type: `https://httpstatuses.com/${response.status}`,\n title: 'UnknownError',\n status: response.status,\n detail: response.statusText || 'An unknown error occurred',\n traceId: response.headers.get('x-trace-id') ?? 'unknown',\n };\n }\n\n logger.error('API request failed', undefined, {\n code: problemDetails.title,\n status: problemDetails.status,\n detail: problemDetails.detail,\n traceId: problemDetails.traceId,\n });\n\n throw new ApiError(problemDetails, response);\n}\n\n/**\n * Error Handler Configuration\n */\nexport interface ErrorHandlerConfig {\n handlers?: Partial<Record<string, (error: ApiError) => void>>;\n showToast?: boolean;\n toastMessages?: Partial<Record<string, string>>;\n onError?: (error: ApiError) => void;\n toast?: (message: string, type: 'error' | 'success' | 'info') => void;\n}\n\n/**\n * Error Handler Class\n */\nexport class ErrorHandler {\n private config: ErrorHandlerConfig;\n\n constructor(config: ErrorHandlerConfig = {}) {\n this.config = {\n showToast: true,\n ...config,\n };\n }\n\n handle(error: ApiError): void {\n const code = error.code;\n\n logger.error('Handling API Error', undefined, {\n code,\n status: error.status,\n traceId: error.traceId,\n });\n\n // Try custom handler first\n if (this.config.handlers?.[code]) {\n this.config.handlers[code]!(error);\n return;\n }\n\n // Fallback\n if (this.config.onError) {\n this.config.onError(error);\n } else {\n this.showDefaultToast(error);\n }\n }\n\n private showToast(message: string, type: 'error' | 'success' | 'info'): void {\n if (this.config.showToast && this.config.toast) {\n this.config.toast(message, type);\n }\n }\n\n private showDefaultToast(error: ApiError): void {\n const message =\n this.config.toastMessages?.[error.code] ||\n error.problemDetails.detail ||\n 'An error occurred';\n this.showToast(message, 'error');\n }\n}\n\n/**\n * Create global error handler\n */\nexport function createErrorHandler(config?: ErrorHandlerConfig): ErrorHandler {\n return new ErrorHandler(config);\n}\n\n/**\n * Map validation errors to form fields\n */\nexport function mapValidationErrors(errors: ValidationError[]): Record<string, string> {\n const mapped: Record<string, string> = {};\n for (const error of errors) {\n const fieldName = error.field.charAt(0).toLowerCase() + error.field.slice(1);\n mapped[fieldName] = error.message;\n }\n return mapped;\n}\n","/**\n * Retry Handler\n * \n * Retry logic for API requests.\n */\n\nimport type { RetryConfig } from './types';\n\nconst DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 3,\n baseDelay: 1000,\n maxDelay: 10000,\n retryCondition: (error: unknown) => {\n if (error instanceof Response) {\n return error.status >= 500 || error.status === 429;\n }\n return false;\n },\n};\n\n/**\n * Calculate backoff delay\n */\nexport function calculateBackoffDelay(\n attempt: number,\n baseDelay: number,\n maxDelay: number\n): number {\n const delay = baseDelay * Math.pow(2, attempt);\n const jitter = Math.random() * 0.3 * delay;\n return Math.min(delay + jitter, maxDelay);\n}\n\n/**\n * Check if error is retryable\n */\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof Response) {\n return error.status >= 500 || error.status === 429;\n }\n if (error instanceof TypeError) {\n // Network errors\n return true;\n }\n return false;\n}\n\n/**\n * Execute function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config?: RetryConfig\n): Promise<T> {\n const { maxRetries, baseDelay, maxDelay, retryCondition } = {\n ...DEFAULT_RETRY_CONFIG,\n ...config,\n };\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (attempt === maxRetries || !retryCondition(error)) {\n throw error;\n }\n\n const delay = calculateBackoffDelay(attempt, baseDelay, maxDelay);\n console.log(`Retry attempt ${attempt + 1}/${maxRetries} after ${delay}ms`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n\n/**\n * Fetch with timeout\n */\nexport async function fetchWithTimeout(\n input: RequestInfo | URL,\n init?: RequestInit & { timeout?: number }\n): Promise<Response> {\n const { timeout = 30000, ...fetchInit } = init || {};\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(input, {\n ...fetchInit,\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Create fetch function with retry\n */\nexport function createRetryFetch(config?: RetryConfig) {\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n return withRetry(() => fetch(input, init), config);\n };\n}\n"],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { isSessionValid } from './redis-session-storage.server';
12
12
  import type { AuthClaimMapping, UserInfo, TokenRefreshResult, LoginOptions, AuthError } from './types';
13
+ export declare function createAuthorizationResponseUrl(requestUrl: string, redirectUri: string): URL;
13
14
  /**
14
15
  * Configure claim mapping so different providers/backends can project their
15
16
  * own claim names onto the shared auth/session primitives.
@@ -1 +1 @@
1
- {"version":3,"file":"auth.server.d.ts","sourceRoot":"","sources":["../../src/auth/auth.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAKL,cAAc,EASf,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAEV,gBAAgB,EAChB,QAAQ,EAGR,kBAAkB,EAClB,YAAY,EAKZ,SAAS,EACV,MAAM,SAAS,CAAC;AAijBjB;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAgBzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAkID;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAkDA;AAyDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAkB/D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAK/C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAGtD;AAMD;;;;;GAKG;AACH,wBAAsB,KAAK,CACzB,OAAO,EAAE,OAAO,EAChB,kBAAkB,CAAC,EAAE,MAAM,GAAG,YAAY,GACzC,OAAO,CAAC,QAAQ,CAAC,CAgGnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0OxE;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkGhE;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8CjF;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmClF;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAQxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAiD7B;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
1
+ {"version":3,"file":"auth.server.d.ts","sourceRoot":"","sources":["../../src/auth/auth.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAKL,cAAc,EASf,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAEV,gBAAgB,EAChB,QAAQ,EAGR,kBAAkB,EAClB,YAAY,EAKZ,SAAS,EACV,MAAM,SAAS,CAAC;AAgOjB,wBAAgB,8BAA8B,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,GAAG,CAM3F;AAmVD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAgBzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAI5C;AAkID;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAkDA;AAyDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAkB/D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAK/C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAGtD;AAMD;;;;;GAKG;AACH,wBAAsB,KAAK,CACzB,OAAO,EAAE,OAAO,EAChB,kBAAkB,CAAC,EAAE,MAAM,GAAG,YAAY,GACzC,OAAO,CAAC,QAAQ,CAAC,CAgGnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2OxE;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkGhE;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8CjF;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmClF;AAED;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAQxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWrE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAiD7B;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"route-protection.server.d.ts","sourceRoot":"","sources":["../../src/auth/route-protection.server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route-protection.server.d.ts","sourceRoot":"","sources":["../../src/auth/route-protection.server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { S as updateAuthSession, _ as isSessionValid, a as createOAuthState, b as listCurrentUserAuthSessions, c as destroyAuthSessionById, d as destroyAuthSessionsBySid, f as destroyAuthSessionsForUser, g as getOAuthState, h as getCurrentAuthSessionId, i as createAuthSession, l as destroyAuthSessionsByIdentitySession, m as getAuthSessionById, n as cleanupExpiredOAuthStates, o as deleteOAuthState, p as getAuthSession, r as closeRedisConnection, s as destroyAuthSession, t as AUTH_ERROR_COOKIE_PREFIX, u as destroyAuthSessionsBySessionState, v as listAuthSessionDataForUser, x as requireAuthSession, y as listAuthSessionsForUser } from "../redis-session-storage.server-DGgQcJ9B.mjs";
2
+ import { A as resetAuthClaimMapping, C as handleCallback, D as logout, E as login, O as refreshTokens, S as handleBackChannelLogout, T as hasAuthError, _ as createAuthorizationResponseUrl, a as configureRouteProtection, b as getAuthError, c as protectRoute, d as resetRouteProtectionConfig, f as shouldRefreshToken, g as configureAuthClaimMapping, h as clearAuthServerCache, i as authRoute, k as requireAuth, l as protectedRoute, m as clearAuthErrorHeaders, n as isTokenExpiredResponse, o as onboardingRoute, p as subscriptionRoute, r as shouldAttemptTokenRefresh, s as policyRoute, t as attemptTokenRefresh, u as publicRoute, v as extractUserInfo, w as handleFrontChannelLogout, x as getUser, y as getAccessToken } from "../server-CqX2yCa4.mjs";
3
+ export { AUTH_ERROR_COOKIE_PREFIX, attemptTokenRefresh, authRoute, cleanupExpiredOAuthStates, clearAuthErrorHeaders, clearAuthServerCache, closeRedisConnection, configureAuthClaimMapping, configureRouteProtection, createAuthSession, createAuthorizationResponseUrl, createOAuthState, deleteOAuthState, destroyAuthSession, destroyAuthSessionById, destroyAuthSessionsByIdentitySession, destroyAuthSessionsBySessionState, destroyAuthSessionsBySid, destroyAuthSessionsForUser, extractUserInfo, getAccessToken, getAuthError, getAuthSession, getAuthSessionById, getCurrentAuthSessionId, getOAuthState, getUser, handleBackChannelLogout, handleCallback, handleFrontChannelLogout, hasAuthError, isSessionValid, isTokenExpiredResponse, listAuthSessionDataForUser, listAuthSessionsForUser, listCurrentUserAuthSessions, login, logout, onboardingRoute, policyRoute, protectRoute, protectedRoute, publicRoute, refreshTokens, requireAuth, requireAuthSession, resetAuthClaimMapping, resetRouteProtectionConfig, shouldAttemptTokenRefresh, shouldRefreshToken, subscriptionRoute, updateAuthSession };
@@ -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
- const separator = endpoint.includes("?") ? "&" : "?";
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 fetchContext = config.fetchContext ?? defaultFetchIdentityContext;
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 fetchPermissions = config.fetchPermissions ?? defaultFetchIdentityPermissions;
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
- const unsubscribe = signalRClient.onIdentityContextChanged(handleContextChange);
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
- //# sourceMappingURL=identity-C5un-xAs.js.map
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 "./logging-DviiAkLx.js";
2
- import { isApiClientError } from "./types-BaaE6wjY.js";
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 = 5 * 60 * 1e3;
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
- //# sourceMappingURL=server-Dxsww9v9.js.map
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-DviiAkLx.js";
2
- import { PermissionChecker } from "./permission-service-CvGpwI0d.js";
3
- import { PERMISSION_MODULES, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, getPermissionChecker, getPermissions, initializePermissions, useAllPermissions, useHasModulePermission, useHasPermission, useHasPermissions, useModulePermission, usePermission, usePermissionChecker, usePermissionStore, usePermissions } from "./permissions-_n1hIG7Q.js";
4
- import { evaluateAccessRequirement } from "./access-CGrsDMwu.js";
5
- import { TenantProvider, configureTenantClient, configureTenantEndpoints, getTenantCookieName, initializeTenantStore, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetTenantClientConfig, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useOrganization, useTenant, useTenantData, useTenantStore, useTenantSwitcher } from "./tenant-DfLemb6n.js";
6
- import { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants } from "./identity-C5un-xAs.js";
7
- import { isApiClientError } from "./types-BaaE6wjY.js";
8
- import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "./api-client-CgZUMukO.js";
9
- import { IdentitySignalRClient, SignalRClient, cleanupSignalR, createIdentitySignalRClient, getIdentitySignalRClient, initializeSignalR } from "./signalr-k-70VHKo.js";
10
- import { cachePresets, createQueryClient, createQueryKeyFactory, invalidationHelpers } from "./query-client-Bh3TyB8W.js";
11
- import "./src-B9rDUgSb.js";
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$1) {
6
- LogLevel$1["DEBUG"] = "debug";
7
- LogLevel$1["INFO"] = "info";
8
- LogLevel$1["WARN"] = "warn";
9
- LogLevel$1["ERROR"] = "error";
10
- return LogLevel$1;
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
- [LogLevel.DEBUG]: 0,
17
- [LogLevel.INFO]: 1,
18
- [LogLevel.WARN]: 2,
19
- [LogLevel.ERROR]: 3
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" ? LogLevel.INFO : LogLevel.DEBUG,
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(LogLevel.DEBUG, message, context);
37
+ this.log("debug", message, context);
40
38
  }
41
39
  info(message, context) {
42
- this.log(LogLevel.INFO, message, context);
40
+ this.log("info", message, context);
43
41
  }
44
42
  warn(message, context) {
45
- this.log(LogLevel.WARN, message, context);
43
+ this.log("warn", message, context);
46
44
  }
47
45
  error(message, error, context) {
48
- this.log(LogLevel.ERROR, message, context, error);
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 LogLevel.DEBUG:
68
+ case "debug":
71
69
  console.debug(formatted);
72
70
  break;
73
- case LogLevel.INFO:
71
+ case "info":
74
72
  console.info(formatted);
75
73
  break;
76
- case LogLevel.WARN:
74
+ case "warn":
77
75
  console.warn(formatted);
78
76
  break;
79
- case LogLevel.ERROR:
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
- //# sourceMappingURL=logging-DviiAkLx.js.map
115
+
116
+ //# sourceMappingURL=index.mjs.map