@eminuckan/spine 0.2.0 → 0.2.1

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 (181) hide show
  1. package/README.md +2 -2
  2. package/dist/api-client/api-config.server.d.ts +40 -0
  3. package/dist/api-client/api-config.server.d.ts.map +1 -0
  4. package/dist/api-client/error-codes.d.ts +57 -0
  5. package/dist/api-client/error-codes.d.ts.map +1 -0
  6. package/dist/api-client/error-handler.d.ts +52 -0
  7. package/dist/api-client/error-handler.d.ts.map +1 -0
  8. package/dist/api-client/fetch-client.server.d.ts +29 -0
  9. package/dist/api-client/fetch-client.server.d.ts.map +1 -0
  10. package/dist/api-client/index.d.ts +11 -3
  11. package/dist/api-client/index.d.ts.map +1 -0
  12. package/dist/api-client/index.js +3 -2
  13. package/dist/api-client/retry-handler.d.ts +29 -0
  14. package/dist/api-client/retry-handler.d.ts.map +1 -0
  15. package/dist/api-client/server.d.ts +9 -4
  16. package/dist/api-client/server.d.ts.map +1 -0
  17. package/dist/api-client/server.js +4 -3
  18. package/dist/api-client/types.d.ts +121 -0
  19. package/dist/api-client/types.d.ts.map +1 -0
  20. package/dist/{api-client-Ftxqm3Ep.js → api-client-DsrxK05Z.js} +1 -1
  21. package/dist/{api-client-Ftxqm3Ep.js.map → api-client-DsrxK05Z.js.map} +1 -1
  22. package/dist/auth/auth.server.d.ts +124 -0
  23. package/dist/auth/auth.server.d.ts.map +1 -0
  24. package/dist/auth/index.d.ts +8 -2
  25. package/dist/auth/index.d.ts.map +1 -0
  26. package/dist/auth/redis-session-storage.server.d.ts +21 -0
  27. package/dist/auth/redis-session-storage.server.d.ts.map +1 -0
  28. package/dist/auth/route-protection.server.d.ts +107 -0
  29. package/dist/auth/route-protection.server.d.ts.map +1 -0
  30. package/dist/auth/server.d.ts +13 -3
  31. package/dist/auth/server.d.ts.map +1 -0
  32. package/dist/auth/server.js +3 -3
  33. package/dist/auth/token-refresh.server.d.ts +17 -0
  34. package/dist/auth/token-refresh.server.d.ts.map +1 -0
  35. package/dist/auth/types.d.ts +160 -0
  36. package/dist/auth/types.d.ts.map +1 -0
  37. package/dist/auth.server-BPiCtlJE.js +5 -0
  38. package/dist/{auth.server-C979TR6W.js → auth.server-Cx2h2qu3.js} +3 -3
  39. package/dist/{auth.server-C979TR6W.js.map → auth.server-Cx2h2qu3.js.map} +1 -1
  40. package/dist/http/response.d.ts +12 -0
  41. package/dist/http/response.d.ts.map +1 -0
  42. package/dist/identity/identity-context-provider.d.ts +19 -0
  43. package/dist/identity/identity-context-provider.d.ts.map +1 -0
  44. package/dist/identity/identity-context.server.d.ts +73 -0
  45. package/dist/identity/identity-context.server.d.ts.map +1 -0
  46. package/dist/identity/identity-store.d.ts +55 -0
  47. package/dist/identity/identity-store.d.ts.map +1 -0
  48. package/dist/identity/index.d.ts +9 -4
  49. package/dist/identity/index.d.ts.map +1 -0
  50. package/dist/identity/server.d.ts +6 -4
  51. package/dist/identity/server.d.ts.map +1 -0
  52. package/dist/identity/server.js +2 -1
  53. package/dist/identity/types.d.ts +91 -0
  54. package/dist/identity/types.d.ts.map +1 -0
  55. package/dist/index.d.ts +14 -12
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +6 -5
  58. package/dist/logging/config.d.ts +7 -0
  59. package/dist/logging/config.d.ts.map +1 -0
  60. package/dist/logging/index.d.ts +9 -3
  61. package/dist/logging/index.d.ts.map +1 -0
  62. package/dist/logging/logger.d.ts +22 -0
  63. package/dist/logging/logger.d.ts.map +1 -0
  64. package/dist/logging/types.d.ts +27 -0
  65. package/dist/logging/types.d.ts.map +1 -0
  66. package/dist/permissions/components.d.ts +48 -0
  67. package/dist/permissions/components.d.ts.map +1 -0
  68. package/dist/permissions/constants.d.ts +9 -0
  69. package/dist/permissions/constants.d.ts.map +1 -0
  70. package/dist/permissions/hooks.d.ts +47 -0
  71. package/dist/permissions/hooks.d.ts.map +1 -0
  72. package/dist/permissions/index.d.ts +28 -2
  73. package/dist/permissions/index.d.ts.map +1 -0
  74. package/dist/permissions/permission-initializer.d.ts +18 -0
  75. package/dist/permissions/permission-initializer.d.ts.map +1 -0
  76. package/dist/permissions/permission-service.d.ts +53 -0
  77. package/dist/permissions/permission-service.d.ts.map +1 -0
  78. package/dist/permissions/permission-store.d.ts +42 -0
  79. package/dist/permissions/permission-store.d.ts.map +1 -0
  80. package/dist/permissions/route-protection.server.d.ts +33 -0
  81. package/dist/permissions/route-protection.server.d.ts.map +1 -0
  82. package/dist/permissions/types.d.ts +44 -0
  83. package/dist/permissions/types.d.ts.map +1 -0
  84. package/dist/query-client/index.d.ts +8 -2
  85. package/dist/query-client/index.d.ts.map +1 -0
  86. package/dist/query-client/index.js +1 -1
  87. package/dist/query-client/query-config.d.ts +55 -0
  88. package/dist/query-client/query-config.d.ts.map +1 -0
  89. package/dist/query-client/types.d.ts +115 -0
  90. package/dist/query-client/types.d.ts.map +1 -0
  91. package/dist/{query-client-DRPQIKHX.js → query-client-Bpe08pk9.js} +1 -1
  92. package/dist/query-client-Bpe08pk9.js.map +1 -0
  93. package/dist/react-router/index.d.ts +9 -12
  94. package/dist/react-router/index.d.ts.map +1 -0
  95. package/dist/react-router/index.js +6 -5
  96. package/dist/react-router/server.d.ts +9 -18
  97. package/dist/react-router/server.d.ts.map +1 -0
  98. package/dist/react-router/server.js +13 -12
  99. package/dist/{redis-session-storage.server-DhufyPjd.js → redis-session-storage.server-BR_0y-QO.js} +2 -2
  100. package/dist/{redis-session-storage.server-DhufyPjd.js.map → redis-session-storage.server-BR_0y-QO.js.map} +1 -1
  101. package/dist/{server-CiSbg-9C.js → server-BHfKHKF_.js} +5 -5
  102. package/dist/{server-CiSbg-9C.js.map → server-BHfKHKF_.js.map} +1 -1
  103. package/dist/{server-C-DiVP9e.js → server-BxvRxjCK.js} +2 -2
  104. package/dist/{server-C-DiVP9e.js.map → server-BxvRxjCK.js.map} +1 -1
  105. package/dist/{server-iWm3oeCM.js → server-C3L8J7iE.js} +3 -2
  106. package/dist/server-C3L8J7iE.js.map +1 -0
  107. package/dist/{server-CNp6gjFE.js → server-CouYoZiw.js} +1 -1
  108. package/dist/{server-CNp6gjFE.js.map → server-CouYoZiw.js.map} +1 -1
  109. package/dist/server-DZmcd2Lr.js +422 -0
  110. package/dist/server-DZmcd2Lr.js.map +1 -0
  111. package/dist/server.d.ts +14 -18
  112. package/dist/server.d.ts.map +1 -0
  113. package/dist/server.js +13 -12
  114. package/dist/signalr/index.d.ts +8 -4
  115. package/dist/signalr/index.d.ts.map +1 -0
  116. package/dist/signalr/index.js +1 -1
  117. package/dist/signalr/signalr-client.d.ts +98 -0
  118. package/dist/signalr/signalr-client.d.ts.map +1 -0
  119. package/dist/signalr/types.d.ts +42 -0
  120. package/dist/signalr/types.d.ts.map +1 -0
  121. package/dist/{signalr-BJGcXc0m.js → signalr-SJ-1sIxw.js} +1 -1
  122. package/dist/{signalr-BJGcXc0m.js.map → signalr-SJ-1sIxw.js.map} +1 -1
  123. package/dist/tenant/index.d.ts +9 -3
  124. package/dist/tenant/index.d.ts.map +1 -0
  125. package/dist/tenant/server.d.ts +7 -3
  126. package/dist/tenant/server.d.ts.map +1 -0
  127. package/dist/tenant/server.js +2 -2
  128. package/dist/tenant/tenant-context.d.ts +45 -0
  129. package/dist/tenant/tenant-context.d.ts.map +1 -0
  130. package/dist/tenant/tenant-cookie.server.d.ts +35 -0
  131. package/dist/tenant/tenant-cookie.server.d.ts.map +1 -0
  132. package/dist/tenant/tenant-store.d.ts +58 -0
  133. package/dist/tenant/tenant-store.d.ts.map +1 -0
  134. package/dist/tenant/tenant.server.d.ts +49 -0
  135. package/dist/tenant/tenant.server.d.ts.map +1 -0
  136. package/dist/tenant/types.d.ts +51 -0
  137. package/dist/tenant/types.d.ts.map +1 -0
  138. package/dist/types-BnKaWdfe.js +8 -0
  139. package/dist/types-BnKaWdfe.js.map +1 -0
  140. package/dist/utils.d.ts +6 -0
  141. package/dist/utils.d.ts.map +1 -0
  142. package/package.json +2 -3
  143. package/dist/auth.server-DU-x2LPA.js +0 -5
  144. package/dist/index-BIJOC8Fb.d.ts +0 -272
  145. package/dist/index-BIJOC8Fb.d.ts.map +0 -1
  146. package/dist/index-Bt9XE6gj.d.ts +0 -104
  147. package/dist/index-Bt9XE6gj.d.ts.map +0 -1
  148. package/dist/index-C2hGpzGr.d.ts +0 -74
  149. package/dist/index-C2hGpzGr.d.ts.map +0 -1
  150. package/dist/index-CfQsb3l_.d.ts +0 -170
  151. package/dist/index-CfQsb3l_.d.ts.map +0 -1
  152. package/dist/index-D6RK_5vm.d.ts +0 -199
  153. package/dist/index-D6RK_5vm.d.ts.map +0 -1
  154. package/dist/index-DrttYPAj.d.ts +0 -30
  155. package/dist/index-DrttYPAj.d.ts.map +0 -1
  156. package/dist/index-geY2ynnN.d.ts +0 -141
  157. package/dist/index-geY2ynnN.d.ts.map +0 -1
  158. package/dist/index-kr8Wk7v5.d.ts +0 -1
  159. package/dist/query-client-DRPQIKHX.js.map +0 -1
  160. package/dist/server-BoeUiXSh.d.ts +0 -40
  161. package/dist/server-BoeUiXSh.d.ts.map +0 -1
  162. package/dist/server-CLbksnvg.d.ts +0 -144
  163. package/dist/server-CLbksnvg.d.ts.map +0 -1
  164. package/dist/server-CXGaRaiS.d.ts +0 -256
  165. package/dist/server-CXGaRaiS.d.ts.map +0 -1
  166. package/dist/server-DLgH3FRR.js +0 -312
  167. package/dist/server-DLgH3FRR.js.map +0 -1
  168. package/dist/server-DazqdM63.d.ts +0 -93
  169. package/dist/server-DazqdM63.d.ts.map +0 -1
  170. package/dist/server-iWm3oeCM.js.map +0 -1
  171. package/dist/server-zEKMDpP7.d.ts +0 -80
  172. package/dist/server-zEKMDpP7.d.ts.map +0 -1
  173. package/dist/types-BTjFzIhv.d.ts +0 -31
  174. package/dist/types-BTjFzIhv.d.ts.map +0 -1
  175. package/dist/types-DoIfDhl6.d.ts +0 -55
  176. package/dist/types-DoIfDhl6.d.ts.map +0 -1
  177. package/dist/types-DufLRY7A.d.ts +0 -164
  178. package/dist/types-DufLRY7A.d.ts.map +0 -1
  179. package/dist/types-uDrDMkCq.d.ts +0 -93
  180. package/dist/types-uDrDMkCq.d.ts.map +0 -1
  181. /package/dist/{src-D7mh9MKA.js → src-CJ2RLi94.js} +0 -0
