@gnwebsoft/ui 4.0.1 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-BXF3QEBN.js +2181 -0
- package/dist/{chunk-YUVXLOSA.cjs → chunk-DS524L7W.cjs} +3 -15
- package/dist/chunk-FYU2OIMD.js +15 -0
- package/dist/chunk-GHW77WIM.cjs +1 -1
- package/dist/{chunk-CE4CSA36.cjs → chunk-GLN5JSFF.cjs} +327 -408
- package/dist/chunk-GVWCGJ3F.js +140 -0
- package/dist/chunk-LUW7V5GI.cjs +15 -0
- package/dist/chunk-M526GQZS.js +2490 -0
- package/dist/chunk-MVPLBJRK.cjs +1 -1
- package/dist/chunk-PUQIPRL2.js +467 -0
- package/dist/chunk-QLQ6OH25.cjs +2490 -0
- package/dist/chunk-Y3QTSDLJ.cjs +140 -0
- package/dist/core/index.cjs +3 -3
- package/dist/core/index.js +3 -3
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +11 -7
- package/dist/index.js +28 -24
- package/dist/types/index.cjs +1 -1
- package/dist/utils/index.cjs +5 -3
- package/dist/utils/index.js +4 -2
- package/dist/wrappers/index.cjs +7 -4
- package/dist/wrappers/index.js +7 -4
- package/package.json +1 -1
- package/dist/chunk-246MYJX6.js +0 -479
- package/dist/chunk-DJSMLHFO.js +0 -140
- package/dist/chunk-L3QQX3BK.js +0 -1596
- package/dist/chunk-NC3JINIM.cjs +0 -1596
- package/dist/chunk-R45OPW5A.js +0 -2262
- package/dist/chunk-UEBN47RB.cjs +0 -140
|
@@ -0,0 +1,2490 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/core/api/CorrelationIdGenerator.ts
|
|
2
|
+
function generateUUID() {
|
|
3
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
4
|
+
return crypto.randomUUID();
|
|
5
|
+
}
|
|
6
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
7
|
+
const r = Math.random() * 16 | 0;
|
|
8
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
9
|
+
return v.toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function generateCorrelationId(prefix) {
|
|
13
|
+
const uuid = generateUUID();
|
|
14
|
+
return prefix ? `${prefix}-${uuid}` : uuid;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/core/api/Errors/ErrorNormalizer.ts
|
|
18
|
+
var ErrorNormalizer = class {
|
|
19
|
+
/**
|
|
20
|
+
* Maps an HTTP status code to a standardized error type category.
|
|
21
|
+
*
|
|
22
|
+
* This categorization helps consumers handle different error classes appropriately:
|
|
23
|
+
* - `validation_error` (400): Client sent invalid data
|
|
24
|
+
* - `client_error` (401-499): Client-side issues (auth, permissions, not found, etc.)
|
|
25
|
+
* - `server_error` (500-599): Server-side failures
|
|
26
|
+
* - `unknown_error`: Unrecognized status codes
|
|
27
|
+
*
|
|
28
|
+
* @param status - HTTP status code from the response
|
|
29
|
+
* @returns The error type category as a string
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* normalizer.getErrorType(400); // => 'validation_error'
|
|
34
|
+
* normalizer.getErrorType(404); // => 'client_error'
|
|
35
|
+
* normalizer.getErrorType(500); // => 'server_error'
|
|
36
|
+
* normalizer.getErrorType(0); // => 'unknown_error'
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
getErrorType(status) {
|
|
40
|
+
if (status >= 400 && status < 500) {
|
|
41
|
+
return status === 400 ? "validation_error" : "client_error";
|
|
42
|
+
} else if (status >= 500) {
|
|
43
|
+
return "server_error";
|
|
44
|
+
}
|
|
45
|
+
return "unknown_error";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Maps an HTTP status code to a human-readable error title.
|
|
49
|
+
*
|
|
50
|
+
* Provides user-friendly error messages for common HTTP status codes.
|
|
51
|
+
* Falls back to a generic "HTTP Error {status}" format for unmapped codes.
|
|
52
|
+
*
|
|
53
|
+
* @param status - HTTP status code from the response
|
|
54
|
+
* @returns A human-readable error title
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* normalizer.getErrorTitle(404); // => 'Not Found'
|
|
59
|
+
* normalizer.getErrorTitle(500); // => 'Internal Server Error'
|
|
60
|
+
* normalizer.getErrorTitle(999); // => 'HTTP Error 999'
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
getErrorTitle(status) {
|
|
64
|
+
const titles = {
|
|
65
|
+
400: "Bad Request",
|
|
66
|
+
401: "Unauthorized",
|
|
67
|
+
403: "Forbidden",
|
|
68
|
+
404: "Not Found",
|
|
69
|
+
405: "Method Not Allowed",
|
|
70
|
+
408: "Request Timeout",
|
|
71
|
+
409: "Conflict",
|
|
72
|
+
422: "Unprocessable Entity",
|
|
73
|
+
429: "Too Many Requests",
|
|
74
|
+
500: "Internal Server Error",
|
|
75
|
+
502: "Bad Gateway",
|
|
76
|
+
503: "Service Unavailable",
|
|
77
|
+
504: "Gateway Timeout"
|
|
78
|
+
};
|
|
79
|
+
return titles[status] || `HTTP Error ${status}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalizes any error into a consistent, structured ApiError format.
|
|
83
|
+
*
|
|
84
|
+
* This method handles various error scenarios and ensures they all conform to
|
|
85
|
+
* the {@link ApiError} interface with appropriate categorization and metadata:
|
|
86
|
+
*
|
|
87
|
+
* - **Existing ApiErrors**: Enhances with missing fields (traceId, config)
|
|
88
|
+
* - **AbortErrors**: Marks as `request_cancelled` with isAborted flag
|
|
89
|
+
* - **Timeout Errors**: Categorizes as `timeout_error` with 408 status
|
|
90
|
+
* - **Network Errors**: Categorizes as `network_error` with 0 status
|
|
91
|
+
* - **Unknown Errors**: Fallback category for unexpected error types
|
|
92
|
+
*
|
|
93
|
+
* All normalized errors include:
|
|
94
|
+
* - `type`: Error category for programmatic handling
|
|
95
|
+
* - `title`: Human-readable error title
|
|
96
|
+
* - `status`: HTTP status code (or 0 for non-HTTP errors)
|
|
97
|
+
* - `traceId`: Correlation ID for distributed tracing
|
|
98
|
+
* - `isAborted`: Boolean flag indicating if request was cancelled
|
|
99
|
+
* - `config`: Original request configuration for debugging
|
|
100
|
+
*
|
|
101
|
+
* @param error - The error to normalize (can be any type)
|
|
102
|
+
* @param config - The request configuration that led to this error
|
|
103
|
+
* @param correlationId - Optional correlation ID for tracing
|
|
104
|
+
* @returns A fully structured ApiError instance
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* Normalizing a fetch AbortError:
|
|
108
|
+
* ```typescript
|
|
109
|
+
* try {
|
|
110
|
+
* await fetch(url, { signal });
|
|
111
|
+
* } catch (error) {
|
|
112
|
+
* const apiError = normalizer.normalizeError(error, config, 'req-123');
|
|
113
|
+
* // apiError.type === 'request_cancelled'
|
|
114
|
+
* // apiError.isAborted === true
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* Normalizing a timeout:
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const timeoutError = new Error('Request timeout after 30000ms');
|
|
122
|
+
* const apiError = normalizer.normalizeError(timeoutError, config);
|
|
123
|
+
* // apiError.type === 'timeout_error'
|
|
124
|
+
* // apiError.status === 408
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
normalizeError(error, config, correlationId) {
|
|
128
|
+
const err = error;
|
|
129
|
+
if (err.type || err.title || err.errors) {
|
|
130
|
+
return Object.assign(
|
|
131
|
+
err instanceof Error ? err : new Error(err.message || "Unknown error"),
|
|
132
|
+
{
|
|
133
|
+
type: err.type,
|
|
134
|
+
title: err.title,
|
|
135
|
+
status: err.status,
|
|
136
|
+
traceId: err.traceId || correlationId,
|
|
137
|
+
errors: err.errors,
|
|
138
|
+
isAborted: err.isAborted || false,
|
|
139
|
+
config
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (err.name === "AbortError" || err.isAborted) {
|
|
144
|
+
return Object.assign(new Error(err.message || "Request was aborted"), {
|
|
145
|
+
type: "request_cancelled",
|
|
146
|
+
title: "Request was cancelled",
|
|
147
|
+
status: 0,
|
|
148
|
+
traceId: correlationId,
|
|
149
|
+
isAborted: true,
|
|
150
|
+
config
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (_optionalChain([err, 'access', _ => _.message, 'optionalAccess', _2 => _2.includes, 'call', _3 => _3("timeout")])) {
|
|
154
|
+
return Object.assign(new Error(err.message), {
|
|
155
|
+
type: "timeout_error",
|
|
156
|
+
title: "Request Timeout",
|
|
157
|
+
status: 408,
|
|
158
|
+
traceId: correlationId,
|
|
159
|
+
isAborted: true,
|
|
160
|
+
config
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (_optionalChain([err, 'access', _4 => _4.message, 'optionalAccess', _5 => _5.includes, 'call', _6 => _6("network")])) {
|
|
164
|
+
return Object.assign(new Error(err.message || "Network request failed"), {
|
|
165
|
+
type: "network_error",
|
|
166
|
+
title: "Network Error",
|
|
167
|
+
status: 0,
|
|
168
|
+
traceId: correlationId,
|
|
169
|
+
isAborted: false,
|
|
170
|
+
config
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return Object.assign(
|
|
174
|
+
new Error(err.message || "An unknown error occurred"),
|
|
175
|
+
{
|
|
176
|
+
type: "unknown_error",
|
|
177
|
+
title: "Unknown Error",
|
|
178
|
+
status: 0,
|
|
179
|
+
traceId: correlationId,
|
|
180
|
+
isAborted: false,
|
|
181
|
+
config
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/core/api/Interceptors/InterceptorManager.ts
|
|
188
|
+
var InterceptorManager = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this); }
|
|
189
|
+
/**
|
|
190
|
+
* Array of registered request interceptors
|
|
191
|
+
* @private
|
|
192
|
+
*/
|
|
193
|
+
__init() {this.requestInterceptors = []}
|
|
194
|
+
/**
|
|
195
|
+
* Array of registered response interceptors
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
__init2() {this.responseInterceptors = []}
|
|
199
|
+
/**
|
|
200
|
+
* Array of registered error interceptors
|
|
201
|
+
* @private
|
|
202
|
+
*/
|
|
203
|
+
__init3() {this.errorInterceptors = []}
|
|
204
|
+
/**
|
|
205
|
+
* Registers a request interceptor to modify requests before they are sent.
|
|
206
|
+
*
|
|
207
|
+
* Request interceptors can:
|
|
208
|
+
* - Add or modify headers
|
|
209
|
+
* - Transform request bodies
|
|
210
|
+
* - Add query parameters
|
|
211
|
+
* - Implement request signing
|
|
212
|
+
* - Log outgoing requests
|
|
213
|
+
*
|
|
214
|
+
* @param interceptor - Async function that receives and returns RequestConfig
|
|
215
|
+
* @returns Cleanup function to unregister this interceptor
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* // Add authentication header
|
|
220
|
+
* const unregister = manager.addRequestInterceptor(async (config) => {
|
|
221
|
+
* const token = await getAuthToken();
|
|
222
|
+
* config.headers = config.headers || new Headers();
|
|
223
|
+
* config.headers.set('Authorization', `Bearer ${token}`);
|
|
224
|
+
* return config;
|
|
225
|
+
* });
|
|
226
|
+
*
|
|
227
|
+
* // Later, remove the interceptor
|
|
228
|
+
* unregister();
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
addRequestInterceptor(interceptor) {
|
|
232
|
+
this.requestInterceptors.push(interceptor);
|
|
233
|
+
return () => {
|
|
234
|
+
const index = this.requestInterceptors.indexOf(interceptor);
|
|
235
|
+
if (index > -1) this.requestInterceptors.splice(index, 1);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Registers a response interceptor to transform responses before they are returned.
|
|
240
|
+
*
|
|
241
|
+
* Response interceptors can:
|
|
242
|
+
* - Transform response data format
|
|
243
|
+
* - Extract nested data structures
|
|
244
|
+
* - Add computed properties
|
|
245
|
+
* - Cache responses
|
|
246
|
+
* - Log successful responses
|
|
247
|
+
*
|
|
248
|
+
* @param interceptor - Async function that receives and returns ApiResponse
|
|
249
|
+
* @returns Cleanup function to unregister this interceptor
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* // Extract data from envelope
|
|
254
|
+
* manager.addResponseInterceptor(async (response) => {
|
|
255
|
+
* if (response.data?.result) {
|
|
256
|
+
* response.data = response.data.result;
|
|
257
|
+
* }
|
|
258
|
+
* return response;
|
|
259
|
+
* });
|
|
260
|
+
*
|
|
261
|
+
* // Add timestamps
|
|
262
|
+
* manager.addResponseInterceptor(async (response) => {
|
|
263
|
+
* return {
|
|
264
|
+
* ...response,
|
|
265
|
+
* receivedAt: new Date().toISOString()
|
|
266
|
+
* };
|
|
267
|
+
* });
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
addResponseInterceptor(interceptor) {
|
|
271
|
+
this.responseInterceptors.push(interceptor);
|
|
272
|
+
return () => {
|
|
273
|
+
const index = this.responseInterceptors.indexOf(interceptor);
|
|
274
|
+
if (index > -1) this.responseInterceptors.splice(index, 1);
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Registers an error interceptor to handle or transform errors before they are thrown.
|
|
279
|
+
*
|
|
280
|
+
* Error interceptors can:
|
|
281
|
+
* - Log errors to monitoring services
|
|
282
|
+
* - Transform error formats
|
|
283
|
+
* - Implement retry logic
|
|
284
|
+
* - Show user notifications
|
|
285
|
+
* - Extract validation errors
|
|
286
|
+
*
|
|
287
|
+
* **Note:** Error interceptors should re-throw the error (or a transformed version)
|
|
288
|
+
* to maintain the error flow. The final error is always thrown.
|
|
289
|
+
*
|
|
290
|
+
* @param interceptor - Async function that receives and returns (or throws) ApiError
|
|
291
|
+
* @returns Cleanup function to unregister this interceptor
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* // Log to monitoring service
|
|
296
|
+
* manager.addErrorInterceptor(async (error) => {
|
|
297
|
+
* if (error.status >= 500) {
|
|
298
|
+
* await Sentry.captureException(error, {
|
|
299
|
+
* extra: { traceId: error.traceId }
|
|
300
|
+
* });
|
|
301
|
+
* }
|
|
302
|
+
* throw error; // Re-throw to continue error flow
|
|
303
|
+
* });
|
|
304
|
+
*
|
|
305
|
+
* // Transform error messages
|
|
306
|
+
* manager.addErrorInterceptor(async (error) => {
|
|
307
|
+
* if (error.status === 404) {
|
|
308
|
+
* error.title = 'Resource not found';
|
|
309
|
+
* }
|
|
310
|
+
* throw error;
|
|
311
|
+
* });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
addErrorInterceptor(interceptor) {
|
|
315
|
+
this.errorInterceptors.push(interceptor);
|
|
316
|
+
return () => {
|
|
317
|
+
const index = this.errorInterceptors.indexOf(interceptor);
|
|
318
|
+
if (index > -1) this.errorInterceptors.splice(index, 1);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Applies all registered request interceptors in sequential order.
|
|
323
|
+
*
|
|
324
|
+
* Each interceptor receives the config modified by the previous interceptor,
|
|
325
|
+
* forming a processing pipeline. If any interceptor throws an error,
|
|
326
|
+
* the pipeline stops and the error propagates.
|
|
327
|
+
*
|
|
328
|
+
* @param config - The initial request configuration
|
|
329
|
+
* @returns The modified request configuration after all interceptors
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* const config = { method: 'GET', url: '/users' };
|
|
334
|
+
* const finalConfig = await manager.applyRequestInterceptors(config);
|
|
335
|
+
* // finalConfig has been processed by all registered interceptors
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
async applyRequestInterceptors(config) {
|
|
339
|
+
let modifiedConfig = { ...config };
|
|
340
|
+
for (const interceptor of this.requestInterceptors) {
|
|
341
|
+
modifiedConfig = await interceptor(modifiedConfig);
|
|
342
|
+
}
|
|
343
|
+
return modifiedConfig;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Applies all registered response interceptors in sequential order.
|
|
347
|
+
*
|
|
348
|
+
* Each interceptor receives the response modified by the previous interceptor,
|
|
349
|
+
* forming a processing pipeline. If any interceptor throws an error,
|
|
350
|
+
* the pipeline stops and the error propagates.
|
|
351
|
+
*
|
|
352
|
+
* @template T - The type of the response data
|
|
353
|
+
* @param response - The initial API response
|
|
354
|
+
* @returns The modified response after all interceptors
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const response = { data: { id: 1, name: 'John' } };
|
|
359
|
+
* const finalResponse = await manager.applyResponseInterceptors(response);
|
|
360
|
+
* // finalResponse has been processed by all registered interceptors
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
async applyResponseInterceptors(response) {
|
|
364
|
+
let modifiedResponse = response;
|
|
365
|
+
for (const interceptor of this.responseInterceptors) {
|
|
366
|
+
modifiedResponse = await interceptor(modifiedResponse);
|
|
367
|
+
}
|
|
368
|
+
return modifiedResponse;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Applies all registered error interceptors in sequential order and re-throws.
|
|
372
|
+
*
|
|
373
|
+
* Each interceptor receives the error (potentially modified by previous interceptors).
|
|
374
|
+
* Interceptors can transform the error before re-throwing it. The final error
|
|
375
|
+
* is always thrown to maintain error flow.
|
|
376
|
+
*
|
|
377
|
+
* If an interceptor itself throws an error, that becomes the new error to process
|
|
378
|
+
* by subsequent interceptors.
|
|
379
|
+
*
|
|
380
|
+
* @param error - The initial API error
|
|
381
|
+
* @returns Never returns (always throws)
|
|
382
|
+
* @throws The final error after all interceptors have processed it
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* try {
|
|
387
|
+
* await manager.applyErrorInterceptors(error);
|
|
388
|
+
* } catch (finalError) {
|
|
389
|
+
* // finalError has been processed by all registered error interceptors
|
|
390
|
+
* }
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
async applyErrorInterceptors(error) {
|
|
394
|
+
let modifiedError = error;
|
|
395
|
+
for (const interceptor of this.errorInterceptors) {
|
|
396
|
+
try {
|
|
397
|
+
modifiedError = await interceptor(modifiedError);
|
|
398
|
+
} catch (e) {
|
|
399
|
+
modifiedError = e;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
throw modifiedError;
|
|
403
|
+
}
|
|
404
|
+
}, _class);
|
|
405
|
+
|
|
406
|
+
// src/core/api/RequestManager.ts
|
|
407
|
+
var RequestManager = (_class2 = class {constructor() { _class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this); }
|
|
408
|
+
/**
|
|
409
|
+
* Map of active request keys to their abort controllers
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
__init4() {this.activeRequests = /* @__PURE__ */ new Map()}
|
|
413
|
+
/**
|
|
414
|
+
* Map of request keys to their correlation IDs for tracing
|
|
415
|
+
* @private
|
|
416
|
+
*/
|
|
417
|
+
__init5() {this.correlationMap = /* @__PURE__ */ new Map()}
|
|
418
|
+
/**
|
|
419
|
+
* Registers a new request for tracking and cancellation management.
|
|
420
|
+
*
|
|
421
|
+
* If a request with the same key already exists, it will be automatically
|
|
422
|
+
* cancelled before the new one is registered (request deduplication).
|
|
423
|
+
*
|
|
424
|
+
* @param key - Unique identifier for the request (typically method + URL + timestamp)
|
|
425
|
+
* @param controller - AbortController for cancelling the request
|
|
426
|
+
* @param correlationId - Correlation ID for distributed tracing
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const controller = new AbortController();
|
|
431
|
+
* manager.add('GET_/api/users_1699999999', controller, 'api-abc123');
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
add(key, controller, correlationId) {
|
|
435
|
+
this.cancel(key);
|
|
436
|
+
this.activeRequests.set(key, controller);
|
|
437
|
+
this.correlationMap.set(key, correlationId);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Removes a request from tracking without cancelling it.
|
|
441
|
+
*
|
|
442
|
+
* This is typically called when a request completes successfully or fails.
|
|
443
|
+
* Use {@link cancel} instead if you need to abort the request.
|
|
444
|
+
*
|
|
445
|
+
* @param key - Unique identifier for the request to remove
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* // Called automatically after request completes
|
|
450
|
+
* manager.remove('GET_/api/users_1699999999');
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
remove(key) {
|
|
454
|
+
this.activeRequests.delete(key);
|
|
455
|
+
this.correlationMap.delete(key);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Cancels a specific request and removes it from tracking.
|
|
459
|
+
*
|
|
460
|
+
* If the request doesn't exist or was already cancelled, this operation is a no-op.
|
|
461
|
+
* The associated AbortController's signal will be triggered, causing any active
|
|
462
|
+
* fetch operations to abort.
|
|
463
|
+
*
|
|
464
|
+
* @param key - Unique identifier for the request to cancel
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```typescript
|
|
468
|
+
* // User navigates away, cancel the pending request
|
|
469
|
+
* manager.cancel('GET_/api/users_1699999999');
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
cancel(key) {
|
|
473
|
+
const controller = this.activeRequests.get(key);
|
|
474
|
+
if (controller) {
|
|
475
|
+
controller.abort();
|
|
476
|
+
this.activeRequests.delete(key);
|
|
477
|
+
this.correlationMap.delete(key);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Cancels all active requests and clears all tracking data.
|
|
482
|
+
*
|
|
483
|
+
* This is useful for cleanup scenarios such as:
|
|
484
|
+
* - User logout
|
|
485
|
+
* - Component unmount
|
|
486
|
+
* - Navigation to a different part of the application
|
|
487
|
+
* - Error recovery that requires a clean slate
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* // Cancel all pending requests on logout
|
|
492
|
+
* function handleLogout() {
|
|
493
|
+
* apiClient.cancelAllRequests();
|
|
494
|
+
* // ... rest of logout logic
|
|
495
|
+
* }
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
cancelAll() {
|
|
499
|
+
this.activeRequests.forEach((controller) => controller.abort());
|
|
500
|
+
this.activeRequests.clear();
|
|
501
|
+
this.correlationMap.clear();
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Checks if a request with the given key is currently being tracked.
|
|
505
|
+
*
|
|
506
|
+
* @param key - Unique identifier for the request
|
|
507
|
+
* @returns `true` if the request is active, `false` otherwise
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```typescript
|
|
511
|
+
* if (manager.has('GET_/api/users_1699999999')) {
|
|
512
|
+
* console.log('Request is still pending');
|
|
513
|
+
* }
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
has(key) {
|
|
517
|
+
return this.activeRequests.has(key);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Retrieves the correlation ID for a given request key.
|
|
521
|
+
*
|
|
522
|
+
* Correlation IDs are used for distributed tracing and request tracking
|
|
523
|
+
* across services and logs.
|
|
524
|
+
*
|
|
525
|
+
* @param key - Unique identifier for the request
|
|
526
|
+
* @returns The correlation ID if found, `undefined` otherwise
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* ```typescript
|
|
530
|
+
* const correlationId = manager.getCorrelationId('GET_/api/users_1699999999');
|
|
531
|
+
* if (correlationId) {
|
|
532
|
+
* console.log('Trace request with ID:', correlationId);
|
|
533
|
+
* }
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
getCorrelationId(key) {
|
|
537
|
+
return this.correlationMap.get(key);
|
|
538
|
+
}
|
|
539
|
+
}, _class2);
|
|
540
|
+
|
|
541
|
+
// src/core/api/Retry/RetryHandler.ts
|
|
542
|
+
var RetryHandler = class {
|
|
543
|
+
/**
|
|
544
|
+
* Retries a failed request with exponential backoff strategy.
|
|
545
|
+
*
|
|
546
|
+
* The retry logic works as follows:
|
|
547
|
+
* 1. Attempts the request immediately
|
|
548
|
+
* 2. On failure, checks if the error is retryable
|
|
549
|
+
* 3. If retryable and retries remain, waits for the current delay
|
|
550
|
+
* 4. Doubles the delay for the next attempt
|
|
551
|
+
* 5. Repeats until success or retries exhausted
|
|
552
|
+
*
|
|
553
|
+
* **Non-Retryable Errors:**
|
|
554
|
+
* - Validation errors (400) - Client sent bad data
|
|
555
|
+
* - AbortErrors - Request was explicitly cancelled
|
|
556
|
+
* - Requests with aborted signals
|
|
557
|
+
*
|
|
558
|
+
* **Abort Handling:**
|
|
559
|
+
* If the signal is aborted during a retry delay, the retry is immediately
|
|
560
|
+
* cancelled and an AbortError is thrown.
|
|
561
|
+
*
|
|
562
|
+
* @template T - The return type of the function being retried
|
|
563
|
+
* @param fn - Async function to retry on failure
|
|
564
|
+
* @param retries - Number of retry attempts remaining (decrements each retry)
|
|
565
|
+
* @param delay - Current delay in milliseconds before next retry
|
|
566
|
+
* @param signal - Optional AbortSignal to cancel retries
|
|
567
|
+
* @returns Promise resolving to the function's result on success
|
|
568
|
+
* @throws The last error encountered if all retries are exhausted
|
|
569
|
+
* @throws AbortError if the signal is aborted during execution or delay
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* Basic retry usage:
|
|
573
|
+
* ```typescript
|
|
574
|
+
* const handler = new RetryHandler();
|
|
575
|
+
* const fetchUser = () => fetch('/api/users/123').then(r => r.json());
|
|
576
|
+
*
|
|
577
|
+
* try {
|
|
578
|
+
* const user = await handler.retryRequest(
|
|
579
|
+
* fetchUser,
|
|
580
|
+
* 3, // 3 retries
|
|
581
|
+
* 1000 // Start with 1s delay
|
|
582
|
+
* );
|
|
583
|
+
* console.log('User:', user);
|
|
584
|
+
* } catch (error) {
|
|
585
|
+
* console.error('Failed after all retries:', error);
|
|
586
|
+
* }
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* With cancellation support:
|
|
591
|
+
* ```typescript
|
|
592
|
+
* const controller = new AbortController();
|
|
593
|
+
* const signal = controller.signal;
|
|
594
|
+
*
|
|
595
|
+
* // Cancel after 5 seconds
|
|
596
|
+
* setTimeout(() => controller.abort(), 5000);
|
|
597
|
+
*
|
|
598
|
+
* try {
|
|
599
|
+
* await handler.retryRequest(fetchUser, 5, 1000, signal);
|
|
600
|
+
* } catch (error) {
|
|
601
|
+
* if (error.name === 'AbortError') {
|
|
602
|
+
* console.log('Retry cancelled');
|
|
603
|
+
* }
|
|
604
|
+
* }
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
async retryRequest(fn, retries, delay, signal) {
|
|
608
|
+
try {
|
|
609
|
+
if (_optionalChain([signal, 'optionalAccess', _7 => _7.aborted])) {
|
|
610
|
+
throw new Error(signal.reason || "Request aborted");
|
|
611
|
+
}
|
|
612
|
+
return await fn();
|
|
613
|
+
} catch (error) {
|
|
614
|
+
const err = error;
|
|
615
|
+
if (err.name === "AbortError" || _optionalChain([signal, 'optionalAccess', _8 => _8.aborted])) {
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
if (err.type === "validation_error" || err.status === 400) {
|
|
619
|
+
throw error;
|
|
620
|
+
}
|
|
621
|
+
if (retries === 0) throw error;
|
|
622
|
+
await new Promise((resolve, reject) => {
|
|
623
|
+
const timeoutId = setTimeout(resolve, delay);
|
|
624
|
+
if (signal) {
|
|
625
|
+
signal.addEventListener(
|
|
626
|
+
"abort",
|
|
627
|
+
() => {
|
|
628
|
+
clearTimeout(timeoutId);
|
|
629
|
+
reject(new Error(signal.reason || "Request aborted"));
|
|
630
|
+
},
|
|
631
|
+
{ once: true }
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
return this.retryRequest(fn, retries - 1, delay * 2, signal);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// src/core/api/Signals/SignalManager.ts
|
|
641
|
+
var SignalManager = class {
|
|
642
|
+
/**
|
|
643
|
+
* Creates a combined AbortController that aborts when any source signal aborts.
|
|
644
|
+
*
|
|
645
|
+
* This method implements the "any" pattern for cancellation: the combined signal
|
|
646
|
+
* will abort as soon as ANY of the source signals abort. This is useful for
|
|
647
|
+
* coordinating multiple cancellation conditions:
|
|
648
|
+
* - User clicks cancel button
|
|
649
|
+
* - Request timeout expires
|
|
650
|
+
* - Component unmounts
|
|
651
|
+
* - Parent request is cancelled
|
|
652
|
+
*
|
|
653
|
+
* **Early Abort Optimization:**
|
|
654
|
+
* If any source signal is already aborted when this method is called,
|
|
655
|
+
* the returned controller is immediately aborted without setting up listeners.
|
|
656
|
+
*
|
|
657
|
+
* **Memory Management:**
|
|
658
|
+
* Event listeners are registered with `{ once: true }` to prevent memory leaks,
|
|
659
|
+
* as they automatically clean up after firing.
|
|
660
|
+
*
|
|
661
|
+
* @param signals - Array of AbortSignals to combine (undefined values are ignored)
|
|
662
|
+
* @returns A new AbortController that aborts when any source signal aborts
|
|
663
|
+
*
|
|
664
|
+
* @example
|
|
665
|
+
* User cancellation + timeout:
|
|
666
|
+
* ```typescript
|
|
667
|
+
* const userController = new AbortController();
|
|
668
|
+
* const timeout = manager.createTimeoutSignal(30000);
|
|
669
|
+
*
|
|
670
|
+
* const combined = manager.createCombinedSignal([
|
|
671
|
+
* userController.signal,
|
|
672
|
+
* timeout.signal
|
|
673
|
+
* ]);
|
|
674
|
+
*
|
|
675
|
+
* // Request will be cancelled after 30s OR when user clicks cancel
|
|
676
|
+
* fetch('/api/data', { signal: combined.signal });
|
|
677
|
+
* ```
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* React component with cleanup:
|
|
681
|
+
* ```typescript
|
|
682
|
+
* useEffect(() => {
|
|
683
|
+
* const controller = new AbortController();
|
|
684
|
+
*
|
|
685
|
+
* const combined = manager.createCombinedSignal([
|
|
686
|
+
* controller.signal,
|
|
687
|
+
* unmountSignal // From component lifecycle
|
|
688
|
+
* ]);
|
|
689
|
+
*
|
|
690
|
+
* fetchData(combined.signal);
|
|
691
|
+
*
|
|
692
|
+
* return () => controller.abort(); // Cleanup
|
|
693
|
+
* }, []);
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
createCombinedSignal(signals) {
|
|
697
|
+
const controller = new AbortController();
|
|
698
|
+
for (const signal of signals) {
|
|
699
|
+
if (signal) {
|
|
700
|
+
if (signal.aborted) {
|
|
701
|
+
controller.abort(signal.reason);
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
signal.addEventListener(
|
|
705
|
+
"abort",
|
|
706
|
+
() => {
|
|
707
|
+
controller.abort(signal.reason);
|
|
708
|
+
},
|
|
709
|
+
{ once: true }
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return controller;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Creates an AbortController that automatically aborts after a specified timeout.
|
|
717
|
+
*
|
|
718
|
+
* This method creates a time-based cancellation mechanism useful for implementing
|
|
719
|
+
* request timeouts and deadlines. The signal will automatically abort after the
|
|
720
|
+
* specified duration, providing a consistent timeout experience.
|
|
721
|
+
*
|
|
722
|
+
* **Automatic Cleanup:**
|
|
723
|
+
* If the signal is aborted by other means before the timeout expires, the internal
|
|
724
|
+
* setTimeout is automatically cleared to prevent memory leaks.
|
|
725
|
+
*
|
|
726
|
+
* **Abort Reason:**
|
|
727
|
+
* The abort reason includes the timeout duration for debugging purposes:
|
|
728
|
+
* `"Request timeout after {timeout}ms"`
|
|
729
|
+
*
|
|
730
|
+
* @param timeout - Timeout duration in milliseconds
|
|
731
|
+
* @returns An AbortController that will abort after the timeout
|
|
732
|
+
*
|
|
733
|
+
* @example
|
|
734
|
+
* Simple request timeout:
|
|
735
|
+
* ```typescript
|
|
736
|
+
* const manager = new SignalManager();
|
|
737
|
+
* const timeout = manager.createTimeoutSignal(5000); // 5 seconds
|
|
738
|
+
*
|
|
739
|
+
* try {
|
|
740
|
+
* const response = await fetch('/api/slow-endpoint', {
|
|
741
|
+
* signal: timeout.signal
|
|
742
|
+
* });
|
|
743
|
+
* const data = await response.json();
|
|
744
|
+
* } catch (error) {
|
|
745
|
+
* if (error.name === 'AbortError') {
|
|
746
|
+
* console.error('Request timed out after 5 seconds');
|
|
747
|
+
* }
|
|
748
|
+
* }
|
|
749
|
+
* ```
|
|
750
|
+
*
|
|
751
|
+
* @example
|
|
752
|
+
* Different timeouts for different operations:
|
|
753
|
+
* ```typescript
|
|
754
|
+
* // Short timeout for quick operations
|
|
755
|
+
* const quickTimeout = manager.createTimeoutSignal(2000);
|
|
756
|
+
* await fetch('/api/health', { signal: quickTimeout.signal });
|
|
757
|
+
*
|
|
758
|
+
* // Long timeout for heavy operations
|
|
759
|
+
* const longTimeout = manager.createTimeoutSignal(60000);
|
|
760
|
+
* await fetch('/api/export', { signal: longTimeout.signal });
|
|
761
|
+
* ```
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* Manual cancellation before timeout:
|
|
765
|
+
* ```typescript
|
|
766
|
+
* const timeout = manager.createTimeoutSignal(30000);
|
|
767
|
+
*
|
|
768
|
+
* // If user cancels, timeout is automatically cleaned up
|
|
769
|
+
* timeout.abort('User cancelled');
|
|
770
|
+
* // Internal setTimeout is cleared, no memory leak
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
createTimeoutSignal(timeout) {
|
|
774
|
+
const controller = new AbortController();
|
|
775
|
+
const timeoutId = setTimeout(() => {
|
|
776
|
+
controller.abort(`Request timeout after ${timeout}ms`);
|
|
777
|
+
}, timeout);
|
|
778
|
+
controller.signal.addEventListener(
|
|
779
|
+
"abort",
|
|
780
|
+
() => {
|
|
781
|
+
clearTimeout(timeoutId);
|
|
782
|
+
},
|
|
783
|
+
{ once: true }
|
|
784
|
+
);
|
|
785
|
+
return controller;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// src/core/api/Utils/ResponseParser.ts
|
|
790
|
+
var ResponseParser = class {
|
|
791
|
+
/**
|
|
792
|
+
* Parses the HTTP response body into an appropriate JavaScript type.
|
|
793
|
+
*
|
|
794
|
+
* The parsing strategy is determined by the Content-Type header:
|
|
795
|
+
* 1. **JSON** (application/json): Calls `response.json()`
|
|
796
|
+
* 2. **Text** (text/*): Calls `response.text()`
|
|
797
|
+
* 3. **Binary** (application/octet-stream): Calls `response.blob()`
|
|
798
|
+
* 4. **Unknown**: Reads as text, attempts JSON parse, falls back to raw text
|
|
799
|
+
*
|
|
800
|
+
* **Fallback Behavior:**
|
|
801
|
+
* For responses without a Content-Type header or with unknown types, the parser
|
|
802
|
+
* attempts to parse as JSON first (common for APIs that don't set proper headers).
|
|
803
|
+
* If JSON parsing fails, it returns the raw text.
|
|
804
|
+
*
|
|
805
|
+
* @param response - The Fetch API Response object to parse
|
|
806
|
+
* @returns Promise resolving to the parsed response data
|
|
807
|
+
* @returns Can be: JSON object/array, string, or Blob depending on Content-Type
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* API response parsing:
|
|
811
|
+
* ```typescript
|
|
812
|
+
* const response = await fetch('/api/users');
|
|
813
|
+
* const data = await parser.parseResponse(response);
|
|
814
|
+
*
|
|
815
|
+
* if (typeof data === 'string') {
|
|
816
|
+
* console.log('Text response:', data);
|
|
817
|
+
* } else if (data instanceof Blob) {
|
|
818
|
+
* console.log('Binary response:', data.size, 'bytes');
|
|
819
|
+
* } else {
|
|
820
|
+
* console.log('JSON response:', data);
|
|
821
|
+
* }
|
|
822
|
+
* ```
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* Handling different content types:
|
|
826
|
+
* ```typescript
|
|
827
|
+
* // CSV file download
|
|
828
|
+
* const csvResponse = await fetch('/api/export.csv');
|
|
829
|
+
* const blob = await parser.parseResponse(csvResponse);
|
|
830
|
+
* // Returns Blob for download
|
|
831
|
+
*
|
|
832
|
+
* // JSON API
|
|
833
|
+
* const jsonResponse = await fetch('/api/users');
|
|
834
|
+
* const users = await parser.parseResponse(jsonResponse);
|
|
835
|
+
* // Returns parsed JSON array
|
|
836
|
+
*
|
|
837
|
+
* // Plain text logs
|
|
838
|
+
* const logResponse = await fetch('/api/logs');
|
|
839
|
+
* const logs = await parser.parseResponse(logResponse);
|
|
840
|
+
* // Returns string
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
async parseResponse(response) {
|
|
844
|
+
const contentType = response.headers.get("content-type");
|
|
845
|
+
if (_optionalChain([contentType, 'optionalAccess', _9 => _9.includes, 'call', _10 => _10("application/json")])) {
|
|
846
|
+
return response.json();
|
|
847
|
+
} else if (_optionalChain([contentType, 'optionalAccess', _11 => _11.includes, 'call', _12 => _12("text/")])) {
|
|
848
|
+
return response.text();
|
|
849
|
+
} else if (_optionalChain([contentType, 'optionalAccess', _13 => _13.includes, 'call', _14 => _14("application/octet-stream")])) {
|
|
850
|
+
return response.blob();
|
|
851
|
+
} else {
|
|
852
|
+
const text = await response.text();
|
|
853
|
+
try {
|
|
854
|
+
return JSON.parse(text);
|
|
855
|
+
} catch (e2) {
|
|
856
|
+
return text;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/core/api/Utils/UrlBuilder.ts
|
|
863
|
+
var UrlBuilder = class {
|
|
864
|
+
/**
|
|
865
|
+
* Builds a complete URL by combining base URL, endpoint, and query parameters.
|
|
866
|
+
*
|
|
867
|
+
* The URL construction process:
|
|
868
|
+
* 1. Combines `baseURL` and `endpoint` using URL API
|
|
869
|
+
* 2. Iterates through query parameters
|
|
870
|
+
* 3. Skips null/undefined values
|
|
871
|
+
* 4. Handles arrays by appending multiple values with same key
|
|
872
|
+
* 5. Converts all values to strings
|
|
873
|
+
* 6. Returns fully-qualified URL string
|
|
874
|
+
*
|
|
875
|
+
* **Path Handling:**
|
|
876
|
+
* The endpoint can be either relative or absolute:
|
|
877
|
+
* - Relative: `/users` → Combined with baseURL
|
|
878
|
+
* - Absolute: `https://other-api.com/users` → Uses absolute URL
|
|
879
|
+
*
|
|
880
|
+
* **Encoding:**
|
|
881
|
+
* All parameter values are automatically URL-encoded by the URL API,
|
|
882
|
+
* so special characters (spaces, &, =, etc.) are safely handled.
|
|
883
|
+
*
|
|
884
|
+
* @param baseURL - Base URL for the API (e.g., 'https://api.example.com')
|
|
885
|
+
* @param endpoint - API endpoint path relative to baseURL (e.g., '/users/123')
|
|
886
|
+
* @param params - Optional query parameters as key-value pairs
|
|
887
|
+
* @returns The fully-qualified URL string with encoded query parameters
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* Basic URL construction:
|
|
891
|
+
* ```typescript
|
|
892
|
+
* const url = builder.buildURL(
|
|
893
|
+
* 'https://api.example.com',
|
|
894
|
+
* '/search',
|
|
895
|
+
* { q: 'hello world', limit: 10 }
|
|
896
|
+
* );
|
|
897
|
+
* // => "https://api.example.com/search?q=hello+world&limit=10"
|
|
898
|
+
* ```
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* Array parameters:
|
|
902
|
+
* ```typescript
|
|
903
|
+
* const url = builder.buildURL(
|
|
904
|
+
* 'https://api.example.com',
|
|
905
|
+
* '/posts',
|
|
906
|
+
* { tags: ['javascript', 'typescript', 'react'] }
|
|
907
|
+
* );
|
|
908
|
+
* // => "https://api.example.com/posts?tags=javascript&tags=typescript&tags=react"
|
|
909
|
+
* ```
|
|
910
|
+
*
|
|
911
|
+
* @example
|
|
912
|
+
* Null/undefined handling:
|
|
913
|
+
* ```typescript
|
|
914
|
+
* const url = builder.buildURL(
|
|
915
|
+
* 'https://api.example.com',
|
|
916
|
+
* '/users',
|
|
917
|
+
* {
|
|
918
|
+
* name: 'John',
|
|
919
|
+
* age: null, // Skipped
|
|
920
|
+
* email: undefined // Skipped
|
|
921
|
+
* }
|
|
922
|
+
* );
|
|
923
|
+
* // => "https://api.example.com/users?name=John"
|
|
924
|
+
* ```
|
|
925
|
+
*
|
|
926
|
+
* @example
|
|
927
|
+
* Special characters encoding:
|
|
928
|
+
* ```typescript
|
|
929
|
+
* const url = builder.buildURL(
|
|
930
|
+
* 'https://api.example.com',
|
|
931
|
+
* '/search',
|
|
932
|
+
* { q: 'foo & bar', category: 'code/examples' }
|
|
933
|
+
* );
|
|
934
|
+
* // => "https://api.example.com/search?q=foo+%26+bar&category=code%2Fexamples"
|
|
935
|
+
* ```
|
|
936
|
+
*/
|
|
937
|
+
buildURL(baseURL, endpoint, params) {
|
|
938
|
+
const url = new URL(endpoint, baseURL);
|
|
939
|
+
if (params) {
|
|
940
|
+
Object.keys(params).forEach((key) => {
|
|
941
|
+
const value = params[key];
|
|
942
|
+
if (value !== void 0 && value !== null) {
|
|
943
|
+
if (Array.isArray(value)) {
|
|
944
|
+
value.forEach((v) => url.searchParams.append(key, String(v)));
|
|
945
|
+
} else {
|
|
946
|
+
url.searchParams.append(key, String(value));
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
return url.toString();
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
// src/core/api/ApiClient.ts
|
|
956
|
+
var ApiClient = (_class3 = class {
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
__init6() {this.interceptorManager = new InterceptorManager()}
|
|
960
|
+
__init7() {this.signalManager = new SignalManager()}
|
|
961
|
+
__init8() {this.errorNormalizer = new ErrorNormalizer()}
|
|
962
|
+
__init9() {this.responseParser = new ResponseParser()}
|
|
963
|
+
__init10() {this.urlBuilder = new UrlBuilder()}
|
|
964
|
+
__init11() {this.retryHandler = new RetryHandler()}
|
|
965
|
+
__init12() {this.requestManager = new RequestManager()}
|
|
966
|
+
__init13() {this.authToken = null}
|
|
967
|
+
__init14() {this.refreshTokenPromise = null}
|
|
968
|
+
__init15() {this.correlationIdPrefix = "api"}
|
|
969
|
+
__init16() {this.includeCorrelationId = true}
|
|
970
|
+
/**
|
|
971
|
+
* Creates a new API client instance
|
|
972
|
+
* @param baseURL - Base URL for all API requests (default: empty string for relative URLs)
|
|
973
|
+
* @param defaultTimeout - Default request timeout in milliseconds (default: 30000)
|
|
974
|
+
*/
|
|
975
|
+
constructor(baseURL = "", defaultTimeout = 3e4) {;_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);
|
|
976
|
+
this.baseURL = baseURL;
|
|
977
|
+
this.defaultTimeout = defaultTimeout;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Sets the prefix for auto-generated correlation IDs
|
|
981
|
+
* @param prefix - The prefix to use for correlation IDs (e.g., 'api', 'web', 'mobile')
|
|
982
|
+
*/
|
|
983
|
+
setCorrelationIdPrefix(prefix) {
|
|
984
|
+
this.correlationIdPrefix = prefix;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Enables or disables automatic correlation ID generation
|
|
988
|
+
* @param include - Whether to include correlation IDs in requests
|
|
989
|
+
*/
|
|
990
|
+
setIncludeCorrelationId(include) {
|
|
991
|
+
this.includeCorrelationId = include;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Registers a request interceptor to modify requests before they're sent
|
|
995
|
+
* @param interceptor - Function to intercept and potentially modify request config
|
|
996
|
+
* @returns Function to unregister this interceptor
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* ```typescript
|
|
1000
|
+
* const unregister = client.addRequestInterceptor(async (config) => {
|
|
1001
|
+
* config.headers = config.headers || new Headers();
|
|
1002
|
+
* config.headers.set('X-Client-Version', '1.0.0');
|
|
1003
|
+
* return config;
|
|
1004
|
+
* });
|
|
1005
|
+
*
|
|
1006
|
+
* // Later, to remove the interceptor:
|
|
1007
|
+
* unregister();
|
|
1008
|
+
* ```
|
|
1009
|
+
*/
|
|
1010
|
+
addRequestInterceptor(interceptor) {
|
|
1011
|
+
return this.interceptorManager.addRequestInterceptor(interceptor);
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Registers a response interceptor to modify responses before they're returned
|
|
1015
|
+
* @param interceptor - Function to intercept and potentially modify responses
|
|
1016
|
+
* @returns Function to unregister this interceptor
|
|
1017
|
+
*
|
|
1018
|
+
* @example
|
|
1019
|
+
* ```typescript
|
|
1020
|
+
* client.addResponseInterceptor(async (response) => {
|
|
1021
|
+
* // Transform data format
|
|
1022
|
+
* if (response.data) {
|
|
1023
|
+
* response.data = camelCaseKeys(response.data);
|
|
1024
|
+
* }
|
|
1025
|
+
* return response;
|
|
1026
|
+
* });
|
|
1027
|
+
* ```
|
|
1028
|
+
*/
|
|
1029
|
+
addResponseInterceptor(interceptor) {
|
|
1030
|
+
return this.interceptorManager.addResponseInterceptor(interceptor);
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Registers an error interceptor to handle or transform errors
|
|
1034
|
+
* @param interceptor - Function to intercept and potentially modify errors
|
|
1035
|
+
* @returns Function to unregister this interceptor
|
|
1036
|
+
*
|
|
1037
|
+
* @example
|
|
1038
|
+
* ```typescript
|
|
1039
|
+
* client.addErrorInterceptor(async (error) => {
|
|
1040
|
+
* // Log errors to monitoring service
|
|
1041
|
+
* if (error.status >= 500) {
|
|
1042
|
+
* await monitoringService.logError(error);
|
|
1043
|
+
* }
|
|
1044
|
+
* return error; // Re-throw the error
|
|
1045
|
+
* });
|
|
1046
|
+
* ```
|
|
1047
|
+
*/
|
|
1048
|
+
addErrorInterceptor(interceptor) {
|
|
1049
|
+
return this.interceptorManager.addErrorInterceptor(interceptor);
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Sets the authentication token for subsequent requests
|
|
1053
|
+
* @param token - JWT token or null to clear authentication
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```typescript
|
|
1057
|
+
* // Set token after login
|
|
1058
|
+
* client.setAuthToken(loginResponse.accessToken);
|
|
1059
|
+
*
|
|
1060
|
+
* // Clear token on logout
|
|
1061
|
+
* client.setAuthToken(null);
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
1064
|
+
setAuthToken(token) {
|
|
1065
|
+
this.authToken = token;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Retrieves the current authentication token
|
|
1069
|
+
* @returns The current auth token or null if not set
|
|
1070
|
+
*/
|
|
1071
|
+
getAuthToken() {
|
|
1072
|
+
return this.authToken;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Cancels a specific request by its key
|
|
1076
|
+
* @param key - The unique key identifying the request to cancel
|
|
1077
|
+
*/
|
|
1078
|
+
cancelRequest(key) {
|
|
1079
|
+
this.requestManager.cancel(key);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Cancels all pending requests
|
|
1083
|
+
* Useful for cleanup on navigation or component unmount
|
|
1084
|
+
*/
|
|
1085
|
+
cancelAllRequests() {
|
|
1086
|
+
this.requestManager.cancelAll();
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Core request method that handles all HTTP operations
|
|
1090
|
+
* @template T - The expected response data type
|
|
1091
|
+
* @param endpoint - API endpoint relative to baseURL
|
|
1092
|
+
* @param config - Request configuration options
|
|
1093
|
+
* @returns Promise resolving to ApiResponse with data or error
|
|
1094
|
+
*
|
|
1095
|
+
* @example
|
|
1096
|
+
* ```typescript
|
|
1097
|
+
* const response = await client.request<User>('/users/123', {
|
|
1098
|
+
* method: 'GET',
|
|
1099
|
+
* timeout: 5000,
|
|
1100
|
+
* throwErrors: false
|
|
1101
|
+
* });
|
|
1102
|
+
* ```
|
|
1103
|
+
*/
|
|
1104
|
+
async request(endpoint, config = {}) {
|
|
1105
|
+
const correlationId = config.correlationId || (!config.skipCorrelationId && this.includeCorrelationId ? generateCorrelationId(this.correlationIdPrefix) : void 0);
|
|
1106
|
+
const requestKey = `${config.method || "GET"}_${endpoint}_${Date.now()}`;
|
|
1107
|
+
const masterController = new AbortController();
|
|
1108
|
+
try {
|
|
1109
|
+
const signals = [
|
|
1110
|
+
config.signal,
|
|
1111
|
+
_optionalChain([config, 'access', _15 => _15.cancelToken, 'optionalAccess', _16 => _16.signal]),
|
|
1112
|
+
masterController.signal
|
|
1113
|
+
];
|
|
1114
|
+
const timeout = config.timeout || this.defaultTimeout;
|
|
1115
|
+
const timeoutController = this.signalManager.createTimeoutSignal(timeout);
|
|
1116
|
+
signals.push(timeoutController.signal);
|
|
1117
|
+
const combinedController = this.signalManager.createCombinedSignal(signals);
|
|
1118
|
+
if (correlationId) {
|
|
1119
|
+
this.requestManager.add(requestKey, masterController, correlationId);
|
|
1120
|
+
}
|
|
1121
|
+
const finalConfig = await this.interceptorManager.applyRequestInterceptors({
|
|
1122
|
+
...config,
|
|
1123
|
+
signal: combinedController.signal,
|
|
1124
|
+
correlationId
|
|
1125
|
+
});
|
|
1126
|
+
const url = this.urlBuilder.buildURL(
|
|
1127
|
+
this.baseURL,
|
|
1128
|
+
endpoint,
|
|
1129
|
+
finalConfig.params
|
|
1130
|
+
);
|
|
1131
|
+
const headers = new Headers(finalConfig.headers);
|
|
1132
|
+
if (correlationId) {
|
|
1133
|
+
headers.set("X-Correlation-Id", correlationId);
|
|
1134
|
+
headers.set("X-Request-Id", correlationId);
|
|
1135
|
+
}
|
|
1136
|
+
if (this.authToken && !finalConfig.skipAuthRefresh) {
|
|
1137
|
+
headers.set("Authorization", `Bearer ${this.authToken}`);
|
|
1138
|
+
}
|
|
1139
|
+
if (finalConfig.body && typeof finalConfig.body === "object" && !(finalConfig.body instanceof FormData)) {
|
|
1140
|
+
headers.set("Content-Type", "application/json");
|
|
1141
|
+
finalConfig.body = JSON.stringify(finalConfig.body);
|
|
1142
|
+
}
|
|
1143
|
+
finalConfig.headers = headers;
|
|
1144
|
+
const fetchPromise = async () => {
|
|
1145
|
+
try {
|
|
1146
|
+
const response = await fetch(url, {
|
|
1147
|
+
...finalConfig,
|
|
1148
|
+
signal: combinedController.signal
|
|
1149
|
+
});
|
|
1150
|
+
const responseData = await this.responseParser.parseResponse(response);
|
|
1151
|
+
if (!response.ok) {
|
|
1152
|
+
const error = Object.assign(
|
|
1153
|
+
new Error(
|
|
1154
|
+
responseData.title || `HTTP ${response.status}: ${response.statusText}`
|
|
1155
|
+
),
|
|
1156
|
+
{
|
|
1157
|
+
type: responseData.type || this.errorNormalizer.getErrorType(response.status),
|
|
1158
|
+
title: responseData.title || this.errorNormalizer.getErrorTitle(response.status),
|
|
1159
|
+
status: response.status,
|
|
1160
|
+
traceId: responseData.traceId || correlationId,
|
|
1161
|
+
errors: responseData.errors,
|
|
1162
|
+
isAborted: false,
|
|
1163
|
+
config: finalConfig
|
|
1164
|
+
}
|
|
1165
|
+
);
|
|
1166
|
+
if (finalConfig.throwErrors !== false) {
|
|
1167
|
+
throw error;
|
|
1168
|
+
} else {
|
|
1169
|
+
return await this.interceptorManager.applyResponseInterceptors({
|
|
1170
|
+
error
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
const apiResponse = {
|
|
1175
|
+
data: responseData
|
|
1176
|
+
};
|
|
1177
|
+
return await this.interceptorManager.applyResponseInterceptors(
|
|
1178
|
+
apiResponse
|
|
1179
|
+
);
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
if (error.name === "AbortError") {
|
|
1182
|
+
const abortError = Object.assign(
|
|
1183
|
+
new Error(error.message || "Request aborted"),
|
|
1184
|
+
{
|
|
1185
|
+
type: "request_cancelled",
|
|
1186
|
+
title: "Request was cancelled",
|
|
1187
|
+
status: 0,
|
|
1188
|
+
traceId: correlationId,
|
|
1189
|
+
isAborted: true,
|
|
1190
|
+
config: finalConfig
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
if (finalConfig.throwErrors !== false) {
|
|
1194
|
+
throw abortError;
|
|
1195
|
+
} else {
|
|
1196
|
+
return await this.interceptorManager.applyResponseInterceptors({
|
|
1197
|
+
error: abortError
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
throw error;
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
if (finalConfig.retries && finalConfig.retries > 0) {
|
|
1205
|
+
return await this.retryHandler.retryRequest(
|
|
1206
|
+
fetchPromise,
|
|
1207
|
+
finalConfig.retries,
|
|
1208
|
+
finalConfig.retryDelay || 1e3,
|
|
1209
|
+
combinedController.signal
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
return await fetchPromise();
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
const apiError = this.errorNormalizer.normalizeError(
|
|
1215
|
+
error,
|
|
1216
|
+
config,
|
|
1217
|
+
correlationId
|
|
1218
|
+
);
|
|
1219
|
+
if (config.throwErrors !== false) {
|
|
1220
|
+
await this.interceptorManager.applyErrorInterceptors(apiError);
|
|
1221
|
+
throw apiError;
|
|
1222
|
+
} else {
|
|
1223
|
+
return {
|
|
1224
|
+
error: apiError
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
} finally {
|
|
1228
|
+
this.requestManager.remove(requestKey);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Performs a GET request
|
|
1233
|
+
* @template T - The expected response data type
|
|
1234
|
+
* @param endpoint - API endpoint
|
|
1235
|
+
* @param config - Optional request configuration
|
|
1236
|
+
* @returns Promise resolving to ApiResponse
|
|
1237
|
+
*
|
|
1238
|
+
* @example
|
|
1239
|
+
* ```typescript
|
|
1240
|
+
* const { data, error } = await client.get<User[]>('/users', {
|
|
1241
|
+
* params: { active: true },
|
|
1242
|
+
* timeout: 5000
|
|
1243
|
+
* });
|
|
1244
|
+
* ```
|
|
1245
|
+
*/
|
|
1246
|
+
get(endpoint, config) {
|
|
1247
|
+
return this.request(endpoint, { ...config, method: "GET" });
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* Performs a POST request
|
|
1251
|
+
* @template T - The expected response data type
|
|
1252
|
+
* @template TData - The request body data type
|
|
1253
|
+
* @param endpoint - API endpoint
|
|
1254
|
+
* @param data - Request body data
|
|
1255
|
+
* @param config - Optional request configuration
|
|
1256
|
+
* @returns Promise resolving to ApiResponse
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```typescript
|
|
1260
|
+
* const { data, error } = await client.post<User, CreateUserDto>('/users', {
|
|
1261
|
+
* name: 'John Doe',
|
|
1262
|
+
* email: 'john@example.com'
|
|
1263
|
+
* });
|
|
1264
|
+
* ```
|
|
1265
|
+
*/
|
|
1266
|
+
post(endpoint, data, config) {
|
|
1267
|
+
return this.request(endpoint, { ...config, method: "POST", body: data });
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Performs a PUT request
|
|
1271
|
+
* @template T - The expected response data type
|
|
1272
|
+
* @template TData - The request body data type
|
|
1273
|
+
* @param endpoint - API endpoint
|
|
1274
|
+
* @param data - Request body data
|
|
1275
|
+
* @param config - Optional request configuration
|
|
1276
|
+
* @returns Promise resolving to ApiResponse
|
|
1277
|
+
*
|
|
1278
|
+
* @example
|
|
1279
|
+
* ```typescript
|
|
1280
|
+
* const { data, error } = await client.put<User, UpdateUserDto>(
|
|
1281
|
+
* '/users/123',
|
|
1282
|
+
* { name: 'Jane Doe' }
|
|
1283
|
+
* );
|
|
1284
|
+
* ```
|
|
1285
|
+
*/
|
|
1286
|
+
put(endpoint, data, config) {
|
|
1287
|
+
return this.request(endpoint, { ...config, method: "PUT", body: data });
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Performs a PATCH request
|
|
1291
|
+
* @template T - The expected response data type
|
|
1292
|
+
* @template TData - The request body data type
|
|
1293
|
+
* @param endpoint - API endpoint
|
|
1294
|
+
* @param data - Request body data
|
|
1295
|
+
* @param config - Optional request configuration
|
|
1296
|
+
* @returns Promise resolving to ApiResponse
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```typescript
|
|
1300
|
+
* const { data, error } = await client.patch<User>(
|
|
1301
|
+
* '/users/123',
|
|
1302
|
+
* { status: 'active' }
|
|
1303
|
+
* );
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
patch(endpoint, data, config) {
|
|
1307
|
+
return this.request(endpoint, {
|
|
1308
|
+
...config,
|
|
1309
|
+
method: "PATCH",
|
|
1310
|
+
body: data
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Performs a DELETE request
|
|
1315
|
+
* @template T - The expected response data type
|
|
1316
|
+
* @param endpoint - API endpoint
|
|
1317
|
+
* @param config - Optional request configuration
|
|
1318
|
+
* @returns Promise resolving to ApiResponse
|
|
1319
|
+
*
|
|
1320
|
+
* @example
|
|
1321
|
+
* ```typescript
|
|
1322
|
+
* const { error } = await client.delete('/users/123');
|
|
1323
|
+
* if (!error) {
|
|
1324
|
+
* console.log('User deleted successfully');
|
|
1325
|
+
* }
|
|
1326
|
+
* ```
|
|
1327
|
+
*/
|
|
1328
|
+
delete(endpoint, config) {
|
|
1329
|
+
return this.request(endpoint, { ...config, method: "DELETE" });
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Performs a filtered list request with pagination and sorting
|
|
1333
|
+
* @template TListModel - The type of individual list items
|
|
1334
|
+
* @template TFilter - The filter criteria type
|
|
1335
|
+
* @param url - API endpoint
|
|
1336
|
+
* @param data - Pagination and filter data
|
|
1337
|
+
* @param config - Optional request configuration
|
|
1338
|
+
* @returns Promise resolving to paginated list response
|
|
1339
|
+
*
|
|
1340
|
+
* @example
|
|
1341
|
+
* ```typescript
|
|
1342
|
+
* const { data, error } = await client.filter<User, UserFilter>(
|
|
1343
|
+
* '/users/filter',
|
|
1344
|
+
* {
|
|
1345
|
+
* pageOffset: 0,
|
|
1346
|
+
* pageSize: 20,
|
|
1347
|
+
* sortField: 'createdAt',
|
|
1348
|
+
* sortOrder: 'desc',
|
|
1349
|
+
* filterModel: { status: 'active' }
|
|
1350
|
+
* }
|
|
1351
|
+
* );
|
|
1352
|
+
*
|
|
1353
|
+
* if (data) {
|
|
1354
|
+
* console.log(`Found ${data.Total} users`);
|
|
1355
|
+
* console.log('Users:', data.Data);
|
|
1356
|
+
* }
|
|
1357
|
+
* ```
|
|
1358
|
+
*/
|
|
1359
|
+
filter(url, data, config) {
|
|
1360
|
+
const mergedData = { ...data, ...data.filterModel };
|
|
1361
|
+
return this.request(url, {
|
|
1362
|
+
...config,
|
|
1363
|
+
method: "POST",
|
|
1364
|
+
body: mergedData
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
}, _class3);
|
|
1368
|
+
|
|
1369
|
+
// src/core/api/createApiClient.ts
|
|
1370
|
+
var globalApiClient = null;
|
|
1371
|
+
function createApiClient(config = {}) {
|
|
1372
|
+
const {
|
|
1373
|
+
baseURL = import.meta.env.VITE_API_URL,
|
|
1374
|
+
timeout = 3e4,
|
|
1375
|
+
correlationIdPrefix = "api",
|
|
1376
|
+
includeCorrelationId = true,
|
|
1377
|
+
requestInterceptors = [],
|
|
1378
|
+
responseInterceptors = [],
|
|
1379
|
+
errorInterceptors = []
|
|
1380
|
+
} = config;
|
|
1381
|
+
const client = new ApiClient(baseURL, timeout);
|
|
1382
|
+
client.addRequestInterceptor((config2) => {
|
|
1383
|
+
const token = localStorage.getItem("serviceToken");
|
|
1384
|
+
if (token && !config2.skipAuthRefresh) {
|
|
1385
|
+
config2.headers = {
|
|
1386
|
+
...config2.headers,
|
|
1387
|
+
Authorization: `Bearer ${token}`
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
return config2;
|
|
1391
|
+
});
|
|
1392
|
+
client.setCorrelationIdPrefix(correlationIdPrefix);
|
|
1393
|
+
client.setIncludeCorrelationId(includeCorrelationId);
|
|
1394
|
+
requestInterceptors.forEach((interceptor) => {
|
|
1395
|
+
client.addRequestInterceptor(interceptor);
|
|
1396
|
+
});
|
|
1397
|
+
responseInterceptors.forEach((interceptor) => {
|
|
1398
|
+
client.addResponseInterceptor(interceptor);
|
|
1399
|
+
});
|
|
1400
|
+
errorInterceptors.forEach((interceptor) => {
|
|
1401
|
+
client.addErrorInterceptor(interceptor);
|
|
1402
|
+
});
|
|
1403
|
+
return client;
|
|
1404
|
+
}
|
|
1405
|
+
function getGlobalApiClient(config) {
|
|
1406
|
+
if (!globalApiClient) {
|
|
1407
|
+
globalApiClient = createApiClient(config);
|
|
1408
|
+
}
|
|
1409
|
+
return globalApiClient;
|
|
1410
|
+
}
|
|
1411
|
+
function setGlobalApiClient(client) {
|
|
1412
|
+
globalApiClient = client;
|
|
1413
|
+
}
|
|
1414
|
+
function resetGlobalApiClient() {
|
|
1415
|
+
globalApiClient = null;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// src/core/api/types/CancelToken.ts
|
|
1419
|
+
var CancelToken = class _CancelToken {
|
|
1420
|
+
|
|
1421
|
+
|
|
1422
|
+
|
|
1423
|
+
constructor() {
|
|
1424
|
+
this.abortController = new AbortController();
|
|
1425
|
+
this.cancelPromise = new Promise((resolve) => {
|
|
1426
|
+
this.cancelResolve = resolve;
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
get signal() {
|
|
1430
|
+
return this.abortController.signal;
|
|
1431
|
+
}
|
|
1432
|
+
cancel(reason) {
|
|
1433
|
+
this.abortController.abort(reason);
|
|
1434
|
+
_optionalChain([this, 'access', _17 => _17.cancelResolve, 'optionalCall', _18 => _18()]);
|
|
1435
|
+
}
|
|
1436
|
+
get isCancelled() {
|
|
1437
|
+
return this.abortController.signal.aborted;
|
|
1438
|
+
}
|
|
1439
|
+
throwIfCancelled() {
|
|
1440
|
+
if (this.isCancelled) {
|
|
1441
|
+
throw new Error("Request cancelled");
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
static source() {
|
|
1445
|
+
const token = new _CancelToken();
|
|
1446
|
+
return {
|
|
1447
|
+
token,
|
|
1448
|
+
cancel: (reason) => token.cancel(reason)
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
// src/core/api/useValidationErrors.ts
|
|
1454
|
+
var _react = require('react');
|
|
1455
|
+
function useValidationErrors(error) {
|
|
1456
|
+
const getFieldError = _react.useCallback.call(void 0,
|
|
1457
|
+
(field) => {
|
|
1458
|
+
if (!_optionalChain([error, 'optionalAccess', _19 => _19.errors]) || !error.errors[field]) return null;
|
|
1459
|
+
const fieldError = error.errors[field];
|
|
1460
|
+
if (typeof fieldError === "string") return fieldError;
|
|
1461
|
+
if (Array.isArray(fieldError)) return fieldError[0];
|
|
1462
|
+
if (typeof fieldError === "object" && "message" in fieldError) {
|
|
1463
|
+
return fieldError.message;
|
|
1464
|
+
}
|
|
1465
|
+
return null;
|
|
1466
|
+
},
|
|
1467
|
+
[error]
|
|
1468
|
+
);
|
|
1469
|
+
const hasFieldError = _react.useCallback.call(void 0,
|
|
1470
|
+
(field) => {
|
|
1471
|
+
return !!getFieldError(field);
|
|
1472
|
+
},
|
|
1473
|
+
[getFieldError]
|
|
1474
|
+
);
|
|
1475
|
+
const getAllErrors = _react.useCallback.call(void 0, () => {
|
|
1476
|
+
if (!_optionalChain([error, 'optionalAccess', _20 => _20.errors])) return {};
|
|
1477
|
+
const result = {};
|
|
1478
|
+
Object.entries(error.errors).forEach(([key, value]) => {
|
|
1479
|
+
if (typeof value === "string") {
|
|
1480
|
+
result[key] = value;
|
|
1481
|
+
} else if (Array.isArray(value)) {
|
|
1482
|
+
result[key] = value.join(", ");
|
|
1483
|
+
} else if (typeof value === "object" && value && "message" in value) {
|
|
1484
|
+
result[key] = value.message;
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
return result;
|
|
1488
|
+
}, [error]);
|
|
1489
|
+
return {
|
|
1490
|
+
getFieldError,
|
|
1491
|
+
hasFieldError,
|
|
1492
|
+
getAllErrors,
|
|
1493
|
+
hasErrors: _optionalChain([error, 'optionalAccess', _21 => _21.errors])
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/core/components/AuthorizedView/AuthorizedView.tsx
|
|
1498
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
1499
|
+
var AuthorizedView = ({ children, show }) => {
|
|
1500
|
+
if (!show) return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, {});
|
|
1501
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children });
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// src/core/components/CancelButton/CancelButton.tsx
|
|
1505
|
+
var _material = require('@mui/material');
|
|
1506
|
+
|
|
1507
|
+
var CancelButton = ({
|
|
1508
|
+
children = "Cancel",
|
|
1509
|
+
variant = "outlined",
|
|
1510
|
+
sx,
|
|
1511
|
+
...rest
|
|
1512
|
+
}) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Button, { variant, sx: { width: "6rem", ...sx }, ...rest, children });
|
|
1513
|
+
|
|
1514
|
+
// src/core/components/ClearButton/ClearButton.tsx
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
var ClearButton = ({
|
|
1518
|
+
isSubmitting,
|
|
1519
|
+
handleClear,
|
|
1520
|
+
sx,
|
|
1521
|
+
storeKey
|
|
1522
|
+
}) => {
|
|
1523
|
+
const onClick = () => {
|
|
1524
|
+
handleClear();
|
|
1525
|
+
if (storeKey != null) {
|
|
1526
|
+
localStorage.removeItem(storeKey);
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1530
|
+
_material.Button,
|
|
1531
|
+
{
|
|
1532
|
+
variant: "outlined",
|
|
1533
|
+
onClick,
|
|
1534
|
+
disabled: isSubmitting,
|
|
1535
|
+
sx,
|
|
1536
|
+
children: "Clear"
|
|
1537
|
+
}
|
|
1538
|
+
);
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1541
|
+
// src/core/components/Containers/SimpleContainer.tsx
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
var SimpleContainer = ({
|
|
1545
|
+
children,
|
|
1546
|
+
className,
|
|
1547
|
+
sx
|
|
1548
|
+
}) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Container, { className, sx: { ...sx }, children });
|
|
1549
|
+
|
|
1550
|
+
// src/core/components/FilterButton/FilterButton.tsx
|
|
1551
|
+
var _FilterAlt = require('@mui/icons-material/FilterAlt'); var _FilterAlt2 = _interopRequireDefault(_FilterAlt);
|
|
1552
|
+
var _lab = require('@mui/lab');
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
var FilterButton = ({
|
|
1556
|
+
isSubmitting,
|
|
1557
|
+
show,
|
|
1558
|
+
title,
|
|
1559
|
+
icon,
|
|
1560
|
+
sx,
|
|
1561
|
+
iconSx
|
|
1562
|
+
}) => {
|
|
1563
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1564
|
+
_lab.LoadingButton,
|
|
1565
|
+
{
|
|
1566
|
+
type: "submit",
|
|
1567
|
+
variant: "contained",
|
|
1568
|
+
loading: isSubmitting,
|
|
1569
|
+
disabled: !show,
|
|
1570
|
+
disableRipple: true,
|
|
1571
|
+
color: "primary",
|
|
1572
|
+
sx: {
|
|
1573
|
+
display: "flex",
|
|
1574
|
+
alignItems: "center",
|
|
1575
|
+
...sx
|
|
1576
|
+
},
|
|
1577
|
+
startIcon: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Badge, { color: "error", variant: "standard", children: icon ? icon : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _FilterAlt2.default, { width: "20", height: "20", sx: iconSx }) }),
|
|
1578
|
+
children: _optionalChain([title, 'optionalAccess', _22 => _22.trim, 'call', _23 => _23()]) === "" || !title ? "Filter" : title
|
|
1579
|
+
}
|
|
1580
|
+
);
|
|
1581
|
+
};
|
|
1582
|
+
|
|
1583
|
+
// src/core/components/FilterDisplay/FilterChip.tsx
|
|
1584
|
+
var _Chip = require('@mui/material/Chip'); var _Chip2 = _interopRequireDefault(_Chip);
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
var FilterChip = _react.memo.call(void 0,
|
|
1588
|
+
({
|
|
1589
|
+
fieldKey,
|
|
1590
|
+
filter,
|
|
1591
|
+
onDelete
|
|
1592
|
+
}) => {
|
|
1593
|
+
const hasValue = filter.Value !== null && filter.Value !== void 0 && filter.Value !== "";
|
|
1594
|
+
const label = `${fieldKey.replace("PK", "")}: ${filter.Label}`;
|
|
1595
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1596
|
+
_Chip2.default,
|
|
1597
|
+
{
|
|
1598
|
+
label,
|
|
1599
|
+
variant: hasValue ? "filled" : "outlined",
|
|
1600
|
+
size: "small",
|
|
1601
|
+
onDelete: hasValue ? onDelete : void 0
|
|
1602
|
+
},
|
|
1603
|
+
fieldKey
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
);
|
|
1607
|
+
FilterChip.displayName = "FilterChip";
|
|
1608
|
+
|
|
1609
|
+
// src/core/components/FilterDisplay/FilterDisplay.tsx
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
var ProgramsFilterDisplay = _react.memo.call(void 0,
|
|
1614
|
+
(props) => {
|
|
1615
|
+
const { friendlyFilter, onFriendlyFilterChange } = props;
|
|
1616
|
+
const deleteHandlers = _react.useMemo.call(void 0, () => {
|
|
1617
|
+
if (!onFriendlyFilterChange) return {};
|
|
1618
|
+
const handlers = {};
|
|
1619
|
+
for (const key of Object.keys(friendlyFilter)) {
|
|
1620
|
+
handlers[key] = () => onFriendlyFilterChange(key);
|
|
1621
|
+
}
|
|
1622
|
+
return handlers;
|
|
1623
|
+
}, [onFriendlyFilterChange, friendlyFilter]);
|
|
1624
|
+
const chipList = _react.useMemo.call(void 0, () => {
|
|
1625
|
+
return Object.entries(friendlyFilter).map(([key, filter]) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1626
|
+
FilterChip,
|
|
1627
|
+
{
|
|
1628
|
+
fieldKey: key,
|
|
1629
|
+
filter,
|
|
1630
|
+
onDelete: deleteHandlers[key]
|
|
1631
|
+
},
|
|
1632
|
+
key
|
|
1633
|
+
));
|
|
1634
|
+
}, [friendlyFilter, deleteHandlers]);
|
|
1635
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Card, { sx: { mb: 2 }, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _material.CardContent, { children: [
|
|
1636
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Typography, { variant: "h6", gutterBottom: true, children: "Active Filters" }),
|
|
1637
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Box, { display: "flex", gap: 1, flexWrap: "wrap", children: chipList })
|
|
1638
|
+
] }) });
|
|
1639
|
+
}
|
|
1640
|
+
);
|
|
1641
|
+
ProgramsFilterDisplay.displayName = "FilterDisplay";
|
|
1642
|
+
|
|
1643
|
+
// src/core/components/FilterWrapper/FilterWrapper.tsx
|
|
1644
|
+
var _ManageSearch = require('@mui/icons-material/ManageSearch'); var _ManageSearch2 = _interopRequireDefault(_ManageSearch);
|
|
1645
|
+
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
|
|
1649
|
+
|
|
1650
|
+
|
|
1651
|
+
|
|
1652
|
+
|
|
1653
|
+
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
var FilterWrapper = ({
|
|
1657
|
+
children,
|
|
1658
|
+
title,
|
|
1659
|
+
filterCount,
|
|
1660
|
+
cardSx,
|
|
1661
|
+
textSx,
|
|
1662
|
+
icon,
|
|
1663
|
+
iconSx,
|
|
1664
|
+
showCount
|
|
1665
|
+
}) => {
|
|
1666
|
+
const theme = _material.useTheme.call(void 0, );
|
|
1667
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1668
|
+
_material.Card,
|
|
1669
|
+
{
|
|
1670
|
+
sx: {
|
|
1671
|
+
position: "relative",
|
|
1672
|
+
borderRadius: "0px",
|
|
1673
|
+
mb: 2,
|
|
1674
|
+
...cardSx
|
|
1675
|
+
},
|
|
1676
|
+
children: [
|
|
1677
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1678
|
+
_material.CardHeader,
|
|
1679
|
+
{
|
|
1680
|
+
sx: {
|
|
1681
|
+
display: "flex",
|
|
1682
|
+
flexWrap: "wrap",
|
|
1683
|
+
p: "1rem",
|
|
1684
|
+
".MuiCardHeader-action": {
|
|
1685
|
+
margin: 0,
|
|
1686
|
+
alignSelf: "center"
|
|
1687
|
+
},
|
|
1688
|
+
alignItems: "center"
|
|
1689
|
+
},
|
|
1690
|
+
title: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _material.Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
|
|
1691
|
+
icon ? icon : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1692
|
+
_ManageSearch2.default,
|
|
1693
|
+
{
|
|
1694
|
+
sx: {
|
|
1695
|
+
height: "2.5rem",
|
|
1696
|
+
color: theme.palette.primary.main,
|
|
1697
|
+
...iconSx
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
),
|
|
1701
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1702
|
+
_material.Typography,
|
|
1703
|
+
{
|
|
1704
|
+
variant: "h5",
|
|
1705
|
+
sx: {
|
|
1706
|
+
fontWeight: "bold",
|
|
1707
|
+
color: theme.palette.primary.main,
|
|
1708
|
+
...textSx
|
|
1709
|
+
},
|
|
1710
|
+
children: [
|
|
1711
|
+
title ? title : "Filter",
|
|
1712
|
+
" ",
|
|
1713
|
+
showCount ? `(${filterCount ? filterCount : 0})` : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, {})
|
|
1714
|
+
]
|
|
1715
|
+
}
|
|
1716
|
+
)
|
|
1717
|
+
] })
|
|
1718
|
+
}
|
|
1719
|
+
),
|
|
1720
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Divider, {}),
|
|
1721
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.CardContent, { sx: { py: 2 }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Grid, { container: true, spacing: 2, children }) })
|
|
1722
|
+
]
|
|
1723
|
+
}
|
|
1724
|
+
);
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
// src/core/components/Footer/Footer.tsx
|
|
1728
|
+
|
|
1729
|
+
|
|
1730
|
+
var Footer = () => {
|
|
1731
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
1732
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1733
|
+
_material.Box,
|
|
1734
|
+
{
|
|
1735
|
+
component: "footer",
|
|
1736
|
+
sx: {
|
|
1737
|
+
py: 2,
|
|
1738
|
+
px: 4,
|
|
1739
|
+
mt: "auto",
|
|
1740
|
+
backgroundColor: (theme) => theme.palette.mode === "light" ? theme.palette.grey[200] : theme.palette.grey[800]
|
|
1741
|
+
},
|
|
1742
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Typography, { variant: "body2", color: "text.secondary", align: "center", children: `\xA9 Copyright ${currentYear} GN. All rights reserved by Parul University.` })
|
|
1743
|
+
}
|
|
1744
|
+
);
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
// src/core/components/LabelText/LabelText.tsx
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
var LabelText = ({
|
|
1751
|
+
label,
|
|
1752
|
+
value,
|
|
1753
|
+
gridSize,
|
|
1754
|
+
containerSize,
|
|
1755
|
+
labelSx,
|
|
1756
|
+
valueSx
|
|
1757
|
+
}) => {
|
|
1758
|
+
const defaultGridSize = {
|
|
1759
|
+
labelSize: { xs: 6, sm: 6, md: 6 },
|
|
1760
|
+
valueSize: { xs: 12, sm: 6, md: 6 }
|
|
1761
|
+
};
|
|
1762
|
+
const defaultContainerSize = { xs: 12, sm: 6, md: 6 };
|
|
1763
|
+
const size = gridSize || defaultGridSize;
|
|
1764
|
+
const container = containerSize || defaultContainerSize;
|
|
1765
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1766
|
+
_material.Grid,
|
|
1767
|
+
{
|
|
1768
|
+
size: container,
|
|
1769
|
+
sx: {
|
|
1770
|
+
display: "flex",
|
|
1771
|
+
flexDirection: { xs: "column", sm: "row", md: "row" },
|
|
1772
|
+
"&:hover": { bgcolor: "#efefef", overflow: "hidden" }
|
|
1773
|
+
},
|
|
1774
|
+
children: [
|
|
1775
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1776
|
+
_material.Grid,
|
|
1777
|
+
{
|
|
1778
|
+
size: size.labelSize,
|
|
1779
|
+
sx: {
|
|
1780
|
+
padding: "5px",
|
|
1781
|
+
fontSize: "14px",
|
|
1782
|
+
textAlign: { xs: "left", sm: "right", md: "right" },
|
|
1783
|
+
...labelSx
|
|
1784
|
+
},
|
|
1785
|
+
children: [
|
|
1786
|
+
label,
|
|
1787
|
+
" :"
|
|
1788
|
+
]
|
|
1789
|
+
}
|
|
1790
|
+
),
|
|
1791
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1792
|
+
_material.Grid,
|
|
1793
|
+
{
|
|
1794
|
+
size: size.valueSize,
|
|
1795
|
+
sx: { padding: "5px", display: "flex", flexWrap: "wrap" },
|
|
1796
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Tooltip, { title: value, arrow: true, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1797
|
+
_material.Typography,
|
|
1798
|
+
{
|
|
1799
|
+
sx: {
|
|
1800
|
+
fontSize: "14px",
|
|
1801
|
+
wordBreak: "break-word",
|
|
1802
|
+
overflow: "hidden",
|
|
1803
|
+
display: "-webkit-box",
|
|
1804
|
+
textOverflow: "ellipsis",
|
|
1805
|
+
WebkitLineClamp: 2,
|
|
1806
|
+
WebkitBoxOrient: "vertical",
|
|
1807
|
+
...valueSx,
|
|
1808
|
+
color: "#078dee"
|
|
1809
|
+
},
|
|
1810
|
+
children: value ? value : "-"
|
|
1811
|
+
}
|
|
1812
|
+
) })
|
|
1813
|
+
}
|
|
1814
|
+
)
|
|
1815
|
+
]
|
|
1816
|
+
}
|
|
1817
|
+
);
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
// src/core/components/RenderIf/RenderIf.tsx
|
|
1821
|
+
|
|
1822
|
+
var RenderIf = ({
|
|
1823
|
+
show,
|
|
1824
|
+
children
|
|
1825
|
+
}) => {
|
|
1826
|
+
return show ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children }) : null;
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1829
|
+
// src/core/components/SectionBox/SectionBox.tsx
|
|
1830
|
+
|
|
1831
|
+
|
|
1832
|
+
|
|
1833
|
+
var getSectionTheme = (variant = "default") => {
|
|
1834
|
+
const themes = {
|
|
1835
|
+
default: {
|
|
1836
|
+
bgcolor: "#faebd7",
|
|
1837
|
+
color: "#925d21"
|
|
1838
|
+
},
|
|
1839
|
+
form: {
|
|
1840
|
+
bgcolor: "#cdced1",
|
|
1841
|
+
color: "black"
|
|
1842
|
+
},
|
|
1843
|
+
info: {
|
|
1844
|
+
bgcolor: "#e3f2fd",
|
|
1845
|
+
color: "#1976d2"
|
|
1846
|
+
},
|
|
1847
|
+
warning: {
|
|
1848
|
+
bgcolor: "#fff3e0",
|
|
1849
|
+
color: "#f57c00"
|
|
1850
|
+
},
|
|
1851
|
+
error: {
|
|
1852
|
+
bgcolor: "#ffebee",
|
|
1853
|
+
color: "#d32f2f"
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
return themes[variant];
|
|
1857
|
+
};
|
|
1858
|
+
var SectionBox = _react.memo.call(void 0,
|
|
1859
|
+
({
|
|
1860
|
+
title,
|
|
1861
|
+
children,
|
|
1862
|
+
spacing = 0,
|
|
1863
|
+
containerSx,
|
|
1864
|
+
titleSx,
|
|
1865
|
+
variant = "default",
|
|
1866
|
+
icon,
|
|
1867
|
+
actions
|
|
1868
|
+
}) => {
|
|
1869
|
+
const themeColors = _react.useMemo.call(void 0, () => getSectionTheme(variant), [variant]);
|
|
1870
|
+
const headerSx = _react.useMemo.call(void 0,
|
|
1871
|
+
() => ({
|
|
1872
|
+
px: 1.5,
|
|
1873
|
+
py: 0.1,
|
|
1874
|
+
width: "fit-content",
|
|
1875
|
+
...themeColors,
|
|
1876
|
+
...titleSx
|
|
1877
|
+
}),
|
|
1878
|
+
[themeColors, titleSx]
|
|
1879
|
+
);
|
|
1880
|
+
const contentSx = _react.useMemo.call(void 0,
|
|
1881
|
+
() => ({
|
|
1882
|
+
padding: "16px",
|
|
1883
|
+
...containerSx
|
|
1884
|
+
}),
|
|
1885
|
+
[containerSx]
|
|
1886
|
+
);
|
|
1887
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1888
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _material.Box, { sx: { display: "flex", flexDirection: "column", width: "100%" }, children: [
|
|
1889
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1890
|
+
_material.Stack,
|
|
1891
|
+
{
|
|
1892
|
+
direction: "row",
|
|
1893
|
+
justifyContent: "space-between",
|
|
1894
|
+
alignItems: "center",
|
|
1895
|
+
sx: headerSx,
|
|
1896
|
+
children: [
|
|
1897
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _material.Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
|
|
1898
|
+
icon,
|
|
1899
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Typography, { sx: { fontSize: "15px", fontWeight: 400 }, children: title })
|
|
1900
|
+
] }),
|
|
1901
|
+
actions
|
|
1902
|
+
]
|
|
1903
|
+
}
|
|
1904
|
+
),
|
|
1905
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Divider, {})
|
|
1906
|
+
] }),
|
|
1907
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Grid, { container: true, spacing, sx: contentSx, children })
|
|
1908
|
+
] });
|
|
1909
|
+
}
|
|
1910
|
+
);
|
|
1911
|
+
|
|
1912
|
+
// src/core/components/SimpleTabs/SimpleTabs.tsx
|
|
1913
|
+
|
|
1914
|
+
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
var SimpleTabs = ({
|
|
1918
|
+
tabs,
|
|
1919
|
+
defaultValue = 1,
|
|
1920
|
+
onTabChange,
|
|
1921
|
+
children,
|
|
1922
|
+
tabSx,
|
|
1923
|
+
tabsSx
|
|
1924
|
+
}) => {
|
|
1925
|
+
const [value, setValue] = _react.useState.call(void 0, defaultValue);
|
|
1926
|
+
const handleChange = (event, newValue) => {
|
|
1927
|
+
setValue(newValue);
|
|
1928
|
+
if (onTabChange) onTabChange(newValue);
|
|
1929
|
+
};
|
|
1930
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _lab.TabContext, { value, children: [
|
|
1931
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _material.Box, { sx: { borderBottom: 1, borderColor: "divider", width: "100%" }, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1932
|
+
_material.Tabs,
|
|
1933
|
+
{
|
|
1934
|
+
value,
|
|
1935
|
+
onChange: handleChange,
|
|
1936
|
+
sx: { px: 2, py: 0, ...tabsSx },
|
|
1937
|
+
children: tabs.map((tab) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1938
|
+
_material.Tab,
|
|
1939
|
+
{
|
|
1940
|
+
label: tab.label,
|
|
1941
|
+
value: tab.value,
|
|
1942
|
+
disabled: tab.permission === false,
|
|
1943
|
+
sx: { fontSize: "1rem", ...tabSx }
|
|
1944
|
+
},
|
|
1945
|
+
tab.value
|
|
1946
|
+
))
|
|
1947
|
+
}
|
|
1948
|
+
) }),
|
|
1949
|
+
children
|
|
1950
|
+
] });
|
|
1951
|
+
};
|
|
1952
|
+
|
|
1953
|
+
// src/core/components/SubmitButton/SubmitButton.tsx
|
|
1954
|
+
|
|
1955
|
+
|
|
1956
|
+
var SubmitButton = ({
|
|
1957
|
+
loading = false,
|
|
1958
|
+
...rest
|
|
1959
|
+
}) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1960
|
+
_lab.LoadingButton,
|
|
1961
|
+
{
|
|
1962
|
+
loading,
|
|
1963
|
+
variant: "contained",
|
|
1964
|
+
color: "primary",
|
|
1965
|
+
type: "submit",
|
|
1966
|
+
...rest,
|
|
1967
|
+
sx: { fontWeight: 400 },
|
|
1968
|
+
children: "Submit"
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
|
|
1972
|
+
// src/core/components/WithRef/WithRef.tsx
|
|
1973
|
+
|
|
1974
|
+
function withDataModal(component) {
|
|
1975
|
+
return _react.forwardRef.call(void 0,
|
|
1976
|
+
(props, ref) => component({ ...props, ref })
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// src/core/config.ts
|
|
1981
|
+
var Config = {
|
|
1982
|
+
defaultPageSize: 20,
|
|
1983
|
+
apiBaseUrl: "http://localhost:5143"
|
|
1984
|
+
// apiBaseUrl: 'http://192.168.1.246:5143',
|
|
1985
|
+
};
|
|
1986
|
+
var dateTimePatterns = {
|
|
1987
|
+
dateTime: "DD MMM YYYY h:mm A",
|
|
1988
|
+
// 17 Apr 2022 12:00 am
|
|
1989
|
+
date: "DD MMM YYYY",
|
|
1990
|
+
// 17 Apr 2022
|
|
1991
|
+
month_year_short_format: "MMM YYYY",
|
|
1992
|
+
month_year_full_format: "MMMM YYYY",
|
|
1993
|
+
year: "YYYY",
|
|
1994
|
+
time: "h:mm a",
|
|
1995
|
+
// 12:00 am
|
|
1996
|
+
split: {
|
|
1997
|
+
dateTime: "DD/MM/YYYY h:mm A",
|
|
1998
|
+
// 17/04/2022 12:00 am
|
|
1999
|
+
date: "DD/MM/YYYY"
|
|
2000
|
+
// 17/04/2022
|
|
2001
|
+
},
|
|
2002
|
+
paramCase: {
|
|
2003
|
+
dateTime: "DD-MM-YYYY h:mm A",
|
|
2004
|
+
// 17-04-2022 12:00 am
|
|
2005
|
+
date: "DD-MM-YYYY",
|
|
2006
|
+
// 17-04-2022
|
|
2007
|
+
dateReverse: "YYYY-MM-DD",
|
|
2008
|
+
// 2022-04-17 for compare date
|
|
2009
|
+
MonthYear: "MMM-YYYY"
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
// src/core/hooks/useApiClient.ts
|
|
2014
|
+
|
|
2015
|
+
function useApiClient(config = {}) {
|
|
2016
|
+
return _react.useMemo.call(void 0,
|
|
2017
|
+
() => createApiClient(config),
|
|
2018
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2019
|
+
[
|
|
2020
|
+
config.baseURL,
|
|
2021
|
+
config.timeout,
|
|
2022
|
+
config.correlationIdPrefix,
|
|
2023
|
+
config.includeCorrelationId,
|
|
2024
|
+
config.authToken,
|
|
2025
|
+
config.requestInterceptors,
|
|
2026
|
+
config.responseInterceptors,
|
|
2027
|
+
config.errorInterceptors
|
|
2028
|
+
]
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
// src/core/hooks/useFormErrorHandler.ts
|
|
2033
|
+
|
|
2034
|
+
var _sonner = require('sonner');
|
|
2035
|
+
var useFormErrorHandler = ({
|
|
2036
|
+
setError,
|
|
2037
|
+
successMessage = {
|
|
2038
|
+
create: "Created successfully",
|
|
2039
|
+
update: "Updated successfully"
|
|
2040
|
+
},
|
|
2041
|
+
errorMessage = {
|
|
2042
|
+
noChanges: "No changes were made",
|
|
2043
|
+
general: "Failed to save. Please try again."
|
|
2044
|
+
}
|
|
2045
|
+
}) => {
|
|
2046
|
+
const getFieldError = _react.useCallback.call(void 0,
|
|
2047
|
+
(fields, fieldName) => {
|
|
2048
|
+
if (!fields || !fields[fieldName]) return void 0;
|
|
2049
|
+
const fieldError = fields[fieldName];
|
|
2050
|
+
if (typeof fieldError === "string") {
|
|
2051
|
+
return fieldError;
|
|
2052
|
+
}
|
|
2053
|
+
if (Array.isArray(fieldError)) {
|
|
2054
|
+
return fieldError.join(", ");
|
|
2055
|
+
}
|
|
2056
|
+
if (typeof fieldError === "object" && "message" in fieldError) {
|
|
2057
|
+
return fieldError.message;
|
|
2058
|
+
}
|
|
2059
|
+
return void 0;
|
|
2060
|
+
},
|
|
2061
|
+
[]
|
|
2062
|
+
);
|
|
2063
|
+
const handleSuccess = _react.useCallback.call(void 0,
|
|
2064
|
+
(isEditing, rowsAffected) => {
|
|
2065
|
+
if (rowsAffected !== void 0 && rowsAffected > 0) {
|
|
2066
|
+
_sonner.toast.success(
|
|
2067
|
+
isEditing ? successMessage.update : successMessage.create
|
|
2068
|
+
);
|
|
2069
|
+
return true;
|
|
2070
|
+
} else if (rowsAffected === 0) {
|
|
2071
|
+
_sonner.toast.error(errorMessage.noChanges);
|
|
2072
|
+
return false;
|
|
2073
|
+
}
|
|
2074
|
+
_sonner.toast.success(isEditing ? successMessage.update : successMessage.create);
|
|
2075
|
+
return true;
|
|
2076
|
+
},
|
|
2077
|
+
[successMessage, errorMessage]
|
|
2078
|
+
);
|
|
2079
|
+
const handleError = _react.useCallback.call(void 0,
|
|
2080
|
+
(processedError) => {
|
|
2081
|
+
if (processedError.type === "validation_error" && processedError.errors && setError) {
|
|
2082
|
+
Object.keys(processedError.errors).forEach((fieldName) => {
|
|
2083
|
+
const fieldError = getFieldError(processedError.errors, fieldName);
|
|
2084
|
+
if (fieldError) {
|
|
2085
|
+
setError(fieldName, {
|
|
2086
|
+
type: "server",
|
|
2087
|
+
message: fieldError
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
_sonner.toast.error(
|
|
2092
|
+
processedError.title || "Please check the form for validation errors"
|
|
2093
|
+
);
|
|
2094
|
+
} else {
|
|
2095
|
+
_sonner.toast.error(processedError.title || errorMessage.general);
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
[errorMessage.general, getFieldError, setError]
|
|
2099
|
+
);
|
|
2100
|
+
return {
|
|
2101
|
+
handleSuccess,
|
|
2102
|
+
handleError
|
|
2103
|
+
};
|
|
2104
|
+
};
|
|
2105
|
+
var useDeleteHandler = ({
|
|
2106
|
+
successMessage = "Deleted successfully",
|
|
2107
|
+
errorMessage = "Failed to delete. Please try again."
|
|
2108
|
+
} = {}) => {
|
|
2109
|
+
return useFormErrorHandler({
|
|
2110
|
+
successMessage: {
|
|
2111
|
+
create: successMessage,
|
|
2112
|
+
// Not used for delete, but required for type
|
|
2113
|
+
update: successMessage
|
|
2114
|
+
},
|
|
2115
|
+
errorMessage: {
|
|
2116
|
+
noChanges: "No changes were made",
|
|
2117
|
+
// Not typically used for delete
|
|
2118
|
+
general: errorMessage
|
|
2119
|
+
}
|
|
2120
|
+
// setError is omitted (undefined) for delete operations
|
|
2121
|
+
});
|
|
2122
|
+
};
|
|
2123
|
+
|
|
2124
|
+
// src/core/utils/CacheUtility/index.ts
|
|
2125
|
+
var _reactquery = require('@tanstack/react-query');
|
|
2126
|
+
|
|
2127
|
+
var CacheUtility = class {
|
|
2128
|
+
constructor(queryClient) {
|
|
2129
|
+
this.queryClient = queryClient;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Get cached data using only the queryKey from query factory
|
|
2133
|
+
*/
|
|
2134
|
+
getCachedData(queryKey) {
|
|
2135
|
+
return this.queryClient.getQueryData(queryKey);
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* Get cached data with transformation using select function
|
|
2139
|
+
*/
|
|
2140
|
+
getCachedDataWithSelect(queryKey, select) {
|
|
2141
|
+
const cachedData = this.queryClient.getQueryData(queryKey);
|
|
2142
|
+
if (cachedData === void 0) {
|
|
2143
|
+
return void 0;
|
|
2144
|
+
}
|
|
2145
|
+
return select(cachedData);
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
function useCacheUtility() {
|
|
2149
|
+
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
2150
|
+
return _react.useMemo.call(void 0, () => new CacheUtility(queryClient), [queryClient]);
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// src/core/utils/watch/core.ts
|
|
2154
|
+
var _reacthookform = require('react-hook-form');
|
|
2155
|
+
var useWatchForm = (control) => _reacthookform.useWatch.call(void 0, { control });
|
|
2156
|
+
var useWatchField = (control, name) => _reacthookform.useWatch.call(void 0, { control, name });
|
|
2157
|
+
var useWatchFields = (control, names) => _reacthookform.useWatch.call(void 0, { control, name: names });
|
|
2158
|
+
|
|
2159
|
+
// src/core/utils/watch/utilities.ts
|
|
2160
|
+
|
|
2161
|
+
|
|
2162
|
+
var useWatchTransform = (control, name, transform) => {
|
|
2163
|
+
const value = _reacthookform.useWatch.call(void 0, { control, name });
|
|
2164
|
+
return _react.useMemo.call(void 0, () => transform(value), [value, transform]);
|
|
2165
|
+
};
|
|
2166
|
+
var useWatchDefault = (control, name, defaultValue) => {
|
|
2167
|
+
const value = _reacthookform.useWatch.call(void 0, { control, name });
|
|
2168
|
+
return _nullishCoalesce(value, () => ( defaultValue));
|
|
2169
|
+
};
|
|
2170
|
+
var useWatchBoolean = (control, name, defaultValue = false) => {
|
|
2171
|
+
const value = _reacthookform.useWatch.call(void 0, { control, name });
|
|
2172
|
+
return Boolean(_nullishCoalesce(value, () => ( defaultValue)));
|
|
2173
|
+
};
|
|
2174
|
+
var useWatchBatch = (control, fields) => {
|
|
2175
|
+
const values = _reacthookform.useWatch.call(void 0, { control, name: fields });
|
|
2176
|
+
return _react.useMemo.call(void 0, () => {
|
|
2177
|
+
const result = {};
|
|
2178
|
+
fields.forEach((field, index) => {
|
|
2179
|
+
result[field] = values[index];
|
|
2180
|
+
});
|
|
2181
|
+
return result;
|
|
2182
|
+
}, [values, fields]);
|
|
2183
|
+
};
|
|
2184
|
+
var useWatchConditional = (control, name, shouldWatch, fallback) => {
|
|
2185
|
+
const activeValue = _reacthookform.useWatch.call(void 0, {
|
|
2186
|
+
control,
|
|
2187
|
+
name,
|
|
2188
|
+
disabled: !shouldWatch
|
|
2189
|
+
});
|
|
2190
|
+
return shouldWatch ? activeValue : fallback;
|
|
2191
|
+
};
|
|
2192
|
+
var useWatchDebounced = (control, name, delay = 300) => {
|
|
2193
|
+
const value = _reacthookform.useWatch.call(void 0, { control, name });
|
|
2194
|
+
const [debouncedValue, setDebouncedValue] = _react.useState.call(void 0, value);
|
|
2195
|
+
_react.useEffect.call(void 0, () => {
|
|
2196
|
+
const timer = setTimeout(() => {
|
|
2197
|
+
setDebouncedValue(value);
|
|
2198
|
+
}, delay);
|
|
2199
|
+
return () => clearTimeout(timer);
|
|
2200
|
+
}, [value, delay]);
|
|
2201
|
+
return debouncedValue;
|
|
2202
|
+
};
|
|
2203
|
+
var useWatchSelector = (control, name, selector, deps = []) => {
|
|
2204
|
+
const value = _reacthookform.useWatch.call(void 0, { control, name });
|
|
2205
|
+
return _react.useMemo.call(void 0,
|
|
2206
|
+
() => selector(value),
|
|
2207
|
+
[value, selector, ...deps]
|
|
2208
|
+
// eslint-disable-line react-hooks/exhaustive-deps
|
|
2209
|
+
);
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
// src/core/utils/watch/index.ts
|
|
2213
|
+
var typedWatch = {
|
|
2214
|
+
// === CORE FUNCTIONS ===
|
|
2215
|
+
/** Watch entire form */
|
|
2216
|
+
form: useWatchForm,
|
|
2217
|
+
/** Watch single field */
|
|
2218
|
+
field: useWatchField,
|
|
2219
|
+
/** Watch multiple fields */
|
|
2220
|
+
fields: useWatchFields,
|
|
2221
|
+
// === UTILITY FUNCTIONS ===
|
|
2222
|
+
/** Watch with transformation */
|
|
2223
|
+
transform: useWatchTransform,
|
|
2224
|
+
/** Watch with default value */
|
|
2225
|
+
withDefault: useWatchDefault,
|
|
2226
|
+
/** Watch as boolean */
|
|
2227
|
+
boolean: useWatchBoolean,
|
|
2228
|
+
/** Watch multiple with custom keys */
|
|
2229
|
+
batch: useWatchBatch,
|
|
2230
|
+
/** Watch conditionally */
|
|
2231
|
+
conditional: useWatchConditional,
|
|
2232
|
+
/** Watch with debouncing */
|
|
2233
|
+
debounced: useWatchDebounced,
|
|
2234
|
+
/** Watch with selector */
|
|
2235
|
+
selector: useWatchSelector
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
// src/core/utils/calculateFilterCount.ts
|
|
2239
|
+
var calculateFilterCount = (model) => Object.values(model).filter(
|
|
2240
|
+
(v) => v !== null && v !== void 0 && String(v).trim() !== ""
|
|
2241
|
+
).length;
|
|
2242
|
+
|
|
2243
|
+
// src/core/utils/format-time.ts
|
|
2244
|
+
var _dayjs = require('dayjs'); var _dayjs2 = _interopRequireDefault(_dayjs);
|
|
2245
|
+
var _duration = require('dayjs/plugin/duration'); var _duration2 = _interopRequireDefault(_duration);
|
|
2246
|
+
var _relativeTime = require('dayjs/plugin/relativeTime'); var _relativeTime2 = _interopRequireDefault(_relativeTime);
|
|
2247
|
+
_dayjs2.default.extend(_duration2.default);
|
|
2248
|
+
_dayjs2.default.extend(_relativeTime2.default);
|
|
2249
|
+
var formatPatterns = {
|
|
2250
|
+
dateTime: "DD MMM YYYY h:mm A",
|
|
2251
|
+
// 17 Apr 2022 12:00 am
|
|
2252
|
+
date: "DD MMM YYYY",
|
|
2253
|
+
// 17 Apr 2022
|
|
2254
|
+
month_year_short_format: "MMM YYYY",
|
|
2255
|
+
month_year_full_format: "MMMM YYYY",
|
|
2256
|
+
year: "YYYY",
|
|
2257
|
+
time: "h:mm a",
|
|
2258
|
+
// 12:00 am
|
|
2259
|
+
split: {
|
|
2260
|
+
dateTime: "DD/MM/YYYY h:mm A",
|
|
2261
|
+
// 17/04/2022 12:00 am
|
|
2262
|
+
date: "DD/MM/YYYY"
|
|
2263
|
+
// 17/04/2022
|
|
2264
|
+
},
|
|
2265
|
+
paramCase: {
|
|
2266
|
+
dateTime: "DD-MM-YYYY h:mm A",
|
|
2267
|
+
// 17-04-2022 12:00 am
|
|
2268
|
+
date: "DD-MM-YYYY",
|
|
2269
|
+
// 17-04-2022
|
|
2270
|
+
dateReverse: "YYYY-MM-DD",
|
|
2271
|
+
// 2022-04-17 for compare date
|
|
2272
|
+
MonthYear: "MMM-YYYY"
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
var isValidDate = (date) => date !== null && date !== void 0 && _dayjs2.default.call(void 0, date).isValid();
|
|
2276
|
+
function today(template) {
|
|
2277
|
+
return _dayjs2.default.call(void 0, /* @__PURE__ */ new Date()).startOf("day").format(template);
|
|
2278
|
+
}
|
|
2279
|
+
function fDateTime(date, template) {
|
|
2280
|
+
if (!isValidDate(date)) {
|
|
2281
|
+
return "Invalid date";
|
|
2282
|
+
}
|
|
2283
|
+
return _dayjs2.default.call(void 0, date).format(_nullishCoalesce(template, () => ( formatPatterns.dateTime)));
|
|
2284
|
+
}
|
|
2285
|
+
function fDate(date, template) {
|
|
2286
|
+
if (!isValidDate(date)) {
|
|
2287
|
+
return "Invalid date";
|
|
2288
|
+
}
|
|
2289
|
+
return _dayjs2.default.call(void 0, date).format(_nullishCoalesce(template, () => ( formatPatterns.date)));
|
|
2290
|
+
}
|
|
2291
|
+
function fTime(date, template) {
|
|
2292
|
+
if (!isValidDate(date)) {
|
|
2293
|
+
return "Invalid date";
|
|
2294
|
+
}
|
|
2295
|
+
return _dayjs2.default.call(void 0, date).format(_nullishCoalesce(template, () => ( formatPatterns.time)));
|
|
2296
|
+
}
|
|
2297
|
+
function fTimestamp(date) {
|
|
2298
|
+
if (!isValidDate(date)) {
|
|
2299
|
+
return "Invalid date";
|
|
2300
|
+
}
|
|
2301
|
+
return _dayjs2.default.call(void 0, date).valueOf();
|
|
2302
|
+
}
|
|
2303
|
+
function fToNow(date) {
|
|
2304
|
+
if (!isValidDate(date)) {
|
|
2305
|
+
return "Invalid date";
|
|
2306
|
+
}
|
|
2307
|
+
return _dayjs2.default.call(void 0, date).toNow(true);
|
|
2308
|
+
}
|
|
2309
|
+
function fIsBetween(inputDate, startDate, endDate) {
|
|
2310
|
+
if (!isValidDate(inputDate) || !isValidDate(startDate) || !isValidDate(endDate)) {
|
|
2311
|
+
return false;
|
|
2312
|
+
}
|
|
2313
|
+
const formattedInputDate = fTimestamp(inputDate);
|
|
2314
|
+
const formattedStartDate = fTimestamp(startDate);
|
|
2315
|
+
const formattedEndDate = fTimestamp(endDate);
|
|
2316
|
+
if (formattedInputDate === "Invalid date" || formattedStartDate === "Invalid date" || formattedEndDate === "Invalid date") {
|
|
2317
|
+
return false;
|
|
2318
|
+
}
|
|
2319
|
+
return formattedInputDate >= formattedStartDate && formattedInputDate <= formattedEndDate;
|
|
2320
|
+
}
|
|
2321
|
+
function fIsAfter(startDate, endDate) {
|
|
2322
|
+
if (!isValidDate(startDate) || !isValidDate(endDate)) {
|
|
2323
|
+
return false;
|
|
2324
|
+
}
|
|
2325
|
+
return _dayjs2.default.call(void 0, startDate).isAfter(endDate);
|
|
2326
|
+
}
|
|
2327
|
+
function fIsSame(startDate, endDate, unitToCompare) {
|
|
2328
|
+
if (!isValidDate(startDate) || !isValidDate(endDate)) {
|
|
2329
|
+
return false;
|
|
2330
|
+
}
|
|
2331
|
+
return _dayjs2.default.call(void 0, startDate).isSame(endDate, _nullishCoalesce(unitToCompare, () => ( "year")));
|
|
2332
|
+
}
|
|
2333
|
+
function fDateRangeShortLabel(startDate, endDate, initial) {
|
|
2334
|
+
if (!isValidDate(startDate) || !isValidDate(endDate) || fIsAfter(startDate, endDate)) {
|
|
2335
|
+
return "Invalid date";
|
|
2336
|
+
}
|
|
2337
|
+
let label = `${fDate(startDate)} - ${fDate(endDate)}`;
|
|
2338
|
+
if (initial) {
|
|
2339
|
+
return label;
|
|
2340
|
+
}
|
|
2341
|
+
const isSameYear = fIsSame(startDate, endDate, "year");
|
|
2342
|
+
const isSameMonth = fIsSame(startDate, endDate, "month");
|
|
2343
|
+
const isSameDay = fIsSame(startDate, endDate, "day");
|
|
2344
|
+
if (isSameYear && !isSameMonth) {
|
|
2345
|
+
label = `${fDate(startDate, "DD MMM")} - ${fDate(endDate)}`;
|
|
2346
|
+
} else if (isSameYear && isSameMonth && !isSameDay) {
|
|
2347
|
+
label = `${fDate(startDate, "DD")} - ${fDate(endDate)}`;
|
|
2348
|
+
} else if (isSameYear && isSameMonth && isSameDay) {
|
|
2349
|
+
label = `${fDate(endDate)}`;
|
|
2350
|
+
}
|
|
2351
|
+
return label;
|
|
2352
|
+
}
|
|
2353
|
+
function fAdd({
|
|
2354
|
+
years = 0,
|
|
2355
|
+
months = 0,
|
|
2356
|
+
days = 0,
|
|
2357
|
+
hours = 0,
|
|
2358
|
+
minutes = 0,
|
|
2359
|
+
seconds = 0,
|
|
2360
|
+
milliseconds = 0
|
|
2361
|
+
}) {
|
|
2362
|
+
const result = _dayjs2.default.call(void 0, ).add(
|
|
2363
|
+
_dayjs2.default.duration({
|
|
2364
|
+
years,
|
|
2365
|
+
months,
|
|
2366
|
+
days,
|
|
2367
|
+
hours,
|
|
2368
|
+
minutes,
|
|
2369
|
+
seconds,
|
|
2370
|
+
milliseconds
|
|
2371
|
+
})
|
|
2372
|
+
).format();
|
|
2373
|
+
return result;
|
|
2374
|
+
}
|
|
2375
|
+
function fSub({
|
|
2376
|
+
years = 0,
|
|
2377
|
+
months = 0,
|
|
2378
|
+
days = 0,
|
|
2379
|
+
hours = 0,
|
|
2380
|
+
minutes = 0,
|
|
2381
|
+
seconds = 0,
|
|
2382
|
+
milliseconds = 0
|
|
2383
|
+
}) {
|
|
2384
|
+
const result = _dayjs2.default.call(void 0, ).subtract(
|
|
2385
|
+
_dayjs2.default.duration({
|
|
2386
|
+
years,
|
|
2387
|
+
months,
|
|
2388
|
+
days,
|
|
2389
|
+
hours,
|
|
2390
|
+
minutes,
|
|
2391
|
+
seconds,
|
|
2392
|
+
milliseconds
|
|
2393
|
+
})
|
|
2394
|
+
).format();
|
|
2395
|
+
return result;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
// src/core/utils/getEmptyObject.ts
|
|
2399
|
+
function getEmptyObject(data, defaultValues = {}) {
|
|
2400
|
+
const obj = {};
|
|
2401
|
+
for (const key of Object.keys(data)) {
|
|
2402
|
+
const value = data[key];
|
|
2403
|
+
const type = typeof value;
|
|
2404
|
+
if (type === "number") {
|
|
2405
|
+
obj[key] = 0;
|
|
2406
|
+
} else if (type === "string" || type === "boolean") {
|
|
2407
|
+
obj[key] = null;
|
|
2408
|
+
} else if (value instanceof Date) {
|
|
2409
|
+
obj[key] = null;
|
|
2410
|
+
} else {
|
|
2411
|
+
obj[key] = null;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return { ...obj, ...defaultValues };
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// src/core/utils/useStableRowCount.ts
|
|
2418
|
+
|
|
2419
|
+
function useStableRowCount(currentTotal) {
|
|
2420
|
+
const rowCountRef = _react.useRef.call(void 0, currentTotal || 0);
|
|
2421
|
+
const stableRowCount = _react.useMemo.call(void 0, () => {
|
|
2422
|
+
if (currentTotal !== void 0) {
|
|
2423
|
+
rowCountRef.current = currentTotal;
|
|
2424
|
+
}
|
|
2425
|
+
return rowCountRef.current;
|
|
2426
|
+
}, [currentTotal]);
|
|
2427
|
+
return stableRowCount;
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
|
|
2431
|
+
|
|
2432
|
+
|
|
2433
|
+
|
|
2434
|
+
|
|
2435
|
+
|
|
2436
|
+
|
|
2437
|
+
|
|
2438
|
+
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
|
|
2442
|
+
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
|
|
2446
|
+
|
|
2447
|
+
|
|
2448
|
+
|
|
2449
|
+
|
|
2450
|
+
|
|
2451
|
+
|
|
2452
|
+
|
|
2453
|
+
|
|
2454
|
+
|
|
2455
|
+
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
|
|
2459
|
+
|
|
2460
|
+
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
|
|
2464
|
+
|
|
2465
|
+
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
|
|
2473
|
+
|
|
2474
|
+
|
|
2475
|
+
|
|
2476
|
+
|
|
2477
|
+
|
|
2478
|
+
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
|
|
2482
|
+
|
|
2483
|
+
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
exports.generateCorrelationId = generateCorrelationId; exports.RequestManager = RequestManager; exports.ApiClient = ApiClient; exports.createApiClient = createApiClient; exports.getGlobalApiClient = getGlobalApiClient; exports.setGlobalApiClient = setGlobalApiClient; exports.resetGlobalApiClient = resetGlobalApiClient; exports.CancelToken = CancelToken; exports.useValidationErrors = useValidationErrors; exports.AuthorizedView = AuthorizedView; exports.CancelButton = CancelButton; exports.ClearButton = ClearButton; exports.SimpleContainer = SimpleContainer; exports.FilterButton = FilterButton; exports.FilterChip = FilterChip; exports.ProgramsFilterDisplay = ProgramsFilterDisplay; exports.FilterWrapper = FilterWrapper; exports.Footer = Footer; exports.LabelText = LabelText; exports.RenderIf = RenderIf; exports.SectionBox = SectionBox; exports.SimpleTabs = SimpleTabs; exports.SubmitButton = SubmitButton; exports.withDataModal = withDataModal; exports.Config = Config; exports.dateTimePatterns = dateTimePatterns; exports.useApiClient = useApiClient; exports.useFormErrorHandler = useFormErrorHandler; exports.useDeleteHandler = useDeleteHandler; exports.CacheUtility = CacheUtility; exports.useCacheUtility = useCacheUtility; exports.useWatchForm = useWatchForm; exports.useWatchField = useWatchField; exports.useWatchFields = useWatchFields; exports.useWatchTransform = useWatchTransform; exports.useWatchDefault = useWatchDefault; exports.useWatchBoolean = useWatchBoolean; exports.useWatchBatch = useWatchBatch; exports.useWatchConditional = useWatchConditional; exports.useWatchDebounced = useWatchDebounced; exports.useWatchSelector = useWatchSelector; exports.typedWatch = typedWatch; exports.calculateFilterCount = calculateFilterCount; exports.formatPatterns = formatPatterns; exports.today = today; exports.fDateTime = fDateTime; exports.fDate = fDate; exports.fTime = fTime; exports.fTimestamp = fTimestamp; exports.fToNow = fToNow; exports.fIsBetween = fIsBetween; exports.fIsAfter = fIsAfter; exports.fIsSame = fIsSame; exports.fDateRangeShortLabel = fDateRangeShortLabel; exports.fAdd = fAdd; exports.fSub = fSub; exports.getEmptyObject = getEmptyObject; exports.useStableRowCount = useStableRowCount;
|
|
2490
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImM6XFxVdHRhbVxcUHJvamVjdHNcXEdOV2ViU29mdC5VSVxccGFja2FnZXNcXHVpXFxkaXN0XFxjaHVuay1RTFE2T0gyNS5janMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQSxTQUFTLFlBQVksQ0FBQyxFQUFFO0FBQ3hCLEVBQUUsR0FBRyxDQUFDLE9BQU8sT0FBTyxJQUFJLFlBQVksR0FBRyxNQUFNLENBQUMsVUFBVSxFQUFFO0FBQzFELElBQUksT0FBTyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDOUIsRUFBRTtBQUNGLEVBQUUsT0FBTyxzQ0FBc0MsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLEdBQUc7QUFDeEUsSUFBSSxNQUFNLEVBQUUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUNwQyxJQUFJLE1BQU0sRUFBRSxFQUFFLEVBQUUsSUFBSSxJQUFJLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQztBQUN2QyxJQUFJLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7QUFDekIsRUFBRSxDQUFDLENBQUM7QUFDSjtBQUNBLFNBQVMscUJBQXFCLENBQUMsTUFBTSxFQUFFO0FBQ3ZDLEVBQUUsTUFBTSxLQUFLLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDN0IsRUFBRSxPQUFPLE9BQU8sRUFBRSxDQUFDLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFlBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLGtCQUFBO0FBQ0Esa0JBQUE7QUFDQSxrQkFBQTtBQUNBLGtCQUFBO0FBQ0EsbUJBQUE7QUFDQSxtQkFBQTtBQUNBLG1CQUFBO0FBQ0EsbUJBQUE7QUFDQSxtQkFBQTtBQUNBLG1CQUFBO0FBQ0EsbUJBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0Esd0JBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsVUFBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLGNBQUE7QUFDQSxnQkFBQTtBQUNBLGNBQUE7QUFDQSxjQUFBO0FBQ0EsZ0JBQUE7QUFDQSxnQkFBQTtBQUNBLGdCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxnQkFBQTtBQUNBLGdCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxjQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsWUFBQTtBQUNBLGNBQUE7QUFDQSxnQkFBQTtBQUNBLGNBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLGNBQUE7QUFDQSxjQUFBO0FBQ0EsZ0JBQUE7QUFDQSxnQkFBQTtBQUNBLGdCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxnQkFBQTtBQUNBLGdCQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsZ0JBQUE7QUFDQSxjQUFBO0FBQ0EsWUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFVBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxvQkFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0Esc0JBQUE7QUFDQSxzQkFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLHdCQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsY0FBQTtBQUNBLGNBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLGdCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsY0FBQTtBQUNBLGdCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxrQkFBQTtBQUNBLG9CQUFBO0FBQ0Esb0JBQUE7QUFDQSxvQkFBQTtBQUNBLGtCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxjQUFBO0FBQ0EsOEJBQUE7QUFDQSxnQkFBQTtBQUNBLGdCQUFBO0FBQ0Esa0JBQUE7QUFDQSxrQkFBQTtBQUNBLG9CQUFBO0FBQ0Esb0JBQUE7QUFDQSxvQkFBQTtBQUNBLGtCQUFBO0FBQ0Esa0JBQUE7QUFDQSxvQkFBQTtBQUNBLG9CQUFBO0FBQ0Esb0JBQUE7QUFDQSxrQkFBQTtBQUNBLGdCQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLHdCQUFBO0FBQ0Esd0JBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSx3QkFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLGNBQUE7QUFDQSxjQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLHdCQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLGdCQUFBO0FBQ0Esa0JBQUE7QUFDQSxrQkFBQTtBQUNBLGtCQUFBO0FBQ0Esa0JBQUE7QUFDQSxrQkFBQTtBQUNBLGtCQUFBO0FBQ0Esa0JBQUE7QUFDQSxrQkFBQTtBQUNBLGtCQUFBO0FBQ0EsZ0JBQUE7QUFDQSxnQkFBQTtBQUNBLGNBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0Esc0JBQUE7QUFDQSx3QkFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSw4QkFBQTtBQUNBLGdCQUFBO0FBQ0EsZ0NBQUE7QUFDQSxjQUFBO0FBQ0EsY0FBQTtBQUNBLFlBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLHdCQUFBO0FBQ0EsTUFBQTtBQUNBLHNCQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxvQkFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsUUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsWUFBQTtBQUNBLFlBQUE7QUFDQSxZQUFBO0FBQ0EsWUFBQTtBQUNBLFVBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQTtBQUNBLElBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBO0FBQ0EsSUFBQTtBQUNBO0FBQ0EsSUFBQTtBQUNBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLFFBQUE7QUFDQSxVQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxRQUFBO0FBQ0EsVUFBQTtBQUNBLFVBQUE7QUFDQSxZQUFBO0FBQ0EsY0FBQTtBQUNBLGNBQUE7QUFDQSxZQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxRQUFBO0FBQ0EsVUFBQTtBQUNBLFFBQUE7QUFDQSxNQUFBO0FBQ0EsUUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsSUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0E7QUFDQSxJQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQTtBQUNBLElBQUE7QUFDQTtBQUNBLElBQUE7QUFDQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBLEVBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxNQUFBO0FBQ0EsTUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsRUFBQTtBQUNBLEVBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUE7QUFDQSxFQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLE1BQUE7QUFDQSxJQUFBO0FBQ0EsTUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBLElBQUE7QUFDQSxNQUFBO0FBQ0EsSUFBQTtBQUNBLElBQUE7QUFDQSxFQUFBO0FBQ0EsRUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiQzpcXFV0dGFtXFxQcm9qZWN0c1xcR05XZWJTb2Z0LlVJXFxwYWNrYWdlc1xcdWlcXGRpc3RcXGNodW5rLVFMUTZPSDI1LmNqcyIsInNvdXJjZXNDb250ZW50IjpbbnVsbF19
|