@butlr/butlr-mcp-server 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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/cache/occupancy-cache.d.ts +72 -0
  4. package/dist/cache/occupancy-cache.d.ts.map +1 -0
  5. package/dist/cache/occupancy-cache.js +166 -0
  6. package/dist/cache/occupancy-cache.js.map +1 -0
  7. package/dist/cache/topology-cache.d.ts +36 -0
  8. package/dist/cache/topology-cache.d.ts.map +1 -0
  9. package/dist/cache/topology-cache.js +74 -0
  10. package/dist/cache/topology-cache.js.map +1 -0
  11. package/dist/clients/auth-client.d.ts +22 -0
  12. package/dist/clients/auth-client.d.ts.map +1 -0
  13. package/dist/clients/auth-client.js +82 -0
  14. package/dist/clients/auth-client.js.map +1 -0
  15. package/dist/clients/graphql-client.d.ts +6 -0
  16. package/dist/clients/graphql-client.d.ts.map +1 -0
  17. package/dist/clients/graphql-client.js +106 -0
  18. package/dist/clients/graphql-client.js.map +1 -0
  19. package/dist/clients/queries/topology.d.ts +36 -0
  20. package/dist/clients/queries/topology.d.ts.map +1 -0
  21. package/dist/clients/queries/topology.js +252 -0
  22. package/dist/clients/queries/topology.js.map +1 -0
  23. package/dist/clients/reporting-client.d.ts +191 -0
  24. package/dist/clients/reporting-client.d.ts.map +1 -0
  25. package/dist/clients/reporting-client.js +353 -0
  26. package/dist/clients/reporting-client.js.map +1 -0
  27. package/dist/clients/stats-client.d.ts +119 -0
  28. package/dist/clients/stats-client.d.ts.map +1 -0
  29. package/dist/clients/stats-client.js +238 -0
  30. package/dist/clients/stats-client.js.map +1 -0
  31. package/dist/clients/types.d.ts +215 -0
  32. package/dist/clients/types.d.ts.map +1 -0
  33. package/dist/clients/types.js +6 -0
  34. package/dist/clients/types.js.map +1 -0
  35. package/dist/constants.d.ts +3 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +3 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/errors/mcp-errors.d.ts +63 -0
  40. package/dist/errors/mcp-errors.d.ts.map +1 -0
  41. package/dist/errors/mcp-errors.js +144 -0
  42. package/dist/errors/mcp-errors.js.map +1 -0
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +43 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tools/butlr-available-rooms.d.ts +49 -0
  48. package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
  49. package/dist/tools/butlr-available-rooms.js +492 -0
  50. package/dist/tools/butlr-available-rooms.js.map +1 -0
  51. package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
  52. package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
  53. package/dist/tools/butlr-fetch-entity-details.js +276 -0
  54. package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
  55. package/dist/tools/butlr-get-asset-details.d.ts +51 -0
  56. package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
  57. package/dist/tools/butlr-get-asset-details.js +391 -0
  58. package/dist/tools/butlr-get-asset-details.js.map +1 -0
  59. package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
  60. package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
  61. package/dist/tools/butlr-get-current-occupancy.js +126 -0
  62. package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
  63. package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
  64. package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
  65. package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
  66. package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
  67. package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
  68. package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
  69. package/dist/tools/butlr-hardware-snapshot.js +556 -0
  70. package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
  71. package/dist/tools/butlr-list-topology.d.ts +27 -0
  72. package/dist/tools/butlr-list-topology.d.ts.map +1 -0
  73. package/dist/tools/butlr-list-topology.js +241 -0
  74. package/dist/tools/butlr-list-topology.js.map +1 -0
  75. package/dist/tools/butlr-search-assets.d.ts +53 -0
  76. package/dist/tools/butlr-search-assets.d.ts.map +1 -0
  77. package/dist/tools/butlr-search-assets.js +206 -0
  78. package/dist/tools/butlr-search-assets.js.map +1 -0
  79. package/dist/tools/butlr-space-busyness.d.ts +23 -0
  80. package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
  81. package/dist/tools/butlr-space-busyness.js +304 -0
  82. package/dist/tools/butlr-space-busyness.js.map +1 -0
  83. package/dist/tools/butlr-traffic-flow.d.ts +39 -0
  84. package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
  85. package/dist/tools/butlr-traffic-flow.js +369 -0
  86. package/dist/tools/butlr-traffic-flow.js.map +1 -0
  87. package/dist/types/responses.d.ts +253 -0
  88. package/dist/types/responses.d.ts.map +1 -0
  89. package/dist/types/responses.js +8 -0
  90. package/dist/types/responses.js.map +1 -0
  91. package/dist/utils/asset-flattener.d.ts +50 -0
  92. package/dist/utils/asset-flattener.d.ts.map +1 -0
  93. package/dist/utils/asset-flattener.js +131 -0
  94. package/dist/utils/asset-flattener.js.map +1 -0
  95. package/dist/utils/asset-helpers.d.ts +8 -0
  96. package/dist/utils/asset-helpers.d.ts.map +1 -0
  97. package/dist/utils/asset-helpers.js +24 -0
  98. package/dist/utils/asset-helpers.js.map +1 -0
  99. package/dist/utils/field-validator.d.ts +29 -0
  100. package/dist/utils/field-validator.d.ts.map +1 -0
  101. package/dist/utils/field-validator.js +197 -0
  102. package/dist/utils/field-validator.js.map +1 -0
  103. package/dist/utils/fuzzy-match.d.ts +29 -0
  104. package/dist/utils/fuzzy-match.d.ts.map +1 -0
  105. package/dist/utils/fuzzy-match.js +70 -0
  106. package/dist/utils/fuzzy-match.js.map +1 -0
  107. package/dist/utils/graphql-helpers.d.ts +29 -0
  108. package/dist/utils/graphql-helpers.d.ts.map +1 -0
  109. package/dist/utils/graphql-helpers.js +43 -0
  110. package/dist/utils/graphql-helpers.js.map +1 -0
  111. package/dist/utils/natural-language.d.ts +73 -0
  112. package/dist/utils/natural-language.d.ts.map +1 -0
  113. package/dist/utils/natural-language.js +172 -0
  114. package/dist/utils/natural-language.js.map +1 -0
  115. package/dist/utils/occupancy-helpers.d.ts +63 -0
  116. package/dist/utils/occupancy-helpers.d.ts.map +1 -0
  117. package/dist/utils/occupancy-helpers.js +203 -0
  118. package/dist/utils/occupancy-helpers.js.map +1 -0
  119. package/dist/utils/path-builder.d.ts +10 -0
  120. package/dist/utils/path-builder.d.ts.map +1 -0
  121. package/dist/utils/path-builder.js +34 -0
  122. package/dist/utils/path-builder.js.map +1 -0
  123. package/dist/utils/time-range-validator.d.ts +13 -0
  124. package/dist/utils/time-range-validator.d.ts.map +1 -0
  125. package/dist/utils/time-range-validator.js +65 -0
  126. package/dist/utils/time-range-validator.js.map +1 -0
  127. package/dist/utils/timezone-helpers.d.ts +49 -0
  128. package/dist/utils/timezone-helpers.d.ts.map +1 -0
  129. package/dist/utils/timezone-helpers.js +209 -0
  130. package/dist/utils/timezone-helpers.js.map +1 -0
  131. package/dist/utils/tree-formatter.d.ts +23 -0
  132. package/dist/utils/tree-formatter.d.ts.map +1 -0
  133. package/dist/utils/tree-formatter.js +258 -0
  134. package/dist/utils/tree-formatter.js.map +1 -0
  135. package/package.json +93 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Time range validation for timeseries queries
