@oneluiz/dual-datepicker 3.4.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,152 @@
1
+ import { DateAdapter } from './date-adapter';
2
+ import * as i0 from "@angular/core";
3
+ export declare class NativeDateAdapter implements DateAdapter {
4
+ /**
5
+ * Normalize date to start of day (00:00:00.000) in local timezone
6
+ *
7
+ * This is the foundation of timezone-safe operations.
8
+ * All other methods use normalized dates for comparisons.
9
+ */
10
+ normalize(date: Date): Date;
11
+ /**
12
+ * Check if two dates are the same calendar day
13
+ *
14
+ * Implementation: Compare YYYY-MM-DD components directly
15
+ * Avoids timezone issues from valueOf() comparisons
16
+ */
17
+ isSameDay(a: Date, b: Date): boolean;
18
+ /**
19
+ * Check if date A is before date B (calendar day level)
20
+ *
21
+ * Implementation: Compare normalized dates using valueOf()
22
+ */
23
+ isBeforeDay(a: Date, b: Date): boolean;
24
+ /**
25
+ * Check if date A is after date B (calendar day level)
26
+ *
27
+ * Implementation: Compare normalized dates using valueOf()
28
+ */
29
+ isAfterDay(a: Date, b: Date): boolean;
30
+ /**
31
+ * Add days to a date
32
+ *
33
+ * Implementation: Use setDate() which handles month rollover automatically
34
+ *
35
+ * Example:
36
+ * Jan 31 + 3 days → Feb 3 ✅
37
+ * Feb 28 + 1 day → Mar 1 ✅ (non-leap year)
38
+ */
39
+ addDays(date: Date, days: number): Date;
40
+ /**
41
+ * Add months to a date
42
+ *
43
+ * CRITICAL: Handles month overflow correctly
44
+ *
45
+ * Algorithm:
46
+ * 1. Add months using setMonth()
47
+ * 2. If day-of-month changed (overflow), set to last day of target month
48
+ *
49
+ * Examples:
50
+ * - Jan 31 + 1 month → Feb 28 (or Feb 29 in leap year) ✅
51
+ * - Jan 31 + 2 months → Mar 31 ✅
52
+ * - Mar 31 + 1 month → Apr 30 ✅
53
+ * - Dec 31 + 1 month → Jan 31 (next year) ✅
54
+ */
55
+ addMonths(date: Date, months: number): Date;
56
+ /**
57
+ * Get start of day (00:00:00.000)
58
+ *
59
+ * Alias for normalize() with explicit intent
60
+ */
61
+ startOfDay(date: Date): Date;
62
+ /**
63
+ * Get end of day (23:59:59.999)
64
+ *
65
+ * Useful for inclusive range queries
66
+ */
67
+ endOfDay(date: Date): Date;
68
+ /**
69
+ * Get first day of month (00:00:00.000)
70
+ */
71
+ startOfMonth(date: Date): Date;
72
+ /**
73
+ * Get last day of month (23:59:59.999)
74
+ *
75
+ * Algorithm: Go to 1st of next month, subtract 1 day
76
+ */
77
+ endOfMonth(date: Date): Date;
78
+ /**
79
+ * Get year (4-digit)
80
+ */
81
+ getYear(date: Date): number;
82
+ /**
83
+ * Get month (0-11)
84
+ */
85
+ getMonth(date: Date): number;
86
+ /**
87
+ * Get day of month (1-31)
88
+ */
89
+ getDate(date: Date): number;
90
+ /**
91
+ * Get day of week (0-6, Sunday=0)
92
+ */
93
+ getDay(date: Date): number;
94
+ /**
95
+ * Convert Date to ISO date string (YYYY-MM-DD)
96
+ *
97
+ * CRITICAL: DO NOT use toISOString() - it converts to UTC!
98
+ *
99
+ * Manual construction ensures local timezone is preserved:
100
+ *
101
+ * Example problem with toISOString():
102
+ * ```
103
+ * // Local timezone: GMT-6 (CST)
104
+ * const date = new Date('2026-02-21T23:00:00'); // 11 PM Feb 21 local
105
+ *
106
+ * // WRONG ❌
107
+ * date.toISOString().split('T')[0]
108
+ * // Returns "2026-02-22" (converted to UTC = Feb 22 05:00 AM)
109
+ *
110
+ * // CORRECT ✅
111
+ * toISODate(date)
112
+ * // Returns "2026-02-21" (local date preserved)
113
+ * ```
114
+ *
115
+ * Implementation: Build YYYY-MM-DD manually from local date components
116
+ */
117
+ toISODate(date: Date): string;
118
+ /**
119
+ * Parse ISO date string (YYYY-MM-DD) to Date
120
+ *
121
+ * CRITICAL: DO NOT use new Date(isoString) - may parse as UTC!
122
+ *
123
+ * Example problem with Date constructor:
124
+ * ```
125
+ * // Local timezone: GMT-6 (CST)
126
+ *
127
+ * // WRONG ❌
128
+ * new Date('2026-02-21')
129
+ * // Parsed as UTC: 2026-02-21T00:00:00Z
130
+ * // In local timezone: Feb 20, 2026 6:00 PM (previous day!)
131
+ *
132
+ * // CORRECT ✅
133
+ * parseISODate('2026-02-21')
134
+ * // Returns: 2026-02-21T00:00:00 local time
135
+ * ```
136
+ *
137
+ * Implementation: Parse components and construct Date in local timezone
138
+ */
139
+ parseISODate(isoDate: string): Date | null;
140
+ /**
141
+ * Get week start day for locale
142
+ *
143
+ * Default: Sunday (0) for most locales
144
+ * Monday (1) for Europe, ISO 8601
145
+ *
146
+ * Implementation: Simple locale detection
147
+ * For advanced needs, use Intl.Locale or external library
148
+ */
149
+ getWeekStart(locale?: string): 0 | 1 | 2 | 3 | 4 | 5 | 6;
150
+ static ɵfac: i0.ɵɵFactoryDeclaration<NativeDateAdapter, never>;
151
+ static ɵprov: i0.ɵɵInjectableDeclaration<NativeDateAdapter>;
152
+ }
@@ -0,0 +1,88 @@
1
+ import * as i0 from "@angular/core";
2
+ export interface RangePreset {
3
+ /**
4
+ * Resolve preset to actual date range
5
+ * @param now - Current date for deterministic calculation
6
+ */
7
+ resolve(now: Date): {
8
+ start: Date;
9
+ end: Date;
10
+ };
11
+ }
12
+ export interface PresetRange {
13
+ start: string;
14
+ end: string;
15
+ }
16
+ /**
17
+ * Registry of built-in presets
18
+ * Can be extended by consumers
19
+ *
20
+ * SSR-Safe Architecture:
21
+ * - Injects DateClock via DI
22
+ * - All presets use clock.now() instead of new Date()
23
+ * - Deterministic: same clock.now() → same preset
24
+ * - Override DATE_CLOCK token in SSR to ensure consistency
25
+ *
26
+ * Timezone-Safe Architecture:
27
+ * - Injects DateAdapter via DI
28
+ * - All date operations use adapter methods
29
+ * - Prevents timezone bugs in cross-timezone scenarios
30
+ * - Override DATE_ADAPTER for Luxon/DayJS/custom implementations
31
+ */
32
+ export declare class PresetEngine {
33
+ private presets;
34
+ private clock;
35
+ private adapter;
36
+ constructor();
37
+ /**
38
+ * Register a custom preset
39
+ */
40
+ register(key: string, preset: RangePreset): void;
41
+ /**
42
+ * Resolve a preset to date range
43
+ *
44
+ * SSR Note: Uses injected DateClock for deterministic resolution
45
+ * Timezone Note: Uses injected DateAdapter for consistent date operations
46
+ *
47
+ * Override tokens in SSR scenarios:
48
+ * - DATE_CLOCK: Control current time
49
+ * - DATE_ADAPTER: Control date operations (e.g., Luxon for timezone support)
50
+ *
51
+ * @param key - Preset key (e.g., 'TODAY', 'LAST_7_DAYS')
52
+ * @param now - Optional override for current date (defaults to clock.now())
53
+ */
54
+ resolve(key: string, now?: Date): PresetRange | null;
55
+ /**
56
+ * Get all available preset keys
57
+ */
58
+ getPresetKeys(): string[];
59
+ /**
60
+ * Register all built-in presets
61
+ *
62
+ * All presets now use DateAdapter for timezone-safe operations
63
+ */
64
+ private registerBuiltInPresets;
65
+ static ɵfac: i0.ɵɵFactoryDeclaration<PresetEngine, never>;
66
+ static ɵprov: i0.ɵɵInjectableDeclaration<PresetEngine>;
67
+ }
68
+ /**
69
+ * Create a custom preset from a function
70
+ */
71
+ export declare function createPreset(resolver: (now: Date) => {
72
+ start: Date;
73
+ end: Date;
74
+ }): RangePreset;
75
+ /**
76
+ * @deprecated Use dependency injection instead:
77
+ * ```typescript
78
+ * private engine = inject(PresetEngine);
79
+ * ```
80
+ *
81
+ * Singleton preset engine instance for backward compatibility
82
+ *
83
+ * WARNING: This singleton uses SystemClock directly and is NOT SSR-safe.
84
+ * For SSR applications, inject PresetEngine and override DATE_CLOCK token.
85
+ *
86
+ * This export will be removed in v4.0.0
87
+ */
88
+ export declare const presetEngine: PresetEngine;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Pure validation functions for date ranges
3
+ * No dependencies, no side effects - just logic
4
+ * Perfect for SSR, testing, and reusability
5
+ */
6
+ export interface ValidationResult {
7
+ valid: boolean;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Validate that end date is not before start date
12
+ */
13
+ export declare function validateRangeOrder(start: Date | null, end: Date | null): ValidationResult;
14
+ /**
15
+ * Validate that date is within min/max bounds
16
+ */
17
+ export declare function validateDateBounds(date: Date | null, minDate?: Date, maxDate?: Date): ValidationResult;
18
+ /**
19
+ * Validate that a range is within bounds
20
+ */
21
+ export declare function validateRangeBounds(start: Date | null, end: Date | null, minDate?: Date, maxDate?: Date): ValidationResult;
22
+ /**
23
+ * Check if a date is disabled
24
+ */
25
+ export declare function isDateDisabled(date: Date, disabledDates?: Date[] | ((date: Date) => boolean)): boolean;
26
+ /**
27
+ * Apply bounds to a date (clamp it)
28
+ */
29
+ export declare function applyBounds(date: Date, minDate?: Date, maxDate?: Date): Date;
30
+ /**
31
+ * Parse ISO date string to Date object (deterministic)
32
+ */
33
+ export declare function parseISODate(dateStr: string): Date | null;
34
+ /**
35
+ * Format Date to ISO string (YYYY-MM-DD)
36
+ */
37
+ export declare function formatISODate(date: Date | null): string;
@@ -0,0 +1,13 @@
1
+ import { DateClock } from './date-clock';
2
+ import * as i0 from "@angular/core";
3
+ export declare class SystemClock implements DateClock {
4
+ /**
5
+ * Returns current system time
6
+ *
7
+ * This is the standard behavior for client-side applications.
8
+ * For SSR, override DATE_CLOCK token with a fixed Date.
9
+ */
10
+ now(): Date;
11
+ static ɵfac: i0.ɵɵFactoryDeclaration<SystemClock, never>;
12
+ static ɵprov: i0.ɵɵInjectableDeclaration<SystemClock>;
13
+ }
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter, OnInit, OnChanges, SimpleChanges, ElementRef } from '@angular/core';
2
2
  import { ControlValueAccessor } from '@angular/forms';