package/README.md CHANGED
@@ -46,7 +46,7 @@ Spine aims to be:
46
46
  | `@eminuckan/spine/identity` | Identity store/provider/types |
47
47
  | `@eminuckan/spine/identity/server` | Identity context cache and server orchestration |
48
48
  | `@eminuckan/spine/api-client` | Client-side API types and errors |
49
- | `@eminuckan/spine/api-client/server` | Server-side API config and Axios helpers |
49
+ | `@eminuckan/spine/api-client/server` | Server-side API config and fetch helpers |
50
50
  | `@eminuckan/spine/permissions` | Client-side permission primitives |
51
51
  | `@eminuckan/spine/logging` | Logging primitives |
52
52
  | `@eminuckan/spine/query-client` | TanStack Query helpers |
@@ -60,7 +60,7 @@ Spine aims to be:
60
60
  - Server-side permission route protection configuration
61
61
  - Multi-tenant client state and server helpers
62
62
  - Identity context cache, fetch orchestration, and client store/provider
63
- - API client factory and Axios interceptor setup
63
+ - API client factory and fetch middleware setup
64
64
  - TanStack Query client defaults and cache presets
65
65
  - SignalR client helpers
66
66
  - Logging primitives
@@ -0,0 +1,40 @@
1
+ /**
2
+ * API Configuration Server Module
3
+ *
4
+ * Centralized configuration for all API clients,
5
+ * automatically handling authentication tokens and tenant context.
6
+ */
7
+ import type { APIConfig, CreateAPIConfigOptions, GetAccessTokenFn, GetCurrentTenantFn, APILogger } from './types';
8
+ /**
9
+ * API Config Factory Options
10
+ */
11
+ export interface APIConfigFactoryOptions {
12
+ /**
13
+ * Base URL for API requests
14
+ */
15
+ baseURL?: string;
16
+ /**
17
+ * Default headers applied to all requests
18
+ */
19
+ defaultHeaders?: Record<string, string>;
20
+ /**
21
+ * User agent string
22
+ */
23
+ userAgent?: string;
24
+ /**
25
+ * Default timeout in milliseconds
26
+ */
27
+ timeout?: number;
28
+ }
29
+ /**
30
+ * Create API Config Factory
31
+ *
32
+ * Creates a factory function that generates API configurations
33
+ * with the provided auth and tenant resolvers.
34
+ */
35
+ export declare function createAPIConfigFactory(getAccessToken: GetAccessTokenFn, getCurrentTenant: GetCurrentTenantFn, logger?: APILogger, factoryOptions?: APIConfigFactoryOptions): {
36
+ createAPIConfig: (request: Request, options?: CreateAPIConfigOptions) => Promise<APIConfig>;
37
+ getAPIBaseURL: () => string;
38
+ };
39
+ export type APIConfigFactory = ReturnType<typeof createAPIConfigFactory>;
40
+ //# sourceMappingURL=api-config.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-config.server.d.ts","sourceRoot":"","sources":["../../src/api-client/api-config.server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAiBlH;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,gBAAgB,EAChC,gBAAgB,EAAE,kBAAkB,EACpC,MAAM,CAAC,EAAE,SAAS,EAClB,cAAc,GAAE,uBAA4B;+BA+DjC,OAAO,YACP,sBAAsB,KAC9B,OAAO,CAAC,SAAS,CAAC;yBAgEK,MAAM;EAQjC;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Error Codes
3
+ *
4
+ * Standard error codes from backend.
5
+ */
6
+ export declare const ErrorCodes: {
7
+ readonly Validation: {
8
+ readonly Failed: "ValidationFailed";
9
+ };
10
+ readonly Identity: {
11
+ readonly InvalidCredentials: "InvalidCredentials";
12
+ readonly UserInactive: "UserInactive";
13
+ readonly TenantContextMissing: "TenantContextMissing";
14
+ readonly InvalidCurrentPassword: "InvalidCurrentPassword";
15
+ readonly OwnerCannotDisableMembership: "OwnerCannotDisableMembership";
16
+ readonly CurrentUserMissing: "CurrentUserMissing";
17
+ readonly UserNotFound: "UserNotFound";
18
+ readonly EmailAlreadyExists: "EmailAlreadyExists";
19
+ };
20
+ readonly Accounting: {
21
+ readonly OpeningBalanceLockedReconciliation: "OpeningBalanceLockedReconciliation";
22
+ readonly OpeningBalanceLockedJournal: "OpeningBalanceLockedJournal";
23
+ readonly SystemLocked: "SystemLocked";
24
+ readonly AccountsDuplicate: "AccountsDuplicate";
25
+ readonly AccountNotFound: "AccountNotFound";
26
+ };
27
+ readonly Subscription: {
28
+ readonly SubscriptionRequired: "SubscriptionRequired";
29
+ readonly SubscriptionExpired: "SubscriptionExpired";
30
+ readonly FeatureNotAvailable: "FeatureNotAvailable";
31
+ };
32
+ readonly PropertyManagement: {
33
+ readonly PropertyNotFound: "PropertyNotFound";
34
+ readonly UnitNotFound: "UnitNotFound";
35
+ readonly DuplicateProperty: "DuplicateProperty";
36
+ readonly DuplicateUnit: "DuplicateUnit";
37
+ };
38
+ readonly Leasing: {
39
+ readonly LeaseNotFound: "LeaseNotFound";
40
+ readonly LeaseConflict: "LeaseConflict";
41
+ readonly TenantNotFound: "TenantNotFound";
42
+ readonly LateFeeValidationError: "LateFeeValidationError";
43
+ };
44
+ readonly NotFound: "NotFound";
45
+ readonly Unauthorized: "Unauthorized";
46
+ readonly Forbidden: "Forbidden";
47
+ readonly InternalError: "InternalError";
48
+ };
49
+ /**
50
+ * Check if error code is from PropertyManagement module
51
+ */
52
+ export declare function isPropertyManagementError(code: string): boolean;
53
+ /**
54
+ * Check if error code is from Leasing module
55
+ */
56
+ export declare function isLeasingError(code: string): boolean;
57
+ //# sourceMappingURL=error-codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-codes.d.ts","sourceRoot":"","sources":["../../src/api-client/error-codes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDb,CAAC;AAEX;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Error Handler
3
+ *
4
+ * Centralized error handling with ProblemDetails parsing.
5
+ */
6
+ import type { ProblemDetails, ValidationError } from './types';
7
+ /**
8
+ * API Error Class
9
+ */
10
+ export declare class ApiError extends Error {
11
+ problemDetails: ProblemDetails;
12
+ response: Response;
13
+ constructor(problemDetails: ProblemDetails, response: Response);
14
+ get code(): string;
15
+ get status(): number;
16
+ get isValidationError(): boolean;
17
+ get validationErrors(): ValidationError[];
18
+ get traceId(): string;
19
+ }
20
+ /**
21
+ * Parse API error response
22
+ */
23
+ export declare function handleApiError(response: Response): Promise<never>;
24
+ /**
25
+ * Error Handler Configuration
26
+ */
27
+ export interface ErrorHandlerConfig {
28
+ handlers?: Partial<Record<string, (error: ApiError) => void>>;
29
+ showToast?: boolean;
30
+ toastMessages?: Partial<Record<string, string>>;
31
+ onError?: (error: ApiError) => void;
32
+ toast?: (message: string, type: 'error' | 'success' | 'info') => void;
33
+ }
34
+ /**
35
+ * Error Handler Class
36
+ */
37
+ export declare class ErrorHandler {
38
+ private config;
39
+ constructor(config?: ErrorHandlerConfig);
40
+ handle(error: ApiError): void;
41
+ private showToast;
42
+ private showDefaultToast;
43
+ }
44
+ /**
45
+ * Create global error handler
46
+ */
47
+ export declare function createErrorHandler(config?: ErrorHandlerConfig): ErrorHandler;
48
+ /**
49
+ * Map validation errors to form fields
50
+ */
51
+ export declare function mapValidationErrors(errors: ValidationError[]): Record<string, string>;
52
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/api-client/error-handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/D;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAExB,cAAc,EAAE,cAAc;IAC9B,QAAQ,EAAE,QAAQ;gBADlB,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,QAAQ;IAM3B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED,IAAI,gBAAgB,IAAI,eAAe,EAAE,CAExC;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;CACF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAuBvE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAC;CACvE;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;gBAEvB,MAAM,GAAE,kBAAuB;IAO3C,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAuB7B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,gBAAgB;CAOzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,YAAY,CAE5E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Fetch-native API client setup for generated OpenAPI clients.
3
+ *
4
+ * This module preserves the shared request behaviors we need around fetch:
5
+ * - request ID enrichment
6
+ * - serialized token refresh on 401
7
+ * - retry with exponential backoff
8
+ * - ProblemDetails-aware error shaping
9
+ *
10
+ * It gives downstream apps a single fetch-native entry point for generated clients.
11
+ */
12
+ import type { APIConfig, APILogger, CreateAPIConfigOptions, EnhancedAPIClient, FetchMiddleware, RetryConfig, TokenRefreshFn } from './types';
13
+ type GeneratedFetchClient = object;
14
+ export interface FetchSetupOptions {
15
+ logger?: APILogger;
16
+ attemptTokenRefresh?: TokenRefreshFn;
17
+ retryConfig?: RetryConfig;
18
+ addRequestId?: boolean;
19
+ }
20
+ export declare function createFetchMiddleware(request: Request, options?: FetchSetupOptions): FetchMiddleware[];
21
+ export interface CreateApiClientOptions extends FetchSetupOptions, CreateAPIConfigOptions {
22
+ credentials?: RequestCredentials;
23
+ }
24
+ export declare function createApiClient<T extends GeneratedFetchClient>(request: Request, ClientClass: new (configuration?: unknown) => T, createAPIConfig: (request: Request, options?: CreateAPIConfigOptions) => Promise<APIConfig>, ConfigurationClass: new (params?: Record<string, unknown>) => unknown, options?: CreateApiClientOptions): Promise<{
25
+ client: EnhancedAPIClient<T>;
26
+ tenantId: string;
27
+ }>;
28
+ export {};
29
+ //# sourceMappingURL=fetch-client.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-client.server.d.ts","sourceRoot":"","sources":["../../src/api-client/fetch-client.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAEV,SAAS,EAET,SAAS,EACT,sBAAsB,EACtB,iBAAiB,EAEjB,eAAe,EAGf,WAAW,EACX,cAAc,EAEf,MAAM,SAAS,CAAC;AASjB,KAAK,oBAAoB,GAAG,MAAM,CAAC;AA4PnC,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,mBAAmB,CAAC,EAAE,cAAc,CAAC;IACrC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,iBAAsB,GAC9B,eAAe,EAAE,CAuHnB;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB,EAAE,sBAAsB;IACvF,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AA8ED,wBAAsB,eAAe,CAAC,CAAC,SAAS,oBAAoB,EAClE,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,KAAK,aAAa,CAAC,EAAE,OAAO,KAAK,CAAC,EAC/C,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,sBAAsB,KAAK,OAAO,CAAC,SAAS,CAAC,EAC3F,kBAAkB,EAAE,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,EACrE,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC7D"}
@@ -1,3 +1,11 @@
1
- import "../types-BTjFzIhv.js";
2
- import { APIConfig, APILogger, ApiError$1 as ApiError, CreateAPIConfigOptions, ErrorCodes$1 as ErrorCodes, ErrorHandler$1 as ErrorHandler, GetAccessTokenFn, GetCurrentTenantFn, ProblemDetails, RetryConfig, TokenRefreshFn, TokenRefreshResult, ValidationError, calculateBackoffDelay$1 as calculateBackoffDelay, createRetryFetch$1 as createRetryFetch, fetchWithTimeout$1 as fetchWithTimeout, handleApiError$1 as handleApiError, isRetryableError$1 as isRetryableError, withRetry$1 as withRetry } from "../index-D6RK_5vm.js";
3
- export { APIConfig, APILogger, ApiError, CreateAPIConfigOptions, ErrorCodes, ErrorHandler, GetAccessTokenFn, GetCurrentTenantFn, ProblemDetails, RetryConfig, TokenRefreshFn, TokenRefreshResult, ValidationError, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry };
1
+ /**
2
+ * API Client Module - Client Exports
3
+ *
4
+ * Client-side exports for API client module.
5
+ */
6
+ export type { APIConfig, APIRequestConfig, APIResponse, APIClientError, CreateAPIConfigOptions, ProblemDetails, ValidationError, RetryConfig, APILogger, GetAccessTokenFn, GetCurrentTenantFn, TokenRefreshResult, TokenRefreshFn, FetchAPI, FetchMiddleware, FetchRequestContext, FetchResponseContext, FetchErrorContext, EnhancedAPIClient, } from './types';
7
+ export { isApiClientError } from './types';
8
+ export { ErrorCodes } from './error-codes';
9
+ export { ApiError, ErrorHandler, handleApiError } from './error-handler';
10
+ export { calculateBackoffDelay, isRetryableError, withRetry, fetchWithTimeout, createRetryFetch } from './retry-handler';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api-client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,eAAe,EACf,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG3C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,iBAAiB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import "../logging-_h2mi3L9.js";
2
- import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "../api-client-Ftxqm3Ep.js";
2
+ import { isApiClientError } from "../types-BnKaWdfe.js";
3
+ import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "../api-client-DsrxK05Z.js";
3
4
 
