@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.
- package/CHANGELOG.md +12 -0
- 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 +4 -0
- package/package.json +33 -21
- 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
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
|
-
|
|
9
|
+
Do not open a public issue or discussion for a suspected vulnerability.
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { PermissionChecker } from "./permission-service-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
170
|
+
const { timeout = 3e4, ...fetchInit } = init || {};
|
|
172
171
|
const controller = new AbortController();
|
|
173
172
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
174
173
|
try {
|
|
175
|
-
|
|
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 {
|
|
195
|
-
|
|
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;
|
|
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":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAazD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,
|
|
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
|
-
|
|
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":";;;;;;;;;AASA,MAAa,yBAA8C;CACzD,kBAAkB;CAClB,gBAAgB;CAChB,iBAAiB;CACjB,aAAa;CACb,SAAS,CAAC;CACV,eAAe,KAAA;CACf,aAAa,CAAC;CACd,gBAAgB;CAChB,WAAW;CACX,QAAQ,KAAA;CACR,OAAO,KAAA;CACP,WAAW,KAAA;CACX,UAAU,KAAA;CACV,aAAa,KAAA;CACb,iBAAiB,KAAA;CACjB,aAAa,KAAA;CACb,UAAU,KAAA;CACV,WAAW,KAAA;CACX,oBAAoB;CACpB,aAAa,KAAA;AACf;AAyCA,MAAM,gCAEF;CACF,iBAAiB;CACjB,qBAAqB;CACrB,YAAY;CACZ,UAAU,OAAO,SAAS,MAAM,OAAO,IAAI;AAC7C;AAEA,IAAI,sBAA2C,EAC7C,GAAG,8BACL;AAEA,IAAM,4BAAN,cAAwC,MAAM;CAEjC;CACA;CAFX,YACE,QACA,UACA;EACA,MAAM,MAAM;EAHH,KAAA,SAAA;EACA,KAAA,WAAA;EAGT,KAAK,OAAO;CACd;AACF;AAEA,SAAS,yBAAqF;CAC5F,OAAO;EACL,GAAG;EACH,GAAG;CACL;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,mBAAmB,UAAkB,cAA+B;CAC3E,IAAI,CAAC,cACH,OAAO;CAIT,OAAO,GAAG,WADQ,SAAS,SAAS,GAAG,IAAI,MAAM,IAClB;AACjC;AAEA,eAAe,iBAAiB,UAAsC;CACpE,MAAM,OAAO,MAAM,SAAS,KAAK;CACjC,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,KAAK,MAAM,IAAI,IAAI;AACrD;AAEA,SAAgB,gCAAgC,SAAuD;CACrG,IAAI,CAAC,SAAS,OAAO,GACnB,OAAO;CAGT,IAAI,QAAQ,YAAY,OACtB,OAAO;CAGT,MAAM,gBAAgB,QAAQ,WAAW,QAAQ,QAAQ,QAAQ,YAAY,QAAQ;CACrF,IAAI,SAAS,aAAa,GACxB,OAAO;CAGT,OAAO;AACT;AAEA,SAAgB,oCAAoC,SAA4B;CAC9E,IAAI,MAAM,QAAQ,OAAO,GACvB,OAAO,QAAQ,QAAQ,eAAqC,OAAO,eAAe,QAAQ;CAG5F,IAAI,CAAC,SAAS,OAAO,GACnB,OAAO,CAAC;CAGV,MAAM,oBACJ,QAAQ,gBACP,SAAS,QAAQ,IAAI,IAAI,QAAQ,KAAK,cAAc,QAAQ,UAC5D,SAAS,QAAQ,OAAO,IAAI,QAAQ,QAAQ,cAAc,KAAA;CAE7D,OAAO,MAAM,QAAQ,iBAAiB,IAClC,kBAAkB,QAAQ,eAAqC,OAAO,eAAe,QAAQ,IAC7F,CAAC;AACP;AAEA,eAAe,4BAA4B,EACzC,UACA,OAAO,SACP,gBAC4E;CAC5E,MAAM,WAAW,MAAM,QAAQ,mBAAmB,UAAU,YAAY,GAAG;EACzE,QAAQ;EACR,SAAS,EACP,gBAAgB,mBAClB;CACF,CAAC;CAED,IAAI,SAAS,IACX,OAAO,gCAAgC,MAAM,iBAAiB,QAAQ,CAAC;CAGzE,IAAI,SAAS,WAAW,KAAK;EAC3B,MAAM,YAAY,MAAM,iBAAiB,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE;EACnE,IACE,SAAS,SAAS,MACjB,UAAU,SAAS,wBAAwB,UAAU,UAAU,uBAEhE,MAAM,IAAI,0BAA0B,wBAAwB,QAAQ;CAExE;CAEA,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,0BAA0B,gBAAgB,QAAQ;CAG9D,QAAQ,MAAM,uCAAuC,SAAS,MAAM;CACpE,OAAO;AACT;AAEA,eAAe,gCAAgC,EAC7C,UACA,OAAO,WAC8C;CACrD,MAAM,WAAW,MAAM,QAAQ,UAAU;EACvC,QAAQ;EACR,SAAS,EACP,gBAAgB,mBAClB;CACF,CAAC;CAED,IAAI,SAAS,IACX,OAAO,oCAAoC,MAAM,iBAAiB,QAAQ,CAAC;CAG7E,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,0BAA0B,gBAAgB,QAAQ;CAG9D,QAAQ,MAAM,kCAAkC,SAAS,MAAM;CAC/D,OAAO,CAAC;AACV;AAEA,SAAS,mBAAmB,SAAgE;CAC1F,MAAM,SAAS,uBAAuB;CACtC,MAAM,sBAAmD;EACvD,GAAG;EACH,YAAY,OAAO;CACrB;CAEA,IAAI,OAAO,gBAAgB;EACzB,OAAO,eAAe,mBAAmB;EACzC;CACF;CAEA,IAAI,OAAO,WAAW,aACpB,OAAO,SAAS,OAAO,OAAO;AAElC;;;;AAKA,SAAgB,uBAAuB,QAAmC;CACxE,sBAAsB;EACpB,GAAG;EACH,GAAG;CACL;AACF;;;;AAKA,SAAgB,2BAAiC;CAC/C,sBAAsB,EACpB,GAAG,8BACL;AACF;;;;AAKA,SAAgB,2BAA2B,QAGlC;CACP,uBAAuB,MAAM;AAC/B;AAEA,MAAa,mBAAmB,QAAuB,KAAK,SAAS;CACnE,SAAS;CAET,aAAa,eAAe;EAC1B,KAAK,WAAW,EACd,SAAS;GACP,GAAG,MAAM;GACT,GAAG;GACH,WAAW;GACX,6BAAa,IAAI,KAAK;EACxB,EACF,EAAE;CACJ;CAEA,gBAAgB,YAAY;EAC1B,IAAI;GACF,MAAM,SAAS,uBAAuB;GAEtC,KAAK,WAAW,EACd,SAAS;IAAE,GAAG,MAAM;IAAS,WAAW;GAAK,EAC/C,EAAE;GAGF,MAAM,aAAa,OADE,OAAO,gBAAgB,4BAAA,CACN;IACpC,UAAU,OAAO;IACjB,OAAO,OAAO;IACd,cAAc;IACd,gBAAgB,IAAI,CAAC,CAAC;GACxB,CAAC;GAED,IAAI,YAAY;IACd,IAAI,EACF,SAAS;KACP,GAAG,IAAI,CAAC,CAAC;KACT,GAAG;KACH,WAAW;KACX,6BAAa,IAAI,KAAK;IACxB,EACF,CAAC;IACD,QAAQ,IAAI,+BAA+B,UAAU;GACvD,OACE,KAAK,WAAW,EACd,SAAS;IAAE,GAAG,MAAM;IAAS,WAAW;GAAM,EAChD,EAAE;EAEN,SAAS,OAAO;GACd,IAAI,iBAAiB,2BAA2B;IAC9C,mBAAmB;KACjB,QAAQ,MAAM;KACd,UAAU,MAAM;IAClB,CAAC;IACD;GACF;GAEA,QAAQ,MAAM,sCAAsC,KAAK;GACzD,KAAK,WAAW,EACd,SAAS;IAAE,GAAG,MAAM;IAAS,WAAW;GAAM,EAChD,EAAE;EACJ;CACF;CAEA,oBAAoB,YAAY;EAC9B,IAAI;GACF,MAAM,SAAS,uBAAuB;GAEtC,QAAQ,IAAI,2BAA2B;GAGvC,MAAM,cAAc,OADK,OAAO,oBAAoB,gCAAA,CACT;IACzC,UAAU,OAAO;IACjB,OAAO,OAAO;IACd,gBAAgB,IAAI,CAAC,CAAC;GACxB,CAAC;GAED,KAAK,WAAW,EACd,SAAS;IACP,GAAG,MAAM;IACT;IACA,6BAAa,IAAI,KAAK;GACxB,EACF,EAAE;GAEF,QAAQ,IAAI,0BAA0B,EACpC,OAAO,YAAY,OACrB,CAAC;EACH,SAAS,OAAO;GACd,IAAI,iBAAiB,2BAA2B;IAC9C,mBAAmB;KACjB,QAAQ,MAAM;KACd,UAAU,MAAM;IAClB,CAAC;IACD;GACF;GAEA,QAAQ,MAAM,iCAAiC,KAAK;EACtD;CACF;CAEA,aAAa;EACX,IAAI,EAAE,SAAS,uBAAuB,CAAC;CACzC;AACF,EAAE;AAGF,MAAa,uBAAuB,kBAAkB,UAAU,MAAM,QAAQ,WAAW;AACzF,MAAa,0BAA0B,kBAAkB,UAAU,MAAM,QAAQ,kBAAkB,MAAM,QAAQ,eAAe;;AAEhI,MAAa,2BAA2B,kBAAkB,UAAU,MAAM,QAAQ,eAAe;AACjG,MAAa,uBACX,kBAAkB,WAAW;CAC3B,SAAS,MAAM,QAAQ;CACvB,eAAe,MAAM,QAAQ;AAC/B,EAAE;AACJ,MAAa,2BAA2B,kBAAkB,UAAU,MAAM,QAAQ,WAAW;AAC7F,MAAa,2BAA2B;CAKtC,OAAO;EAAE,SAJO,kBAAkB,UAAU,MAAM,OAInC;EAAG,gBAHK,kBAAkB,UAAU,MAAM,cAG1B;EAAG,oBAFP,kBAAkB,UAAU,MAAM,kBAEV;CAAE;AACvD;;;;;;;;ACtWA,SAAgB,wBAAwB,EACtC,UACA,gBACA,aACA,iBAC+B;CAC/B,MAAM,aAAa,kBAAkB,UAAU,MAAM,UAAU;CAC/D,MAAM,iBAAiB,kBAAkB,UAAU,MAAM,cAAc;CACvE,MAAM,qBAAqB,kBAAkB,UAAU,MAAM,kBAAkB;CAC/E,MAAM,iBAAiB,kBAAkB,UAAU,MAAM,QAAQ,cAAc;CAG/E,gBAAgB;EACd,IAAI,gBACF,WAAW,cAAc;CAE7B,GAAG,CAAC,gBAAgB,UAAU,CAAC;CAG/B,gBAAgB;EACd,IAAI,CAAC,eAAe;EAEpB,MAAM,uBAAuB,UAAuC;GAClE,QAAQ,IAAI,yCAAyC,KAAK;GAE1D,IAAI,MAAM,kBAAkB,kBAAkB,IAAI;IAChD,QAAQ,IAAI,4CAA4C;IAExD,IAAI,MAAM,WAAW,wBAAwB,MAAM,WAAW,yBAAyB;KACrF,QAAQ,IAAI,qEAAqE;KACjF,mBAAmB;IACrB,OACE,eAAe;GAEnB;EACF;EAIA,OAFoB,cAAc,yBAAyB,mBAE1C;CACnB,GAAG;EAAC;EAAa;EAAgB;EAAgB;EAAoB;CAAa,CAAC;CAGnF,gBAAgB;EACd,IAAI,CAAC,gBACH,eAAe;CAEnB,GAAG,CAAC,gBAAgB,cAAc,CAAC;CAGnC,gBAAgB;EACd,aAAa;GACX,IAAI,eACF,cAAc,WAAW,CAAC,CAAC,OAAO,UAAU;IAC1C,QAAQ,MAAM,gCAAgC,KAAK;GACrD,CAAC;EAEL;CACF,GAAG,CAAC,aAAa,CAAC;CAElB,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB"}
|
|
@@ -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":";;;AAeA,IAAI,qBAAgD;;;;AAKpD,SAAgB,4BAA4B,SAAmC;CAC7E,qBAAqB;AACvB;AAGA,MAAM,+BAAe,IAAI,IAOvB;AAEF,MAAM,oBAAoB,MAAS;;;;AAKnC,eAAsB,qBAAqB,SAAgD;CACzF,IAAI,CAAC,oBACH,MAAM,IAAI,MAAM,8EAA8E;CAGhG,IAAI;EACF,OAAO,KAAK,2BAA2B;EACvC,MAAM,UAAU,MAAM,mBAAmB,OAAO;EAEhD,OAAO,MAAM,4BAA4B;GACvC,kBAAkB,QAAQ;GAC1B,gBAAgB,QAAQ,kBAAkB,QAAQ;GAClD,aAAa,QAAQ;GACrB,iBAAiB,QAAQ,aAAa,UAAU;EAClD,CAAC;EAED,OAAO;CACT,SAAS,OAAgB;EACvB,OAAO,MAAM,oCAAoC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EAG3F,IAAI,iBAAiB,KAAK,KAAK,MAAM,UAAU,WAAW,OAAO,MAAM,gBAAgB,UAAU,sBAAsB;GACrH,OAAO,MAAM,wDAAwD;GACrE,MAAM,sCAAsB,IAAI,MAAM,iBAAiB;GACvD,oBAA+D,mBAAmB;GAClF,MAAM;EACR;EAEA,MAAM;CACR;AACF;;;;AAKA,eAAsB,mBACpB,SACA,QACA,eAAe,OACe;CAC9B,MAAM,WAAW;CACjB,MAAM,SAAS,aAAa,IAAI,QAAQ;CACxC,MAAM,MAAM,KAAK,IAAI;CAGrB,IAAI,CAAC,gBAAgB,UAAU,MAAM,OAAO,YAAY,mBAAmB;EACzE,OAAO,MAAM,0CAA0C,EAAE,OAAO,CAAC;EACjE,OAAO,OAAO;CAChB;CAGA,MAAM,UAAU,MAAM,qBAAqB,OAAO;CAGlD,aAAa,IAAI,UAAU;EACzB;EACA,WAAW;EACX,SAAS,QAAQ,kBAAkB;CACrC,CAAC;CAED,OAAO,MAAM,0CAA0C,EAAE,OAAO,CAAC;CACjE,OAAO;AACT;;;;AAKA,SAAgB,0BAA0B,QAAuB;CAC/D,IAAI,QAAQ;EACV,aAAa,OAAO,MAAM;EAC1B,OAAO,MAAM,2CAA2C,EAAE,OAAO,CAAC;CACpE,OAAO;EACL,aAAa,MAAM;EACnB,OAAO,MAAM,oCAAoC;CACnD;AACF;AAOA,IAAI,oBAA8C;;;;AAKlD,SAAgB,2BAA2B,SAAkC;CAC3E,oBAAoB;AACtB;;;;AAKA,eAAsB,qBAAqB,SAAkB,UAAqC;CAChG,IAAI,CAAC,UAAU;EACb,OAAO,KAAK,kDAAkD;EAC9D,OAAO,CAAC;CACV;CAEA,IAAI,CAAC,mBACH,MAAM,IAAI,MAAM,2EAA2E;CAG7F,IAAI;EACF,OAAO,MAAM,kBAAkB,SAAS,QAAQ;CAClD,SAAS,OAAO;EACd,OAAO,MAAM,oCAAoC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EAC3F,OAAO,CAAC;CACV;AACF;;;;AA+BA,eAAsB,kBACpB,SACA,UAAoC,CAAC,GACX;CAC1B,MAAM,cAAc,QAAQ,eAAe,CAAC;CAC5C,MAAM,UAAU,YAAY,KAAK,eAAe,WAAW,QAAQ;CACnE,MAAM,wBACJ,QAAQ,iBAAiB,QAAQ,SAAS,QAAQ,aAAa,IAC3D,QAAQ,gBACR,QAAQ,MAAM,KAAA;CAEpB,IAAI,cAAwB,CAAC;CAC7B,IAAI,yBAAyB,QAAQ,SACnC,cAAc,MAAM,qBAAqB,QAAQ,SAAS,qBAAqB;CAGjF,OAAO;EACL;EACA;EACA,eAAe;EACf,aAAa,QAAQ,eAAe;EACpC;EACA,gBAAgB,QAAQ,kBAAkB,QAAQ,mBAAmB;EACrE,iBAAiB,QAAQ,mBAAmB,QAAQ,kBAAkB;EACtE,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,aAAa,QAAQ;EACrB,iBAAiB,QAAQ;EACzB,aAAa,QAAQ;EACrB,UAAU,QAAQ;EAClB,WAAW,QAAQ;EACnB,oBAAoB,QAAQ,sBAAsB;CACpD;AACF;;;;AAKA,SAAgB,yBAAyB,QAAgB,YAA6B;CACpF,MAAM,SAAS,aAAa,IAAI,MAAM;CACtC,OAAO,CAAC,UAAU,OAAO,YAAY;AACvC;;;;AAKA,SAAgB,qBAAqB,QAAgB,YAA0B;CAC7E,MAAM,SAAS,aAAa,IAAI,MAAM;CACtC,IAAI,CAAC,QAAQ;CAEb,OAAO,UAAU;CACjB,OAAO,YAAY;CACnB,OAAO,MAAM,6CAA6C;EACxD;EACA,SAAS;CACX,CAAC;AACH"}
|
|
@@ -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
|