@tracearr/shared 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +7 -0
  3. package/dist/catalogLetters.d.ts +3 -0
  4. package/dist/catalogLetters.d.ts.map +1 -0
  5. package/dist/catalogLetters.js +3 -0
  6. package/dist/catalogLetters.js.map +1 -0
  7. package/dist/constants.d.ts +475 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +847 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/dynamicRange.d.ts +16 -0
  12. package/dist/dynamicRange.d.ts.map +1 -0
  13. package/dist/dynamicRange.js +46 -0
  14. package/dist/dynamicRange.js.map +1 -0
  15. package/dist/index.d.ts +17 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +68 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/media.d.ts +16 -0
  20. package/dist/media.d.ts.map +1 -0
  21. package/dist/media.js +18 -0
  22. package/dist/media.js.map +1 -0
  23. package/dist/resolution.d.ts +56 -0
  24. package/dist/resolution.d.ts.map +1 -0
  25. package/dist/resolution.js +130 -0
  26. package/dist/resolution.js.map +1 -0
  27. package/dist/schemas.d.ts +1702 -0
  28. package/dist/schemas.d.ts.map +1 -0
  29. package/dist/schemas.js +1057 -0
  30. package/dist/schemas.js.map +1 -0
  31. package/dist/serverScope.d.ts +27 -0
  32. package/dist/serverScope.d.ts.map +1 -0
  33. package/dist/serverScope.js +36 -0
  34. package/dist/serverScope.js.map +1 -0
  35. package/dist/types.d.ts +2484 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +57 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/violations.d.ts +37 -0
  40. package/dist/violations.d.ts.map +1 -0
  41. package/dist/violations.js +377 -0
  42. package/dist/violations.js.map +1 -0
  43. package/package.json +46 -0