4
- export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry };
5
+ export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isApiClientError, isRetryableError, withRetry };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Retry Handler
3
+ *
4
+ * Retry logic for API requests.
5
+ */
6
+ import type { RetryConfig } from './types';
7
+ /**
8
+ * Calculate backoff delay
9
+ */
10
+ export declare function calculateBackoffDelay(attempt: number, baseDelay: number, maxDelay: number): number;
11
+ /**
12
+ * Check if error is retryable
13
+ */
14
+ export declare function isRetryableError(error: unknown): boolean;
15
+ /**
16
+ * Execute function with retry logic
17
+ */
18
+ export declare function withRetry<T>(fn: () => Promise<T>, config?: RetryConfig): Promise<T>;
19
+ /**
20
+ * Fetch with timeout
21
+ */
22
+ export declare function fetchWithTimeout(input: RequestInfo | URL, init?: RequestInit & {
23
+ timeout?: number;
24
+ }): Promise<Response>;
25
+ /**
26
+ * Create fetch function with retry
27
+ */
28
+ export declare function createRetryFetch(config?: RetryConfig): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
29
+ //# sourceMappingURL=retry-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-handler.d.ts","sourceRoot":"","sources":["../../src/api-client/retry-handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAc3C;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASxD;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,QAAQ,CAAC,CAenB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,IACrC,OAAO,WAAW,GAAG,GAAG,EAAE,OAAO,WAAW,KAAG,OAAO,CAAC,QAAQ,CAAC,CAG/E"}
@@ -1,4 +1,9 @@
1
- import "../types-BTjFzIhv.js";
2
- import { APIConfig, APILogger, ApiError$1 as ApiError, CreateAPIConfigOptions, ErrorCodes$1 as ErrorCodes, ErrorHandler$1 as ErrorHandler, GetAccessTokenFn, GetCurrentTenantFn, ProblemDetails, RetryConfig, TokenRefreshFn, TokenRefreshResult, ValidationError, calculateBackoffDelay$1 as calculateBackoffDelay, createRetryFetch$1 as createRetryFetch, fetchWithTimeout$1 as fetchWithTimeout, handleApiError$1 as handleApiError, isRetryableError$1 as isRetryableError, withRetry$1 as withRetry } from "../index-D6RK_5vm.js";
3
- import { APIConfigFactory, APIConfigFactoryOptions, AxiosSetupOptions, CreateEnhancedClientOptions, createAPIConfigFactory$1 as createAPIConfigFactory, createEnhancedClient$1 as createEnhancedClient, setupAxiosInterceptors$1 as setupAxiosInterceptors } from "../server-CLbksnvg.js";
4
- export { APIConfig, APIConfigFactory, APIConfigFactoryOptions, APILogger, ApiError, AxiosSetupOptions, CreateAPIConfigOptions, CreateEnhancedClientOptions, ErrorCodes, ErrorHandler, GetAccessTokenFn, GetCurrentTenantFn, ProblemDetails, RetryConfig, TokenRefreshFn, TokenRefreshResult, ValidationError, calculateBackoffDelay, createAPIConfigFactory, createEnhancedClient, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, setupAxiosInterceptors, withRetry };
1
+ /**
2
+ * API Client Module - Server Exports
3
+ *
4
+ * Server-side exports for API client module.
5
+ */
6
+ export * from './index';
7
+ export { createAPIConfigFactory, type APIConfigFactory, type APIConfigFactoryOptions, } from './api-config.server';
8
+ export { createFetchMiddleware, createApiClient, type FetchSetupOptions, type CreateApiClientOptions, } from './fetch-client.server';
9
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api-client/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC5B,MAAM,uBAAuB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import "../logging-_h2mi3L9.js";
2
- import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "../api-client-Ftxqm3Ep.js";
3
- import { createAPIConfigFactory, createEnhancedClient, setupAxiosInterceptors } from "../server-DLgH3FRR.js";
2
+ import { isApiClientError } from "../types-BnKaWdfe.js";
3
+ import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "../api-client-DsrxK05Z.js";
4
+ import { createAPIConfigFactory, createApiClient, createFetchMiddleware } from "../server-DZmcd2Lr.js";
4
5
 
