@uniai-fe/uds-templates 0.6.10 → 0.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.6.10",
3
+ "version": "0.6.12",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -11,13 +11,36 @@ export interface CctvDebugEvent {
11
11
  source: string;
12
12
  }
13
13
 
14
+ export interface CctvDebugReconnectStartSummary {
15
+ at: string;
16
+ caller: string | null;
17
+ camId: string | null;
18
+ connectionState: string | null;
19
+ isPostConnectedReconnectReady: boolean | null;
20
+ reconnectReason: string | null;
21
+ }
22
+
23
+ export interface CctvDebugSummary {
24
+ connectionStateCounts: Record<string, number>;
25
+ eventCounts: Record<string, number>;
26
+ first: string | null;
27
+ generatedAt: string;
28
+ last: string | null;
29
+ reconnectCallers: Record<string, number>;
30
+ reconnectReasons: Record<string, number>;
31
+ reconnectStarts: CctvDebugReconnectStartSummary[];
32
+ total: number;
33
+ }
34
+
14
35
  export interface CctvDebugBuffer {
15
36
  clear: () => void;
16
37
  dump: () => CctvDebugEvent[];
17
38
  enabled: true;
18
39
  events: CctvDebugEvent[];
40
+ filter: (keywords?: readonly string[]) => CctvDebugEvent[];
19
41
  limit: number;
20
42
  sequence: number;
43
+ summary: () => CctvDebugSummary;
21
44
  }
22
45
 