@@ -0,0 +1,3 @@
1
+ /** Alphabet rail letters, shared by the catalog route and the browse grid. */
2
+ export const LETTER_RAIL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
3
+ //# sourceMappingURL=catalogLetters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogLetters.js","sourceRoot":"","sources":["../src/catalogLetters.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,MAAM,CAAC,MAAM,oBAAoB,GAAG,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC"}
@@ -0,0 +1,475 @@
1
+ /**
2
+ * Shared constants for Tracearr
3
+ */
4
+ import { classifyByDimensions, type ResolutionLabel } from './resolution.js';
5
+ export declare const RULE_DEFAULTS: {
6
+ readonly impossible_travel: {
7
+ readonly maxSpeedKmh: 500;
8
+ readonly ignoreVpnRanges: false;
9
+ readonly excludePrivateIps: false;
10
+ };
11
+ readonly simultaneous_locations: {
12
+ readonly minDistanceKm: 100;
13
+ readonly excludePrivateIps: false;
14
+ };
15
+ readonly device_velocity: {
16
+ readonly maxIps: 5;
17
+ readonly windowHours: 24;
18
+ readonly excludePrivateIps: false;
19
+ readonly groupByDevice: false;
20
+ };
21
+ readonly concurrent_streams: {
22
+ readonly maxStreams: 3;
23
+ readonly excludePrivateIps: false;
24
+ };
25
+ readonly geo_restriction: {
26
+ readonly mode: "blocklist";
27
+ readonly countries: readonly [];
28
+ readonly excludePrivateIps: false;
29
+ };
30
+ readonly account_inactivity: {
31
+ readonly inactivityValue: 30;
32
+ readonly inactivityUnit: "days";
33
+ };
34
+ };
35
+ export declare const RULE_DISPLAY_NAMES: {
36
+ readonly impossible_travel: "Impossible Travel";
37
+ readonly simultaneous_locations: "Simultaneous Locations";
38
+ readonly device_velocity: "Device Velocity";
39
+ readonly concurrent_streams: "Concurrent Streams";
40
+ readonly geo_restriction: "Geo Restriction";
41
+ readonly account_inactivity: "Account Inactivity";
42
+ };
43
+ export declare const IDENTITY_AWARE_CONDITION_FIELDS: readonly ["concurrent_streams", "active_session_distance_km", "travel_speed_kmh", "unique_ips_in_window", "unique_devices_in_window"];
44
+ export declare const SEVERITY_LEVELS: {
45
+ readonly low: {
46
+ readonly label: "Low";
47
+ readonly priority: 1;
48
+ };
49
+ readonly warning: {
50
+ readonly label: "Warning";
51
+ readonly priority: 2;
52
+ };
53
+ readonly high: {
54
+ readonly label: "High";
55
+ readonly priority: 3;
56
+ };
57
+ };
58
+ export type SeverityPriority = 1 | 2 | 3;
59
+ export declare function getSeverityPriority(severity: keyof typeof SEVERITY_LEVELS): SeverityPriority;
60
+ export declare const WS_EVENTS: {
61
+ readonly SESSION_STARTED: "session:started";
62
+ readonly SESSION_STOPPED: "session:stopped";
63
+ readonly SESSION_UPDATED: "session:updated";
64
+ readonly VIOLATION_NEW: "violation:new";
65
+ readonly STATS_UPDATED: "stats:updated";
66
+ readonly IMPORT_PROGRESS: "import:progress";
67
+ readonly IMPORT_JELLYSTAT_PROGRESS: "import:jellystat:progress";
68
+ readonly MAINTENANCE_PROGRESS: "maintenance:progress";
69
+ /** Library sync progress updates */
70
+ readonly LIBRARY_SYNC_PROGRESS: "library:sync:progress";
71
+ /** Unified running tasks updates */
72
+ readonly TASKS_UPDATED: "tasks:updated";
73
+ readonly SUBSCRIBE_SESSIONS: "subscribe:sessions";
74
+ readonly UNSUBSCRIBE_SESSIONS: "unsubscribe:sessions";
75
+ readonly VERSION_UPDATE: "version:update";
76
+ readonly SERVER_DOWN: "server:down";
77
+ readonly SERVER_UP: "server:up";
78
+ readonly SERVER_CONNECTION: "server:connection";
79
+ };
80
+ /**
81
+ * Set the global prefix prepended to all Redis keys.
82
+ * Call this at server startup before any Redis operations.
83
+ * @param prefix - Prefix string (e.g. 'myapp:')
84
+ */
85
+ export declare function setRedisPrefix(prefix: string): void;
86
+ /**
87
+ * Get the current Redis key prefix.
88
+ */
89
+ export declare function getRedisPrefix(): string;
90
+ export declare const REDIS_KEYS: {
91
+ readonly ACTIVE_SESSION_IDS: string;
92
+ readonly ACTIVE_SESSIONS: string;
93
+ SESSION_BY_ID: (id: string) => string;
94
+ /**
95
+ * Pending session data (before DB write) - keyed by serverId + sessionKey.
96
+ */
97
+ PENDING_SESSION: (serverId: string, sessionKey: string) => string;
98
+ /** Set of all pending session keys (serverId:sessionKey format) for enumeration */
99
+ readonly PENDING_SESSION_IDS: string;
100
+ readonly DASHBOARD_STATS: string;
101
+ RATE_LIMIT_LOGIN: (ip: string) => string;
102
+ RATE_LIMIT_MOBILE_PAIR: (ip: string) => string;
103
+ RATE_LIMIT_MOBILE_REFRESH: (ip: string) => string;
104
+ SERVER_HEALTH: (serverId: string) => string;
105
+ SERVER_HEALTH_FAIL_COUNT: (serverId: string) => string;
106
+ SERVER_CONNECTION: (serverId: string) => string;
107
+ readonly PUBSUB_EVENTS: string;
108
+ PUSH_RATE_MINUTE: (sessionId: string) => string;
109
+ PUSH_RATE_HOUR: (sessionId: string) => string;
110
+ LOCATION_FILTERS: (userId: string, serverIds: string[]) => string;
111
+ readonly VERSION_LATEST: string;
112
+ readonly VERSION_CHECK_COOLDOWN: string;
113
+ readonly LIBRARY_STATS: string;
114
+ readonly LIBRARY_GROWTH: string;
115
+ readonly LIBRARY_QUALITY: string;
116
+ readonly LIBRARY_STALE: string;
117
+ readonly LIBRARY_DUPLICATES: string;
118
+ readonly LIBRARY_STORAGE: string;
119
+ readonly LIBRARY_WATCH: string;
120
+ readonly LIBRARY_ROI: string;
121
+ readonly LIBRARY_PATTERNS: string;
122
+ readonly LIBRARY_COMPLETION: string;
123
+ readonly LIBRARY_TOP_MOVIES: string;
124
+ readonly LIBRARY_TOP_SHOWS: string;
125
+ readonly LIBRARY_CODECS: string;
126
+ readonly LIBRARY_RESOLUTION: string;
127
+ readonly LIBRARY_SHELVES: string;
128
+ readonly LIBRARY_GENRES: string;
129
+ readonly LIBRARY_CATALOG_LETTERS: string;
130
+ readonly LIBRARY_LIBRARIES: string;
131
+ readonly LIBRARY_CATALOG_WATCHED: string;
132
+ readonly LIBRARY_CATALOG_TOTALS: string;
133
+ LIBRARY_SINGLE_FLIGHT_LOCK: (cacheKey: string) => string;
134
+ LIBRARY_MEDIA_DETAIL: (cacheKey: string) => string;
135
+ LIBRARY_SYNC_LAST: (serverId: string, libraryId: string) => string;
136
+ LIBRARY_SYNC_COUNT: (serverId: string, libraryId: string) => string;
137
+ LIBRARY_SYNC_CYCLE: (serverId: string, libraryId: string) => string;
138
+ LIBRARY_SYNC_SHORTFALL: (serverId: string, libraryId: string) => string;
139
+ LIBRARY_PRECACHE_WATERMARK: (serverId: string) => string;
140
+ LIBRARY_PRECACHE_LAST_FULL: (serverId: string) => string;
141
+ REFRESH_TOKEN: (hash: string) => string;
142
+ PLEX_TEMP_TOKEN: (token: string) => string;
143
+ MOBILE_REFRESH_TOKEN: (hash: string) => string;
144
+ MOBILE_BLACKLISTED_TOKEN: (deviceId: string) => string;
145
+ MOBILE_LAST_SEEN: (deviceId: string) => string;
146
+ MOBILE_TOKEN_GEN_RATE: (userId: string) => string;
147
+ readonly HEAVY_OPS_LOCK: string;
148
+ SESSION_LOCK: (serverId: string, sessionKey: string) => string;
149
+ TERMINATION_COOLDOWN: (serverId: string, sessionKey: string, ratingKey: string) => string;
150
+ TERMINATION_COOLDOWN_COMPOSITE: (serverId: string, serverUserId: string, deviceId: string, ratingKey: string) => string;
151
+ RULE_COOLDOWN: (ruleId: string, targetId: string) => string;
152
+ SESSION_WRITE_RETRY: (sessionId: string) => string;
153
+ readonly SESSION_WRITE_RETRY_SET: string;
154
+ FILTER_OPTIONS: (userId: string, scopeHash: string) => string;
155
+ PLEX_GEOIP: (ip: string) => string;
156
+ PUBLIC_MEDIA_STATS: (cacheKey: string) => string;
157
+ };
158
+ export declare const CACHE_TTL: {
159
+ readonly DASHBOARD_STATS: 60;
160
+ readonly PUBLIC_MEDIA_STATS: 60;
161
+ readonly ACTIVE_SESSIONS: 150;
162
+ readonly PENDING_SESSIONS: 300;
163
+ readonly RATE_LIMIT: 900;
164
+ readonly SERVER_HEALTH: 600;
165
+ readonly SERVER_CONNECTION: 600;
166
+ readonly LOCATION_FILTERS: 300;
167
+ readonly VERSION_CHECK: 21600;
168
+ readonly LIBRARY_STATS: 300;
169
+ readonly LIBRARY_GROWTH: 300;
170
+ readonly LIBRARY_QUALITY: 300;
171
+ readonly LIBRARY_STALE: 3600;
172
+ readonly LIBRARY_DUPLICATES: 3600;
173
+ readonly LIBRARY_STORAGE: 300;
174
+ readonly LIBRARY_WATCH: 300;
175
+ readonly LIBRARY_ROI: 3600;
176
+ readonly LIBRARY_PATTERNS: 3600;
177
+ readonly LIBRARY_COMPLETION: 300;
178
+ readonly LIBRARY_TOP_MOVIES: 300;
179
+ readonly LIBRARY_TOP_SHOWS: 300;
180
+ readonly LIBRARY_CODECS: 300;
181
+ readonly LIBRARY_RESOLUTION: 300;
182
+ readonly LIBRARY_SHELVES: 300;
183
+ readonly LIBRARY_GENRES: 3600;
184
+ readonly LIBRARY_CATALOG_LETTERS: 300;
185
+ readonly LIBRARY_LIBRARIES: 300;
186
+ readonly LIBRARY_MEDIA_DETAIL: 60;
187
+ readonly MOBILE_LAST_SEEN: 300;
188
+ readonly FILTER_OPTIONS: 120;
189
+ readonly PLEX_GEOIP: 86400;
190
+ readonly PLEX_GEOIP_NEGATIVE: 600;
191
+ };
192
+ export declare const NOTIFICATION_EVENTS: {
193
+ readonly VIOLATION_DETECTED: "violation_detected";
194
+ readonly STREAM_STARTED: "stream_started";
195
+ readonly STREAM_STOPPED: "stream_stopped";
196
+ readonly CONCURRENT_STREAMS: "concurrent_streams";
197
+ readonly NEW_DEVICE: "new_device";
198
+ readonly TRUST_SCORE_CHANGED: "trust_score_changed";
199
+ readonly SERVER_DOWN: "server_down";
200
+ readonly SERVER_UP: "server_up";
201
+ readonly PLUGIN_UPDATE_AVAILABLE: "plugin_update_available";
202
+ };
203
+ /**
204
+ * server_version_key of the placeholder version rows the multi-version
205
+ * migration seeds from flat library_items columns. Never surfaced as a real
206
+ * version and hard-deleted (not tombstoned) when observed versions replace it.
207
+ */
208
+ export declare const LEGACY_VERSION_SENTINEL = "legacy:1";
209
+ export declare const API_VERSION = "v1";
210
+ export declare const API_BASE_PATH = "/api/v1";
211
+ export declare const API_VERSION_V2 = "v2";
212
+ export declare const API_V2_BASE_PATH = "/api/v2";
213
+ export declare const JWT_CONFIG: {
214
+ readonly ACCESS_TOKEN_EXPIRY: "48h";
215
+ readonly REFRESH_TOKEN_EXPIRY: "30d";
216
+ readonly ALGORITHM: "HS256";
217
+ };
218
+ export declare const POLLING_INTERVALS: {
219
+ readonly SESSIONS_ACTIVE: 3000;
220
+ readonly SESSIONS_IDLE: 10000;
221
+ /** @deprecated Use SESSIONS_ACTIVE */
222
+ readonly SESSIONS: 7000;
223
+ readonly STATS_REFRESH: 60000;
224
+ readonly SERVER_HEALTH: 30000;
225
+ readonly SSE_RECONCILIATION: number;
226
+ };
227
+ export declare const POLLER_CONFIG: {
228
+ readonly DOWN_THRESHOLD: 3;
229
+ };
230
+ export declare const SSE_CONFIG: {
231
+ readonly INITIAL_RETRY_DELAY_MS: 1000;
232
+ readonly MAX_RETRY_DELAY_MS: 30000;
233
+ readonly RETRY_MULTIPLIER: 2;
234
+ readonly MAX_RETRIES: 10;
235
+ readonly HEARTBEAT_TIMEOUT_MS: 30000;
236
+ readonly FALLBACK_THRESHOLD: 5;
237
+ };
238
+ export declare const PLEX_SSE_EVENTS: {
239
+ readonly PLAYING: "playing";
240
+ readonly PROGRESS: "progress";
241
+ readonly STOPPED: "stopped";
242
+ readonly PAUSED: "paused";
243
+ readonly RESUMED: "resumed";
244
+ readonly LIBRARY_UPDATE: "library.update";
245
+ readonly LIBRARY_SCAN: "library.scan";
246
+ readonly SERVER_BACKUP: "server.backup";
247
+ readonly SERVER_UPDATE: "server.update";
248
+ readonly ACTIVITY: "activity";
249
+ readonly TRANSCODE_SESSION_UPDATE: "transcodeSession.update";
250
+ readonly TRANSCODE_SESSION_END: "transcodeSession.end";
251
+ };
252
+ export declare const SSE_STATE: {
253
+ readonly CONNECTING: "connecting";
254
+ readonly CONNECTED: "connected";
255
+ readonly RECONNECTING: "reconnecting";
256
+ readonly DISCONNECTED: "disconnected";
257
+ readonly FALLBACK: "fallback";
258
+ };
259
+ export declare const PAGINATION: {
260
+ readonly DEFAULT_PAGE: 1;
261
+ readonly DEFAULT_PAGE_SIZE: 20;
262
+ readonly MAX_PAGE_SIZE: 100;
263
+ };
264
+ export declare const GEOIP_CONFIG: {
265
+ readonly EARTH_RADIUS_KM: 6371;
266
+ readonly DEFAULT_UNKNOWN_LOCATION: "Unknown";
267
+ };
268
+ export declare const UNIT_CONVERSION: {
269
+ readonly KM_TO_MILES: 0.621371;
270
+ readonly MILES_TO_KM: 1.60934;
271
+ };
272
+ export type UnitSystem = 'metric' | 'imperial';
273
+ /**
274
+ * Convert kilometers to miles
275
+ */
276
+ export declare function kmToMiles(km: number): number;
277
+ /**
278
+ * Convert miles to kilometers
279
+ */
280
+ export declare function milesToKm(miles: number): number;
281
+ /**
282
+ * Format distance based on unit system
283
+ * @param km - Distance in kilometers (internal unit)
284
+ * @param unitSystem - User's preferred unit system
285
+ * @param decimals - Number of decimal places (default: 0)
286
+ */
287
+ export declare function formatDistance(km: number, unitSystem: UnitSystem, decimals?: number): string;
288
+ /**
289
+ * Format speed based on unit system
290
+ * @param kmh - Speed in km/h (internal unit)
291
+ * @param unitSystem - User's preferred unit system
292
+ * @param decimals - Number of decimal places (default: 0)
293
+ */
294
+ export declare function formatSpeed(kmh: number, unitSystem: UnitSystem, decimals?: number): string;
295
+ /**
296
+ * Get distance unit label
297
+ */
298
+ export declare function getDistanceUnit(unitSystem: UnitSystem): string;
299
+ /**
300
+ * Get speed unit label
301
+ */
302
+ export declare function getSpeedUnit(unitSystem: UnitSystem): string;
303
+ /**
304
+ * Convert display value to internal metric value (for form inputs)
305
+ * @param value - Value in user's preferred unit
306
+ * @param unitSystem - User's preferred unit system
307
+ * @returns Value in kilometers (internal unit)
308
+ */
309
+ export declare function toMetricDistance(value: number, unitSystem: UnitSystem): number;
310
+ /**
311
+ * Convert internal metric value to display value (for form inputs)
312
+ * @param km - Value in kilometers (internal unit)
313
+ * @param unitSystem - User's preferred unit system
314
+ * @returns Value in user's preferred unit
315
+ */
316
+ export declare function fromMetricDistance(km: number, unitSystem: UnitSystem): number;
317
+ /**
318
+ * Convert a condition field value for display based on user's unit preference.
319
+ *
320
+ * @param value - The value to convert
321
+ * @param field - The condition field name
322
+ * @param unitSystem - User's preferred unit system
323
+ * @returns Object with displayValue (rounded) and unit label
324
+ */
325
+ export declare function formatConditionFieldValue(value: number, field: string, unitSystem: UnitSystem): {
326
+ displayValue: number;
327
+ unit: string;
328
+ };
329
+ /**
330
+ * Format bitrate for display with appropriate unit (kbps, Mbps, Gbps)
331
+ * @param kbps - Bitrate in kilobits per second
332
+ * @returns Formatted string with unit (e.g., "20.5 Mbps", "800 kbps")
333
+ */
334
+ export declare function formatBitrate(kbps: number | null | undefined): string;
335
+ /**
336
+ * Format a media tech string (resolution, codec, dynamic range) for display.
337
+ * Uses a lookup map for known values, falls back to uppercase for unknown.
338
+ *
339
+ * @param value - Tech string (e.g., "4k", "hevc", "truehd", "dolby vision")
340
+ * @returns Formatted string with proper casing
341
+ *
342
+ * @example
343
+ * formatMediaTech("4k") // "4K"
344
+ * formatMediaTech("hevc") // "HEVC"
345
+ * formatMediaTech("truehd") // "TrueHD"
346
+ * formatMediaTech("dolby vision") // "Dolby Vision"
347
+ */
348
+ export declare function formatMediaTech(value: string | null | undefined): string;
349
+ export type { ResolutionLabel };
350
+ /**
351
+ * Get video resolution label from width and height. Thin re-export of the
352
+ * shared dimension ladder in resolution.ts - kept here so existing imports
353
+ * of `getResolutionLabel` from `@tracearr/shared` keep working.
354
+ *
355
+ * @param width - Video width in pixels
356
+ * @param height - Video height in pixels
357
+ * @returns Resolution label: "8K", "4K", "1440p", "1080p", "720p", "480p", "SD", or null
358
+ *
359
+ * @example
360
+ * getResolutionLabel(7680, 4320) // "8K"
361
+ * getResolutionLabel(3840, 2160) // "4K"
362
+ * getResolutionLabel(2560, 1440) // "1440p"
363
+ * getResolutionLabel(1920, 1080) // "1080p"
364
+ * getResolutionLabel(1920, 800) // "1080p" (widescreen - width indicates quality)
365
+ * getResolutionLabel(1440, 1080) // "1080p" (4:3 - height indicates quality)
366
+ * getResolutionLabel(1280, 720) // "720p"
367
+ * getResolutionLabel(null, 1080) // "1080p" (fallback to height)
368
+ */
369
+ export declare const getResolutionLabel: typeof classifyByDimensions;
370
+ /**
371
+ * Format video resolution with dimensions and label for display.
372
+ *
373
+ * @param width - Video width in pixels
374
+ * @param height - Video height in pixels
375
+ * @returns Formatted string like "1920×1080 (1080p)" or "—" if unknown
376
+ *
377
+ * @example
378
+ * formatResolutionDisplay(1920, 1080) // "1920×1080 (1080p)"
379
+ * formatResolutionDisplay(1440, 1080) // "1440×1080 (1080p)" - 4:3 correctly labeled
380
+ * formatResolutionDisplay(1920, 800) // "1920×800 (1080p)" - cinemascope correctly labeled
381
+ * formatResolutionDisplay(null, 1080) // "1080p (1080p)"
382
+ * formatResolutionDisplay(null, null) // "—"
383
+ */
384
+ export declare function formatResolutionDisplay(width: number | null | undefined, height: number | null | undefined): string;
385
+ /**
386
+ * Format audio channels for display.
387
+ *
388
+ * @param channels - Number of audio channels
389
+ * @returns Formatted string: "7.1", "5.1", "Stereo", "Mono", or "Nch"
390
+ *
391
+ * @example
392
+ * formatAudioChannels(8) // "7.1"
393
+ * formatAudioChannels(6) // "5.1"
394
+ * formatAudioChannels(2) // "Stereo"
395
+ * formatAudioChannels(1) // "Mono"
396
+ */
397
+ export declare function formatAudioChannels(channels: number | null | undefined): string | null;
398
+ export declare const SERVER_COLOR_PALETTE: readonly [{
399
+ readonly hex: "#F4A825";
400
+ readonly label: "Gold";
401
+ }, {
402
+ readonly hex: "#895FDD";
403
+ readonly label: "Purple";
404
+ }, {
405
+ readonly hex: "#39C668";
406
+ readonly label: "Green";
407
+ }, {
408
+ readonly hex: "#3B82F6";
409
+ readonly label: "Blue";
410
+ }, {
411
+ readonly hex: "#EF4444";
412
+ readonly label: "Red";
413
+ }, {
414
+ readonly hex: "#14B8A6";
415
+ readonly label: "Teal";
416
+ }];
417
+ export declare const SERVER_TYPE_BRAND_COLORS: Record<string, string>;
418
+ /** Pick best color for a server given its type and colors already used by other servers */
419
+ export declare function pickServerColor(type: string, usedColors: (string | null | undefined)[]): string;
420
+ export declare const TIME_MS: {
421
+ readonly SECOND: 1000;
422
+ readonly MINUTE: number;
423
+ readonly HOUR: number;
424
+ readonly DAY: number;
425
+ readonly WEEK: number;
426
+ };
427
+ export declare const SERVER_STATS_CONFIG: {
428
+ readonly POLL_INTERVAL_SECONDS: 6;
429
+ readonly TIMESPAN_SECONDS: 6;
430
+ readonly WINDOW_SECONDS: 120;
431
+ readonly DATA_POINTS: 20;
432
+ };
433
+ export declare const BANDWIDTH_STATS_CONFIG: {
434
+ readonly POLL_INTERVAL_SECONDS: 6;
435
+ readonly TIMESPAN_SECONDS: 6;
436
+ readonly WINDOW_SECONDS: 120;
437
+ readonly DATA_POINTS: 120;
438
+ };
439
+ export declare const MERGE_SAME_SERVER_CONFIRMATION_REQUIRED = "same_server_combine_requires_confirmation";
440
+ export declare const SESSION_LIMITS: {
441
+ readonly MAX_RECENT_PER_USER: 100;
442
+ readonly RESUME_WINDOW_HOURS: 24;
443
+ readonly WATCH_COMPLETION_THRESHOLD: 0.85;
444
+ readonly STALE_SESSION_TIMEOUT_SECONDS: 300;
445
+ readonly MIN_PLAY_TIME_MS: number;
446
+ readonly CONTINUED_SESSION_THRESHOLD_MS: number;
447
+ readonly STALE_SWEEP_INTERVAL_MS: number;
448
+ };
449
+ /**
450
+ * Session write retry configuration.
451
+ * Used when DB writes fail during session stop.
452
+ */
453
+ export declare const SESSION_WRITE_RETRY: {
454
+ /** Number of immediate retries before queueing */
455
+ readonly IMMEDIATE_RETRIES: 3;
456
+ /** Base delay for exponential backoff (ms) */
457
+ readonly IMMEDIATE_BACKOFF_MS: 50;
458
+ /** Maximum total attempts before giving up */
459
+ readonly MAX_TOTAL_ATTEMPTS: 5;
460
+ };
461
+ /**
462
+ * Get the client's IANA timezone identifier.
463
+ * Works in both browser and React Native environments.
464
+ *
465
+ * @returns IANA timezone string (e.g., 'America/Los_Angeles') or 'UTC' as fallback
466
+ */
467
+ export declare function getClientTimezone(): string;
468
+ /**
469
+ * Validate an IANA timezone identifier.
470
+ *
471
+ * @param tz - Timezone string to validate
472
+ * @returns true if valid IANA timezone, false otherwise
473
+ */
474
+ export declare function isValidTimezone(tz: string): boolean;
475
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG7E,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BhB,CAAC;AAGX,eAAO,MAAM,kBAAkB;;;;;;;CAOrB,CAAC;AASX,eAAO,MAAM,+BAA+B,uIAMlC,CAAC;AAGX,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAIlB,CAAC;AAGX,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAGzC,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,OAAO,eAAe,GAAG,gBAAgB,CAE5F;AAGD,eAAO,MAAM,SAAS;;;;;;;;;IASpB,oCAAoC;;IAEpC,oCAAoC;;;;;;;;CAQ5B,CAAC;AAKX;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,QAE5C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAGD,eAAO,MAAM,UAAU;;;wBAUD,MAAM;IAC1B;;OAEG;gCACyB,MAAM,cAAc,MAAM;IAEtD,mFAAmF;;;2BAO5D,MAAM;iCACA,MAAM;oCACH,MAAM;8BAEZ,MAAM;yCACK,MAAM;kCAEb,MAAM;;kCAKN,MAAM;gCACR,MAAM;+BAEP,MAAM,aAAa,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;2CAoFf,MAAM;qCAGZ,MAAM;kCAGT,MAAM,aAAa,MAAM;mCAExB,MAAM,aAAa,MAAM;mCAEzB,MAAM,aAAa,MAAM;uCAGrB,MAAM,aAAa,MAAM;2CAIrB,MAAM;2CAEN,MAAM;0BAGvB,MAAM;6BACH,MAAM;iCACF,MAAM;yCACE,MAAM;iCAEd,MAAM;oCAEH,MAAM;;6BAKb,MAAM,cAAc,MAAM;qCAElB,MAAM,cAAc,MAAM,aAAa,MAAM;+CAGlE,MAAM,gBACF,MAAM,YACV,MAAM,aACL,MAAM;4BAIK,MAAM,YAAY,MAAM;qCAGf,MAAM;;6BAMd,MAAM,aAAa,MAAM;qBAGjC,MAAM;mCAEQ,MAAM;CAEtC,CAAC;AAGF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCZ,CAAC;AAGX,eAAO,MAAM,mBAAmB;;;;;;;;;;CAUtB,CAAC;AAGX;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,aAAa,CAAC;AAElD,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,aAAa,YAAwB,CAAC;AACnD,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,gBAAgB,YAA2B,CAAC;AAGzD,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAGX,eAAO,MAAM,iBAAiB;;;IAG5B,sCAAsC;;;;;CAM9B,CAAC;AAGX,eAAO,MAAM,aAAa;;CAEhB,CAAC;AAGX,eAAO,MAAM,UAAU;;;;;;;CAWb,CAAC;AAGX,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAkBlB,CAAC;AAGX,eAAO,MAAM,SAAS;;;;;;CAMZ,CAAC;AAGX,eAAO,MAAM,UAAU;;;;CAIb,CAAC;AAGX,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAGX,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAGX,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C;;GAEG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,SAAI,GAAG,MAAM,CAMvF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,SAAI,GAAG,MAAM,CAMrF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAK9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAK7E;AAQD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,UAAU,GACrB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAmBxC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAgBrE;AAuGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAIxE;AAED,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,kBAAkB,6BAAuB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,MAAM,CAQR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAOtF;AAGD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;EAOvB,CAAC;AAEX,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI3D,CAAC;AAEF,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAQ/F;AAGD,eAAO,MAAM,OAAO;;;;;;CAMV,CAAC;AAIX,eAAO,MAAM,mBAAmB;;;;;CAStB,CAAC;AAKX,eAAO,MAAM,sBAAsB;;;;;CASzB,CAAC;AAIX,eAAO,MAAM,uCAAuC,8CAA8C,CAAC;AAGnG,eAAO,MAAM,cAAc;;;;;;;;CAajB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB;IAC9B,kDAAkD;;IAElD,8CAA8C;;IAE9C,8CAA8C;;CAEtC,CAAC;AAMX;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAOnD"}