3
+ * Prevents excessive data queries that could timeout or overwhelm the API
4
+ */
5
+ /**
6
+ * Validate time range based on interval
7
+ * @param interval Aggregation interval ('1m', '1h', '1d')
8
+ * @param start Start time (ISO-8601 or relative like '-24h')
9
+ * @param stop Stop time (ISO-8601 or relative like 'now')
10
+ * @throws Error if time range exceeds limits for the given interval
11
+ */
12
+ export function validateTimeRange(interval, start, stop) {
13
+ // Parse times - handle relative times by converting to absolute
14
+ const startTime = parseTime(start);
15
+ const stopTime = parseTime(stop);
16
+ // Calculate duration in hours
17
+ const durationMs = stopTime.getTime() - startTime.getTime();
18
+ const durationHours = durationMs / (1000 * 60 * 60);
19
+ const durationDays = durationHours / 24;
20
+ // Validate based on interval
21
+ if (interval === "1m" && durationHours > 1) {
22
+ throw new Error(`Time range too large for 1m interval. Maximum: 1 hour. Requested: ${durationHours.toFixed(1)} hours.`);
23
+ }
24
+ if (interval === "1h" && durationHours > 48) {
25
+ throw new Error(`Time range too large for 1h interval. Maximum: 48 hours. Requested: ${durationHours.toFixed(1)} hours.`);
26
+ }
27
+ if (interval === "1d" && durationDays > 60) {
28
+ throw new Error(`Time range too large for 1d interval. Maximum: 60 days. Requested: ${durationDays.toFixed(1)} days.`);
29
+ }
30
+ // Validate start is before stop
31
+ if (startTime >= stopTime) {
32
+ throw new Error(`Start time must be before stop time. Start: ${start}, Stop: ${stop}`);
33
+ }
34
+ }
35
+ /**
36
+ * Parse time string (ISO-8601 or relative) to Date
37
+ */
38
+ function parseTime(timeStr) {
39
+ // Handle relative times like '-24h', '-1d', 'now'
40
+ if (timeStr === "now") {
41
+ return new Date();
42
+ }
43
+ // Relative time pattern: -<number><unit>
44
+ const relativeMatch = timeStr.match(/^-(\d+)(m|h|d)$/);
45
+ if (relativeMatch) {
46
+ const amount = parseInt(relativeMatch[1], 10);
47
+ const unit = relativeMatch[2];
48
+ const now = new Date();
49
+ switch (unit) {
50
+ case "m":
51
+ return new Date(now.getTime() - amount * 60 * 1000);
52
+ case "h":
53
+ return new Date(now.getTime() - amount * 60 * 60 * 1000);
54
+ case "d":
55
+ return new Date(now.getTime() - amount * 24 * 60 * 60 * 1000);
56
+ }
57
+ }
58
+ // Try parsing as ISO-8601
59
+ const parsed = new Date(timeStr);
60
+ if (isNaN(parsed.getTime())) {
61
+ throw new Error(`Invalid time format: ${timeStr}. Use ISO-8601 or relative format like '-24h'.`);
62
+ }
63
+ return parsed;
64
+ }
65
+ //# sourceMappingURL=time-range-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-range-validator.js","sourceRoot":"","sources":["../../src/utils/time-range-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAY;IAC7E,gEAAgE;IAChE,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAEjC,8BAA8B;IAC9B,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IAC5D,MAAM,aAAa,GAAG,UAAU,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,aAAa,GAAG,EAAE,CAAC;IAExC,6BAA6B;IAC7B,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,qEAAqE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACvG,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,GAAG,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,uEAAuE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CACzG,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,KAAK,IAAI,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,sEAAsE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CACtG,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,+CAA+C,KAAK,WAAW,IAAI,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,kDAAkD;IAClD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;QACtB,OAAO,IAAI,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,yCAAyC;IACzC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACvD,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YACtD,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC3D,KAAK,GAAG;gBACN,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,wBAAwB,OAAO,gDAAgD,CAChF,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Timezone utilities for converting between UTC and site-specific timezones
3
+ * All Butlr timestamps are UTC; timezones are stored at Site level
4
+ */
5
+ import type { Site, Building, Floor } from "../clients/types.js";
6
+ /**
7
+ * Get the site timezone for any asset (floor, room, zone)
8
+ * Traverses hierarchy to find parent site
9
+ */
10
+ export declare function getTimezoneForAsset(assetId: string, assetType: "floor" | "room" | "zone", floors: Floor[], buildings: Building[], sites: Site[]): string | null;
11
+ /**
12
+ * Get timezone abbreviation (PST, PDT, CST, EST, etc.)
13
+ * Uses Intl.DateTimeFormat to get the correct abbreviation for the given date
14
+ */
15
+ export declare function getTimezoneAbbreviation(timezone: string, date?: Date): string;
16
+ /**
17
+ * Get UTC offset for a timezone at a specific date
18
+ * Returns format like "UTC-5" or "UTC+9"
19
+ */
20
+ export declare function getUTCOffset(timezone: string, date?: Date): string;
21
+ /**
22
+ * Check if daylight saving time is currently active for a timezone
23
+ */
24
+ export declare function isDSTActive(timezone: string, date?: Date): boolean;
25
+ /**
26
+ * Format current local time for display
27
+ * Returns: "2:35 PM PST on Oct 15, 2025"
28
+ */
29
+ export declare function getCurrentLocalTime(timezone: string): string;
30
+ /**
31
+ * Get midnight (start of day) in a specific timezone
32
+ * Returns UTC Date object representing midnight in the given timezone
33
+ *
34
+ * Example: getLocalMidnight(new Date("2025-10-15T10:00:00Z"), "Asia/Kolkata")
35
+ * Returns: Date object for "2025-10-14T18:30:00Z" (which is Oct 15 midnight IST)
36
+ */
37
+ export declare function getLocalMidnight(date: Date, timezone: string): Date;
38
+ /**
39
+ * Build rich timezone metadata for an asset
40
+ */
41
+ export interface TimezoneMetadata {
42
+ site_timezone: string;
43
+ timezone_offset: string;
44
+ timezone_abbr: string;
45
+ current_local_time: string;
46
+ dst_active: boolean;
47
+ }
48
+ export declare function buildTimezoneMetadata(timezone: string): TimezoneMetadata;
49
+ //# sourceMappingURL=timezone-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezone-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/timezone-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,EACpC,MAAM,EAAE,KAAK,EAAE,EACf,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,GACZ,MAAM,GAAG,IAAI,CAqCf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,CAgBzF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,CAsB9E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,OAAO,CAqB9E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuB5D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAsDnE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CASxE"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Timezone utilities for converting between UTC and site-specific timezones
3
+ * All Butlr timestamps are UTC; timezones are stored at Site level
4
+ */
5
+ /**
6
+ * Get the site timezone for any asset (floor, room, zone)
7
+ * Traverses hierarchy to find parent site
8
+ */
9
+ export function getTimezoneForAsset(assetId, assetType, floors, buildings, sites) {
10
+ if (assetType === "floor") {
11
+ const floor = floors.find((f) => f.id === assetId);
12
+ if (!floor)
13
+ return null;
14
+ const building = buildings.find((b) => b.id === floor.building_id);
15
+ if (!building)
16
+ return null;
17
+ const site = sites.find((s) => s.id === building.site_id);
18
+ return site?.timezone || null;
19
+ }
20
+ if (assetType === "room") {
21
+ // Find floor for this room
22
+ const floor = floors.find((f) => f.rooms?.some((r) => r.id === assetId));
23
+ if (!floor)
24
+ return null;
25
+ const building = buildings.find((b) => b.id === floor.building_id);
26
+ if (!building)
27
+ return null;
28
+ const site = sites.find((s) => s.id === building.site_id);
29
+ return site?.timezone || null;
30
+ }
31
+ if (assetType === "zone") {
32
+ // Find floor for this zone
33
+ const floor = floors.find((f) => f.zones?.some((z) => z.id === assetId));
34
+ if (!floor)
35
+ return null;
36
+ const building = buildings.find((b) => b.id === floor.building_id);
37
+ if (!building)
38
+ return null;
39
+ const site = sites.find((s) => s.id === building.site_id);
40
+ return site?.timezone || null;
41
+ }
42
+ return null;
43
+ }
44
+ /**
45
+ * Get timezone abbreviation (PST, PDT, CST, EST, etc.)
46
+ * Uses Intl.DateTimeFormat to get the correct abbreviation for the given date
47
+ */
48
+ export function getTimezoneAbbreviation(timezone, date = new Date()) {
49
+ try {
50
+ const formatter = new Intl.DateTimeFormat("en-US", {
51
+ timeZone: timezone,
52
+ timeZoneName: "short",
53
+ });
54
+ const parts = formatter.formatToParts(date);
55
+ const tzPart = parts.find((p) => p.type === "timeZoneName");
56
+ return tzPart?.value || "UTC";
57
+ }
58
+ catch (error) {
59
+ if (process.env.DEBUG) {
60
+ console.error(`[timezone-helpers] Failed to get abbreviation for ${timezone}:`, error);
61
+ }
62
+ return "UTC";
63
+ }
64
+ }
65
+ /**
66
+ * Get UTC offset for a timezone at a specific date
67
+ * Returns format like "UTC-5" or "UTC+9"
68
+ */
69
+ export function getUTCOffset(timezone, date = new Date()) {
70
+ try {
71
+ const formatter = new Intl.DateTimeFormat("en-US", {
72
+ timeZone: timezone,
73
+ timeZoneName: "longOffset",
74
+ });
75
+ const parts = formatter.formatToParts(date);
76
+ const offsetPart = parts.find((p) => p.type === "timeZoneName");
77
+ if (offsetPart?.value) {
78
+ // Convert "GMT-5" to "UTC-5"
79
+ return offsetPart.value.replace("GMT", "UTC");
80
+ }
81
+ return "UTC";
82
+ }
83
+ catch (error) {
84
+ if (process.env.DEBUG) {
85
+ console.error(`[timezone-helpers] Failed to get offset for ${timezone}:`, error);
86
+ }
87
+ return "UTC";
88
+ }
89
+ }
90
+ /**
91
+ * Check if daylight saving time is currently active for a timezone
92
+ */
93
+ export function isDSTActive(timezone, date = new Date()) {
94
+ try {
95
+ const jan = new Date(date.getFullYear(), 0, 1);
96
+ const jul = new Date(date.getFullYear(), 6, 1);
97
+ const janOffset = getUTCOffset(timezone, jan);
98
+ const julOffset = getUTCOffset(timezone, jul);
99
+ const currentOffset = getUTCOffset(timezone, date);
100
+ // If Jan and Jul offsets are the same, timezone has no DST
101
+ if (janOffset === julOffset)
102
+ return false;
103
+ // Standard time has the smaller (more negative) UTC offset string.
104
+ // This works for both hemispheres:
105
+ // - Northern: Jan is standard (e.g., "-08:00"), Jul is DST ("-07:00")
106
+ // - Southern: Jul is standard (e.g., "+10:00"), Jan is DST ("+11:00")
107
+ const standardOffset = janOffset < julOffset ? janOffset : julOffset;
108
+ return currentOffset !== standardOffset;
109
+ }
110
+ catch (error) {
111
+ return false;
112
+ }
113
+ }
114
+ /**
115
+ * Format current local time for display
116
+ * Returns: "2:35 PM PST on Oct 15, 2025"
117
+ */
118
+ export function getCurrentLocalTime(timezone) {
119
+ try {
120
+ const now = new Date();
121
+ const formatter = new Intl.DateTimeFormat("en-US", {
122
+ timeZone: timezone,
123
+ month: "short",
124
+ day: "numeric",
125
+ year: "numeric",
126
+ hour: "numeric",
127
+ minute: "2-digit",
128
+ hour12: true,
129
+ });
130
+ const tzAbbr = getTimezoneAbbreviation(timezone, now);
131
+ const dateStr = formatter.format(now);
132
+ return `${dateStr} ${tzAbbr}`;
133
+ }
134
+ catch (error) {
135
+ if (process.env.DEBUG) {
136
+ console.error(`[timezone-helpers] Failed to format time for ${timezone}:`, error);
137
+ }
138
+ return new Date().toISOString();
139
+ }
140
+ }
141
+ /**
142
+ * Get midnight (start of day) in a specific timezone
143
+ * Returns UTC Date object representing midnight in the given timezone
144
+ *
145
+ * Example: getLocalMidnight(new Date("2025-10-15T10:00:00Z"), "Asia/Kolkata")
146
+ * Returns: Date object for "2025-10-14T18:30:00Z" (which is Oct 15 midnight IST)
147
+ */
148
+ export function getLocalMidnight(date, timezone) {
149
+ try {
150
+ // Get the local date in the target timezone using Intl.DateTimeFormat
151
+ const localDate = new Intl.DateTimeFormat("en-CA", {
152
+ timeZone: timezone,
153
+ year: "numeric",
154
+ month: "2-digit",
155
+ day: "2-digit",
156
+ }).format(date);
157
+ // localDate is "YYYY-MM-DD" in the target timezone
158
+ const [year, month, day] = localDate.split("-").map(Number);
159
+ // Start with a rough estimate: UTC midnight on that date
160
+ let estimate = new Date(Date.UTC(year, month - 1, day, 0, 0, 0));
161
+ // Iteratively adjust to find the UTC time that corresponds to midnight local time
162
+ for (let i = 0; i < 3; i++) {
163
+ const localAtEstimate = new Intl.DateTimeFormat("en-CA", {
164
+ timeZone: timezone,
165
+ year: "numeric",
166
+ month: "2-digit",
167
+ day: "2-digit",
168
+ hour: "2-digit",
169
+ minute: "2-digit",
170
+ second: "2-digit",
171
+ hour12: false,
172
+ }).formatToParts(estimate);
173
+ const h = parseInt(localAtEstimate.find((p) => p.type === "hour")?.value || "0");
174
+ const m = parseInt(localAtEstimate.find((p) => p.type === "minute")?.value || "0");
175
+ const d = parseInt(localAtEstimate.find((p) => p.type === "day")?.value || "0");
176
+ if (h === 0 && m === 0 && d === day)
177
+ break; // Found it
178
+ // Adjust: we want local to be 00:00 on `day`
179
+ let diffMinutes = h * 60 + m;
180
+ if (d !== day) {
181
+ // Day is wrong, big adjustment needed
182
+ if (d < day)
183
+ diffMinutes -= 24 * 60; // We're a day behind
184
+ else
185
+ diffMinutes += 24 * 60; // We're a day ahead
186
+ }
187
+ estimate = new Date(estimate.getTime() - diffMinutes * 60 * 1000);
188
+ }
189
+ return estimate;
190
+ }
191
+ catch (error) {
192
+ console.error(`[timezone] Failed to calculate local midnight for ${timezone}:`, error);
193
+ // Fallback to UTC midnight
194
+ const utcMidnight = new Date(date);
195
+ utcMidnight.setUTCHours(0, 0, 0, 0);
196
+ return utcMidnight;
197
+ }
198
+ }
199
+ export function buildTimezoneMetadata(timezone) {
200
+ const now = new Date();
201
+ return {
202
+ site_timezone: timezone,
203
+ timezone_offset: getUTCOffset(timezone, now),
204
+ timezone_abbr: getTimezoneAbbreviation(timezone, now),
205
+ current_local_time: getCurrentLocalTime(timezone),
206
+ dst_active: isDSTActive(timezone, now),
207
+ };
208
+ }
209
+ //# sourceMappingURL=timezone-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezone-helpers.js","sourceRoot":"","sources":["../../src/utils/timezone-helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,SAAoC,EACpC,MAAe,EACf,SAAqB,EACrB,KAAa;IAEb,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,2BAA2B;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,2BAA2B;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IAC/E,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;QAC5D,OAAO,MAAM,EAAE,KAAK,IAAI,KAAK,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,qDAAqD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IACpE,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,YAAY;SAC3B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;QAEhE,IAAI,UAAU,EAAE,KAAK,EAAE,CAAC;YACtB,6BAA6B;YAC7B,OAAO,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,+CAA+C,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,OAAa,IAAI,IAAI,EAAE;IACnE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAE/C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEnD,2DAA2D;QAC3D,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAE1C,mEAAmE;QACnE,mCAAmC;QACnC,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,OAAO,aAAa,KAAK,cAAc,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,OAAO;YACd,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAEtC,OAAO,GAAG,OAAO,IAAI,MAAM,EAAE,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,gDAAgD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAU,EAAE,QAAgB;IAC3D,IAAI,CAAC;QACH,sEAAsE;QACtE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACjD,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;SACf,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChB,mDAAmD;QAEnD,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE5D,yDAAyD;QACzD,IAAI,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjE,kFAAkF;QAClF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,eAAe,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;gBACvD,QAAQ,EAAE,QAAQ;gBAClB,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,SAAS;gBAChB,GAAG,EAAE,SAAS;gBACd,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,KAAK;aACd,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAE3B,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YACjF,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YACnF,MAAM,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;YAEhF,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG;gBAAE,MAAM,CAAC,WAAW;YAEvD,6CAA6C;YAC7C,IAAI,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACd,sCAAsC;gBACtC,IAAI,CAAC,GAAG,GAAG;oBACT,WAAW,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,qBAAqB;;oBAC1C,WAAW,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,oBAAoB;YACnD,CAAC;YACD,QAAQ,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qDAAqD,QAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QACvF,2BAA2B;QAC3B,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAaD,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,OAAO;QACL,aAAa,EAAE,QAAQ;QACvB,eAAe,EAAE,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC;QAC5C,aAAa,EAAE,uBAAuB,CAAC,QAAQ,EAAE,GAAG,CAAC;QACrD,kBAAkB,EAAE,mBAAmB,CAAC,QAAQ,CAAC;QACjD,UAAU,EAAE,WAAW,CAAC,QAAQ,EAAE,GAAG,CAAC;KACvC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tree formatter for topology visualization
3
+ * Formats organizational hierarchy in a tree structure with depth control
4
+ *
5
+ * ULTRA-COMPACT FORMAT: Positional arrays [id, display_identifier, children?]
6
+ * - Position 0: Asset ID
7
+ * - Position 1: Display identifier (name for sites/buildings/floors/rooms/zones, serialNumber for hives, mac_address for sensors)
8
+ * - Position 2: Children array (optional, only if has children)
9
+ */
10
+ import type { Site } from "../clients/types.js";
11
+ import type { TopologyNode } from "../types/responses.js";
12
+ /**
13
+ * Format topology as a tree structure with depth controls
14
+ * Hierarchy: Sites(0) -> Buildings(1) -> Floors(2) -> Rooms/Zones(3) -> Hives(4) -> Sensors(5)
15
+ * Format: [id, display_name, children?]
16
+ *
17
+ * @param sites Array of sites with full topology
18
+ * @param startingDepth Depth level to start showing (0=sites, 1=buildings, etc.)
19
+ * @param traversalDepth How many levels below starting_depth to traverse (0=starting level only)
20
+ * @returns Ultra-compact array tree structure
21
+ */
22
+ export declare function formatTopologyTree(sites: Site[], startingDepth?: number, traversalDepth?: number): TopologyNode[];
23
+ //# sourceMappingURL=tree-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/tree-formatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA6C,MAAM,qBAAqB,CAAC;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAyB1D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,GAAE,MAAU,EACzB,cAAc,GAAE,MAAU,GACzB,YAAY,EAAE,CAUhB"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Tree formatter for topology visualization
3
+ * Formats organizational hierarchy in a tree structure with depth control
4
+ *
5
+ * ULTRA-COMPACT FORMAT: Positional arrays [id, display_identifier, children?]
6
+ * - Position 0: Asset ID
7
+ * - Position 1: Display identifier (name for sites/buildings/floors/rooms/zones, serialNumber for hives, mac_address for sensors)
8
+ * - Position 2: Children array (optional, only if has children)
9
+ */
10
+ /**
11
+ * Depth level mappings
12
+ */
13
+ const DEPTH_LEVELS = {
14
+ SITE: 0,
15
+ BUILDING: 1,
16
+ FLOOR: 2,
17
+ ROOM_ZONE: 3,
18
+ HIVE: 4,
19
+ SENSOR: 5,
20
+ };
21
+ /**
22
+ * Determine whether children should be included at the current depth level.
23
+ */
24
+ function shouldTraverse(currentDepth, startingDepth, traversalDepth) {
25
+ return currentDepth >= startingDepth && currentDepth - startingDepth < traversalDepth;
26
+ }
27
+ /**
28
+ * Format topology as a tree structure with depth controls
29
+ * Hierarchy: Sites(0) -> Buildings(1) -> Floors(2) -> Rooms/Zones(3) -> Hives(4) -> Sensors(5)
30
+ * Format: [id, display_name, children?]
31
+ *
32
+ * @param sites Array of sites with full topology
33
+ * @param startingDepth Depth level to start showing (0=sites, 1=buildings, etc.)
34
+ * @param traversalDepth How many levels below starting_depth to traverse (0=starting level only)
35
+ * @returns Ultra-compact array tree structure
36
+ */
37
+ export function formatTopologyTree(sites, startingDepth = 0, traversalDepth = 0) {
38
+ const currentDepth = DEPTH_LEVELS.SITE;
39
+ // If starting depth is 0 (sites), format sites
40
+ if (startingDepth === 0) {
41
+ return sites.map((site) => formatSite(site, currentDepth, startingDepth, traversalDepth));
42
+ }
43
+ // Otherwise, collect assets at starting depth from all sites
44
+ return collectAssetsAtDepth(sites, startingDepth, traversalDepth);
45
+ }
46
+ /**
47
+ * Collect all assets at a specific depth level
48
+ */
49
+ function collectAssetsAtDepth(sites, targetDepth, traversalDepth) {
50
+ const results = [];
51
+ for (const site of sites) {
52
+ if (targetDepth === DEPTH_LEVELS.BUILDING) {
53
+ // Collect buildings
54
+ site.buildings?.forEach((building) => {
55
+ results.push(formatBuilding(building, DEPTH_LEVELS.BUILDING, targetDepth, traversalDepth));
56
+ });
57
+ }
58
+ else if (targetDepth === DEPTH_LEVELS.FLOOR) {
59
+ // Collect floors
60
+ site.buildings?.forEach((building) => {
61
+ building.floors?.forEach((floor) => {
62
+ results.push(formatFloor(floor, DEPTH_LEVELS.FLOOR, targetDepth, traversalDepth));
63
+ });
64
+ });
65
+ }
66
+ else if (targetDepth === DEPTH_LEVELS.ROOM_ZONE) {
67
+ // Collect rooms and zones
68
+ site.buildings?.forEach((building) => {
69
+ building.floors?.forEach((floor) => {
70
+ floor.rooms?.forEach((room) => {
71
+ results.push(formatRoom(room, floor, DEPTH_LEVELS.ROOM_ZONE, targetDepth, traversalDepth));
72
+ });
73
+ floor.zones?.forEach((zone) => {
74
+ results.push(formatZone(zone));
75
+ });
76
+ });
77
+ });
78
+ }
79
+ else if (targetDepth === DEPTH_LEVELS.HIVE) {
80
+ // Collect hives
81
+ site.buildings?.forEach((building) => {
82
+ building.floors?.forEach((floor) => {
83
+ floor.hives?.forEach((hive) => {
84
+ results.push(formatHive(hive, floor, DEPTH_LEVELS.HIVE, targetDepth, traversalDepth));
85
+ });
86
+ });
87
+ });
88
+ }
89
+ else if (targetDepth === DEPTH_LEVELS.SENSOR) {
90
+ // Collect sensors
91
+ site.buildings?.forEach((building) => {
92
+ building.floors?.forEach((floor) => {
93
+ floor.sensors?.forEach((sensor) => {
94
+ results.push(formatSensor(sensor));
95
+ });
96
+ });
97
+ });
98
+ }
99
+ }
100
+ return results;
101
+ }
102
+ /**
103
+ * Format a single site
104
+ * Returns: [id, name, children?]
105
+ */
106
+ function formatSite(site, currentDepth, startingDepth, traversalDepth) {
107
+ const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
108
+ if (includeChildren && site.buildings && site.buildings.length > 0) {
109
+ const children = site.buildings.map((building) => formatBuilding(building, DEPTH_LEVELS.BUILDING, startingDepth, traversalDepth));
110
+ return [site.id, site.name, children];
111
+ }
112
+ return [site.id, site.name];
113
+ }
114
+ /**
115
+ * Format a single building
116
+ * Returns: [id, name, children?]
117
+ */
118
+ function formatBuilding(building, currentDepth, startingDepth, traversalDepth) {
119
+ const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
120
+ if (includeChildren && building.floors && building.floors.length > 0) {
121
+ const children = building.floors.map((floor) => formatFloor(floor, DEPTH_LEVELS.FLOOR, startingDepth, traversalDepth));
122
+ return [building.id, building.name, children];
123
+ }
124
+ return [building.id, building.name];
125
+ }
126
+ /**
127
+ * Format a single floor
128
+ * Returns: [id, name, children?]
129
+ */
130
+ function formatFloor(floor, currentDepth, startingDepth, traversalDepth) {
131
+ const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
132
+ if (!includeChildren) {
133
+ return [floor.id, floor.name];
134
+ }
135
+ const children = [];
136
+ // Add rooms
137
+ if (floor.rooms && floor.rooms.length > 0) {
138
+ children.push(...floor.rooms.map((room) => formatRoom(room, floor, DEPTH_LEVELS.ROOM_ZONE, startingDepth, traversalDepth)));
139
+ }
140
+ // Add floor-level zones (no room assignment)
141
+ if (floor.zones && floor.zones.length > 0) {
142
+ const floorLevelZones = floor.zones.filter((zone) => !(zone.roomID || zone.room_id));
143
+ children.push(...floorLevelZones.map((zone) => formatZone(zone)));
144
+ }
145
+ // Add floor-level hives (no room assignment)
146
+ if (floor.hives && floor.hives.length > 0) {
147
+ const floorLevelHives = floor.hives.filter((hive) => !(hive.roomID || hive.room_id));
148
+ children.push(...floorLevelHives.map((hive) => formatHive(hive, floor, DEPTH_LEVELS.HIVE, startingDepth, traversalDepth)));
149
+ }
150
+ // Add floor-level orphan sensors (only if depth allows)
151
+ const shouldIncludeSensors = DEPTH_LEVELS.SENSOR - startingDepth <= traversalDepth;
152
+ if (shouldIncludeSensors) {
153
+ const floorOrphans = getOrphanSensors(floor, null);
154
+ if (floorOrphans.length > 0) {
155
+ // Orphan group: ["orphan", "Orphan (no parent hive)", [sensors]]
156
+ children.push([
157
+ "orphan",
158
+ "Orphan (no parent hive)",
159
+ floorOrphans.map((sensor) => formatSensor(sensor)),
160
+ ]);
161
+ }
162
+ }
163
+ if (children.length > 0) {
164
+ return [floor.id, floor.name, children];
165
+ }
166
+ return [floor.id, floor.name];
167
+ }
168
+ /**
169
+ * Format a single room
170
+ * Returns: [id, name, children?]
171
+ */
172
+ function formatRoom(room, floor, currentDepth, startingDepth, traversalDepth) {
173
+ const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
174
+ if (!includeChildren) {
175
+ return [room.id, room.name];
176
+ }
177
+ const children = [];
178
+ // Add zones belonging to this room (zones at room level, not separate depth)
179
+ if (floor.zones && floor.zones.length > 0) {
180
+ const roomZones = floor.zones.filter((zone) => (zone.roomID || zone.room_id) === room.id);
181
+ children.push(...roomZones.map((zone) => formatZone(zone)));
182
+ }
183
+ // Add hives belonging to this room
184
+ if (floor.hives && floor.hives.length > 0) {
185
+ const roomHives = floor.hives.filter((hive) => (hive.roomID || hive.room_id) === room.id);
186
+ children.push(...roomHives.map((hive) => formatHive(hive, floor, DEPTH_LEVELS.HIVE, startingDepth, traversalDepth)));
187
+ }
188
+ // Add orphan sensors belonging to this room (only if depth allows)
189
+ const shouldIncludeSensors = DEPTH_LEVELS.SENSOR - startingDepth <= traversalDepth;
190
+ if (shouldIncludeSensors) {
191
+ const roomOrphans = getOrphanSensors(floor, room.id);
192
+ if (roomOrphans.length > 0) {
193
+ // Orphan group: ["orphan", "Orphan (no parent hive)", [sensors]]
194
+ children.push([
195
+ "orphan",
196
+ "Orphan (no parent hive)",
197
+ roomOrphans.map((sensor) => formatSensor(sensor)),
198
+ ]);
199
+ }
200
+ }
201
+ if (children.length > 0) {
202
+ return [room.id, room.name, children];
203
+ }
204
+ return [room.id, room.name];
205
+ }
206
+ /**
207
+ * Format a single zone
208
+ * Returns: [id, name]
209
+ */
210
+ function formatZone(zone) {
211
+ return [zone.id, zone.name];
212
+ }
213
+ /**
214
+ * Format a single hive (with its sensors)
215
+ * Returns: [id, serialNumber, children?]
216
+ * NOTE: Uses serialNumber, NOT name (name is optional metadata)
217
+ */
218
+ function formatHive(hive, floor, currentDepth, startingDepth, traversalDepth) {
219
+ const includeChildren = shouldTraverse(currentDepth, startingDepth, traversalDepth);
220
+ // Find sensors belonging to this hive
221
+ if (includeChildren && floor.sensors && floor.sensors.length > 0) {
222
+ const hiveSensors = floor.sensors.filter((sensor) => sensor.hive_serial === hive.serialNumber);
223
+ if (hiveSensors.length > 0) {
224
+ const children = hiveSensors.map((sensor) => formatSensor(sensor));
225
+ return [hive.id, hive.serialNumber, children];
226
+ }
227
+ }
228
+ return [hive.id, hive.serialNumber];
229
+ }
230
+ /**
231
+ * Format a single sensor
232
+ * Returns: [id, mac_address]
233
+ * NOTE: Uses mac_address, NOT name (name is optional metadata)
234
+ */
235
+ function formatSensor(sensor) {
236
+ return [sensor.id, sensor.mac_address];
237
+ }
238
+ /**
239
+ * Get orphan sensors (sensors without a parent hive) for a room or floor
240
+ * @param floor Floor object containing sensors and hives
241
+ * @param roomID Room ID to filter by (null for floor-level)
242
+ * @returns Array of orphan sensors
243
+ */
244
+ function getOrphanSensors(floor, roomID) {
245
+ if (!floor.sensors || floor.sensors.length === 0) {
246
+ return [];
247
+ }
248
+ // Get all hive serial numbers
249
+ const hiveSerials = new Set(floor.hives?.map((hive) => hive.serialNumber) || []);
250
+ // Find sensors without a parent hive that belong to this room/floor
251
+ return floor.sensors.filter((sensor) => {
252
+ const hasNoHive = !sensor.hive_serial || !hiveSerials.has(sensor.hive_serial);
253
+ const sensorRoomID = sensor.roomID || sensor.room_id || null;
254
+ const matchesLocation = sensorRoomID === roomID;
255
+ return hasNoHive && matchesLocation;
256
+ });
257
+ }
258
+ //# sourceMappingURL=tree-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-formatter.js","sourceRoot":"","sources":["../../src/utils/tree-formatter.ts"],"names":[],"mappings":"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"}