5
- export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createAPIConfigFactory, createEnhancedClient, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, setupAxiosInterceptors, withRetry };
6
+ export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createAPIConfigFactory, createApiClient, createFetchMiddleware, createRetryFetch, fetchWithTimeout, handleApiError, isApiClientError, isRetryableError, withRetry };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * API Client Types
3
+ */
4
+ export type { LoggerConfig as Logger } from '../logging/types';
5
+ export interface APIConfig {
6
+ basePath: string;
7
+ accessToken?: string;
8
+ headers: Record<string, string>;
9
+ tenantId: string;
10
+ credentials?: RequestCredentials;
11
+ baseOptions?: {
12
+ timeout?: number;
13
+ };
14
+ }
15
+ export interface CreateAPIConfigOptions {
16
+ requireTenant?: boolean;
17
+ includeAuth?: boolean;
18
+ tenantId?: string;
19
+ customHeaders?: Record<string, string>;
20
+ }
21
+ export interface ProblemDetails {
22
+ type: string;
23
+ title: string;
24
+ status: number;
25
+ detail: string;
26
+ traceId: string;
27
+ requestId?: string;
28
+ instance?: string;
29
+ meta?: {
30
+ errors?: ValidationError[];
31
+ [key: string]: unknown;
32
+ };
33
+ }
34
+ export interface ValidationError {
35
+ field: string;
36
+ message: string;
37
+ code?: string;
38
+ }
39
+ export interface RetryConfig {
40
+ maxRetries?: number;
41
+ baseDelay?: number;
42
+ maxDelay?: number;
43
+ retryCondition?: (error: unknown) => boolean;
44
+ }
45
+ export interface APIRequestConfig {
46
+ url?: string;
47
+ method?: string;
48
+ headers?: Record<string, string>;
49
+ }
50
+ export interface APIResponse<T = unknown> {
51
+ data: T;
52
+ status: number;
53
+ statusText: string;
54
+ headers: Record<string, string>;
55
+ raw?: Response;
56
+ }
57
+ export interface APIClientError<T = unknown> extends Error {
58
+ isApiClientError: true;
59
+ response?: APIResponse<T>;
60
+ config?: APIRequestConfig;
61
+ problemDetails?: ProblemDetails;
62
+ shouldLogout?: boolean;
63
+ originalError?: unknown;
64
+ cause?: unknown;
65
+ }
66
+ export declare function isApiClientError(error: unknown): error is APIClientError;
67
+ export type FetchAPI = typeof fetch;
68
+ export interface FetchRequestContext {
69
+ fetch: FetchAPI;
70
+ url: string;
71
+ init: RequestInit;
72
+ }
73
+ export interface FetchResponseContext extends FetchRequestContext {
74
+ response: Response;
75
+ }
76
+ export interface FetchErrorContext extends FetchRequestContext {
77
+ error: unknown;
78
+ response?: Response;
79
+ }
80
+ export interface FetchMiddleware {
81
+ pre?: (context: FetchRequestContext) => Promise<{
82
+ url: string;
83
+ init: RequestInit;
84
+ } | void>;
85
+ post?: (context: FetchResponseContext) => Promise<Response | void>;
86
+ onError?: (context: FetchErrorContext) => Promise<Response | void>;
87
+ }
88
+ export type EnhancedAPIClient<T> = {
89
+ [K in keyof T]: T[K] extends (...args: infer A) => Promise<infer R> ? K extends `${string}Raw` ? T[K] : (...args: A) => Promise<APIResponse<R>> : T[K];
90
+ };
91
+ /**
92
+ * API Logger interface for dependency injection (simplified)
93
+ */
94
+ export interface APILogger {
95
+ debug?: (message: string, context?: Record<string, unknown>) => void;
96
+ info?: (message: string, context?: Record<string, unknown>) => void;
97
+ warn?: (message: string, error?: Error, context?: Record<string, unknown>) => void;
98
+ error?: (message: string, error?: Error, context?: Record<string, unknown>) => void;
99
+ }
100
+ /**
101
+ * Function type for getting access token from request
102
+ */
103
+ export type GetAccessTokenFn = (request: Request) => Promise<string | null>;
104
+ /**
105
+ * Function type for getting current tenant from request
106
+ */
107
+ export type GetCurrentTenantFn = (request: Request) => Promise<string | null>;
108
+ /**
109
+ * Token refresh result
110
+ */
111
+ export interface TokenRefreshResult {
112
+ success: boolean;
113
+ newAccessToken?: string;
114
+ shouldLogout?: boolean;
115
+ error?: string;
116
+ }
117
+ /**
118
+ * Function type for attempting token refresh
119
+ */
120
+ export type TokenRefreshFn = (request: Request) => Promise<TokenRefreshResult>;
121
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api-client/types.ts"],"names":[],"mappings":"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"}
@@ -209,4 +209,4 @@ function createRetryFetch(config) {
209
209
 
210
210
  //#endregion
211
211
  export { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry };
212
- //# sourceMappingURL=api-client-Ftxqm3Ep.js.map
212
+ //# sourceMappingURL=api-client-DsrxK05Z.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client-Ftxqm3Ep.js","names":["problemDetails: ProblemDetails","response: Response","config: ErrorHandlerConfig","error: ApiError","message: string","type: 'error' | 'success' | 'info'","DEFAULT_RETRY_CONFIG: Required<RetryConfig>","error: unknown","attempt: number","baseDelay: number","maxDelay: number","fn: () => Promise<T>","config?: RetryConfig","lastError: unknown","input: RequestInfo | URL","init?: RequestInit & { timeout?: number }","init?: RequestInit"],"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 * Standard error codes from backend.\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 // Accounting\n Accounting: {\n OpeningBalanceLockedReconciliation: 'OpeningBalanceLockedReconciliation',\n OpeningBalanceLockedJournal: 'OpeningBalanceLockedJournal',\n SystemLocked: 'SystemLocked',\n AccountsDuplicate: 'AccountsDuplicate',\n AccountNotFound: 'AccountNotFound',\n },\n\n // Subscription\n Subscription: {\n SubscriptionRequired: 'SubscriptionRequired',\n SubscriptionExpired: 'SubscriptionExpired',\n FeatureNotAvailable: 'FeatureNotAvailable',\n },\n\n // Property Management\n PropertyManagement: {\n PropertyNotFound: 'PropertyNotFound',\n UnitNotFound: 'UnitNotFound',\n DuplicateProperty: 'DuplicateProperty',\n DuplicateUnit: 'DuplicateUnit',\n },\n\n // Leasing\n Leasing: {\n LeaseNotFound: 'LeaseNotFound',\n LeaseConflict: 'LeaseConflict',\n TenantNotFound: 'TenantNotFound',\n LateFeeValidationError: 'LateFeeValidationError',\n },\n\n // General\n NotFound: 'NotFound',\n Unauthorized: 'Unauthorized',\n Forbidden: 'Forbidden',\n InternalError: 'InternalError',\n} as const;\n\n/**\n * Check if error code is from PropertyManagement module\n */\nexport function isPropertyManagementError(code: string): boolean {\n return Object.values(ErrorCodes.PropertyManagement).includes(code as any);\n}\n\n/**\n * Check if error code is from Leasing module\n */\nexport function isLeasingError(code: string): boolean {\n return Object.values(ErrorCodes.Leasing).includes(code as any);\n}\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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"}
1
+ {"version":3,"file":"api-client-DsrxK05Z.js","names":["problemDetails: ProblemDetails","response: Response","config: ErrorHandlerConfig","error: ApiError","message: string","type: 'error' | 'success' | 'info'","DEFAULT_RETRY_CONFIG: Required<RetryConfig>","error: unknown","attempt: number","baseDelay: number","maxDelay: number","fn: () => Promise<T>","config?: RetryConfig","lastError: unknown","input: RequestInfo | URL","init?: RequestInit & { timeout?: number }","init?: RequestInit"],"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 * Standard error codes from backend.\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 // Accounting\n Accounting: {\n OpeningBalanceLockedReconciliation: 'OpeningBalanceLockedReconciliation',\n OpeningBalanceLockedJournal: 'OpeningBalanceLockedJournal',\n SystemLocked: 'SystemLocked',\n AccountsDuplicate: 'AccountsDuplicate',\n AccountNotFound: 'AccountNotFound',\n },\n\n // Subscription\n Subscription: {\n SubscriptionRequired: 'SubscriptionRequired',\n SubscriptionExpired: 'SubscriptionExpired',\n FeatureNotAvailable: 'FeatureNotAvailable',\n },\n\n // Property Management\n PropertyManagement: {\n PropertyNotFound: 'PropertyNotFound',\n UnitNotFound: 'UnitNotFound',\n DuplicateProperty: 'DuplicateProperty',\n DuplicateUnit: 'DuplicateUnit',\n },\n\n // Leasing\n Leasing: {\n LeaseNotFound: 'LeaseNotFound',\n LeaseConflict: 'LeaseConflict',\n TenantNotFound: 'TenantNotFound',\n LateFeeValidationError: 'LateFeeValidationError',\n },\n\n // General\n NotFound: 'NotFound',\n Unauthorized: 'Unauthorized',\n Forbidden: 'Forbidden',\n InternalError: 'InternalError',\n} as const;\n\n/**\n * Check if error code is from PropertyManagement module\n */\nexport function isPropertyManagementError(code: string): boolean {\n return Object.values(ErrorCodes.PropertyManagement).includes(code as any);\n}\n\n/**\n * Check if error code is from Leasing module\n */\nexport function isLeasingError(code: string): boolean {\n return Object.values(ErrorCodes.Leasing).includes(code as any);\n}\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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"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * OAuth2/OIDC Authentication Server Module
3
+ *
4
+ * Handles:
5
+ * - Login flow (PKCE)
6
+ * - Token exchange
7
+ * - Token refresh
8
+ * - Logout
9
+ * - Session management
10
+ */
11
+ import { isSessionValid } from './redis-session-storage.server';
12
+ import type { AuthClaimMapping, UserInfo, TokenRefreshResult, LoginOptions, AuthError } from './types';
13
+ /**
14
+ * Configure claim mapping so different providers/backends can project their
15
+ * own claim names onto the shared auth/session primitives.
16
+ */
17
+ export declare function configureAuthClaimMapping(mapping: AuthClaimMapping): void;
18
+ /**
19
+ * Reset claim mapping to the built-in defaults.
20
+ */
21
+ export declare function resetAuthClaimMapping(): void;
22
+ /**
23
+ * Extract user info from ID token claims
24
+ */
25
+ export declare function extractUserInfo(claims: Record<string, unknown>): {
26
+ tenants: string[];
27
+ currentTenant?: string;
28
+ isOnboarded: boolean;
29
+ tenantRoles: Record<string, string[]>;
30
+ permissions: string[];
31
+ };
32
+ /**
33
+ * Check if there's an auth error in cookies
34
+ *
35
+ * This should be called by apps (especially those without landing pages like Dashboard)
36
+ * BEFORE starting OAuth flow to prevent redirect loops.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // In dashboard's index route loader
41
+ * const authError = getAuthError(request);
42
+ * if (authError) {
43
+ * return { authError }; // Show error to user
44
+ * }
45
+ * // No error, proceed with session check
46
+ * const user = await getUser(request);
47
+ * if (!user) {
48
+ * return login(request);
49
+ * }
50
+ * ```
51
+ */
52
+ export declare function getAuthError(request: Request): AuthError | null;
53
+ /**
54
+ * Clear auth error cookies
55
+ *
56
+ * Call this after displaying the error to the user
57
+ */
58
+ export declare function clearAuthErrorHeaders(): Headers;
59
+ /**
60
+ * Check if request has auth error (quick check without parsing)
61
+ */
62
+ export declare function hasAuthError(request: Request): boolean;
63
+ /**
64
+ * Initiate OAuth login flow
65
+ *
66
+ * @param request - The incoming request
67
+ * @param returnUrlOrOptions - Either a return URL string or LoginOptions object
68
+ */
69
+ export declare function login(request: Request, returnUrlOrOptions?: string | LoginOptions): Promise<Response>;
70
+ /**
71
+ * Handle OAuth callback
72
+ *
73
+ * Handles OAuth authorization response including success and error cases.
74
+ *
75
+ * Error Handling:
76
+ * - `access_denied`: User doesn't have access to this application
77
+ * - For apps with landing page: Redirect home with error cookie
78
+ * - For apps without landing page: Redirect home with error cookie (must handle specially)
79
+ * - `login_required`: No active identity session
80
+ * - Redirect to home to start fresh login
81
+ */
82
+ export declare function handleCallback(request: Request): Promise<Response>;
83
+ /**
84
+ * Logout user
85
+ *
86
+ * Backend uses id_token_hint to identify which tokens to revoke.
87
+ *
88
+ * **Scenario 1: Full SSO Logout (Manual Sign Out)**
89
+ * - Query param: sso_logout=true
90
+ * - Result: Full logout + identity cookie cleared
91
+ * - User must re-authenticate everywhere
92
+ *
93
+ * **Scenario 2: Standard Logout (Automatic/Token Expired)**
94
+ * - No sso_logout param
95
+ * - Result: Tokens revoked based on id_token_hint
96
+ * - Backend determines which tokens to revoke
97
+ *
98
+ * Configuration:
99
+ * - `OIDC_SSO_LOGOUT` env var ('true' | 'false') - default behavior
100
+ * - Query param `sso_logout=true` - override for full logout
101
+ */
102
+ export declare function logout(request: Request): Promise<Response>;
103
+ /**
104
+ * Get current user from session
105
+ */
106
+ export declare function getUser(request: Request): Promise<UserInfo | null>;
107
+ /**
108
+ * Require authenticated user (throws redirect to login if not authenticated)
109
+ */
110
+ export declare function requireAuth(request: Request): Promise<UserInfo>;
111
+ /**
112
+ * Get access token from session
113
+ */
114
+ export declare function getAccessToken(request: Request): Promise<string | null>;
115
+ /**
116
+ * Refresh tokens
117
+ */
118
+ export declare function refreshTokens(request: Request, refreshToken?: string): Promise<TokenRefreshResult>;
119
+ export { isSessionValid };
120
+ /**
121
+ * Clear auth server cache (useful for testing)
122
+ */
123
+ export declare function clearAuthServerCache(): void;
124
+ //# sourceMappingURL=auth.server.d.ts.map