streamer-node 1.0.30 → 1.0.37

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 (123) hide show
  1. package/dist/package.json +6 -2
  2. package/dist/scripts/README.md +670 -0
  3. package/dist/scripts/install-streamer-node.sh +117 -0
  4. package/dist/scripts/reframe_ball.py +354 -0
  5. package/dist/src/application/interfaces/StartStreamUseCase.types.d.ts +3 -1
  6. package/dist/src/application/interfaces/StartStreamUseCase.types.d.ts.map +1 -1
  7. package/dist/src/application/interfaces/StartStreamUseCase.types.js.map +1 -1
  8. package/dist/src/application/services/HttpClient.d.ts +21 -0
  9. package/dist/src/application/services/HttpClient.d.ts.map +1 -1
  10. package/dist/src/application/services/HttpClient.js +37 -5
  11. package/dist/src/application/services/HttpClient.js.map +1 -1
  12. package/dist/src/application/services/StreamManagerService.d.ts.map +1 -1
  13. package/dist/src/application/services/StreamManagerService.js +2 -0
  14. package/dist/src/application/services/StreamManagerService.js.map +1 -1
  15. package/dist/src/application/use-cases/CaptureHighlightUseCase.d.ts +52 -0
  16. package/dist/src/application/use-cases/CaptureHighlightUseCase.d.ts.map +1 -0
  17. package/dist/src/application/use-cases/CaptureHighlightUseCase.js +169 -0
  18. package/dist/src/application/use-cases/CaptureHighlightUseCase.js.map +1 -0
  19. package/dist/src/application/use-cases/StartStreamUseCase.d.ts +9 -1
  20. package/dist/src/application/use-cases/StartStreamUseCase.d.ts.map +1 -1
  21. package/dist/src/application/use-cases/StartStreamUseCase.js +125 -2
  22. package/dist/src/application/use-cases/StartStreamUseCase.js.map +1 -1
  23. package/dist/src/application/use-cases/StopStreamUseCase.d.ts +5 -1
  24. package/dist/src/application/use-cases/StopStreamUseCase.d.ts.map +1 -1
  25. package/dist/src/application/use-cases/StopStreamUseCase.js +7 -1
  26. package/dist/src/application/use-cases/StopStreamUseCase.js.map +1 -1
  27. package/dist/src/domain/events/StreamEvent.d.ts +5 -0
  28. package/dist/src/domain/events/StreamEvent.d.ts.map +1 -1
  29. package/dist/src/domain/services/BallReframer.d.ts +35 -0
  30. package/dist/src/domain/services/BallReframer.d.ts.map +1 -0
  31. package/dist/src/domain/services/BallReframer.js +3 -0
  32. package/dist/src/domain/services/BallReframer.js.map +1 -0
  33. package/dist/src/domain/services/FFmpegService.d.ts +6 -2
  34. package/dist/src/domain/services/FFmpegService.d.ts.map +1 -1
  35. package/dist/src/domain/services/HighlightSignalSource.d.ts +113 -0
  36. package/dist/src/domain/services/HighlightSignalSource.d.ts.map +1 -0
  37. package/dist/src/domain/services/HighlightSignalSource.js +3 -0
  38. package/dist/src/domain/services/HighlightSignalSource.js.map +1 -0
  39. package/dist/src/domain/services/SSEService.d.ts +1 -0
  40. package/dist/src/domain/services/SSEService.d.ts.map +1 -1
  41. package/dist/src/infrastructure/config/Config.d.ts +32 -0
  42. package/dist/src/infrastructure/config/Config.d.ts.map +1 -1
  43. package/dist/src/infrastructure/config/Config.js +60 -0
  44. package/dist/src/infrastructure/config/Config.js.map +1 -1
  45. package/dist/src/infrastructure/listeners/SupabaseListener.d.ts +5 -0
  46. package/dist/src/infrastructure/listeners/SupabaseListener.d.ts.map +1 -1
  47. package/dist/src/infrastructure/listeners/SupabaseListener.js +43 -5
  48. package/dist/src/infrastructure/listeners/SupabaseListener.js.map +1 -1
  49. package/dist/src/infrastructure/logging/LogShippingService.d.ts +3 -1
  50. package/dist/src/infrastructure/logging/LogShippingService.d.ts.map +1 -1
  51. package/dist/src/infrastructure/logging/LogShippingService.js +13 -4
  52. package/dist/src/infrastructure/logging/LogShippingService.js.map +1 -1
  53. package/dist/src/infrastructure/logging/RemoteLogger.d.ts +1 -0
  54. package/dist/src/infrastructure/logging/RemoteLogger.d.ts.map +1 -1
  55. package/dist/src/infrastructure/logging/RemoteLogger.js +1 -1
  56. package/dist/src/infrastructure/logging/RemoteLogger.js.map +1 -1
  57. package/dist/src/infrastructure/services/AdDownloaderService.d.ts +85 -0
  58. package/dist/src/infrastructure/services/AdDownloaderService.d.ts.map +1 -0
  59. package/dist/src/infrastructure/services/AdDownloaderService.js +530 -0
  60. package/dist/src/infrastructure/services/AdDownloaderService.js.map +1 -0
  61. package/dist/src/infrastructure/services/AdRotator.d.ts +63 -0
  62. package/dist/src/infrastructure/services/AdRotator.d.ts.map +1 -0
  63. package/dist/src/infrastructure/services/AdRotator.js +223 -0
  64. package/dist/src/infrastructure/services/AdRotator.js.map +1 -0
  65. package/dist/src/infrastructure/services/HighlightBufferManager.d.ts +65 -0
  66. package/dist/src/infrastructure/services/HighlightBufferManager.d.ts.map +1 -0
  67. package/dist/src/infrastructure/services/HighlightBufferManager.js +203 -0
  68. package/dist/src/infrastructure/services/HighlightBufferManager.js.map +1 -0
  69. package/dist/src/infrastructure/services/HighlightBufferRegistry.d.ts +23 -0
  70. package/dist/src/infrastructure/services/HighlightBufferRegistry.d.ts.map +1 -0
  71. package/dist/src/infrastructure/services/HighlightBufferRegistry.js +44 -0
  72. package/dist/src/infrastructure/services/HighlightBufferRegistry.js.map +1 -0
  73. package/dist/src/infrastructure/services/HighlightExtractorService.d.ts +65 -0
  74. package/dist/src/infrastructure/services/HighlightExtractorService.d.ts.map +1 -0
  75. package/dist/src/infrastructure/services/HighlightExtractorService.js +264 -0
  76. package/dist/src/infrastructure/services/HighlightExtractorService.js.map +1 -0
  77. package/dist/src/infrastructure/services/HighlightRendererService.d.ts +38 -0
  78. package/dist/src/infrastructure/services/HighlightRendererService.d.ts.map +1 -0
  79. package/dist/src/infrastructure/services/HighlightRendererService.js +182 -0
  80. package/dist/src/infrastructure/services/HighlightRendererService.js.map +1 -0
  81. package/dist/src/infrastructure/services/NodeFFmpegService.d.ts +6 -4
  82. package/dist/src/infrastructure/services/NodeFFmpegService.d.ts.map +1 -1
  83. package/dist/src/infrastructure/services/NodeFFmpegService.js +154 -32
  84. package/dist/src/infrastructure/services/NodeFFmpegService.js.map +1 -1
  85. package/dist/src/infrastructure/services/NodeSSEService.d.ts +8 -0
  86. package/dist/src/infrastructure/services/NodeSSEService.d.ts.map +1 -1
  87. package/dist/src/infrastructure/services/NodeSSEService.js +71 -27
  88. package/dist/src/infrastructure/services/NodeSSEService.js.map +1 -1
  89. package/dist/src/infrastructure/services/NullBallReframer.d.ts +11 -0
  90. package/dist/src/infrastructure/services/NullBallReframer.d.ts.map +1 -0
  91. package/dist/src/infrastructure/services/NullBallReframer.js +16 -0
  92. package/dist/src/infrastructure/services/NullBallReframer.js.map +1 -0
  93. package/dist/src/infrastructure/services/PythonBallReframer.d.ts +35 -0
  94. package/dist/src/infrastructure/services/PythonBallReframer.d.ts.map +1 -0
  95. package/dist/src/infrastructure/services/PythonBallReframer.js +238 -0
  96. package/dist/src/infrastructure/services/PythonBallReframer.js.map +1 -0
  97. package/dist/src/infrastructure/services/SerialHighlightListener.d.ts +45 -0
  98. package/dist/src/infrastructure/services/SerialHighlightListener.d.ts.map +1 -0
  99. package/dist/src/infrastructure/services/SerialHighlightListener.js +440 -0
  100. package/dist/src/infrastructure/services/SerialHighlightListener.js.map +1 -0
  101. package/dist/src/infrastructure/services/YouTubeUploadService.d.ts +36 -0
  102. package/dist/src/infrastructure/services/YouTubeUploadService.d.ts.map +1 -0
  103. package/dist/src/infrastructure/services/YouTubeUploadService.js +402 -0
  104. package/dist/src/infrastructure/services/YouTubeUploadService.js.map +1 -0
  105. package/dist/src/infrastructure/utils/ResourceSampler.d.ts +47 -0
  106. package/dist/src/infrastructure/utils/ResourceSampler.d.ts.map +1 -0
  107. package/dist/src/infrastructure/utils/ResourceSampler.js +102 -0
  108. package/dist/src/infrastructure/utils/ResourceSampler.js.map +1 -0
  109. package/dist/src/infrastructure/utils/lowPriority.d.ts +29 -0
  110. package/dist/src/infrastructure/utils/lowPriority.d.ts.map +1 -0
  111. package/dist/src/infrastructure/utils/lowPriority.js +31 -0
  112. package/dist/src/infrastructure/utils/lowPriority.js.map +1 -0
  113. package/dist/src/infrastructure/utils/paths.d.ts +27 -0
  114. package/dist/src/infrastructure/utils/paths.d.ts.map +1 -0
  115. package/dist/src/infrastructure/utils/paths.js +69 -0
  116. package/dist/src/infrastructure/utils/paths.js.map +1 -0
  117. package/dist/src/infrastructure/utils/spawnToFile.d.ts +20 -0
  118. package/dist/src/infrastructure/utils/spawnToFile.d.ts.map +1 -0
  119. package/dist/src/infrastructure/utils/spawnToFile.js +98 -0
  120. package/dist/src/infrastructure/utils/spawnToFile.js.map +1 -0
  121. package/dist/src/main.js +228 -2
  122. package/dist/src/main.js.map +1 -1
  123. package/package.json +6 -2
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PythonBallReframer = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const lowPriority_1 = require("../utils/lowPriority");
41
+ /**
42
+ * BallReframer backed by a one-shot Python/OpenCV script (scripts/reframe_ball.py).
43
+ *
44
+ * ── IN SIMPLE WORDS ──
45
+ * Hands the raw clip to a small Python program that watches the ball and writes
46
+ * a cropped, vertical version. If Python isn't installed, the script errors, or
47
+ * it takes too long, we just return null and the full-frame clip is used.
48
+ *
49
+ * ── WHY IT'S BUILT THIS WAY (change at your peril) ──
50
+ * Same one-shot-subprocess pattern as the ffmpeg calls: spawn, wait with a
51
+ * timeout, read back the produced file. It is fail-soft by construction — a
52
+ * missing interpreter, a crash, a timeout, or a missing output file all resolve
53
+ * to null. A highlight must never be lost because tracking misbehaved.
54
+ *
55
+ * ── DO NOT ──
56
+ * - Do NOT let this throw; every error path returns null.
57
+ * - Do NOT enable in production before the CV script is tuned on real padel
58
+ * footage — until then the crop quality is unproven (hence default OFF).
59
+ */
60
+ class PythonBallReframer {
61
+ constructor(reelAspect, logger, scriptPath) {
62
+ this.reelAspect = reelAspect;
63
+ this.logger = logger;
64
+ // The reframe is the heaviest highlight stage (two-pass OpenCV). It runs at
65
+ // low CPU priority (see withLowPriority), so on a weak box it can take a
66
+ // while — give it a generous ceiling rather than killing a nearly-done pass.
67
+ // Detection is downscaled + frame-skipped (reframe_ball.py) to keep it well
68
+ // under this in practice.
69
+ this.timeoutMs = 240000;
70
+ // The app ships as an npm package run via `npx` — so the script must be
71
+ // found INSIDE the package (the build copies scripts/ into dist/), not from
72
+ // the CWD. Resolve relative to this compiled module (dist/scripts) first,
73
+ // then fall back to a repo-root checkout (dev / git deploy). __dirname here
74
+ // is <pkg>/dist/src/infrastructure/services, so ../../../scripts = dist/scripts.
75
+ const candidates = [
76
+ path.join(__dirname, "..", "..", "..", "scripts", "reframe_ball.py"),
77
+ path.resolve("scripts/reframe_ball.py"),
78
+ ];
79
+ this.scriptPath =
80
+ scriptPath ?? candidates.find((p) => fs.existsSync(p)) ?? candidates[0];
81
+ }
82
+ async reframe(clipPath, courtId) {
83
+ if (!fs.existsSync(this.scriptPath)) {
84
+ this.logger.warn("Ball reframer script missing; using full frame", {
85
+ courtId,
86
+ scriptPath: this.scriptPath,
87
+ });
88
+ return null;
89
+ }
90
+ // Nothing to crop → nothing to track. The reframer takes a window as WIDE
91
+ // as `aspect` allows out of a full-height source, so when the target is as
92
+ // wide as (or wider than) the source there is no horizontal room to pan and
93
+ // the pass would be a pure re-encode: ~40s of CPU on a streamer box for a
94
+ // frame-identical result. Skip it and let the caller use the raw clip.
95
+ //
96
+ // This is the normal case for the default 16:9 target on a 16:9 camera.
97
+ const srcDims = await this.probeDimensions(clipPath);
98
+ if (srcDims) {
99
+ const target = this.parseAspect(this.reelAspect);
100
+ const source = srcDims.width / srcDims.height;
101
+ if (target !== null && target >= source - 0.001) {
102
+ this.logger.info("Ball reframe skipped: target aspect is not narrower than the source, nothing to crop", { courtId, aspect: this.reelAspect, source: `${srcDims.width}x${srcDims.height}` });
103
+ return null; // caller falls back to the full-frame clip — the desired result
104
+ }
105
+ }
106
+ const dir = path.dirname(clipPath);
107
+ const base = path.basename(clipPath, path.extname(clipPath));
108
+ const out = path.join(dir, `reframed-${base}.mp4`);
109
+ try {
110
+ // --debug makes the script print a one-line detector summary to stderr
111
+ // (frames / hit_rate / motion); we log it so staging can SEE whether
112
+ // tracking actually locked onto players, not just that the script ran.
113
+ const stderr = await this.runPython([
114
+ this.scriptPath,
115
+ "--input", clipPath,
116
+ "--output", out,
117
+ "--aspect", this.reelAspect,
118
+ "--debug",
119
+ ]);
120
+ const summary = stderr
121
+ .split("\n")
122
+ .map((l) => l.trim())
123
+ .filter((l) => l.startsWith("reframe_ball:"))
124
+ .pop();
125
+ if (summary) {
126
+ this.logger.info("Ball reframe stats", { courtId, summary });
127
+ }
128
+ if (!fs.existsSync(out)) {
129
+ this.logger.warn("Ball reframer produced no output; using full frame", {
130
+ courtId,
131
+ });
132
+ return null;
133
+ }
134
+ // Existence isn't enough — a 0-byte / corrupt file would then be trusted
135
+ // over the good raw clip. Require a probeable, non-zero-duration video.
136
+ if (!(await this.isPlayable(out))) {
137
+ this.logger.warn("Ball reframer output invalid; using full frame", {
138
+ courtId,
139
+ out,
140
+ });
141
+ fs.unlink(out, () => { }); // don't leave the bad file to be picked up
142
+ return null;
143
+ }
144
+ this.logger.info("Ball reframe complete", { courtId, out });
145
+ return out;
146
+ }
147
+ catch (error) {
148
+ this.logger.warn("Ball reframe failed; using full frame", {
149
+ courtId,
150
+ error: error instanceof Error ? error.message : String(error),
151
+ });
152
+ return null;
153
+ }
154
+ }
155
+ /** "16:9" -> 1.777…; null when unparseable (caller then just runs the pass). */
156
+ parseAspect(aspect) {
157
+ const m = aspect.match(/^\s*(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)\s*$/);
158
+ if (!m)
159
+ return null;
160
+ const w = parseFloat(m[1]);
161
+ const h = parseFloat(m[2]);
162
+ return h > 0 && w > 0 ? w / h : null;
163
+ }
164
+ // Width/height of `file`, or null on any probe failure (never throws).
165
+ probeDimensions(file) {
166
+ return new Promise((resolve) => {
167
+ const proc = (0, child_process_1.spawn)("ffprobe", [
168
+ "-v", "error",
169
+ "-select_streams", "v:0",
170
+ "-show_entries", "stream=width,height",
171
+ "-of", "csv=p=0",
172
+ file,
173
+ ]);
174
+ let out = "";
175
+ proc.stdout?.on("data", (d) => (out += d.toString()));
176
+ proc.on("exit", (code) => {
177
+ if (code !== 0)
178
+ return resolve(null);
179
+ const m = out.trim().match(/(\d+)\s*,\s*(\d+)/);
180
+ resolve(m ? { width: parseInt(m[1], 10), height: parseInt(m[2], 10) } : null);
181
+ });
182
+ proc.on("error", () => resolve(null));
183
+ });
184
+ }
185
+ // True when ffprobe reports a positive duration for `file` (i.e. a real,
186
+ // non-empty video). Resolves false on any probe failure — never throws.
187
+ isPlayable(file) {
188
+ return new Promise((resolve) => {
189
+ const proc = (0, child_process_1.spawn)("ffprobe", [
190
+ "-v", "error",
191
+ "-show_entries", "format=duration",
192
+ "-of", "default=nw=1:nk=1",
193
+ file,
194
+ ]);
195
+ let out = "";
196
+ proc.stdout?.on("data", (d) => {
197
+ out += d.toString();
198
+ });
199
+ proc.on("exit", (code) => {
200
+ const dur = parseFloat(out.trim());
201
+ resolve(code === 0 && Number.isFinite(dur) && dur > 0);
202
+ });
203
+ proc.on("error", () => resolve(false));
204
+ });
205
+ }
206
+ // Resolves with the process's stderr (used for the --debug summary line);
207
+ // rejects on non-zero exit, spawn error, or timeout.
208
+ runPython(args) {
209
+ return new Promise((resolve, reject) => {
210
+ // Low CPU priority so the OpenCV passes can't starve the live ffmpeg.
211
+ const lp = (0, lowPriority_1.withLowPriority)("python3", args);
212
+ const proc = (0, child_process_1.spawn)(lp.command, lp.args, {
213
+ stdio: ["ignore", "ignore", "pipe"],
214
+ });
215
+ let stderr = "";
216
+ proc.stderr?.on("data", (d) => {
217
+ stderr += d.toString();
218
+ });
219
+ const timeout = setTimeout(() => {
220
+ proc.kill("SIGKILL");
221
+ reject(new Error("python reframe timed out"));
222
+ }, this.timeoutMs);
223
+ proc.on("exit", (code) => {
224
+ clearTimeout(timeout);
225
+ if (code === 0)
226
+ resolve(stderr);
227
+ else
228
+ reject(new Error(`python exited ${code}: ${stderr.slice(-200)}`));
229
+ });
230
+ proc.on("error", (err) => {
231
+ clearTimeout(timeout);
232
+ reject(err); // e.g. python3 not installed
233
+ });
234
+ });
235
+ }
236
+ }
237
+ exports.PythonBallReframer = PythonBallReframer;
238
+ //# sourceMappingURL=PythonBallReframer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PythonBallReframer.js","sourceRoot":"","sources":["../../../../src/infrastructure/services/PythonBallReframer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { EventEmitter } from "events";
2
+ import { CourtScoreSignal, HighlightSignal, HighlightSignalSource } from "../../domain/services/HighlightSignalSource";
3
+ import { Logger } from "../../application/interfaces/Logger";
4
+ export declare class SerialHighlightListener extends EventEmitter implements HighlightSignalSource {
5
+ private readonly portPath;
6
+ private readonly baudRate;
7
+ private readonly logger;
8
+ private readonly forcePresent;
9
+ private readonly triggerFile;
10
+ private port;
11
+ private parser;
12
+ private lastTrafficAtMs;
13
+ private portOpenedAtMs;
14
+ private started;
15
+ private reconnectTimer;
16
+ private reconnectAttempts;
17
+ private triggerTimer;
18
+ private lastButtonSeq;
19
+ private serialLib;
20
+ private parserLib;
21
+ constructor(portPath: string, // "auto" or an explicit device path
22
+ baudRate: number, logger: Logger, forcePresent?: boolean, triggerFile?: string);
23
+ onHighlight(listener: (signal: HighlightSignal) => void): void;
24
+ onScore(listener: (signal: CourtScoreSignal) => void): void;
25
+ start(): void;
26
+ stop(): void;
27
+ private startTriggerFileWatch;
28
+ isDevicePresent(): boolean;
29
+ private openPort;
30
+ private handleLine;
31
+ /**
32
+ * A `score` packet: the board's current state, forwarded verbatim.
33
+ *
34
+ * Score packets are the one type that carries NO `source` field, so anything
35
+ * keying on `source` being present would drop them — switch on `type` only.
36
+ * Values are passed through untouched: interpreting "AD" or mapping the mode
37
+ * is the consumer's job, not the transport's.
38
+ */
39
+ private handleScore;
40
+ private resolvePortPath;
41
+ private handlePortDown;
42
+ private teardownPort;
43
+ private scheduleReconnect;
44
+ }
45
+ //# sourceMappingURL=SerialHighlightListener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SerialHighlightListener.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/services/SerialHighlightListener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAsE7D,qBAAa,uBACX,SAAQ,YACR,YAAW,qBAAqB;IAmB9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAIvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAxB9B,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,YAAY,CAA+B;IAEnD,OAAO,CAAC,aAAa,CAAqB;IAI1C,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,SAAS,CAA6D;gBAG3D,QAAQ,EAAE,MAAM,EAAE,oCAAoC;IACtD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAId,YAAY,GAAE,OAAe,EAC7B,WAAW,GAAE,MAAW;IAKpC,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAI9D,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAI3D,KAAK,IAAI,IAAI;IAoBb,IAAI,IAAI,IAAI;IAkBnB,OAAO,CAAC,qBAAqB;IAiBtB,eAAe,IAAI,OAAO;YAanB,QAAQ;IAwEtB,OAAO,CAAC,UAAU;IA8ElB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;YAsBL,eAAe;IAwC7B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,iBAAiB;CAiB1B"}