23
46
  declare global {
@@ -28,15 +51,175 @@ declare global {
28
51
 
29
52
  const DEBUG_QUERY_KEYS = ["udsCctvDebug", "cctvDebug"] as const;
30
53
  const DEBUG_STORAGE_KEYS = ["uds:cctv:debug", "UDS_CCTV_DEBUG"] as const;
54
+ const DEBUG_CONSOLE_QUERY_KEYS = [
55
+ "udsCctvDebugConsole",
56
+ "cctvDebugConsole",
57
+ ] as const;
58
+ const DEBUG_CONSOLE_STORAGE_KEYS = [
59
+ "uds:cctv:debug:console",
60
+ "UDS_CCTV_DEBUG_CONSOLE",
61
+ ] as const;
31
62
  const DEBUG_ENABLED_VALUES = new Set(["1", "true", "yes", "on", "debug"]);
63
+ const DEBUG_DISABLED_VALUES = new Set(["0", "false", "no", "off"]);
32
64
  const DEBUG_BUFFER_LIMIT = 1000;
65
+ const DEBUG_DEFAULT_FILTER_KEYWORDS = [
66
+ "reconnect",
67
+ "close",
68
+ "rejected",
69
+ "connection-state:change",
70
+ "track:received",
71
+ ] as const;
33
72
 
34
73
  const isBrowser = (): boolean =>
35
74
  typeof window !== "undefined" && typeof document !== "undefined";
36
75
 
37
- const isDebugValueEnabled = (value: string | null): boolean =>
38
- value === "" ||
39
- (typeof value === "string" && DEBUG_ENABLED_VALUES.has(value));
76
+ const getDebugValueOverride = (value: string | null): boolean | null => {
77
+ if (value === "") return true;
78
+ if (typeof value !== "string") return null;
79
+
80
+ const normalizedValue = value.toLowerCase();
81
+ if (DEBUG_ENABLED_VALUES.has(normalizedValue)) return true;
82
+ if (DEBUG_DISABLED_VALUES.has(normalizedValue)) return false;
83
+ return null;
84
+ };
85
+
86
+ const getDebugQueryOverride = (keys: readonly string[]): boolean | null => {
87
+ const query = new URLSearchParams(window.location.search);
88
+ for (const key of keys) {
89
+ if (!query.has(key)) continue;
90
+
91
+ const override = getDebugValueOverride(query.get(key));
92
+ if (override !== null) return override;
93
+ }
94
+
95
+ return null;
96
+ };
97
+
98
+ const getDebugStorageOverride = (keys: readonly string[]): boolean | null => {
99
+ for (const key of keys) {
100
+ try {
101
+ const override = getDebugValueOverride(window.localStorage.getItem(key));
102
+ if (override !== null) return override;
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ return null;
109
+ };
110
+
111
+ const isLocalhostDebugDefaultEnabled = (): boolean => {
112
+ const { hostname } = window.location;
113
+ return (
114
+ hostname === "localhost" ||
115
+ hostname === "127.0.0.1" ||
116
+ hostname === "[::1]" ||
117
+ hostname === "::1" ||
118
+ hostname.endsWith(".localhost")
119
+ );
120
+ };
121
+
122
+ const getPayloadString = (
123
+ payload: Record<string, unknown> | undefined,
124
+ key: string,
125
+ ): string | null => {
126
+ const value = payload?.[key];
127
+ return typeof value === "string" ? value : null;
128
+ };
129
+
130
+ const getPayloadBoolean = (
131
+ payload: Record<string, unknown> | undefined,
132
+ key: string,
133
+ ): boolean | null => {
134
+ const value = payload?.[key];
135
+ return typeof value === "boolean" ? value : null;
136
+ };
137
+
138
+ const getReconnectCaller = (
139
+ payload: Record<string, unknown> | undefined,
140
+ ): string | null => {
141
+ const callerStack = payload?.callerStack;
142
+ const stackLines = Array.isArray(callerStack)
143
+ ? callerStack.filter((line): line is string => typeof line === "string")
144
+ : typeof callerStack === "string"
145
+ ? callerStack.split("\n")
146
+ : [];
147
+
148
+ return (
149
+ stackLines
150
+ .find(
151
+ line =>
152
+ line.includes("CCTVManagerVideoOverlay") ||
153
+ line.includes("reconnectOnFocus"),
154
+ )
155
+ ?.trim() ?? null
156
+ );
157
+ };
158
+
159
+ const countBy = <T extends string>(
160
+ target: Record<T, number>,
161
+ key: T | null,
162
+ ): void => {
163
+ if (!key) return;
164
+ target[key] = (target[key] ?? 0) + 1;
165
+ };
166
+
167
+ const filterCctvDebugEvents = (
168
+ events: readonly CctvDebugEvent[],
169
+ keywords: readonly string[] = DEBUG_DEFAULT_FILTER_KEYWORDS,
170
+ ): CctvDebugEvent[] =>
171
+ events.filter(entry =>
172
+ keywords.some(keyword => entry.event.includes(keyword)),
173
+ );
174
+
175
+ const summarizeCctvDebugEvents = (
176
+ events: readonly CctvDebugEvent[],
177
+ ): CctvDebugSummary => {
178
+ const eventCounts: Record<string, number> = {};
179
+ const connectionStateCounts: Record<string, number> = {};
180
+ const reconnectReasons: Record<string, number> = {};
181
+ const reconnectCallers: Record<string, number> = {};
182
+ const reconnectStarts: CctvDebugReconnectStartSummary[] = [];
183
+
184
+ for (const entry of events) {
185
+ countBy(eventCounts, entry.event);
186
+
187
+ if (entry.event === "connection-state:change") {
188
+ countBy(connectionStateCounts, getPayloadString(entry.payload, "state"));
189
+ }
190
+
191
+ if (entry.event !== "reconnect-stream:start") continue;
192
+
193
+ const reconnectReason = getPayloadString(entry.payload, "reconnectReason");
194
+ const caller = getReconnectCaller(entry.payload);
195
+
196
+ countBy(reconnectReasons, reconnectReason ?? "none");
197
+ countBy(reconnectCallers, caller ?? "unknown");
198
+ reconnectStarts.push({
199
+ at: entry.at,
200
+ caller,
201
+ camId: getPayloadString(entry.payload, "camId"),
202
+ connectionState: getPayloadString(entry.payload, "connectionState"),
203
+ isPostConnectedReconnectReady: getPayloadBoolean(
204
+ entry.payload,
205
+ "isPostConnectedReconnectReady",
206
+ ),
207
+ reconnectReason,
208
+ });
209
+ }
210
+
211
+ return {
212
+ connectionStateCounts,
213
+ eventCounts,
214
+ first: events[0]?.at ?? null,
215
+ generatedAt: new Date().toISOString(),
216
+ last: events[events.length - 1]?.at ?? null,
217
+ reconnectCallers,
218
+ reconnectReasons,
219
+ reconnectStarts,
220
+ total: events.length,
221
+ };
222
+ };
40
223
 
41
224
  /**
42
225
  * identity/stream key를 원문 대신 추적 가능한 hash label로 바꾼다.
@@ -79,20 +262,31 @@ export const getCctvDebugUrlLabel = (
79
262
  export const isCctvDebugEnabled = (): boolean => {
80
263
  if (!isBrowser()) return false;
81
264
 
82
- const query = new URLSearchParams(window.location.search);
83
- for (const key of DEBUG_QUERY_KEYS) {
84
- if (query.has(key) && isDebugValueEnabled(query.get(key))) return true;
85
- }
265
+ const queryOverride = getDebugQueryOverride(DEBUG_QUERY_KEYS);
266
+ if (queryOverride !== null) return queryOverride;
86
267
 
87
- for (const key of DEBUG_STORAGE_KEYS) {
88
- try {
89
- if (isDebugValueEnabled(window.localStorage.getItem(key))) return true;
90
- } catch {
91
- return false;
92
- }
93
- }
268
+ const storageOverride = getDebugStorageOverride(DEBUG_STORAGE_KEYS);
269
+ if (storageOverride !== null) return storageOverride;
94
270
 
95
- return false;
271
+ return isLocalhostDebugDefaultEnabled();
272
+ };
273
+
274
+ const isCctvDebugConsoleEnabled = (): boolean => {
275
+ if (!isBrowser()) return false;
276
+
277
+ const consoleQueryOverride = getDebugQueryOverride(DEBUG_CONSOLE_QUERY_KEYS);
278
+ if (consoleQueryOverride !== null) return consoleQueryOverride;
279
+
280
+ const consoleStorageOverride = getDebugStorageOverride(
281
+ DEBUG_CONSOLE_STORAGE_KEYS,
282
+ );
283
+ if (consoleStorageOverride !== null) return consoleStorageOverride;
284
+
285
+ return (
286
+ getDebugQueryOverride(DEBUG_QUERY_KEYS) === true ||
287
+ getDebugStorageOverride(DEBUG_STORAGE_KEYS) === true ||
288
+ isLocalhostDebugDefaultEnabled()
289
+ );
96
290
  };
97
291
 
98
292
  /**
@@ -126,6 +320,8 @@ const getCctvDebugBuffer = (): CctvDebugBuffer | null => {
126
320
  buffer.sequence = 0;
127
321
  },
128
322
  dump: () => [...buffer.events],
323
+ filter: keywords => filterCctvDebugEvents(buffer.events, keywords),
324
+ summary: () => summarizeCctvDebugEvents(buffer.events),
129
325
  };
130
326
 
131
327
  window.__UDS_CCTV_DEBUG__ = buffer;
@@ -166,6 +362,8 @@ export const logCctvDebugEvent = ({
166
362
  buffer.events.splice(0, buffer.events.length - buffer.limit);
167
363
  }
168
364
 
365
+ if (!isCctvDebugConsoleEnabled()) return;
366
+
169
367
  const logger =
170
368
  level === "error"
171
369
  ? console.error