@eminuckan/spine 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +116 -98
  3. package/SECURITY.md +5 -15
  4. package/dist/access/index.mjs +2 -0
  5. package/dist/{access-CGrsDMwu.js → access-BHy1v5sr.mjs} +6 -8
  6. package/dist/access-BHy1v5sr.mjs.map +1 -0
  7. package/dist/api-client/index.mjs +3 -0
  8. package/dist/api-client/server.mjs +4 -0
  9. package/dist/{api-client-CgZUMukO.js → api-client-CB7XZaMg.mjs} +8 -10
  10. package/dist/api-client-CB7XZaMg.mjs.map +1 -0
  11. package/dist/auth/auth.server.d.ts +1 -0
  12. package/dist/auth/auth.server.d.ts.map +1 -1
  13. package/dist/auth/index.mjs +1 -0
  14. package/dist/auth/route-protection.server.d.ts.map +1 -1
  15. package/dist/auth/server.mjs +3 -0
  16. package/dist/{identity-C5un-xAs.js → identity/index.mjs} +24 -21
  17. package/dist/identity/index.mjs.map +1 -0
  18. package/dist/{server-Dxsww9v9.js → identity/server.mjs} +7 -8
  19. package/dist/identity/server.mjs.map +1 -0
  20. package/dist/{index.js → index.mjs} +11 -13
  21. package/dist/{logging-DviiAkLx.js → logging/index.mjs} +22 -24
  22. package/dist/logging/index.mjs.map +1 -0
  23. package/dist/{permission-service-CvGpwI0d.js → permission-service-DUyKomU3.mjs} +6 -10
  24. package/dist/permission-service-DUyKomU3.mjs.map +1 -0
  25. package/dist/permissions/index.mjs +3 -0
  26. package/dist/{permissions-_n1hIG7Q.js → permissions-C5dTP9y4.mjs} +23 -15
  27. package/dist/permissions-C5dTP9y4.mjs.map +1 -0
  28. package/dist/query-client/index.mjs +2 -0
  29. package/dist/{query-client-Bh3TyB8W.js → query-client-_xB300gc.mjs} +49 -15
  30. package/dist/query-client-_xB300gc.mjs.map +1 -0
  31. package/dist/react-router/{index.js → index.mjs} +11 -13
  32. package/dist/react-router/server.mjs +17 -0
  33. package/dist/{redis-session-storage.server-v2VcpFsQ.js → redis-session-storage.server-DGgQcJ9B.mjs} +30 -42
  34. package/dist/redis-session-storage.server-DGgQcJ9B.mjs.map +1 -0
  35. package/dist/{server-C96NMFBy.js → server-ByScGRXA.mjs} +39 -36
  36. package/dist/server-ByScGRXA.mjs.map +1 -0
  37. package/dist/{auth.server-D5oiz9aF.js → server-CqX2yCa4.mjs} +388 -119
  38. package/dist/server-CqX2yCa4.mjs.map +1 -0
  39. package/dist/server.mjs +90 -0
  40. package/dist/server.mjs.map +1 -0
  41. package/dist/signalr/index.mjs +2 -0
  42. package/dist/{signalr-k-70VHKo.js → signalr-C-TA8nEE.mjs} +11 -6
  43. package/dist/signalr-C-TA8nEE.mjs.map +1 -0
  44. package/dist/{tenant-DfLemb6n.js → tenant/index.mjs} +19 -12
  45. package/dist/tenant/index.mjs.map +1 -0
  46. package/dist/{server-Ca-3DJf2.js → tenant/server.mjs} +12 -12
  47. package/dist/tenant/server.mjs.map +1 -0
  48. package/dist/{types-BaaE6wjY.js → types-DqCXinAk.mjs} +3 -3
  49. package/dist/types-DqCXinAk.mjs.map +1 -0
  50. package/docs/codex-for-oss-application.md +14 -2
  51. package/package.json +14 -2
  52. package/dist/access/index.js +0 -4
  53. package/dist/access-CGrsDMwu.js.map +0 -1
  54. package/dist/api-client/index.js +0 -5
  55. package/dist/api-client/server.js +0 -6
  56. package/dist/api-client-CgZUMukO.js.map +0 -1
  57. package/dist/auth/index.js +0 -0
  58. package/dist/auth/server.js +0 -6
  59. package/dist/auth.server-D5oiz9aF.js.map +0 -1
  60. package/dist/auth.server-lC94lmlx.js +0 -5
  61. package/dist/identity/index.js +0 -3
  62. package/dist/identity/server.js +0 -5
  63. package/dist/identity-C5un-xAs.js.map +0 -1
  64. package/dist/logging/index.js +0 -3
  65. package/dist/logging-DviiAkLx.js.map +0 -1
  66. package/dist/permission-service-CvGpwI0d.js.map +0 -1
  67. package/dist/permissions/index.js +0 -4
  68. package/dist/permissions-_n1hIG7Q.js.map +0 -1
  69. package/dist/query-client/index.js +0 -3
  70. package/dist/query-client-Bh3TyB8W.js.map +0 -1
  71. package/dist/react-router/server.js +0 -20
  72. package/dist/redis-session-storage.server-v2VcpFsQ.js.map +0 -1
  73. package/dist/server-B6PCxc-T.js +0 -304
  74. package/dist/server-B6PCxc-T.js.map +0 -1
  75. package/dist/server-C96NMFBy.js.map +0 -1
  76. package/dist/server-Ca-3DJf2.js.map +0 -1
  77. package/dist/server-Dxsww9v9.js.map +0 -1
  78. package/dist/server-hGrElJMG.js +0 -78
  79. package/dist/server-hGrElJMG.js.map +0 -1
  80. package/dist/server.js +0 -20
  81. package/dist/signalr/index.js +0 -3
  82. package/dist/signalr-k-70VHKo.js.map +0 -1
  83. package/dist/src-B9rDUgSb.js +0 -0
  84. package/dist/tenant/index.js +0 -3
  85. package/dist/tenant/server.js +0 -5
  86. package/dist/tenant-DfLemb6n.js.map +0 -1
  87. package/dist/types-BaaE6wjY.js.map +0 -1
@@ -1,12 +1,19 @@
1
1
  import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query";
2
-
3
2
  //#region src/query-client/query-config.ts
4
3
  /**
4
+ * Query Client Configuration
5
+ *
6
+ * Optimized caching strategy for different types of data:
7
+ * - Static data (rarely changes): Long staleTime and gcTime
8
+ * - Dynamic data (changes frequently): Short staleTime
9
+ * - Critical data (needs to be fresh): Very short staleTime with prefetching
10
+ */
11
+ /**
5
12
  * Default configuration
6
13
  */