3
+ import { DualDateRangeStore } from './core/dual-date-range.store';
3
4
  import * as i0 from "@angular/core";
4
5
  export interface DateRange {
5
6
  startDate: string;
@@ -30,8 +31,10 @@ export type ThemeType = 'default' | 'bootstrap' | 'bulma' | 'foundation' | 'tail
30
31
  export declare class DualDatepickerComponent implements OnInit, OnChanges, ControlValueAccessor {
31
32
  private elementRef;
32
33
  placeholder: string;
33
- startDate: string;
34
- endDate: string;
34
+ set startDate(value: string);
35
+ get startDate(): string;
36
+ set endDate(value: string);
37
+ get endDate(): string;
35
38
  showPresets: boolean;
36
39
  showClearButton: boolean;
37
40
  multiRange: boolean;
@@ -61,24 +64,22 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges, Contr
61
64
  multiDateRangeChange: EventEmitter<MultiDateRange>;
62
65
  multiDateRangeSelected: EventEmitter<MultiDateRange>;
63
66
  private dateAdapter;
67
+ protected readonly rangeStore: DualDateRangeStore;
64
68
  showDatePicker: import("@angular/core").WritableSignal<boolean>;
65
- dateRangeText: import("@angular/core").WritableSignal<string>;
66
- selectingStartDate: import("@angular/core").WritableSignal<boolean>;
67
69
  currentMonth: import("@angular/core").WritableSignal<any>;
68
70
  previousMonth: import("@angular/core").WritableSignal<any>;
69
71
  currentMonthDays: import("@angular/core").WritableSignal<any[]>;
70
72
  previousMonthDays: import("@angular/core").WritableSignal<any[]>;
71
73
  isDisabled: import("@angular/core").WritableSignal<boolean>;
72
- pendingStartDate: string;
73
- pendingEndDate: string;
74
- hasPendingChanges: import("@angular/core").WritableSignal<boolean>;
75
- startHour: number;
76
- startMinute: number;
77
- endHour: number;
78
- endMinute: number;
79
74
  showStartTimePicker: import("@angular/core").WritableSignal<boolean>;
80
75
  showEndTimePicker: import("@angular/core").WritableSignal<boolean>;
81
76
  hoverDate: import("@angular/core").WritableSignal<string>;
77
+ get startHour(): number;
78
+ get startMinute(): number;
79
+ get endHour(): number;
80
+ get endMinute(): number;
81
+ private setStartHourMinute;
82
+ private setEndHourMinute;
82
83
  selectedRanges: import("@angular/core").WritableSignal<DateRange[]>;
83
84
  currentRangeIndex: import("@angular/core").WritableSignal<number>;
84
85
  focusedDay: import("@angular/core").WritableSignal<{
@@ -88,6 +89,11 @@ export declare class DualDatepickerComponent implements OnInit, OnChanges, Contr
88
89
  currentMonthName: import("@angular/core").Signal<string>;
89
90
  previousMonthName: import("@angular/core").Signal<string>;
90
91
  weekDayNames: import("@angular/core").Signal<string[]>;
92
+ dateRangeText: import("@angular/core").Signal<string>;
93
+ selectingStartDate: import("@angular/core").Signal<boolean>;
94
+ hasPendingChanges: import("@angular/core").Signal<boolean>;
95
+ get pendingStartDate(): string;
96
+ get pendingEndDate(): string;
91
97
  private readonly defaultMonthNames;
92
98
  private readonly defaultMonthNamesShort;
93
99
  private readonly defaultDayNames;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Date Adapter Abstraction for Timezone-Safe Date Operations
3
+ *
4
+ * Problem:
5
+ * Using Date natively in calendar/range logic causes enterprise bugs:
6
+ * - Date ranges shift by 1 day due to timezone/DST
7
+ * - Server (UTC) vs Client (local timezone) discrepancies
8
+ * - Inconsistent reporting in BI/ERP/invoicing/hotel systems
9
+ *
10
+ * Solution:
11
+ * All date calculations go through an adapter layer.
12
+ * This allows:
13
+ * - Timezone-safe operations by default (NativeDateAdapter normalizes to day boundaries)
14
+ * - Optional integration with Luxon/DayJS/date-fns for advanced timezone handling
15
+ * - Consistent behavior across SSR and client
16
+ * - Migration path to timezone-aware libraries without breaking changes
17
+ *
18
+ * Architecture:
19
+ * ```
20
+ * DualDateRangeStore
21
+ * ↓ uses
22
+ * DateAdapter ← Inject via DATE_ADAPTER token
23
+ * ↓ implements
24
+ * NativeDateAdapter (default, zero deps)
25
+ * LuxonDateAdapter (optional, full timezone support)
26
+ * DayJSDateAdapter (optional, lightweight)
27
+ * ```
28
+ *
29
+ * Usage:
30
+ * ```typescript
31
+ * // Default (NativeDateAdapter)
32
+ * bootstrapApplication(AppComponent);
33
+ *
34
+ * // Advanced (Luxon with timezone)
35
+ * import { LuxonDateAdapter } from '@oneluiz/dual-datepicker/luxon';
36
+ *
37
+ * bootstrapApplication(AppComponent, {
38
+ * providers: [
39
+ * { provide: DATE_ADAPTER, useClass: LuxonDateAdapter }
40
+ * ]
41
+ * });
42
+ * ```
43
+ */
44
+ import { InjectionToken } from '@angular/core';
45
+ /**
46
+ * Injection token for DateAdapter
47
+ *
48
+ * Default: NativeDateAdapter (zero dependencies)
49
+ *
50
+ * Override for advanced timezone handling:
51
+ *
52
+ * ```typescript
53
+ * // Luxon with timezone
54
+ * import { LuxonDateAdapter } from '@oneluiz/dual-datepicker/luxon';
55
+ *
56
+ * bootstrapApplication(AppComponent, {
57
+ * providers: [
58
+ * {
59
+ * provide: DATE_ADAPTER,
60
+ * useClass: LuxonDateAdapter
61
+ * }
62
+ * ]
63
+ * });
64
+ * ```
65
+ *
66
+ * Custom implementation:
67
+ *
68
+ * ```typescript
69
+ * class CustomDateAdapter implements DateAdapter {
70
+ * // Your implementation for backend-specific date handling
71
+ * }
72
+ *
73
+ * provide(DATE_ADAPTER, { useClass: CustomDateAdapter });
74
+ * ```
75
+ */
76
+ export const DATE_ADAPTER = new InjectionToken('DATE_ADAPTER');
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvcmUvZGF0ZS1hZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQ0c7QUFFSCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBaVAvQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOEJHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHLElBQUksY0FBYyxDQUFjLGNBQWMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEYXRlIEFkYXB0ZXIgQWJzdHJhY3Rpb24gZm9yIFRpbWV6b25lLVNhZmUgRGF0ZSBPcGVyYXRpb25zXG4gKiBcbiAqIFByb2JsZW06XG4gKiBVc2luZyBEYXRlIG5hdGl2ZWx5IGluIGNhbGVuZGFyL3JhbmdlIGxvZ2ljIGNhdXNlcyBlbnRlcnByaXNlIGJ1Z3M6XG4gKiAtIERhdGUgcmFuZ2VzIHNoaWZ0IGJ5IDEgZGF5IGR1ZSB0byB0aW1lem9uZS9EU1RcbiAqIC0gU2VydmVyIChVVEMpIHZzIENsaWVudCAobG9jYWwgdGltZXpvbmUpIGRpc2NyZXBhbmNpZXNcbiAqIC0gSW5jb25zaXN0ZW50IHJlcG9ydGluZyBpbiBCSS9FUlAvaW52b2ljaW5nL2hvdGVsIHN5c3RlbXNcbiAqIFxuICogU29sdXRpb246XG4gKiBBbGwgZGF0ZSBjYWxjdWxhdGlvbnMgZ28gdGhyb3VnaCBhbiBhZGFwdGVyIGxheWVyLlxuICogVGhpcyBhbGxvd3M6XG4gKiAtIFRpbWV6b25lLXNhZmUgb3BlcmF0aW9ucyBieSBkZWZhdWx0IChOYXRpdmVEYXRlQWRhcHRlciBub3JtYWxpemVzIHRvIGRheSBib3VuZGFyaWVzKVxuICogLSBPcHRpb25hbCBpbnRlZ3JhdGlvbiB3aXRoIEx1eG9uL0RheUpTL2RhdGUtZm5zIGZvciBhZHZhbmNlZCB0aW1lem9uZSBoYW5kbGluZ1xuICogLSBDb25zaXN0ZW50IGJlaGF2aW9yIGFjcm9zcyBTU1IgYW5kIGNsaWVudFxuICogLSBNaWdyYXRpb24gcGF0aCB0byB0aW1lem9uZS1hd2FyZSBsaWJyYXJpZXMgd2l0aG91dCBicmVha2luZyBjaGFuZ2VzXG4gKiBcbiAqIEFyY2hpdGVjdHVyZTpcbiAqIGBgYFxuICogRHVhbERhdGVSYW5nZVN0b3JlXG4gKiAgICAg4oaTIHVzZXNcbiAqIERhdGVBZGFwdGVyIOKGkCBJbmplY3QgdmlhIERBVEVfQURBUFRFUiB0b2tlblxuICogICAgIOKGkyBpbXBsZW1lbnRzXG4gKiBOYXRpdmVEYXRlQWRhcHRlciAoZGVmYXVsdCwgemVybyBkZXBzKVxuICogTHV4b25EYXRlQWRhcHRlciAob3B0aW9uYWwsIGZ1bGwgdGltZXpvbmUgc3VwcG9ydClcbiAqIERheUpTRGF0ZUFkYXB0ZXIgKG9wdGlvbmFsLCBsaWdodHdlaWdodClcbiAqIGBgYFxuICogXG4gKiBVc2FnZTpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIERlZmF1bHQgKE5hdGl2ZURhdGVBZGFwdGVyKVxuICogYm9vdHN0cmFwQXBwbGljYXRpb24oQXBwQ29tcG9uZW50KTtcbiAqIFxuICogLy8gQWR2YW5jZWQgKEx1eG9uIHdpdGggdGltZXpvbmUpXG4gKiBpbXBvcnQgeyBMdXhvbkRhdGVBZGFwdGVyIH0gZnJvbSAnQG9uZWx1aXovZHVhbC1kYXRlcGlja2VyL2x1eG9uJztcbiAqIFxuICogYm9vdHN0cmFwQXBwbGljYXRpb24oQXBwQ29tcG9uZW50LCB7XG4gKiAgIHByb3ZpZGVyczogW1xuICogICAgIHsgcHJvdmlkZTogREFURV9BREFQVEVSLCB1c2VDbGFzczogTHV4b25EYXRlQWRhcHRlciB9XG4gKiAgIF1cbiAqIH0pO1xuICogYGBgXG4gKi9cblxuaW1wb3J0IHsgSW5qZWN0aW9uVG9rZW4gfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuLyoqXG4gKiBEYXRlIGFkYXB0ZXIgaW50ZXJmYWNlIGZvciBhbGwgY2FsZW5kYXIvcmFuZ2Ugb3BlcmF0aW9uc1xuICogXG4gKiBBbGwgbWV0aG9kcyBvcGVyYXRlIG9uIFwiY2FsZW5kYXIgZGF5XCIgbGV2ZWwsIGlnbm9yaW5nIHRpbWUgY29tcG9uZW50cy5cbiAqIEltcGxlbWVudGF0aW9ucyBtdXN0IGVuc3VyZSB0aW1lem9uZS1zYWZlIGJlaGF2aW9yLlxuICogXG4gKiBJbXBsZW1lbnRhdGlvbnM6XG4gKiAtIE5hdGl2ZURhdGVBZGFwdGVyOiBEZWZhdWx0LCB6ZXJvIGRlcGVuZGVuY2llcywgdXNlcyBEYXRlIHdpdGggbm9ybWFsaXphdGlvblxuICogLSBMdXhvbkRhdGVBZGFwdGVyOiBPcHRpb25hbCwgZnVsbCB0aW1lem9uZSBzdXBwb3J0IHdpdGggTHV4b25cbiAqIC0gRGF5SlNEYXRlQWRhcHRlcjogT3B0aW9uYWwsIGxpZ2h0d2VpZ2h0IHRpbWV6b25lIHN1cHBvcnRcbiAqIC0gQ3VzdG9tOiBJbXBsZW1lbnQgZm9yIHlvdXIgc3BlY2lmaWMgYmFja2VuZC90aW1lem9uZSByZXF1aXJlbWVudHNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEYXRlQWRhcHRlciB7XG4gIC8qKlxuICAgKiBOb3JtYWxpemUgZGF0ZSB0byBzdGFydCBvZiBkYXkgKDAwOjAwOjAwLjAwMClcbiAgICogXG4gICAqIENyaXRpY2FsIGZvciB0aW1lem9uZS1zYWZlIGNvbXBhcmlzb25zLlxuICAgKiBcbiAgICogRXhhbXBsZTpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBkYXRlID0gbmV3IERhdGUoJzIwMjYtMDItMjFUMTU6MzA6MDAnKTtcbiAgICogY29uc3Qgbm9ybWFsaXplZCA9IGFkYXB0ZXIubm9ybWFsaXplKGRhdGUpO1xuICAgKiAvLyDihpIgMjAyNi0wMi0yMVQwMDowMDowMC4wMDAgKGluIGxvY2FsIHRpbWV6b25lKVxuICAgKiBgYGBcbiAgICogXG4gICAqIEBwYXJhbSBkYXRlIC0gRGF0ZSB0byBub3JtYWxpemVcbiAgICogQHJldHVybnMgRGF0ZSB3aXRoIHRpbWUgc2V0IHRvIDAwOjAwOjAwLjAwMFxuICAgKi9cbiAgbm9ybWFsaXplKGRhdGU6IERhdGUpOiBEYXRlO1xuXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0d28gZGF0ZXMgcmVwcmVzZW50IHRoZSBzYW1lIGNhbGVuZGFyIGRheVxuICAgKiBcbiAgICogSWdub3JlcyB0aW1lIGNvbXBvbmVudC4gVGltZXpvbmUtc2FmZS5cbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgYSA9IG5ldyBEYXRlKCcyMDI2LTAyLTIxVDIzOjU5OjU5Jyk7XG4gICAqIGNvbnN0IGIgPSBuZXcgRGF0ZSgnMjAyNi0wMi0yMVQwMDowMDowMScpO1xuICAgKiBhZGFwdGVyLmlzU2FtZURheShhLCBiKTsgLy8g4oaSIHRydWVcbiAgICogYGBgXG4gICAqL1xuICBpc1NhbWVEYXkoYTogRGF0ZSwgYjogRGF0ZSk6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGRhdGUgQSBpcyBiZWZvcmUgZGF0ZSBCIChjYWxlbmRhciBkYXkgbGV2ZWwpXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGFkYXB0ZXIuaXNCZWZvcmVEYXkobmV3IERhdGUoJzIwMjYtMDItMjAnKSwgbmV3IERhdGUoJzIwMjYtMDItMjEnKSk7IC8vIOKGkiB0cnVlXG4gICAqIGFkYXB0ZXIuaXNCZWZvcmVEYXkobmV3IERhdGUoJzIwMjYtMDItMjEnKSwgbmV3IERhdGUoJzIwMjYtMDItMjEnKSk7IC8vIOKGkiBmYWxzZVxuICAgKiBgYGBcbiAgICovXG4gIGlzQmVmb3JlRGF5KGE6IERhdGUsIGI6IERhdGUpOiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBkYXRlIEEgaXMgYWZ0ZXIgZGF0ZSBCIChjYWxlbmRhciBkYXkgbGV2ZWwpXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGFkYXB0ZXIuaXNBZnRlckRheShuZXcgRGF0ZSgnMjAyNi0wMi0yMicpLCBuZXcgRGF0ZSgnMjAyNi0wMi0yMScpKTsgLy8g4oaSIHRydWVcbiAgICogYWRhcHRlci5pc0FmdGVyRGF5KG5ldyBEYXRlKCcyMDI2LTAyLTIxJyksIG5ldyBEYXRlKCcyMDI2LTAyLTIxJykpOyAvLyDihpIgZmFsc2VcbiAgICogYGBgXG4gICAqL1xuICBpc0FmdGVyRGF5KGE6IERhdGUsIGI6IERhdGUpOiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBBZGQgZGF5cyB0byBhIGRhdGVcbiAgICogXG4gICAqIE11c3QgaGFuZGxlIERTVCB0cmFuc2l0aW9ucyBjb3JyZWN0bHkuXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgnMjAyNi0wMi0yMScpO1xuICAgKiBjb25zdCBmdXR1cmUgPSBhZGFwdGVyLmFkZERheXMoZGF0ZSwgNyk7XG4gICAqIC8vIOKGkiAyMDI2LTAyLTI4XG4gICAqIGBgYFxuICAgKi9cbiAgYWRkRGF5cyhkYXRlOiBEYXRlLCBkYXlzOiBudW1iZXIpOiBEYXRlO1xuXG4gIC8qKlxuICAgKiBBZGQgbW9udGhzIHRvIGEgZGF0ZVxuICAgKiBcbiAgICogTXVzdCBoYW5kbGUgbW9udGggb3ZlcmZsb3cgKGUuZy4sIEphbiAzMSArIDEgbW9udGgg4oaSIEZlYiAyOCkuXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgnMjAyNi0wMS0zMScpO1xuICAgKiBjb25zdCBuZXh0ID0gYWRhcHRlci5hZGRNb250aHMoZGF0ZSwgMSk7XG4gICAqIC8vIOKGkiAyMDI2LTAyLTI4IChub3QgTWFyY2ggM3JkKVxuICAgKiBgYGBcbiAgICovXG4gIGFkZE1vbnRocyhkYXRlOiBEYXRlLCBtb250aHM6IG51bWJlcik6IERhdGU7XG5cbiAgLyoqXG4gICAqIEdldCBzdGFydCBvZiBkYXkgKDAwOjAwOjAwLjAwMClcbiAgICogXG4gICAqIFNpbWlsYXIgdG8gbm9ybWFsaXplKCkgYnV0IGV4cGxpY2l0IGludGVudC5cbiAgICovXG4gIHN0YXJ0T2ZEYXkoZGF0ZTogRGF0ZSk6IERhdGU7XG5cbiAgLyoqXG4gICAqIEdldCBlbmQgb2YgZGF5ICgyMzo1OTo1OS45OTkpXG4gICAqIFxuICAgKiBVc2VmdWwgZm9yIHJhbmdlIHF1ZXJpZXMgdGhhdCBuZWVkIHRvIGluY2x1ZGUgZW50aXJlIGRheS5cbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgZGF0ZSA9IG5ldyBEYXRlKCcyMDI2LTAyLTIxJyk7XG4gICAqIGNvbnN0IGVuZCA9IGFkYXB0ZXIuZW5kT2ZEYXkoZGF0ZSk7XG4gICAqIC8vIOKGkiAyMDI2LTAyLTIxVDIzOjU5OjU5Ljk5OVxuICAgKiBgYGBcbiAgICovXG4gIGVuZE9mRGF5KGRhdGU6IERhdGUpOiBEYXRlO1xuXG4gIC8qKlxuICAgKiBHZXQgZmlyc3QgZGF5IG9mIG1vbnRoICgwMDowMDowMC4wMDApXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgnMjAyNi0wMi0yMScpO1xuICAgKiBjb25zdCBzdGFydCA9IGFkYXB0ZXIuc3RhcnRPZk1vbnRoKGRhdGUpO1xuICAgKiAvLyDihpIgMjAyNi0wMi0wMVQwMDowMDowMC4wMDBcbiAgICogYGBgXG4gICAqL1xuICBzdGFydE9mTW9udGgoZGF0ZTogRGF0ZSk6IERhdGU7XG5cbiAgLyoqXG4gICAqIEdldCBsYXN0IGRheSBvZiBtb250aCAoMjM6NTk6NTkuOTk5KVxuICAgKiBcbiAgICogRXhhbXBsZTpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBkYXRlID0gbmV3IERhdGUoJzIwMjYtMDItMjEnKTtcbiAgICogY29uc3QgZW5kID0gYWRhcHRlci5lbmRPZk1vbnRoKGRhdGUpO1xuICAgKiAvLyDihpIgMjAyNi0wMi0yOFQyMzo1OTo1OS45OTlcbiAgICogYGBgXG4gICAqL1xuICBlbmRPZk1vbnRoKGRhdGU6IERhdGUpOiBEYXRlO1xuXG4gIC8qKlxuICAgKiBHZXQgeWVhciAoNC1kaWdpdClcbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYWRhcHRlci5nZXRZZWFyKG5ldyBEYXRlKCcyMDI2LTAyLTIxJykpOyAvLyDihpIgMjAyNlxuICAgKiBgYGBcbiAgICovXG4gIGdldFllYXIoZGF0ZTogRGF0ZSk6IG51bWJlcjtcblxuICAvKipcbiAgICogR2V0IG1vbnRoICgwLTExLCB3aGVyZSAwID0gSmFudWFyeSlcbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYWRhcHRlci5nZXRNb250aChuZXcgRGF0ZSgnMjAyNi0wMi0yMScpKTsgLy8g4oaSIDEgKEZlYnJ1YXJ5KVxuICAgKiBgYGBcbiAgICovXG4gIGdldE1vbnRoKGRhdGU6IERhdGUpOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEdldCBkYXkgb2YgbW9udGggKDEtMzEpXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGFkYXB0ZXIuZ2V0RGF0ZShuZXcgRGF0ZSgnMjAyNi0wMi0yMScpKTsgLy8g4oaSIDIxXG4gICAqIGBgYFxuICAgKi9cbiAgZ2V0RGF0ZShkYXRlOiBEYXRlKTogbnVtYmVyO1xuXG4gIC8qKlxuICAgKiBHZXQgZGF5IG9mIHdlZWsgKDAtNiwgd2hlcmUgMCA9IFN1bmRheSlcbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYWRhcHRlci5nZXREYXkobmV3IERhdGUoJzIwMjYtMDItMjEnKSk7IC8vIOKGkiA2IChTYXR1cmRheSlcbiAgICogYGBgXG4gICAqL1xuICBnZXREYXkoZGF0ZTogRGF0ZSk6IG51bWJlcjtcblxuICAvKipcbiAgICogQ29udmVydCBEYXRlIHRvIElTTyBkYXRlIHN0cmluZyAoWVlZWS1NTS1ERClcbiAgICogXG4gICAqIENSSVRJQ0FMOiBNdXN0IGJlIHRpbWV6b25lLXNhZmUhXG4gICAqIERPIE5PVCB1c2UgZGF0ZS50b0lTT1N0cmluZygpIGFzIGl0IGNvbnZlcnRzIHRvIFVUQy5cbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogLy8gTG9jYWwgdGltZXpvbmUgR01ULTYgKENTVClcbiAgICogY29uc3QgZGF0ZSA9IG5ldyBEYXRlKCcyMDI2LTAyLTIxVDIzOjAwOjAwJyk7IC8vIDExIFBNIENTVFxuICAgKiBcbiAgICogLy8g4p2MIFdST05HOiBkYXRlLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXVxuICAgKiAvLyBSZXR1cm5zIFwiMjAyNi0wMi0yMlwiIChzaGlmdGVkIHRvIFVUQyEpXG4gICAqIFxuICAgKiAvLyDinIUgQ09SUkVDVDogYWRhcHRlci50b0lTT0RhdGUoZGF0ZSlcbiAgICogLy8gUmV0dXJucyBcIjIwMjYtMDItMjFcIiAobG9jYWwgZGF0ZSBwcmVzZXJ2ZWQpXG4gICAqIGBgYFxuICAgKiBcbiAgICogQHBhcmFtIGRhdGUgLSBEYXRlIHRvIGZvcm1hdCAob3IgbnVsbClcbiAgICogQHJldHVybnMgSVNPIGRhdGUgc3RyaW5nIGluIFlZWVktTU0tREQgZm9ybWF0IChsb2NhbCB0aW1lem9uZSksIGVtcHR5IHN0cmluZyBpZiBudWxsXG4gICAqL1xuICB0b0lTT0RhdGUoZGF0ZTogRGF0ZSB8IG51bGwpOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFBhcnNlIElTTyBkYXRlIHN0cmluZyAoWVlZWS1NTS1ERCkgdG8gRGF0ZVxuICAgKiBcbiAgICogQ1JJVElDQUw6IE11c3QgYmUgdGltZXpvbmUtc2FmZSFcbiAgICogRE8gTk9UIHVzZSBuZXcgRGF0ZShpc29TdHJpbmcpIGFzIGl0IG1heSBwYXJzZSBhcyBVVEMuXG4gICAqIFxuICAgKiBFeGFtcGxlOlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIC8vIOKdjCBXUk9ORzogbmV3IERhdGUoJzIwMjYtMDItMjEnKVxuICAgKiAvLyBNYXkgcGFyc2UgYXMgVVRDIG1pZG5pZ2h0LCB3aGljaCBpcyBwcmV2aW91cyBkYXkgaW4gc29tZSB0aW1lem9uZXNcbiAgICogXG4gICAqIC8vIOKchSBDT1JSRUNUOiBhZGFwdGVyLnBhcnNlSVNPRGF0ZSgnMjAyNi0wMi0yMScpXG4gICAqIC8vIFJldHVybnMgRGF0ZSByZXByZXNlbnRpbmcgMjAyNi0wMi0yMSAwMDowMDowMCBpbiBsb2NhbCB0aW1lem9uZVxuICAgKiBgYGBcbiAgICogXG4gICAqIEBwYXJhbSBpc29EYXRlIC0gSVNPIGRhdGUgc3RyaW5nIChZWVlZLU1NLUREKSBvciBudWxsXG4gICAqIEByZXR1cm5zIERhdGUgb2JqZWN0IG9yIG51bGwgaWYgaW52YWxpZFxuICAgKi9cbiAgcGFyc2VJU09EYXRlKGlzb0RhdGU6IHN0cmluZyB8IG51bGwpOiBEYXRlIHwgbnVsbDtcblxuICAvKipcbiAgICogR2V0IHdlZWsgc3RhcnQgZGF5IGZvciBsb2NhbGVcbiAgICogXG4gICAqIDAgPSBTdW5kYXksIDEgPSBNb25kYXksIGV0Yy5cbiAgICogXG4gICAqIEV4YW1wbGU6XG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYWRhcHRlci5nZXRXZWVrU3RhcnQoJ2VuLVVTJyk7IC8vIOKGkiAwIChTdW5kYXkpXG4gICAqIGFkYXB0ZXIuZ2V0V2Vla1N0YXJ0KCdlbi1HQicpOyAvLyDihpIgMSAoTW9uZGF5KVxuICAgKiBgYGBcbiAgICogXG4gICAqIEBwYXJhbSBsb2NhbGUgLSBMb2NhbGUgc3RyaW5nIChlLmcuLCAnZW4tVVMnLCAnZXMtRVMnKVxuICAgKiBAcmV0dXJucyBEYXkgbnVtYmVyICgwLTYpXG4gICAqL1xuICBnZXRXZWVrU3RhcnQobG9jYWxlPzogc3RyaW5nKTogMCB8IDEgfCAyIHwgMyB8IDQgfCA1IHwgNjtcbn1cblxuLyoqXG4gKiBJbmplY3Rpb24gdG9rZW4gZm9yIERhdGVBZGFwdGVyXG4gKiBcbiAqIERlZmF1bHQ6IE5hdGl2ZURhdGVBZGFwdGVyICh6ZXJvIGRlcGVuZGVuY2llcylcbiAqIFxuICogT3ZlcnJpZGUgZm9yIGFkdmFuY2VkIHRpbWV6b25lIGhhbmRsaW5nOlxuICogXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBMdXhvbiB3aXRoIHRpbWV6b25lXG4gKiBpbXBvcnQgeyBMdXhvbkRhdGVBZGFwdGVyIH0gZnJvbSAnQG9uZWx1aXovZHVhbC1kYXRlcGlja2VyL2x1eG9uJztcbiAqIFxuICogYm9vdHN0cmFwQXBwbGljYXRpb24oQXBwQ29tcG9uZW50LCB7XG4gKiAgIHByb3ZpZGVyczogW1xuICogICAgIHtcbiAqICAgICAgIHByb3ZpZGU6IERBVEVfQURBUFRFUixcbiAqICAgICAgIHVzZUNsYXNzOiBMdXhvbkRhdGVBZGFwdGVyXG4gKiAgICAgfVxuICogICBdXG4gKiB9KTtcbiAqIGBgYFxuICogXG4gKiBDdXN0b20gaW1wbGVtZW50YXRpb246XG4gKiBcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNsYXNzIEN1c3RvbURhdGVBZGFwdGVyIGltcGxlbWVudHMgRGF0ZUFkYXB0ZXIge1xuICogICAvLyBZb3VyIGltcGxlbWVudGF0aW9uIGZvciBiYWNrZW5kLXNwZWNpZmljIGRhdGUgaGFuZGxpbmdcbiAqIH1cbiAqIFxuICogcHJvdmlkZShEQVRFX0FEQVBURVIsIHsgdXNlQ2xhc3M6IEN1c3RvbURhdGVBZGFwdGVyIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjb25zdCBEQVRFX0FEQVBURVIgPSBuZXcgSW5qZWN0aW9uVG9rZW48RGF0ZUFkYXB0ZXI+KCdEQVRFX0FEQVBURVInKTtcbiJdfQ==
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Date Clock Abstraction for SSR-Safe Date Resolution
3
+ *
4
+ * Problem:
5
+ * Presets like "Last 7 Days" or "This Month" use new Date() which causes:
6
+ * - SSR hydration mismatch
7
+ * - Server renders "2026-02-14", client renders "2026-02-15"
8
+ * - Different filters in dashboards
9
+ * - Different queries in ERP/BI
10
+ * - Cache inconsistency
11
+ *
12
+ * Solution:
13
+ * Inject a DateClock to control time deterministically:
14
+ *
15
+ * Server (SSR):
16
+ * provide(DATE_CLOCK, {
17
+ * useValue: { now: () => new Date('2026-02-21T00:00:00Z') }
18
+ * })
19
+ *
20
+ * Client (Browser):
21
+ * Uses SystemClock by default (new Date())
22
+ *
23
+ * Testing:
24
+ * provide(DATE_CLOCK, {
25
+ * useValue: { now: () => new Date('2026-01-15T10:30:00Z') }
26
+ * })
27
+ *
28
+ * Architecture:
29
+ * - PresetEngine receives DateClock via DI
30
+ * - All preset calculations use clock.now() instead of new Date()
31
+ * - Deterministic: Same clock.now() → Same preset result
32
+ * - SSR-compatible: Server and client resolve identical ranges
33
+ */
34
+ import { InjectionToken } from '@angular/core';
35
+ /**
36
+ * Injection token for DateClock
37
+ *
38
+ * Usage:
39
+ * ```typescript
40
+ * // Default (uses SystemClock)
41
+ * bootstrapApplication(AppComponent);
42
+ *
43
+ * // SSR Override
44
+ * bootstrapApplication(AppComponent, {
45
+ * providers: [
46
+ * {
47
+ * provide: DATE_CLOCK,
48
+ * useValue: { now: () => new Date('2026-02-21T00:00:00Z') }
49
+ * }
50
+ * ]
51
+ * });
52
+ *
53
+ * // Testing Override
54
+ * TestBed.configureTestingModule({
55
+ * providers: [
56
+ * {
57
+ * provide: DATE_CLOCK,
58
+ * useValue: { now: () => new Date('2026-01-15T12:00:00Z') }
59
+ * }
60
+ * ]
61
+ * });
62
+ * ```
63
+ */
64
+ export const DATE_CLOCK = new InjectionToken('DATE_CLOCK');
65
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS1jbG9jay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb3JlL2RhdGUtY2xvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NHO0FBRUgsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQXFCL0M7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Qkc7QUFDSCxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsSUFBSSxjQUFjLENBQVksWUFBWSxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIERhdGUgQ2xvY2sgQWJzdHJhY3Rpb24gZm9yIFNTUi1TYWZlIERhdGUgUmVzb2x1dGlvblxuICogXG4gKiBQcm9ibGVtOlxuICogUHJlc2V0cyBsaWtlIFwiTGFzdCA3IERheXNcIiBvciBcIlRoaXMgTW9udGhcIiB1c2UgbmV3IERhdGUoKSB3aGljaCBjYXVzZXM6XG4gKiAtIFNTUiBoeWRyYXRpb24gbWlzbWF0Y2hcbiAqIC0gU2VydmVyIHJlbmRlcnMgXCIyMDI2LTAyLTE0XCIsIGNsaWVudCByZW5kZXJzIFwiMjAyNi0wMi0xNVwiXG4gKiAtIERpZmZlcmVudCBmaWx0ZXJzIGluIGRhc2hib2FyZHNcbiAqIC0gRGlmZmVyZW50IHF1ZXJpZXMgaW4gRVJQL0JJXG4gKiAtIENhY2hlIGluY29uc2lzdGVuY3lcbiAqIFxuICogU29sdXRpb246XG4gKiBJbmplY3QgYSBEYXRlQ2xvY2sgdG8gY29udHJvbCB0aW1lIGRldGVybWluaXN0aWNhbGx5OlxuICogXG4gKiBTZXJ2ZXIgKFNTUik6XG4gKiBwcm92aWRlKERBVEVfQ0xPQ0ssIHtcbiAqICAgdXNlVmFsdWU6IHsgbm93OiAoKSA9PiBuZXcgRGF0ZSgnMjAyNi0wMi0yMVQwMDowMDowMFonKSB9XG4gKiB9KVxuICogXG4gKiBDbGllbnQgKEJyb3dzZXIpOlxuICogVXNlcyBTeXN0ZW1DbG9jayBieSBkZWZhdWx0IChuZXcgRGF0ZSgpKVxuICogXG4gKiBUZXN0aW5nOlxuICogcHJvdmlkZShEQVRFX0NMT0NLLCB7XG4gKiAgIHVzZVZhbHVlOiB7IG5vdzogKCkgPT4gbmV3IERhdGUoJzIwMjYtMDEtMTVUMTA6MzA6MDBaJykgfVxuICogfSlcbiAqIFxuICogQXJjaGl0ZWN0dXJlOlxuICogLSBQcmVzZXRFbmdpbmUgcmVjZWl2ZXMgRGF0ZUNsb2NrIHZpYSBESVxuICogLSBBbGwgcHJlc2V0IGNhbGN1bGF0aW9ucyB1c2UgY2xvY2subm93KCkgaW5zdGVhZCBvZiBuZXcgRGF0ZSgpXG4gKiAtIERldGVybWluaXN0aWM6IFNhbWUgY2xvY2subm93KCkg4oaSIFNhbWUgcHJlc2V0IHJlc3VsdFxuICogLSBTU1ItY29tcGF0aWJsZTogU2VydmVyIGFuZCBjbGllbnQgcmVzb2x2ZSBpZGVudGljYWwgcmFuZ2VzXG4gKi9cblxuaW1wb3J0IHsgSW5qZWN0aW9uVG9rZW4gfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuLyoqXG4gKiBDbG9jayBhYnN0cmFjdGlvbiBmb3IgZGV0ZXJtaW5pc3RpYyBkYXRlIHJlc29sdXRpb25cbiAqIFxuICogVXNlIGNhc2VzOlxuICogLSBTU1I6IEVuc3VyZSBzZXJ2ZXIgYW5kIGNsaWVudCBnZW5lcmF0ZSBpZGVudGljYWwgcHJlc2V0c1xuICogLSBUZXN0aW5nOiBDb250cm9sIHRpbWUgZm9yIHByZWRpY3RhYmxlIHRlc3QgcmVzdWx0c1xuICogLSBSZXBsYXk6IFJlcHJvZHVjZSBleGFjdCB1c2VyIHN0YXRlIGZyb20gcGFzdCBzZXNzaW9uc1xuICogLSBEZW1vOiBGcmVlemUgdGltZSBmb3IgcmVwcm9kdWNpYmxlIGRlbW9zXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGF0ZUNsb2NrIHtcbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IGRhdGUvdGltZVxuICAgKiBcbiAgICogRGVmYXVsdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5zIG5ldyBEYXRlKClcbiAgICogT3ZlcnJpZGUgZm9yIFNTUiwgdGVzdGluZywgb3IgdGltZS10cmF2ZWwgc2NlbmFyaW9zXG4gICAqL1xuICBub3coKTogRGF0ZTtcbn1cblxuLyoqXG4gKiBJbmplY3Rpb24gdG9rZW4gZm9yIERhdGVDbG9ja1xuICogXG4gKiBVc2FnZTpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIERlZmF1bHQgKHVzZXMgU3lzdGVtQ2xvY2spXG4gKiBib290c3RyYXBBcHBsaWNhdGlvbihBcHBDb21wb25lbnQpO1xuICogXG4gKiAvLyBTU1IgT3ZlcnJpZGVcbiAqIGJvb3RzdHJhcEFwcGxpY2F0aW9uKEFwcENvbXBvbmVudCwge1xuICogICBwcm92aWRlcnM6IFtcbiAqICAgICB7XG4gKiAgICAgICBwcm92aWRlOiBEQVRFX0NMT0NLLFxuICogICAgICAgdXNlVmFsdWU6IHsgbm93OiAoKSA9PiBuZXcgRGF0ZSgnMjAyNi0wMi0yMVQwMDowMDowMFonKSB9XG4gKiAgICAgfVxuICogICBdXG4gKiB9KTtcbiAqIFxuICogLy8gVGVzdGluZyBPdmVycmlkZVxuICogVGVzdEJlZC5jb25maWd1cmVUZXN0aW5nTW9kdWxlKHtcbiAqICAgcHJvdmlkZXJzOiBbXG4gKiAgICAge1xuICogICAgICAgcHJvdmlkZTogREFURV9DTE9DSyxcbiAqICAgICAgIHVzZVZhbHVlOiB7IG5vdzogKCkgPT4gbmV3IERhdGUoJzIwMjYtMDEtMTVUMTI6MDA6MDBaJykgfVxuICogICAgIH1cbiAqICAgXVxuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNvbnN0IERBVEVfQ0xPQ0sgPSBuZXcgSW5qZWN0aW9uVG9rZW48RGF0ZUNsb2NrPignREFURV9DTE9DSycpO1xuIl19