7
14
  const DEFAULT_CONFIG = {
8
- staleTime: 5 * 60 * 1e3,
9
- gcTime: 10 * 60 * 1e3,
15
+ staleTime: 300 * 1e3,
16
+ gcTime: 600 * 1e3,
10
17
  maxRetries: 3,
11
18
  maxMutationRetries: 1,
12
19
  refetchOnWindowFocus: true,
@@ -54,7 +61,7 @@ function createQueryClient(config = {}) {
54
61
  ...config
55
62
  };
56
63
  const handleLogout = onLogout || createDefaultLogoutHandler();
57
- const queryClient = new QueryClient({
64
+ return new QueryClient({
58
65
  defaultOptions: {
59
66
  queries: {
60
67
  staleTime,
@@ -97,7 +104,6 @@ function createQueryClient(config = {}) {
97
104
  logger?.error?.("Mutation error", error instanceof Error ? error : void 0, { is401: is401Error(error) });
98
105
  } })
99
106
  });
100
- return queryClient;
101
107
  }
102
108
  /**
103
109
  * Cache Presets for different data types
@@ -105,24 +111,40 @@ function createQueryClient(config = {}) {
105
111
  * Use these presets for consistent caching across the application.
106
112
  */
107
113
  const cachePresets = {
114
+ /**
115
+ * Static data - rarely changes
116
+ * Good for: permissions, roles, system settings
117
+ */
108
118
  static: {
109
- staleTime: 30 * 60 * 1e3,
110
- gcTime: 60 * 60 * 1e3,
119
+ staleTime: 1800 * 1e3,
120
+ gcTime: 3600 * 1e3,
111
121
  refetchOnWindowFocus: false
112
122
  },
123
+ /**
124
+ * Normal data - changes occasionally
125
+ * Good for: resources, users, tenant data
126
+ */
113
127
  normal: {
114
- staleTime: 5 * 60 * 1e3,
115
- gcTime: 10 * 60 * 1e3,
128
+ staleTime: 300 * 1e3,
129
+ gcTime: 600 * 1e3,
116
130
  refetchOnWindowFocus: true
117
131
  },
132
+ /**
133
+ * Dynamic data - changes frequently
134
+ * Good for: transactions, activities, notifications
135
+ */
118
136
  dynamic: {
119
- staleTime: 1 * 60 * 1e3,
120
- gcTime: 5 * 60 * 1e3,
137
+ staleTime: 60 * 1e3,
138
+ gcTime: 300 * 1e3,
121
139
  refetchOnWindowFocus: true
122
140
  },
141
+ /**
142
+ * Realtime data - always fresh
143
+ * Good for: live status, counters, dashboards
144
+ */
123
145
  realtime: {
124
146
  staleTime: 0,
125
- gcTime: 1 * 60 * 1e3,
147
+ gcTime: 60 * 1e3,
126
148
  refetchOnWindowFocus: true
127
149
  }
128
150
  };
@@ -150,12 +172,21 @@ function createQueryKeyFactory(module) {
150
172
  * Invalidation helpers for common patterns
151
173
  */
152
174
  const invalidationHelpers = {
175
+ /**
176
+ * Invalidate all queries for a module
177
+ */
153
178
  invalidateModule: async (queryClient, module) => {
154
179
  await queryClient.invalidateQueries({ queryKey: [module] });
155
180
  },
181
+ /**
182
+ * Invalidate all list queries for a module
183
+ */
156
184
  invalidateLists: async (queryClient, module) => {
157
185
  await queryClient.invalidateQueries({ queryKey: [module, "list"] });
158
186
  },
187
+ /**
188
+ * Invalidate a specific detail query
189
+ */
159
190
  invalidateDetail: async (queryClient, module, id) => {
160
191
  await queryClient.invalidateQueries({ queryKey: [
161
192
  module,
@@ -163,12 +194,15 @@ const invalidationHelpers = {
163
194
  id
164
195
  ] });
165
196
  },
197
+ /**
198
+ * Invalidate and refetch (useful for critical data)
199
+ */
166
200
  invalidateAndRefetch: async (queryClient, queryKey) => {
167
201
  await queryClient.invalidateQueries({ queryKey });
168
202
  await queryClient.refetchQueries({ queryKey });
169
203
  }
170
204
  };
171
-
172
205
  //#endregion
173
- export { cachePresets, createQueryClient, createQueryKeyFactory, invalidationHelpers };
174
- //# sourceMappingURL=query-client-Bh3TyB8W.js.map
206
+ export { invalidationHelpers as i, createQueryClient as n, createQueryKeyFactory as r, cachePresets as t };
207
+
208
+ //# sourceMappingURL=query-client-_xB300gc.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-client-_xB300gc.mjs","names":[],"sources":["../src/query-client/query-config.ts"],"sourcesContent":["/**\n * Query Client Configuration\n * \n * Optimized caching strategy for different types of data:\n * - Static data (rarely changes): Long staleTime and gcTime\n * - Dynamic data (changes frequently): Short staleTime\n * - Critical data (needs to be fresh): Very short staleTime with prefetching\n */\n\nimport { QueryClient, QueryCache, MutationCache } from '@tanstack/react-query';\nimport type { QueryClientConfig, CachePresets } from './types';\n\n/**\n * Default configuration\n */\nconst DEFAULT_CONFIG: Required<Omit<QueryClientConfig, 'logger' | 'onLogout'>> = {\n staleTime: 5 * 60 * 1000, // 5 minutes\n gcTime: 10 * 60 * 1000, // 10 minutes\n maxRetries: 3,\n maxMutationRetries: 1,\n refetchOnWindowFocus: true,\n refetchOnReconnect: false,\n refetchOnMount: false,\n};\n\n/**\n * Check if error is a 401 Unauthorized error\n */\nfunction is401Error(error: unknown): boolean {\n if (error instanceof Response) {\n return error.status === 401;\n }\n\n if (error && typeof error === 'object' && 'status' in error) {\n return (error as any).status === 401;\n }\n\n return false;\n}\n\n/**\n * Check if error requires logout (refresh token expired)\n */\nfunction shouldLogout(error: unknown): boolean {\n if (error && typeof error === 'object') {\n // Check for shouldLogout flag from server\n if ('shouldLogout' in error && (error as any).shouldLogout === true) {\n return true;\n }\n\n // Check for SESSION_EXPIRED code\n if ('code' in error && (error as any).code === 'SESSION_EXPIRED') {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default logout handler\n */\nfunction createDefaultLogoutHandler(): (returnUrl?: string) => void {\n return (returnUrl?: string) => {\n if (typeof window !== 'undefined') {\n const currentUrl = returnUrl || window.location.pathname + window.location.search;\n window.location.href = `/auth/logout?logout=local&returnUrl=${encodeURIComponent(currentUrl)}`;\n }\n };\n}\n\n/**\n * Create optimized QueryClient instance\n * \n * @param config - Configuration options\n * @returns Configured QueryClient with optimized defaults\n */\nexport function createQueryClient(config: QueryClientConfig = {}): QueryClient {\n const {\n staleTime,\n gcTime,\n maxRetries,\n maxMutationRetries,\n refetchOnWindowFocus,\n refetchOnReconnect,\n refetchOnMount,\n logger,\n onLogout,\n } = { ...DEFAULT_CONFIG, ...config };\n\n const handleLogout = onLogout || createDefaultLogoutHandler();\n\n const queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime,\n gcTime,\n retry: (failureCount, error) => {\n // Don't retry 401 errors - the fetch client handles refresh flows separately\n if (is401Error(error)) {\n logger?.debug?.('Query failed with 401, not retrying');\n return false;\n }\n return failureCount < maxRetries;\n },\n retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),\n refetchOnWindowFocus,\n refetchOnReconnect,\n refetchOnMount,\n },\n mutations: {\n retry: (failureCount, error) => {\n if (is401Error(error)) {\n logger?.debug?.('Mutation failed with 401, not retrying');\n return false;\n }\n return failureCount < maxMutationRetries;\n },\n retryDelay: 1000,\n },\n },\n queryCache: new QueryCache({\n onError: (error) => {\n if (shouldLogout(error)) {\n handleLogout();\n return;\n }\n logger?.error?.('Query error', error instanceof Error ? error : undefined, {\n is401: is401Error(error),\n });\n },\n }),\n mutationCache: new MutationCache({\n onError: (error) => {\n if (shouldLogout(error)) {\n handleLogout();\n return;\n }\n logger?.error?.('Mutation error', error instanceof Error ? error : undefined, {\n is401: is401Error(error),\n });\n },\n }),\n });\n\n return queryClient;\n}\n\n/**\n * Cache Presets for different data types\n * \n * Use these presets for consistent caching across the application.\n */\nexport const cachePresets: CachePresets = {\n /**\n * Static data - rarely changes\n * Good for: permissions, roles, system settings\n */\n static: {\n staleTime: 30 * 60 * 1000, // 30 minutes\n gcTime: 60 * 60 * 1000, // 1 hour\n refetchOnWindowFocus: false,\n },\n\n /**\n * Normal data - changes occasionally\n * Good for: resources, users, tenant data\n */\n normal: {\n staleTime: 5 * 60 * 1000, // 5 minutes\n gcTime: 10 * 60 * 1000, // 10 minutes\n refetchOnWindowFocus: true,\n },\n\n /**\n * Dynamic data - changes frequently\n * Good for: transactions, activities, notifications\n */\n dynamic: {\n staleTime: 1 * 60 * 1000, // 1 minute\n gcTime: 5 * 60 * 1000, // 5 minutes\n refetchOnWindowFocus: true,\n },\n\n /**\n * Realtime data - always fresh\n * Good for: live status, counters, dashboards\n */\n realtime: {\n staleTime: 0, // Always stale\n gcTime: 1 * 60 * 1000, // 1 minute\n refetchOnWindowFocus: true,\n },\n};\n\n/**\n * Create base query keys for a module\n */\nexport function createQueryKeyFactory<T extends string>(\n module: T\n): {\n all: readonly [T];\n lists: () => readonly [T, 'list'];\n list: (filters?: Record<string, unknown>) => readonly [T, 'list', Record<string, unknown>?];\n details: () => readonly [T, 'detail'];\n detail: (id: string) => readonly [T, 'detail', string];\n} {\n return {\n all: [module] as const,\n lists: () => [module, 'list'] as const,\n list: (filters?: Record<string, unknown>) => [module, 'list', filters] as const,\n details: () => [module, 'detail'] as const,\n detail: (id: string) => [module, 'detail', id] as const,\n };\n}\n\n/**\n * Invalidation helpers for common patterns\n */\nexport const invalidationHelpers = {\n /**\n * Invalidate all queries for a module\n */\n invalidateModule: async (queryClient: QueryClient, module: string): Promise<void> => {\n await queryClient.invalidateQueries({ queryKey: [module] });\n },\n\n /**\n * Invalidate all list queries for a module\n */\n invalidateLists: async (queryClient: QueryClient, module: string): Promise<void> => {\n await queryClient.invalidateQueries({ queryKey: [module, 'list'] });\n },\n\n /**\n * Invalidate a specific detail query\n */\n invalidateDetail: async (\n queryClient: QueryClient,\n module: string,\n id: string\n ): Promise<void> => {\n await queryClient.invalidateQueries({ queryKey: [module, 'detail', id] });\n },\n\n /**\n * Invalidate and refetch (useful for critical data)\n */\n invalidateAndRefetch: async (queryClient: QueryClient, queryKey: readonly unknown[]): Promise<void> => {\n await queryClient.invalidateQueries({ queryKey });\n await queryClient.refetchQueries({ queryKey });\n },\n};\n"],"mappings":"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"}
@@ -1,13 +1,11 @@
1
- import { LOG_LEVEL_PRIORITY, LogLevel, Logger, createLogger, defaultLoggerConfig, logger } from "../logging-DviiAkLx.js";
2
- import { PermissionChecker } from "../permission-service-CvGpwI0d.js";
3
- import { PERMISSION_MODULES, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, getPermissionChecker, getPermissions, initializePermissions, useAllPermissions, useHasModulePermission, useHasPermission, useHasPermissions, useModulePermission, usePermission, usePermissionChecker, usePermissionStore, usePermissions } from "../permissions-_n1hIG7Q.js";
4
- import { evaluateAccessRequirement } from "../access-CGrsDMwu.js";
5
- import { TenantProvider, configureTenantClient, configureTenantEndpoints, getTenantCookieName, initializeTenantStore, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetTenantClientConfig, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useOrganization, useTenant, useTenantData, useTenantStore, useTenantSwitcher } from "../tenant-DfLemb6n.js";
6
- import { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants } from "../identity-C5un-xAs.js";
7
- import { isApiClientError } from "../types-BaaE6wjY.js";
8
- import { ApiError, ErrorCodes, ErrorHandler, calculateBackoffDelay, createRetryFetch, fetchWithTimeout, handleApiError, isRetryableError, withRetry } from "../api-client-CgZUMukO.js";
9
- import { IdentitySignalRClient, SignalRClient, cleanupSignalR, createIdentitySignalRClient, getIdentitySignalRClient, initializeSignalR } from "../signalr-k-70VHKo.js";
10
- import { cachePresets, createQueryClient, createQueryKeyFactory, invalidationHelpers } from "../query-client-Bh3TyB8W.js";
11
- import "../src-B9rDUgSb.js";
12
-
13
- export { ApiError, ErrorCodes, ErrorHandler, IdentityContextProvider, IdentitySignalRClient, LOG_LEVEL_PRIORITY, LogLevel, Logger, PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, SignalRClient, TenantProvider, cachePresets, calculateBackoffDelay, cleanupSignalR, configureIdentityEndpoints, configureIdentityStore, configureTenantClient, configureTenantEndpoints, createIdentitySignalRClient, createLogger, createQueryClient, createQueryKeyFactory, createRetryFetch, defaultLoggerConfig, evaluateAccessRequirement, fetchWithTimeout, getIdentitySignalRClient, getPermissionChecker, getPermissions, getTenantCookieName, handleApiError, initialIdentityContext, initializePermissions, initializeSignalR, initializeTenantStore, invalidationHelpers, isApiClientError, isRetryableError, logger, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetIdentityStoreConfig, resetTenantClientConfig, useAllPermissions, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useHasEntitlement, useHasModulePermission, useHasPermission, useHasPermissions, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useModulePermission, useOrganization, usePermission, usePermissionChecker, usePermissionStore, usePermissions, useTenant, useTenantData, useTenantStore, useTenantSwitcher, useUserPermissions, useUserTenants, withRetry };
1
+ import { LOG_LEVEL_PRIORITY, LogLevel, Logger, createLogger, defaultLoggerConfig, logger } from "../logging/index.mjs";
2
+ import { t as PermissionChecker } from "../permission-service-DUyKomU3.mjs";
3
+ import { _ as initializePermissions, a as ProtectedSection, c as useHasPermission, d as usePermission, f as usePermissionChecker, g as getPermissions, h as getPermissionChecker, i as ProtectedLink, l as useHasPermissions, m as PermissionInitializer, n as ProtectedAction, o as useAllPermissions, p as usePermissions, r as ProtectedButton, s as useHasModulePermission, t as PERMISSION_MODULES, u as useModulePermission, v as usePermissionStore } from "../permissions-C5dTP9y4.mjs";
4
+ import { t as evaluateAccessRequirement } from "../access-BHy1v5sr.mjs";
5
+ import { TenantProvider, configureTenantClient, configureTenantEndpoints, getTenantCookieName, initializeTenantStore, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetTenantClientConfig, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useOrganization, useTenant, useTenantData, useTenantStore, useTenantSwitcher } from "../tenant/index.mjs";
6
+ import { IdentityContextProvider, configureIdentityEndpoints, configureIdentityStore, initialIdentityContext, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, resetIdentityStoreConfig, useHasEntitlement, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useUserPermissions, useUserTenants } from "../identity/index.mjs";
7
+ import { t as isApiClientError } from "../types-DqCXinAk.mjs";
8
+ import { a as withRetry, c as handleApiError, i as isRetryableError, l as ErrorCodes, n as createRetryFetch, o as ApiError, r as fetchWithTimeout, s as ErrorHandler, t as calculateBackoffDelay } from "../api-client-CB7XZaMg.mjs";
9
+ import { a as getIdentitySignalRClient, i as createIdentitySignalRClient, n as SignalRClient, o as initializeSignalR, r as cleanupSignalR, t as IdentitySignalRClient } from "../signalr-C-TA8nEE.mjs";
10
+ import { i as invalidationHelpers, n as createQueryClient, r as createQueryKeyFactory, t as cachePresets } from "../query-client-_xB300gc.mjs";
11
+ export { ApiError, ErrorCodes, ErrorHandler, IdentityContextProvider, IdentitySignalRClient, LOG_LEVEL_PRIORITY, LogLevel, Logger, PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, SignalRClient, TenantProvider, cachePresets, calculateBackoffDelay, cleanupSignalR, configureIdentityEndpoints, configureIdentityStore, configureTenantClient, configureTenantEndpoints, createIdentitySignalRClient, createLogger, createQueryClient, createQueryKeyFactory, createRetryFetch, defaultLoggerConfig, evaluateAccessRequirement, fetchWithTimeout, getIdentitySignalRClient, getPermissionChecker, getPermissions, getTenantCookieName, handleApiError, initialIdentityContext, initializePermissions, initializeSignalR, initializeTenantStore, invalidationHelpers, isApiClientError, isRetryableError, logger, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, normalizeTenantDataPayload, normalizeTenantSwitchPayload, resetIdentityStoreConfig, resetTenantClientConfig, useAllPermissions, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useHasEntitlement, useHasModulePermission, useHasPermission, useHasPermissions, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useModulePermission, useOrganization, usePermission, usePermissionChecker, usePermissionStore, usePermissions, useTenant, useTenantData, useTenantStore, useTenantSwitcher, useUserPermissions, useUserTenants, withRetry };
@@ -0,0 +1,17 @@
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
+ 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";
12
+ 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";
13
+ import { clearActiveTenant, clearCurrentTenant, configureIdentityContextFetcher, configureTenantCookie, configureTenantResolution, getActiveTenant, getAvailableTenants, getCurrentTenant, getTenantFromIdentityContext, initializeTenant, resetTenantCookieConfig, resetTenantResolutionConfig, setActiveTenant, setCurrentTenant } from "../tenant/server.mjs";
14
+ import { clearIdentityContextCache, configureIdentityAPIFetcher, configurePermissionFetcher, contextToUserInfo, fetchIdentityContext, fetchUserPermissions, getIdentityContext, hasContextVersionChanged, updateContextVersion } from "../identity/server.mjs";
15
+ import { n as createFetchMiddleware, r as createAPIConfigFactory, t as createApiClient } from "../server-ByScGRXA.mjs";
16
+ import { checkPermission, configurePermissionRouteProtection, requirePermission, resetPermissionRouteProtection, withPermission } from "../server.mjs";
17
+ export { AUTH_ERROR_COOKIE_PREFIX, ApiError, ErrorCodes, ErrorHandler, IdentityContextProvider, IdentitySignalRClient, LOG_LEVEL_PRIORITY, LogLevel, Logger, PERMISSION_MODULES, PermissionChecker, PermissionInitializer, ProtectedAction, ProtectedButton, ProtectedLink, ProtectedSection, SignalRClient, TenantProvider, attemptTokenRefresh, authRoute, cachePresets, calculateBackoffDelay, checkPermission, cleanupExpiredOAuthStates, cleanupSignalR, clearActiveTenant, clearAuthErrorHeaders, clearAuthServerCache, clearCurrentTenant, clearIdentityContextCache, closeRedisConnection, configureAuthClaimMapping, configureIdentityAPIFetcher, configureIdentityContextFetcher, configureIdentityEndpoints, configureIdentityStore, configurePermissionFetcher, configurePermissionRouteProtection, configureRouteProtection, configureTenantClient, configureTenantCookie, configureTenantEndpoints, configureTenantResolution, contextToUserInfo, createAPIConfigFactory, createApiClient, createAuthSession, createAuthorizationResponseUrl, createFetchMiddleware, createIdentitySignalRClient, createLogger, createOAuthState, createQueryClient, createQueryKeyFactory, createRetryFetch, defaultLoggerConfig, deleteOAuthState, destroyAuthSession, destroyAuthSessionById, destroyAuthSessionsByIdentitySession, destroyAuthSessionsBySessionState, destroyAuthSessionsBySid, destroyAuthSessionsForUser, evaluateAccessRequirement, extractUserInfo, fetchIdentityContext, fetchUserPermissions, fetchWithTimeout, getAccessToken, getActiveTenant, getAuthError, getAuthSession, getAuthSessionById, getAvailableTenants, getCurrentAuthSessionId, getCurrentTenant, getIdentityContext, getIdentitySignalRClient, getOAuthState, getPermissionChecker, getPermissions, getTenantCookieName, getTenantFromIdentityContext, getUser, handleApiError, handleBackChannelLogout, handleCallback, handleFrontChannelLogout, hasAuthError, hasContextVersionChanged, initialIdentityContext, initializePermissions, initializeSignalR, initializeTenant, initializeTenantStore, invalidationHelpers, isApiClientError, isRetryableError, isSessionValid, isTokenExpiredResponse, listAuthSessionDataForUser, listAuthSessionsForUser, listCurrentUserAuthSessions, logger, login, logout, normalizeIdentityContextPayload, normalizeIdentityPermissionsPayload, normalizeTenantDataPayload, normalizeTenantSwitchPayload, onboardingRoute, policyRoute, protectRoute, protectedRoute, publicRoute, refreshTokens, requireAuth, requireAuthSession, requirePermission, resetAuthClaimMapping, resetIdentityStoreConfig, resetPermissionRouteProtection, resetRouteProtectionConfig, resetTenantClientConfig, resetTenantCookieConfig, resetTenantResolutionConfig, setActiveTenant, setCurrentTenant, shouldAttemptTokenRefresh, shouldRefreshToken, subscriptionRoute, updateAuthSession, updateContextVersion, useAllPermissions, useCurrentOrganization, useCurrentTenant, useCurrentTenantData, useHasEntitlement, useHasModulePermission, useHasPermission, useHasPermissions, useHasSubscription, useIdentityContext, useIdentityStore, useIsOnboarded, useModulePermission, useOrganization, usePermission, usePermissionChecker, usePermissionStore, usePermissions, useTenant, useTenantData, useTenantStore, useTenantSwitcher, useUserPermissions, useUserTenants, withPermission, withRetry };
@@ -1,8 +1,14 @@
1
- import { logger } from "./logging-DviiAkLx.js";
1
+ import { logger } from "./logging/index.mjs";
2
2
  import Redis from "ioredis";
3
3
  import { createCipheriv, createDecipheriv, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
4
-
5
4
  //#region src/auth/redis-session-storage.server.ts
5
+ /**
6
+ * Redis Session Storage Server Module
7
+ *
8
+ * Redis-backed session storage for authentication.
9
+ * Provides secure session management with automatic expiration,
10
+ * without depending on a framework-specific session implementation.
11
+ */
6
12
  let redis = null;
7
13
  function getRedis() {
8
14
  if (!redis) {
@@ -25,9 +31,8 @@ const REDIS_KEYS = {
25
31
  sessionBySessionState: (sessionState) => `${KEY_PREFIX}session:index:session_state:${sessionState}`
26
32
  };
27
33
  const DEFAULT_SESSION_SECRET = "default-secret-change-in-production";
28
- const DEFAULT_SESSION_TTL = 60 * 60 * 24 * 7;
29
- const DEFAULT_OAUTH_STATE_TTL = 10 * 60;
30
- const OAUTH_STATE_TTL = resolvePositiveIntegerEnv("OAUTH_STATE_TTL", DEFAULT_OAUTH_STATE_TTL);
34
+ const DEFAULT_SESSION_TTL = 3600 * 24 * 7;
35
+ const OAUTH_STATE_TTL = resolvePositiveIntegerEnv("OAUTH_STATE_TTL", 600);
31
36
  const SESSION_TTL = resolvePositiveIntegerEnv("SESSION_DEFAULT_TTL", resolvePositiveIntegerEnv("SESSION_TTL", DEFAULT_SESSION_TTL));
32
37
  const SESSION_COOKIE_NAME = process.env.SESSION_COOKIE_NAME || "__session_id";
33
38
  const SESSION_SECRET = resolveSessionSecret();
@@ -120,8 +125,7 @@ function safelyCompareValues(left, right) {
120
125
  return timingSafeEqual(leftBuffer, rightBuffer);
121
126
  }
122
127
  function signSessionId(sessionId) {
123
- const signature = createCookieSignature(sessionId, SESSION_SECRET);
124
- return `${sessionId}.${signature}`;
128
+ return `${sessionId}.${createCookieSignature(sessionId, SESSION_SECRET)}`;
125
129
  }
126
130
  function unsignSessionId(cookieValue) {
127
131
  if (!cookieValue) return null;
@@ -134,9 +138,7 @@ function unsignSessionId(cookieValue) {
134
138
  const separatorIndex = decodedValue.lastIndexOf(".");
135
139
  if (separatorIndex <= 0 || separatorIndex === decodedValue.length - 1) return null;
136
140
  const sessionId = decodedValue.slice(0, separatorIndex);
137
- const signature = decodedValue.slice(separatorIndex + 1);
138
- const expectedSignature = createCookieSignature(sessionId, SESSION_SECRET);
139
- return safelyCompareValues(signature, expectedSignature) ? sessionId : null;
141
+ return safelyCompareValues(decodedValue.slice(separatorIndex + 1), createCookieSignature(sessionId, SESSION_SECRET)) ? sessionId : null;
140
142
  }
141
143
  function serializeSessionCookieValue(sessionId, options) {
142
144
  const signedValue = encodeURIComponent(signSessionId(sessionId));
@@ -164,11 +166,10 @@ function serializeExpiredSessionCookie() {
164
166
  return segments.join("; ");
165
167
  }
166
168
  async function getSessionIdFromRequest(request) {
167
- const cookies = parseCookieHeader(request.headers.get("Cookie"));
168
- return unsignSessionId(cookies[SESSION_COOKIE_NAME]);
169
+ return unsignSessionId(parseCookieHeader(request.headers.get("Cookie"))[SESSION_COOKIE_NAME]);
169
170
  }
170
171
  function getSessionIndexKeys(data) {
171
- const keys = new Set();
172
+ const keys = /* @__PURE__ */ new Set();
172
173
  if (data.userId) keys.add(REDIS_KEYS.sessionByUser(data.userId));
173
174
  if (data.sid) keys.add(REDIS_KEYS.sessionBySid(data.sid));
174
175
  if (data.sessionState) keys.add(REDIS_KEYS.sessionBySessionState(data.sessionState));
@@ -177,8 +178,7 @@ function getSessionIndexKeys(data) {
177
178
  async function registerSessionIndexes(sessionId, data) {
178
179
  const indexKeys = getSessionIndexKeys(data);
179
180
  if (indexKeys.length === 0) return;
180
- const redisClient = getRedis();
181
- const pipeline = redisClient.pipeline();
181
+ const pipeline = getRedis().pipeline();
182
182
  for (const indexKey of indexKeys) {
183
183
  pipeline.sadd(indexKey, sessionId);
184
184
  pipeline.expire(indexKey, SESSION_TTL);
@@ -188,8 +188,7 @@ async function registerSessionIndexes(sessionId, data) {
188
188
  async function unregisterSessionIndexes(sessionId, data) {
189
189
  const indexKeys = getSessionIndexKeys(data);
190
190
  if (indexKeys.length === 0) return;
191
- const redisClient = getRedis();
192
- const pipeline = redisClient.pipeline();
191
+ const pipeline = getRedis().pipeline();
193
192
  for (const indexKey of indexKeys) pipeline.srem(indexKey, sessionId);
194
193
  await pipeline.exec();
195
194
  }
@@ -279,11 +278,8 @@ async function getSessionIdsFromIndex(indexKey) {
279
278
  const sessionIds = await getRedis().smembers(indexKey);
280
279
  const activeSessionIds = [];
281
280
  const staleSessionIds = [];
282
- for (const sessionId of sessionIds) {
283
- const exists = await getRedis().exists(REDIS_KEYS.session(sessionId));
284
- if (exists) activeSessionIds.push(sessionId);
285
- else staleSessionIds.push(sessionId);
286
- }
281
+ for (const sessionId of sessionIds) if (await getRedis().exists(REDIS_KEYS.session(sessionId))) activeSessionIds.push(sessionId);
282
+ else staleSessionIds.push(sessionId);
287
283
  if (staleSessionIds.length > 0) await getRedis().srem(indexKey, ...staleSessionIds);
288
284
  return activeSessionIds;
289
285
  }
@@ -317,8 +313,7 @@ async function listSessionDataByIds(sessionIds) {
317
313
  return sessions;
318
314
  }
319
315
  function getRequestIpAddress(request) {
320
- const forwardedFor = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();
321
- return forwardedFor || request.headers.get("cf-connecting-ip")?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
316
+ return request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("cf-connecting-ip")?.trim() || request.headers.get("x-real-ip")?.trim() || void 0;
322
317
  }
323
318
  async function createAuthSession(request, sessionData) {
324
319
  const currentSessionId = await getSessionIdFromRequest(request);
@@ -361,11 +356,10 @@ async function updateAuthSession(request, updates) {
361
356
  const currentSessionId = await getSessionIdFromRequest(request);
362
357
  const currentSessionData = currentSessionId ? await readSessionData(currentSessionId) ?? {} : {};
363
358
  const nextSessionId = currentSessionId ?? crypto.randomUUID();
364
- const nextSessionData = mergeSessionData(currentSessionData, {
359
+ await writeSessionData(nextSessionId, mergeSessionData(currentSessionData, {
365
360
  ...updates,
366
361
  sessionId: nextSessionId
367
- });
368
- await writeSessionData(nextSessionId, nextSessionData);
362
+ }));
369
363
  return new Headers({ "Set-Cookie": serializeSessionCookieValue(nextSessionId, { maxAge: SESSION_TTL }) });
370
364
  }
371
365
  async function destroyAuthSession(request) {
@@ -384,12 +378,10 @@ async function getAuthSessionById(sessionId) {
384
378
  } : null;
385
379
  }
386
380
  async function listAuthSessionDataForUser(userId) {
387
- const sessionIds = await getSessionIdsFromIndex(REDIS_KEYS.sessionByUser(userId));
388
- return await listSessionDataByIds(sessionIds);
381
+ return await listSessionDataByIds(await getSessionIdsFromIndex(REDIS_KEYS.sessionByUser(userId)));
389
382
  }
390
383
  async function listAuthSessionsForUser(userId, currentSessionId) {
391
- const sessions = await listAuthSessionDataForUser(userId);
392
- return sessions.map(({ sessionId, data }) => toAuthSessionSummary(sessionId, data, currentSessionId)).sort((left, right) => (right.lastActivity ?? right.createdAt ?? 0) - (left.lastActivity ?? left.createdAt ?? 0));
384
+ return (await listAuthSessionDataForUser(userId)).map(({ sessionId, data }) => toAuthSessionSummary(sessionId, data, currentSessionId)).sort((left, right) => (right.lastActivity ?? right.createdAt ?? 0) - (left.lastActivity ?? left.createdAt ?? 0));
393
385
  }
394
386
  async function listCurrentUserAuthSessions(request) {
395
387
  const currentSession = await getAuthSession(request);
@@ -397,8 +389,7 @@ async function listCurrentUserAuthSessions(request) {
397
389
  return await listAuthSessionsForUser(currentSession.userId, currentSession.sessionId);
398
390
  }
399
391
  async function destroyAuthSessionById(sessionId) {
400
- const session = await readSessionData(sessionId);
401
- if (!session) return false;
392
+ if (!await readSessionData(sessionId)) return false;
402
393
  await deleteSessionData(sessionId);
403
394
  return true;
404
395
  }
@@ -474,12 +465,9 @@ async function cleanupExpiredOAuthStates() {
474
465
  const pattern = REDIS_KEYS.oauthState("*");
475
466
  const keys = await getRedis().keys(pattern);
476
467
  let cleaned = 0;
477
- for (const key of keys) {
478
- const ttl = await getRedis().ttl(key);
479
- if (ttl === -1) {
480
- await getRedis().del(key);
481
- cleaned++;
482
- }
468
+ for (const key of keys) if (await getRedis().ttl(key) === -1) {
469
+ await getRedis().del(key);
470
+ cleaned++;
483
471
  }
484
472
  logger.info("OAuth state cleanup completed", {
485
473
  cleaned,
@@ -494,7 +482,7 @@ async function closeRedisConnection() {
494
482
  logger.info("Redis connection closed");
495
483
  }
496
484
  }
497
-
498
485
  //#endregion
499
- export { AUTH_ERROR_COOKIE_PREFIX, cleanupExpiredOAuthStates, closeRedisConnection, createAuthSession, createOAuthState, deleteOAuthState, destroyAuthSession, destroyAuthSessionById, destroyAuthSessionsByIdentitySession, destroyAuthSessionsBySessionState, destroyAuthSessionsBySid, destroyAuthSessionsForUser, getAuthSession, getAuthSessionById, getCurrentAuthSessionId, getOAuthState, isSessionValid, listAuthSessionDataForUser, listAuthSessionsForUser, listCurrentUserAuthSessions, requireAuthSession, updateAuthSession };
500
- //# sourceMappingURL=redis-session-storage.server-v2VcpFsQ.js.map
486
+ export { updateAuthSession as S, isSessionValid as _, createOAuthState as a, listCurrentUserAuthSessions as b, destroyAuthSessionById as c, destroyAuthSessionsBySid as d, destroyAuthSessionsForUser as f, getOAuthState as g, getCurrentAuthSessionId as h, createAuthSession as i, destroyAuthSessionsByIdentitySession as l, getAuthSessionById as m, cleanupExpiredOAuthStates as n, deleteOAuthState as o, getAuthSession as p, closeRedisConnection as r, destroyAuthSession as s, AUTH_ERROR_COOKIE_PREFIX as t, destroyAuthSessionsBySessionState as u, listAuthSessionDataForUser as v, requireAuthSession as x, listAuthSessionsForUser as y };
487
+
488
+ //# sourceMappingURL=redis-session-storage.server-DGgQcJ9B.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-session-storage.server-DGgQcJ9B.mjs","names":[],"sources":["../src/auth/redis-session-storage.server.ts"],"sourcesContent":["/**\n * Redis Session Storage Server Module\n *\n * Redis-backed session storage for authentication.\n * Provides secure session management with automatic expiration,\n * without depending on a framework-specific session implementation.\n */\n\nimport Redis from 'ioredis';\nimport { createCipheriv, createDecipheriv, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';\nimport type { AuthSessionSummary, SessionData, OAuthState } from './types';\nimport { logger } from '../logging';\n\n// ============================================================================\n// Redis Client Setup\n// ============================================================================\n\nlet redis: Redis | null = null;\n\nfunction getRedis(): Redis {\n if (!redis) {\n redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');\n\n redis.on('error', (err) => {\n logger.error('Redis Client Error', err);\n });\n\n redis.on('connect', () => {\n logger.info('Redis connected successfully');\n });\n }\n return redis;\n}\n\n// ============================================================================\n// Redis Key Patterns\n// ============================================================================\n\nconst KEY_PREFIX = process.env.REDIS_KEY_PREFIX || '';\n\nconst REDIS_KEYS = {\n oauthState: (stateId: string) => `${KEY_PREFIX}oauth:state:${stateId}`,\n session: (sessionId: string) => `${KEY_PREFIX}session:${sessionId}`,\n sessionByUser: (userId: string) => `${KEY_PREFIX}session:index:user:${userId}`,\n sessionBySid: (sid: string) => `${KEY_PREFIX}session:index:sid:${sid}`,\n sessionBySessionState: (sessionState: string) => `${KEY_PREFIX}session:index:session_state:${sessionState}`,\n};\n\nconst DEFAULT_SESSION_SECRET = 'default-secret-change-in-production';\nconst DEFAULT_SESSION_TTL = 60 * 60 * 24 * 7;\nconst DEFAULT_OAUTH_STATE_TTL = 10 * 60;\n\nconst OAUTH_STATE_TTL = resolvePositiveIntegerEnv('OAUTH_STATE_TTL', DEFAULT_OAUTH_STATE_TTL);\nconst SESSION_TTL = resolvePositiveIntegerEnv('SESSION_DEFAULT_TTL', resolvePositiveIntegerEnv('SESSION_TTL', DEFAULT_SESSION_TTL));\n\nconst SESSION_COOKIE_NAME = process.env.SESSION_COOKIE_NAME || '__session_id';\nconst SESSION_SECRET = resolveSessionSecret();\nconst SESSION_COOKIE_SECURE = resolveCookieSecure();\nconst SESSION_COOKIE_PATH = '/';\nconst SESSION_COOKIE_SAME_SITE = resolveCookieSameSite();\nconst SESSION_COOKIE_DOMAIN = process.env.SESSION_COOKIE_DOMAIN?.trim();\nconst SESSION_ENCRYPTION_ENABLED = resolveSessionEncryptionEnabled();\nconst SESSION_ENCRYPTION_KEY = resolveSessionEncryptionKey(SESSION_ENCRYPTION_ENABLED);\n\nexport const AUTH_ERROR_COOKIE_PREFIX = SESSION_COOKIE_NAME.replace('_session', '').replace('__', '');\n\nfunction resolveBooleanEnv(name: string, defaultValue: boolean): boolean {\n const value = process.env[name]?.trim().toLowerCase();\n if (!value) {\n return defaultValue;\n }\n\n if (['1', 'true', 'yes', 'on'].includes(value)) {\n return true;\n }\n\n if (['0', 'false', 'no', 'off'].includes(value)) {\n return false;\n }\n\n throw new Error(`${name} must be a boolean value.`);\n}\n\nfunction resolvePositiveIntegerEnv(name: string, defaultValue: number): number {\n const value = process.env[name]?.trim();\n if (!value) {\n return defaultValue;\n }\n\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\n throw new Error(`${name} must be a positive integer.`);\n }\n\n return parsed;\n}\n\nfunction resolveSessionSecret(): string {\n const secret = process.env.SESSION_SECRET?.trim();\n if (!secret || secret === DEFAULT_SESSION_SECRET) {\n if (process.env.NODE_ENV === 'production') {\n throw new Error('SESSION_SECRET must be set to a unique random value in production.');\n }\n\n return DEFAULT_SESSION_SECRET;\n }\n\n if (secret.length < 32) {\n throw new Error('SESSION_SECRET must be at least 32 characters long.');\n }\n\n return secret;\n}\n\nfunction resolveCookieSecure(): boolean {\n return resolveBooleanEnv('SESSION_COOKIE_SECURE', process.env.NODE_ENV === 'production');\n}\n\nfunction resolveCookieSameSite(): 'Lax' | 'Strict' | 'None' {\n const value = process.env.SESSION_COOKIE_SAME_SITE?.trim().toLowerCase();\n const sameSite = value === 'strict' ? 'Strict' : value === 'none' ? 'None' : 'Lax';\n\n if (sameSite === 'None' && !SESSION_COOKIE_SECURE) {\n throw new Error('SESSION_COOKIE_SAME_SITE=None requires SESSION_COOKIE_SECURE=true.');\n }\n\n return sameSite;\n}\n\nfunction resolveSessionEncryptionEnabled(): boolean {\n const enabled = resolveBooleanEnv('SESSION_ENCRYPTION', process.env.NODE_ENV === 'production');\n if (process.env.NODE_ENV === 'production' && !enabled) {\n throw new Error('SESSION_ENCRYPTION cannot be disabled in production.');\n }\n\n return enabled;\n}\n\nfunction decodeBase64Url(value: string): Buffer {\n const normalized = value.replace(/-/g, '+').replace(/_/g, '/');\n const padded = normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=');\n return Buffer.from(padded, 'base64');\n}\n\nfunction resolveSessionEncryptionKey(enabled: boolean): Buffer | null {\n if (!enabled) {\n return null;\n }\n\n const value = process.env.SESSION_ENCRYPTION_KEY?.trim();\n if (!value) {\n throw new Error('SESSION_ENCRYPTION_KEY is required when session encryption is enabled.');\n }\n\n const key = /^[0-9a-f]{64}$/i.test(value)\n ? Buffer.from(value, 'hex')\n : decodeBase64Url(value);\n\n if (key.length !== 32) {\n throw new Error('SESSION_ENCRYPTION_KEY must decode to exactly 32 bytes. Generate one with: openssl rand -base64 32');\n }\n\n return key;\n}\n\ntype SessionCookieOptions = {\n maxAge: number;\n};\n\nfunction parseCookieHeader(cookieHeader: string | null): Record<string, string> {\n if (!cookieHeader) {\n return {};\n }\n\n return cookieHeader\n .split(';')\n .map((part) => part.trim())\n .filter(Boolean)\n .reduce<Record<string, string>>((cookies, part) => {\n const separatorIndex = part.indexOf('=');\n if (separatorIndex <= 0) {\n return cookies;\n }\n\n const name = part.slice(0, separatorIndex).trim();\n const value = part.slice(separatorIndex + 1).trim();\n if (!name) {\n return cookies;\n }\n\n cookies[name] = value;\n return cookies;\n }, {});\n}\n\nfunction createCookieSignature(value: string, secret: string): string {\n return createHmac('sha256', secret).update(value).digest('base64url');\n}\n\nfunction safelyCompareValues(left: string, right: string): boolean {\n const leftBuffer = Buffer.from(left);\n const rightBuffer = Buffer.from(right);\n\n if (leftBuffer.length !== rightBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(leftBuffer, rightBuffer);\n}\n\nfunction signSessionId(sessionId: string): string {\n const signature = createCookieSignature(sessionId, SESSION_SECRET);\n return `${sessionId}.${signature}`;\n}\n\nfunction unsignSessionId(cookieValue: string | undefined): string | null {\n if (!cookieValue) {\n return null;\n }\n\n let decodedValue: string;\n try {\n decodedValue = decodeURIComponent(cookieValue);\n } catch {\n return null;\n }\n\n const separatorIndex = decodedValue.lastIndexOf('.');\n if (separatorIndex <= 0 || separatorIndex === decodedValue.length - 1) {\n return null;\n }\n\n const sessionId = decodedValue.slice(0, separatorIndex);\n const signature = decodedValue.slice(separatorIndex + 1);\n const expectedSignature = createCookieSignature(sessionId, SESSION_SECRET);\n\n return safelyCompareValues(signature, expectedSignature) ? sessionId : null;\n}\n\nfunction serializeSessionCookieValue(sessionId: string, options: SessionCookieOptions): string {\n const signedValue = encodeURIComponent(signSessionId(sessionId));\n const segments = [\n `${SESSION_COOKIE_NAME}=${signedValue}`,\n `Max-Age=${options.maxAge}`,\n `Path=${SESSION_COOKIE_PATH}`,\n `SameSite=${SESSION_COOKIE_SAME_SITE}`,\n 'HttpOnly',\n ];\n\n if (SESSION_COOKIE_DOMAIN) {\n segments.push(`Domain=${SESSION_COOKIE_DOMAIN}`);\n }\n\n if (SESSION_COOKIE_SECURE) {\n segments.push('Secure');\n }\n\n return segments.join('; ');\n}\n\nfunction serializeExpiredSessionCookie(): string {\n const segments = [\n `${SESSION_COOKIE_NAME}=`,\n 'Max-Age=0',\n `Path=${SESSION_COOKIE_PATH}`,\n `SameSite=${SESSION_COOKIE_SAME_SITE}`,\n 'HttpOnly',\n ];\n\n if (SESSION_COOKIE_DOMAIN) {\n segments.push(`Domain=${SESSION_COOKIE_DOMAIN}`);\n }\n\n if (SESSION_COOKIE_SECURE) {\n segments.push('Secure');\n }\n\n return segments.join('; ');\n}\n\nasync function getSessionIdFromRequest(request: Request): Promise<string | null> {\n const cookies = parseCookieHeader(request.headers.get('Cookie'));\n return unsignSessionId(cookies[SESSION_COOKIE_NAME]);\n}\n\nfunction getSessionIndexKeys(data: SessionData): string[] {\n const keys = new Set<string>();\n\n if (data.userId) {\n keys.add(REDIS_KEYS.sessionByUser(data.userId));\n }\n\n if (data.sid) {\n keys.add(REDIS_KEYS.sessionBySid(data.sid));\n }\n\n if (data.sessionState) {\n keys.add(REDIS_KEYS.sessionBySessionState(data.sessionState));\n }\n\n return Array.from(keys);\n}\n\nasync function registerSessionIndexes(sessionId: string, data: SessionData): Promise<void> {\n const indexKeys = getSessionIndexKeys(data);\n if (indexKeys.length === 0) {\n return;\n }\n\n const redisClient = getRedis();\n const pipeline = redisClient.pipeline();\n\n for (const indexKey of indexKeys) {\n pipeline.sadd(indexKey, sessionId);\n pipeline.expire(indexKey, SESSION_TTL);\n }\n\n await pipeline.exec();\n}\n\nasync function unregisterSessionIndexes(sessionId: string, data: SessionData): Promise<void> {\n const indexKeys = getSessionIndexKeys(data);\n if (indexKeys.length === 0) {\n return;\n }\n\n const redisClient = getRedis();\n const pipeline = redisClient.pipeline();\n\n for (const indexKey of indexKeys) {\n pipeline.srem(indexKey, sessionId);\n }\n\n await pipeline.exec();\n}\n\nfunction parseSessionData(sessionData: string | null): SessionData | null {\n if (!sessionData) {\n return null;\n }\n\n try {\n return JSON.parse(decodeSessionData(sessionData)) as SessionData;\n } catch (error) {\n logger.error('Failed to parse session data', error instanceof Error ? error : undefined);\n return null;\n }\n}\n\nfunction encodeSessionData(data: SessionData): string {\n const plaintext = JSON.stringify(data);\n if (!SESSION_ENCRYPTION_KEY) {\n return plaintext;\n }\n\n const iv = randomBytes(12);\n const cipher = createCipheriv('aes-256-gcm', SESSION_ENCRYPTION_KEY, iv);\n const ciphertext = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n const tag = cipher.getAuthTag();\n\n return [\n 'enc:v1',\n iv.toString('base64url'),\n tag.toString('base64url'),\n ciphertext.toString('base64url'),\n ].join(':');\n}\n\nfunction decodeSessionData(value: string): string {\n if (!value.startsWith('enc:v1:')) {\n if (SESSION_ENCRYPTION_ENABLED) {\n logger.warn('Reading legacy plaintext auth session payload while session encryption is enabled.');\n }\n\n return value;\n }\n\n if (!SESSION_ENCRYPTION_KEY) {\n throw new Error('Cannot decrypt auth session payload because SESSION_ENCRYPTION_KEY is not configured.');\n }\n\n const [, version, ivValue, tagValue, ciphertextValue] = value.split(':');\n if (version !== 'v1' || !ivValue || !tagValue || !ciphertextValue) {\n throw new Error('Invalid encrypted auth session payload format.');\n }\n\n const decipher = createDecipheriv('aes-256-gcm', SESSION_ENCRYPTION_KEY, Buffer.from(ivValue, 'base64url'));\n decipher.setAuthTag(Buffer.from(tagValue, 'base64url'));\n\n return Buffer.concat([\n decipher.update(Buffer.from(ciphertextValue, 'base64url')),\n decipher.final(),\n ]).toString('utf8');\n}\n\nasync function readSessionData(sessionId: string): Promise<SessionData | null> {\n try {\n const sessionData = await getRedis().get(REDIS_KEYS.session(sessionId));\n if (!sessionData) {\n logger.debug('Session not found in Redis', { sessionId });\n return null;\n }\n\n await getRedis().expire(REDIS_KEYS.session(sessionId), SESSION_TTL);\n return parseSessionData(sessionData);\n } catch (error) {\n logger.error('Failed to read session from Redis', error instanceof Error ? error : undefined);\n return null;\n }\n}\n\nasync function writeSessionData(sessionId: string, data: SessionData): Promise<void> {\n try {\n const existingSessionData = parseSessionData(await getRedis().get(REDIS_KEYS.session(sessionId)));\n if (existingSessionData) {\n await unregisterSessionIndexes(sessionId, existingSessionData);\n }\n\n await getRedis().setex(REDIS_KEYS.session(sessionId), SESSION_TTL, encodeSessionData(data));\n await registerSessionIndexes(sessionId, data);\n logger.debug('Updated Redis session', { sessionId });\n } catch (error) {\n logger.error('Failed to update session in Redis', error instanceof Error ? error : undefined);\n throw error;\n }\n}\n\nasync function deleteSessionData(sessionId: string): Promise<void> {\n try {\n const existingSessionData = parseSessionData(await getRedis().get(REDIS_KEYS.session(sessionId)));\n if (existingSessionData) {\n await unregisterSessionIndexes(sessionId, existingSessionData);\n }\n\n await getRedis().del(REDIS_KEYS.session(sessionId));\n logger.debug('Deleted Redis session', { sessionId });\n } catch (error) {\n logger.error('Failed to delete session from Redis', error instanceof Error ? error : undefined);\n }\n}\n\nfunction mergeSessionData(existingData: SessionData, updates: Partial<SessionData>): SessionData {\n const nextData = { ...existingData } as Record<string, unknown>;\n\n Object.entries(updates).forEach(([key, value]) => {\n if (value === undefined) {\n delete nextData[key];\n return;\n }\n\n nextData[key] = value;\n });\n\n return nextData as SessionData;\n}\n\nasync function getSessionIdsFromIndex(indexKey: string): Promise<string[]> {\n const sessionIds = await getRedis().smembers(indexKey);\n const activeSessionIds: string[] = [];\n const staleSessionIds: string[] = [];\n\n for (const sessionId of sessionIds) {\n const exists = await getRedis().exists(REDIS_KEYS.session(sessionId));\n if (exists) {\n activeSessionIds.push(sessionId);\n } else {\n staleSessionIds.push(sessionId);\n }\n }\n\n if (staleSessionIds.length > 0) {\n await getRedis().srem(indexKey, ...staleSessionIds);\n }\n\n return activeSessionIds;\n}\n\nfunction toAuthSessionSummary(\n sessionId: string,\n session: SessionData,\n currentSessionId?: string | null\n): AuthSessionSummary {\n return {\n sessionId,\n userId: session.userId,\n email: session.user?.email,\n name: session.user?.name,\n issuer: session.issuer,\n sid: session.sid,\n sessionState: session.sessionState,\n clientId: session.clientId,\n ipAddress: session.ipAddress,\n userAgent: session.userAgent,\n createdAt: session.createdAt,\n lastActivity: session.lastActivity,\n expiresAt: session.expiresAt,\n isCurrent: currentSessionId ? sessionId === currentSessionId : undefined,\n };\n}\n\nasync function listSessionDataByIds(sessionIds: string[]): Promise<Array<{ sessionId: string; data: SessionData }>> {\n const sessions: Array<{ sessionId: string; data: SessionData }> = [];\n\n for (const sessionId of sessionIds) {\n const data = await readSessionData(sessionId);\n if (data) {\n sessions.push({ sessionId, data });\n }\n }\n\n return sessions;\n}\n\nfunction getRequestIpAddress(request: Request): string | undefined {\n const forwardedFor = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim();\n return (\n forwardedFor ||\n request.headers.get('cf-connecting-ip')?.trim() ||\n request.headers.get('x-real-ip')?.trim() ||\n undefined\n );\n}\n\n// ============================================================================\n// Session Helper Functions\n// ============================================================================\n\nexport async function createAuthSession(\n request: Request,\n sessionData: Partial<SessionData>\n): Promise<Headers> {\n const currentSessionId = await getSessionIdFromRequest(request);\n const nextSessionId = crypto.randomUUID();\n const nextSessionData = mergeSessionData({}, {\n ...sessionData,\n sessionId: nextSessionId,\n ipAddress: sessionData.ipAddress ?? getRequestIpAddress(request),\n userAgent: sessionData.userAgent ?? request.headers.get('user-agent') ?? undefined,\n });\n\n logger.debug('Creating auth session', { dataKeys: Object.keys(sessionData) });\n\n if (currentSessionId) {\n await deleteSessionData(currentSessionId);\n }\n\n await writeSessionData(nextSessionId, nextSessionData);\n\n return new Headers({\n 'Set-Cookie': serializeSessionCookieValue(nextSessionId, { maxAge: SESSION_TTL }),\n });\n}\n\nexport async function getAuthSession(request: Request): Promise<SessionData> {\n const sessionId = await getSessionIdFromRequest(request);\n if (!sessionId) {\n return {};\n }\n\n const session = await readSessionData(sessionId);\n if (!session) {\n return {};\n }\n\n return {\n sessionId,\n userId: session.userId,\n accessToken: session.accessToken,\n refreshToken: session.refreshToken,\n idToken: session.idToken,\n expiresAt: session.expiresAt,\n issuer: session.issuer,\n sid: session.sid,\n sessionState: session.sessionState,\n clientId: session.clientId,\n ipAddress: session.ipAddress,\n userAgent: session.userAgent,\n user: session.user,\n createdAt: session.createdAt,\n lastActivity: session.lastActivity,\n };\n}\n\nexport async function updateAuthSession(\n request: Request,\n updates: Partial<SessionData>\n): Promise<Headers> {\n const currentSessionId = await getSessionIdFromRequest(request);\n const currentSessionData = currentSessionId\n ? (await readSessionData(currentSessionId)) ?? {}\n : {};\n const nextSessionId = currentSessionId ?? crypto.randomUUID();\n const nextSessionData = mergeSessionData(currentSessionData, {\n ...updates,\n sessionId: nextSessionId,\n });\n\n await writeSessionData(nextSessionId, nextSessionData);\n\n return new Headers({\n 'Set-Cookie': serializeSessionCookieValue(nextSessionId, { maxAge: SESSION_TTL }),\n });\n}\n\nexport async function destroyAuthSession(request: Request): Promise<Headers> {\n const sessionId = await getSessionIdFromRequest(request);\n if (sessionId) {\n await deleteSessionData(sessionId);\n }\n\n return new Headers({\n 'Set-Cookie': serializeExpiredSessionCookie(),\n });\n}\n\nexport async function getCurrentAuthSessionId(request: Request): Promise<string | null> {\n return await getSessionIdFromRequest(request);\n}\n\nexport async function getAuthSessionById(sessionId: string): Promise<SessionData | null> {\n const session = await readSessionData(sessionId);\n return session ? { ...session, sessionId } : null;\n}\n\nexport async function listAuthSessionDataForUser(\n userId: string\n): Promise<Array<{ sessionId: string; data: SessionData }>> {\n const sessionIds = await getSessionIdsFromIndex(REDIS_KEYS.sessionByUser(userId));\n return await listSessionDataByIds(sessionIds);\n}\n\nexport async function listAuthSessionsForUser(\n userId: string,\n currentSessionId?: string | null\n): Promise<AuthSessionSummary[]> {\n const sessions = await listAuthSessionDataForUser(userId);\n\n return sessions\n .map(({ sessionId, data }) => toAuthSessionSummary(sessionId, data, currentSessionId))\n .sort((left, right) => (right.lastActivity ?? right.createdAt ?? 0) - (left.lastActivity ?? left.createdAt ?? 0));\n}\n\nexport async function listCurrentUserAuthSessions(request: Request): Promise<AuthSessionSummary[]> {\n const currentSession = await getAuthSession(request);\n if (!currentSession.userId) {\n return [];\n }\n\n return await listAuthSessionsForUser(currentSession.userId, currentSession.sessionId);\n}\n\nexport async function destroyAuthSessionById(sessionId: string): Promise<boolean> {\n const session = await readSessionData(sessionId);\n if (!session) {\n return false;\n }\n\n await deleteSessionData(sessionId);\n return true;\n}\n\nexport async function destroyAuthSessionsForUser(userId: string): Promise<number> {\n const sessionIds = await getSessionIdsFromIndex(REDIS_KEYS.sessionByUser(userId));\n let destroyedSessions = 0;\n\n for (const sessionId of sessionIds) {\n if (await destroyAuthSessionById(sessionId)) {\n destroyedSessions += 1;\n }\n }\n\n return destroyedSessions;\n}\n\nexport async function destroyAuthSessionsBySid(sid: string): Promise<number> {\n const sessionIds = await getSessionIdsFromIndex(REDIS_KEYS.sessionBySid(sid));\n let destroyedSessions = 0;\n\n for (const sessionId of sessionIds) {\n if (await destroyAuthSessionById(sessionId)) {\n destroyedSessions += 1;\n }\n }\n\n return destroyedSessions;\n}\n\nexport async function destroyAuthSessionsBySessionState(sessionState: string): Promise<number> {\n const sessionIds = await getSessionIdsFromIndex(REDIS_KEYS.sessionBySessionState(sessionState));\n let destroyedSessions = 0;\n\n for (const sessionId of sessionIds) {\n if (await destroyAuthSessionById(sessionId)) {\n destroyedSessions += 1;\n }\n }\n\n return destroyedSessions;\n}\n\nexport async function destroyAuthSessionsByIdentitySession(params: {\n sid?: string;\n sessionState?: string;\n userId?: string;\n}): Promise<number> {\n if (params.sid) {\n return await destroyAuthSessionsBySid(params.sid);\n }\n\n if (params.sessionState) {\n return await destroyAuthSessionsBySessionState(params.sessionState);\n }\n\n if (params.userId) {\n return await destroyAuthSessionsForUser(params.userId);\n }\n\n return 0;\n}\n\nexport async function requireAuthSession(request: Request): Promise<SessionData> {\n const sessionData = await getAuthSession(request);\n\n if (!sessionData.userId || !sessionData.accessToken) {\n throw new Response('Unauthorized', { status: 401 });\n }\n\n return sessionData;\n}\n\nexport async function isSessionValid(request: Request): Promise<boolean> {\n try {\n const sessionData = await getAuthSession(request);\n\n if (!sessionData.user || !sessionData.accessToken) {\n return false;\n }\n\n if (sessionData.expiresAt && Date.now() >= sessionData.expiresAt) {\n return false;\n }\n\n return true;\n } catch {\n return false;\n }\n}\n\n// ============================================================================\n// OAuth State Management\n// ============================================================================\n\nexport async function createOAuthState(state: OAuthState): Promise<string> {\n const stateId = crypto.randomUUID();\n const key = REDIS_KEYS.oauthState(stateId);\n\n await getRedis().setex(key, OAUTH_STATE_TTL, JSON.stringify(state));\n\n logger.debug('OAuth state created', { stateId, ttl: OAUTH_STATE_TTL });\n\n return stateId;\n}\n\nexport async function getOAuthState(stateId: string): Promise<OAuthState | null> {\n const key = REDIS_KEYS.oauthState(stateId);\n const data = await getRedis().get(key);\n\n if (!data) {\n logger.warn('OAuth state not found or expired', { stateId });\n return null;\n }\n\n try {\n return JSON.parse(data);\n } catch (error) {\n logger.error('Failed to parse OAuth state', error instanceof Error ? error : undefined);\n return null;\n }\n}\n\nexport async function deleteOAuthState(stateId: string): Promise<void> {\n const key = REDIS_KEYS.oauthState(stateId);\n await getRedis().del(key);\n logger.debug('OAuth state deleted', { stateId });\n}\n\nexport async function cleanupExpiredOAuthStates(): Promise<number> {\n const pattern = REDIS_KEYS.oauthState('*');\n const keys = await getRedis().keys(pattern);\n\n let cleaned = 0;\n for (const key of keys) {\n const ttl = await getRedis().ttl(key);\n if (ttl === -1) {\n await getRedis().del(key);\n cleaned++;\n }\n }\n\n logger.info('OAuth state cleanup completed', { cleaned, total: keys.length });\n return cleaned;\n}\n\n// ============================================================================\n// Cleanup\n// ============================================================================\n\nexport async function closeRedisConnection(): Promise<void> {\n if (redis) {\n await redis.quit();\n redis = null;\n logger.info('Redis connection closed');\n }\n}\n"],"mappings":";;;;;;;;;;;AAiBA,IAAI,QAAsB;AAE1B,SAAS,WAAkB;CACzB,IAAI,CAAC,OAAO;EACV,QAAQ,IAAI,MAAM,QAAQ,IAAI,aAAa,wBAAwB;EAEnE,MAAM,GAAG,UAAU,QAAQ;GACzB,OAAO,MAAM,sBAAsB,GAAG;EACxC,CAAC;EAED,MAAM,GAAG,iBAAiB;GACxB,OAAO,KAAK,8BAA8B;EAC5C,CAAC;CACH;CACA,OAAO;AACT;AAMA,MAAM,aAAa,QAAQ,IAAI,oBAAoB;AAEnD,MAAM,aAAa;CACjB,aAAa,YAAoB,GAAG,WAAW,cAAc;CAC7D,UAAU,cAAsB,GAAG,WAAW,UAAU;CACxD,gBAAgB,WAAmB,GAAG,WAAW,qBAAqB;CACtE,eAAe,QAAgB,GAAG,WAAW,oBAAoB;CACjE,wBAAwB,iBAAyB,GAAG,WAAW,8BAA8B;AAC/F;AAEA,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB,OAAU,KAAK;AAG3C,MAAM,kBAAkB,0BAA0B,mBAAmB,GAAuB;AAC5F,MAAM,cAAc,0BAA0B,uBAAuB,0BAA0B,eAAe,mBAAmB,CAAC;AAElI,MAAM,sBAAsB,QAAQ,IAAI,uBAAuB;AAC/D,MAAM,iBAAiB,qBAAqB;AAC5C,MAAM,wBAAwB,oBAAoB;AAClD,MAAM,sBAAsB;AAC5B,MAAM,2BAA2B,sBAAsB;AACvD,MAAM,wBAAwB,QAAQ,IAAI,uBAAuB,KAAK;AACtE,MAAM,6BAA6B,gCAAgC;AACnE,MAAM,yBAAyB,4BAA4B,0BAA0B;AAErF,MAAa,2BAA2B,oBAAoB,QAAQ,YAAY,EAAE,CAAC,CAAC,QAAQ,MAAM,EAAE;AAEpG,SAAS,kBAAkB,MAAc,cAAgC;CACvE,MAAM,QAAQ,QAAQ,IAAI,KAAK,EAAE,KAAK,CAAC,CAAC,YAAY;CACpD,IAAI,CAAC,OACH,OAAO;CAGT,IAAI;EAAC;EAAK;EAAQ;EAAO;CAAI,CAAC,CAAC,SAAS,KAAK,GAC3C,OAAO;CAGT,IAAI;EAAC;EAAK;EAAS;EAAM;CAAK,CAAC,CAAC,SAAS,KAAK,GAC5C,OAAO;CAGT,MAAM,IAAI,MAAM,GAAG,KAAK,0BAA0B;AACpD;AAEA,SAAS,0BAA0B,MAAc,cAA8B;CAC7E,MAAM,QAAQ,QAAQ,IAAI,KAAK,EAAE,KAAK;CACtC,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,UAAU,GAC7C,MAAM,IAAI,MAAM,GAAG,KAAK,6BAA6B;CAGvD,OAAO;AACT;AAEA,SAAS,uBAA+B;CACtC,MAAM,SAAS,QAAQ,IAAI,gBAAgB,KAAK;CAChD,IAAI,CAAC,UAAU,WAAW,wBAAwB;EAChD,IAAI,QAAQ,IAAI,aAAa,cAC3B,MAAM,IAAI,MAAM,oEAAoE;EAGtF,OAAO;CACT;CAEA,IAAI,OAAO,SAAS,IAClB,MAAM,IAAI,MAAM,qDAAqD;CAGvE,OAAO;AACT;AAEA,SAAS,sBAA+B;CACtC,OAAO,kBAAkB,yBAAyB,QAAQ,IAAI,aAAa,YAAY;AACzF;AAEA,SAAS,wBAAmD;CAC1D,MAAM,QAAQ,QAAQ,IAAI,0BAA0B,KAAK,CAAC,CAAC,YAAY;CACvE,MAAM,WAAW,UAAU,WAAW,WAAW,UAAU,SAAS,SAAS;CAE7E,IAAI,aAAa,UAAU,CAAC,uBAC1B,MAAM,IAAI,MAAM,oEAAoE;CAGtF,OAAO;AACT;AAEA,SAAS,kCAA2C;CAClD,MAAM,UAAU,kBAAkB,sBAAsB,QAAQ,IAAI,aAAa,YAAY;CAC7F,IAAI,QAAQ,IAAI,aAAa,gBAAgB,CAAC,SAC5C,MAAM,IAAI,MAAM,sDAAsD;CAGxE,OAAO;AACT;AAEA,SAAS,gBAAgB,OAAuB;CAC9C,MAAM,aAAa,MAAM,QAAQ,MAAM,GAAG,CAAC,CAAC,QAAQ,MAAM,GAAG;CAC7D,MAAM,SAAS,WAAW,OAAO,WAAW,UAAW,IAAK,WAAW,SAAS,KAAM,GAAI,GAAG;CAC7F,OAAO,OAAO,KAAK,QAAQ,QAAQ;AACrC;AAEA,SAAS,4BAA4B,SAAiC;CACpE,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,QAAQ,QAAQ,IAAI,wBAAwB,KAAK;CACvD,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,wEAAwE;CAG1F,MAAM,MAAM,kBAAkB,KAAK,KAAK,IACpC,OAAO,KAAK,OAAO,KAAK,IACxB,gBAAgB,KAAK;CAEzB,IAAI,IAAI,WAAW,IACjB,MAAM,IAAI,MAAM,oGAAoG;CAGtH,OAAO;AACT;AAMA,SAAS,kBAAkB,cAAqD;CAC9E,IAAI,CAAC,cACH,OAAO,CAAC;CAGV,OAAO,aACJ,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,OAAO,OAAO,CAAC,CACf,QAAgC,SAAS,SAAS;EACjD,MAAM,iBAAiB,KAAK,QAAQ,GAAG;EACvC,IAAI,kBAAkB,GACpB,OAAO;EAGT,MAAM,OAAO,KAAK,MAAM,GAAG,cAAc,CAAC,CAAC,KAAK;EAChD,MAAM,QAAQ,KAAK,MAAM,iBAAiB,CAAC,CAAC,CAAC,KAAK;EAClD,IAAI,CAAC,MACH,OAAO;EAGT,QAAQ,QAAQ;EAChB,OAAO;CACT,GAAG,CAAC,CAAC;AACT;AAEA,SAAS,sBAAsB,OAAe,QAAwB;CACpE,OAAO,WAAW,UAAU,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,WAAW;AACtE;AAEA,SAAS,oBAAoB,MAAc,OAAwB;CACjE,MAAM,aAAa,OAAO,KAAK,IAAI;CACnC,MAAM,cAAc,OAAO,KAAK,KAAK;CAErC,IAAI,WAAW,WAAW,YAAY,QACpC,OAAO;CAGT,OAAO,gBAAgB,YAAY,WAAW;AAChD;AAEA,SAAS,cAAc,WAA2B;CAEhD,OAAO,GAAG,UAAU,GADF,sBAAsB,WAAW,cACpB;AACjC;AAEA,SAAS,gBAAgB,aAAgD;CACvE,IAAI,CAAC,aACH,OAAO;CAGT,IAAI;CACJ,IAAI;EACF,eAAe,mBAAmB,WAAW;CAC/C,QAAQ;EACN,OAAO;CACT;CAEA,MAAM,iBAAiB,aAAa,YAAY,GAAG;CACnD,IAAI,kBAAkB,KAAK,mBAAmB,aAAa,SAAS,GAClE,OAAO;CAGT,MAAM,YAAY,aAAa,MAAM,GAAG,cAAc;CAItD,OAAO,oBAHW,aAAa,MAAM,iBAAiB,CAGnB,GAFT,sBAAsB,WAAW,cAEL,CAAC,IAAI,YAAY;AACzE;AAEA,SAAS,4BAA4B,WAAmB,SAAuC;CAC7F,MAAM,cAAc,mBAAmB,cAAc,SAAS,CAAC;CAC/D,MAAM,WAAW;EACf,GAAG,oBAAoB,GAAG;EAC1B,WAAW,QAAQ;EACnB,QAAQ;EACR,YAAY;EACZ;CACF;CAEA,IAAI,uBACF,SAAS,KAAK,UAAU,uBAAuB;CAGjD,IAAI,uBACF,SAAS,KAAK,QAAQ;CAGxB,OAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,gCAAwC;CAC/C,MAAM,WAAW;EACf,GAAG,oBAAoB;EACvB;EACA,QAAQ;EACR,YAAY;EACZ;CACF;CAEA,IAAI,uBACF,SAAS,KAAK,UAAU,uBAAuB;CAGjD,IAAI,uBACF,SAAS,KAAK,QAAQ;CAGxB,OAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,eAAe,wBAAwB,SAA0C;CAE/E,OAAO,gBADS,kBAAkB,QAAQ,QAAQ,IAAI,QAAQ,CACjC,CAAC,CAAC,oBAAoB;AACrD;AAEA,SAAS,oBAAoB,MAA6B;CACxD,MAAM,uBAAO,IAAI,IAAY;CAE7B,IAAI,KAAK,QACP,KAAK,IAAI,WAAW,cAAc,KAAK,MAAM,CAAC;CAGhD,IAAI,KAAK,KACP,KAAK,IAAI,WAAW,aAAa,KAAK,GAAG,CAAC;CAG5C,IAAI,KAAK,cACP,KAAK,IAAI,WAAW,sBAAsB,KAAK,YAAY,CAAC;CAG9D,OAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAe,uBAAuB,WAAmB,MAAkC;CACzF,MAAM,YAAY,oBAAoB,IAAI;CAC1C,IAAI,UAAU,WAAW,GACvB;CAIF,MAAM,WADc,SACO,CAAC,CAAC,SAAS;CAEtC,KAAK,MAAM,YAAY,WAAW;EAChC,SAAS,KAAK,UAAU,SAAS;EACjC,SAAS,OAAO,UAAU,WAAW;CACvC;CAEA,MAAM,SAAS,KAAK;AACtB;AAEA,eAAe,yBAAyB,WAAmB,MAAkC;CAC3F,MAAM,YAAY,oBAAoB,IAAI;CAC1C,IAAI,UAAU,WAAW,GACvB;CAIF,MAAM,WADc,SACO,CAAC,CAAC,SAAS;CAEtC,KAAK,MAAM,YAAY,WACrB,SAAS,KAAK,UAAU,SAAS;CAGnC,MAAM,SAAS,KAAK;AACtB;AAEA,SAAS,iBAAiB,aAAgD;CACxE,IAAI,CAAC,aACH,OAAO;CAGT,IAAI;EACF,OAAO,KAAK,MAAM,kBAAkB,WAAW,CAAC;CAClD,SAAS,OAAO;EACd,OAAO,MAAM,gCAAgC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EACvF,OAAO;CACT;AACF;AAEA,SAAS,kBAAkB,MAA2B;CACpD,MAAM,YAAY,KAAK,UAAU,IAAI;CACrC,IAAI,CAAC,wBACH,OAAO;CAGT,MAAM,KAAK,YAAY,EAAE;CACzB,MAAM,SAAS,eAAe,eAAe,wBAAwB,EAAE;CACvE,MAAM,aAAa,OAAO,OAAO,CAAC,OAAO,OAAO,WAAW,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;CACnF,MAAM,MAAM,OAAO,WAAW;CAE9B,OAAO;EACL;EACA,GAAG,SAAS,WAAW;EACvB,IAAI,SAAS,WAAW;EACxB,WAAW,SAAS,WAAW;CACjC,CAAC,CAAC,KAAK,GAAG;AACZ;AAEA,SAAS,kBAAkB,OAAuB;CAChD,IAAI,CAAC,MAAM,WAAW,SAAS,GAAG;EAChC,IAAI,4BACF,OAAO,KAAK,oFAAoF;EAGlG,OAAO;CACT;CAEA,IAAI,CAAC,wBACH,MAAM,IAAI,MAAM,uFAAuF;CAGzG,MAAM,GAAG,SAAS,SAAS,UAAU,mBAAmB,MAAM,MAAM,GAAG;CACvE,IAAI,YAAY,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,iBAChD,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,WAAW,iBAAiB,eAAe,wBAAwB,OAAO,KAAK,SAAS,WAAW,CAAC;CAC1G,SAAS,WAAW,OAAO,KAAK,UAAU,WAAW,CAAC;CAEtD,OAAO,OAAO,OAAO,CACnB,SAAS,OAAO,OAAO,KAAK,iBAAiB,WAAW,CAAC,GACzD,SAAS,MAAM,CACjB,CAAC,CAAC,CAAC,SAAS,MAAM;AACpB;AAEA,eAAe,gBAAgB,WAAgD;CAC7E,IAAI;EACF,MAAM,cAAc,MAAM,SAAS,CAAC,CAAC,IAAI,WAAW,QAAQ,SAAS,CAAC;EACtE,IAAI,CAAC,aAAa;GAChB,OAAO,MAAM,8BAA8B,EAAE,UAAU,CAAC;GACxD,OAAO;EACT;EAEA,MAAM,SAAS,CAAC,CAAC,OAAO,WAAW,QAAQ,SAAS,GAAG,WAAW;EAClE,OAAO,iBAAiB,WAAW;CACrC,SAAS,OAAO;EACd,OAAO,MAAM,qCAAqC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EAC5F,OAAO;CACT;AACF;AAEA,eAAe,iBAAiB,WAAmB,MAAkC;CACnF,IAAI;EACF,MAAM,sBAAsB,iBAAiB,MAAM,SAAS,CAAC,CAAC,IAAI,WAAW,QAAQ,SAAS,CAAC,CAAC;EAChG,IAAI,qBACF,MAAM,yBAAyB,WAAW,mBAAmB;EAG/D,MAAM,SAAS,CAAC,CAAC,MAAM,WAAW,QAAQ,SAAS,GAAG,aAAa,kBAAkB,IAAI,CAAC;EAC1F,MAAM,uBAAuB,WAAW,IAAI;EAC5C,OAAO,MAAM,yBAAyB,EAAE,UAAU,CAAC;CACrD,SAAS,OAAO;EACd,OAAO,MAAM,qCAAqC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EAC5F,MAAM;CACR;AACF;AAEA,eAAe,kBAAkB,WAAkC;CACjE,IAAI;EACF,MAAM,sBAAsB,iBAAiB,MAAM,SAAS,CAAC,CAAC,IAAI,WAAW,QAAQ,SAAS,CAAC,CAAC;EAChG,IAAI,qBACF,MAAM,yBAAyB,WAAW,mBAAmB;EAG/D,MAAM,SAAS,CAAC,CAAC,IAAI,WAAW,QAAQ,SAAS,CAAC;EAClD,OAAO,MAAM,yBAAyB,EAAE,UAAU,CAAC;CACrD,SAAS,OAAO;EACd,OAAO,MAAM,uCAAuC,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;CAChG;AACF;AAEA,SAAS,iBAAiB,cAA2B,SAA4C;CAC/F,MAAM,WAAW,EAAE,GAAG,aAAa;CAEnC,OAAO,QAAQ,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAChD,IAAI,UAAU,KAAA,GAAW;GACvB,OAAO,SAAS;GAChB;EACF;EAEA,SAAS,OAAO;CAClB,CAAC;CAED,OAAO;AACT;AAEA,eAAe,uBAAuB,UAAqC;CACzE,MAAM,aAAa,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ;CACrD,MAAM,mBAA6B,CAAC;CACpC,MAAM,kBAA4B,CAAC;CAEnC,KAAK,MAAM,aAAa,YAEtB,IAAI,MADiB,SAAS,CAAC,CAAC,OAAO,WAAW,QAAQ,SAAS,CAAC,GAElE,iBAAiB,KAAK,SAAS;MAE/B,gBAAgB,KAAK,SAAS;CAIlC,IAAI,gBAAgB,SAAS,GAC3B,MAAM,SAAS,CAAC,CAAC,KAAK,UAAU,GAAG,eAAe;CAGpD,OAAO;AACT;AAEA,SAAS,qBACP,WACA,SACA,kBACoB;CACpB,OAAO;EACL;EACA,QAAQ,QAAQ;EAChB,OAAO,QAAQ,MAAM;EACrB,MAAM,QAAQ,MAAM;EACpB,QAAQ,QAAQ;EAChB,KAAK,QAAQ;EACb,cAAc,QAAQ;EACtB,UAAU,QAAQ;EAClB,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,cAAc,QAAQ;EACtB,WAAW,QAAQ;EACnB,WAAW,mBAAmB,cAAc,mBAAmB,KAAA;CACjE;AACF;AAEA,eAAe,qBAAqB,YAAgF;CAClH,MAAM,WAA4D,CAAC;CAEnE,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,OAAO,MAAM,gBAAgB,SAAS;EAC5C,IAAI,MACF,SAAS,KAAK;GAAE;GAAW;EAAK,CAAC;CAErC;CAEA,OAAO;AACT;AAEA,SAAS,oBAAoB,SAAsC;CAEjE,OADqB,QAAQ,QAAQ,IAAI,iBAAiB,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,KAG/E,QAAQ,QAAQ,IAAI,kBAAkB,CAAC,EAAE,KAAK,KAC9C,QAAQ,QAAQ,IAAI,WAAW,CAAC,EAAE,KAAK,KACvC,KAAA;AAEJ;AAMA,eAAsB,kBACpB,SACA,aACkB;CAClB,MAAM,mBAAmB,MAAM,wBAAwB,OAAO;CAC9D,MAAM,gBAAgB,OAAO,WAAW;CACxC,MAAM,kBAAkB,iBAAiB,CAAC,GAAG;EAC3C,GAAG;EACH,WAAW;EACX,WAAW,YAAY,aAAa,oBAAoB,OAAO;EAC/D,WAAW,YAAY,aAAa,QAAQ,QAAQ,IAAI,YAAY,KAAK,KAAA;CAC3E,CAAC;CAED,OAAO,MAAM,yBAAyB,EAAE,UAAU,OAAO,KAAK,WAAW,EAAE,CAAC;CAE5E,IAAI,kBACF,MAAM,kBAAkB,gBAAgB;CAG1C,MAAM,iBAAiB,eAAe,eAAe;CAErD,OAAO,IAAI,QAAQ,EACjB,cAAc,4BAA4B,eAAe,EAAE,QAAQ,YAAY,CAAC,EAClF,CAAC;AACH;AAEA,eAAsB,eAAe,SAAwC;CAC3E,MAAM,YAAY,MAAM,wBAAwB,OAAO;CACvD,IAAI,CAAC,WACH,OAAO,CAAC;CAGV,MAAM,UAAU,MAAM,gBAAgB,SAAS;CAC/C,IAAI,CAAC,SACH,OAAO,CAAC;CAGV,OAAO;EACL;EACA,QAAQ,QAAQ;EAChB,aAAa,QAAQ;EACrB,cAAc,QAAQ;EACtB,SAAS,QAAQ;EACjB,WAAW,QAAQ;EACnB,QAAQ,QAAQ;EAChB,KAAK,QAAQ;EACb,cAAc,QAAQ;EACtB,UAAU,QAAQ;EAClB,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd,WAAW,QAAQ;EACnB,cAAc,QAAQ;CACxB;AACF;AAEA,eAAsB,kBACpB,SACA,SACkB;CAClB,MAAM,mBAAmB,MAAM,wBAAwB,OAAO;CAC9D,MAAM,qBAAqB,mBACtB,MAAM,gBAAgB,gBAAgB,KAAM,CAAC,IAC9C,CAAC;CACL,MAAM,gBAAgB,oBAAoB,OAAO,WAAW;CAM5D,MAAM,iBAAiB,eALC,iBAAiB,oBAAoB;EAC3D,GAAG;EACH,WAAW;CACb,CAEoD,CAAC;CAErD,OAAO,IAAI,QAAQ,EACjB,cAAc,4BAA4B,eAAe,EAAE,QAAQ,YAAY,CAAC,EAClF,CAAC;AACH;AAEA,eAAsB,mBAAmB,SAAoC;CAC3E,MAAM,YAAY,MAAM,wBAAwB,OAAO;CACvD,IAAI,WACF,MAAM,kBAAkB,SAAS;CAGnC,OAAO,IAAI,QAAQ,EACjB,cAAc,8BAA8B,EAC9C,CAAC;AACH;AAEA,eAAsB,wBAAwB,SAA0C;CACtF,OAAO,MAAM,wBAAwB,OAAO;AAC9C;AAEA,eAAsB,mBAAmB,WAAgD;CACvF,MAAM,UAAU,MAAM,gBAAgB,SAAS;CAC/C,OAAO,UAAU;EAAE,GAAG;EAAS;CAAU,IAAI;AAC/C;AAEA,eAAsB,2BACpB,QAC0D;CAE1D,OAAO,MAAM,qBAAqB,MADT,uBAAuB,WAAW,cAAc,MAAM,CAAC,CACpC;AAC9C;AAEA,eAAsB,wBACpB,QACA,kBAC+B;CAG/B,QAAO,MAFgB,2BAA2B,MAAM,EAAA,CAGrD,KAAK,EAAE,WAAW,WAAW,qBAAqB,WAAW,MAAM,gBAAgB,CAAC,CAAC,CACrF,MAAM,MAAM,WAAW,MAAM,gBAAgB,MAAM,aAAa,MAAM,KAAK,gBAAgB,KAAK,aAAa,EAAE;AACpH;AAEA,eAAsB,4BAA4B,SAAiD;CACjG,MAAM,iBAAiB,MAAM,eAAe,OAAO;CACnD,IAAI,CAAC,eAAe,QAClB,OAAO,CAAC;CAGV,OAAO,MAAM,wBAAwB,eAAe,QAAQ,eAAe,SAAS;AACtF;AAEA,eAAsB,uBAAuB,WAAqC;CAEhF,IAAI,CAAC,MADiB,gBAAgB,SAAS,GAE7C,OAAO;CAGT,MAAM,kBAAkB,SAAS;CACjC,OAAO;AACT;AAEA,eAAsB,2BAA2B,QAAiC;CAChF,MAAM,aAAa,MAAM,uBAAuB,WAAW,cAAc,MAAM,CAAC;CAChF,IAAI,oBAAoB;CAExB,KAAK,MAAM,aAAa,YACtB,IAAI,MAAM,uBAAuB,SAAS,GACxC,qBAAqB;CAIzB,OAAO;AACT;AAEA,eAAsB,yBAAyB,KAA8B;CAC3E,MAAM,aAAa,MAAM,uBAAuB,WAAW,aAAa,GAAG,CAAC;CAC5E,IAAI,oBAAoB;CAExB,KAAK,MAAM,aAAa,YACtB,IAAI,MAAM,uBAAuB,SAAS,GACxC,qBAAqB;CAIzB,OAAO;AACT;AAEA,eAAsB,kCAAkC,cAAuC;CAC7F,MAAM,aAAa,MAAM,uBAAuB,WAAW,sBAAsB,YAAY,CAAC;CAC9F,IAAI,oBAAoB;CAExB,KAAK,MAAM,aAAa,YACtB,IAAI,MAAM,uBAAuB,SAAS,GACxC,qBAAqB;CAIzB,OAAO;AACT;AAEA,eAAsB,qCAAqC,QAIvC;CAClB,IAAI,OAAO,KACT,OAAO,MAAM,yBAAyB,OAAO,GAAG;CAGlD,IAAI,OAAO,cACT,OAAO,MAAM,kCAAkC,OAAO,YAAY;CAGpE,IAAI,OAAO,QACT,OAAO,MAAM,2BAA2B,OAAO,MAAM;CAGvD,OAAO;AACT;AAEA,eAAsB,mBAAmB,SAAwC;CAC/E,MAAM,cAAc,MAAM,eAAe,OAAO;CAEhD,IAAI,CAAC,YAAY,UAAU,CAAC,YAAY,aACtC,MAAM,IAAI,SAAS,gBAAgB,EAAE,QAAQ,IAAI,CAAC;CAGpD,OAAO;AACT;AAEA,eAAsB,eAAe,SAAoC;CACvE,IAAI;EACF,MAAM,cAAc,MAAM,eAAe,OAAO;EAEhD,IAAI,CAAC,YAAY,QAAQ,CAAC,YAAY,aACpC,OAAO;EAGT,IAAI,YAAY,aAAa,KAAK,IAAI,KAAK,YAAY,WACrD,OAAO;EAGT,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAMA,eAAsB,iBAAiB,OAAoC;CACzE,MAAM,UAAU,OAAO,WAAW;CAClC,MAAM,MAAM,WAAW,WAAW,OAAO;CAEzC,MAAM,SAAS,CAAC,CAAC,MAAM,KAAK,iBAAiB,KAAK,UAAU,KAAK,CAAC;CAElE,OAAO,MAAM,uBAAuB;EAAE;EAAS,KAAK;CAAgB,CAAC;CAErE,OAAO;AACT;AAEA,eAAsB,cAAc,SAA6C;CAC/E,MAAM,MAAM,WAAW,WAAW,OAAO;CACzC,MAAM,OAAO,MAAM,SAAS,CAAC,CAAC,IAAI,GAAG;CAErC,IAAI,CAAC,MAAM;EACT,OAAO,KAAK,oCAAoC,EAAE,QAAQ,CAAC;EAC3D,OAAO;CACT;CAEA,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,SAAS,OAAO;EACd,OAAO,MAAM,+BAA+B,iBAAiB,QAAQ,QAAQ,KAAA,CAAS;EACtF,OAAO;CACT;AACF;AAEA,eAAsB,iBAAiB,SAAgC;CACrE,MAAM,MAAM,WAAW,WAAW,OAAO;CACzC,MAAM,SAAS,CAAC,CAAC,IAAI,GAAG;CACxB,OAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC;AACjD;AAEA,eAAsB,4BAA6C;CACjE,MAAM,UAAU,WAAW,WAAW,GAAG;CACzC,MAAM,OAAO,MAAM,SAAS,CAAC,CAAC,KAAK,OAAO;CAE1C,IAAI,UAAU;CACd,KAAK,MAAM,OAAO,MAEhB,IAAI,MADc,SAAS,CAAC,CAAC,IAAI,GAAG,MACxB,IAAI;EACd,MAAM,SAAS,CAAC,CAAC,IAAI,GAAG;EACxB;CACF;CAGF,OAAO,KAAK,iCAAiC;EAAE;EAAS,OAAO,KAAK;CAAO,CAAC;CAC5E,OAAO;AACT;AAMA,eAAsB,uBAAsC;CAC1D,IAAI,OAAO;EACT,MAAM,MAAM,KAAK;EACjB,QAAQ;EACR,OAAO,KAAK,yBAAyB;CACvC;AACF"}