@trillboards/edge-core 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 (49) hide show
  1. package/dist/DeviceIdentity.d.ts +32 -0
  2. package/dist/DeviceIdentity.d.ts.map +1 -0
  3. package/dist/DeviceIdentity.js +131 -0
  4. package/dist/DeviceIdentity.js.map +1 -0
  5. package/dist/DeviceProfile.d.ts +39 -0
  6. package/dist/DeviceProfile.d.ts.map +1 -0
  7. package/dist/DeviceProfile.js +199 -0
  8. package/dist/DeviceProfile.js.map +1 -0
  9. package/dist/ObservationSignalClassifier.d.ts +33 -0
  10. package/dist/ObservationSignalClassifier.d.ts.map +1 -0
  11. package/dist/ObservationSignalClassifier.js +116 -0
  12. package/dist/ObservationSignalClassifier.js.map +1 -0
  13. package/dist/RecoveryLadder.d.ts +58 -0
  14. package/dist/RecoveryLadder.d.ts.map +1 -0
  15. package/dist/RecoveryLadder.js +199 -0
  16. package/dist/RecoveryLadder.js.map +1 -0
  17. package/dist/SensingConfig.d.ts +33 -0
  18. package/dist/SensingConfig.d.ts.map +1 -0
  19. package/dist/SensingConfig.js +179 -0
  20. package/dist/SensingConfig.js.map +1 -0
  21. package/dist/SignalBuffer.d.ts +56 -0
  22. package/dist/SignalBuffer.d.ts.map +1 -0
  23. package/dist/SignalBuffer.js +143 -0
  24. package/dist/SignalBuffer.js.map +1 -0
  25. package/dist/SocketManager.d.ts +73 -0
  26. package/dist/SocketManager.d.ts.map +1 -0
  27. package/dist/SocketManager.js +160 -0
  28. package/dist/SocketManager.js.map +1 -0
  29. package/dist/SubsystemHealthRegistry.d.ts +61 -0
  30. package/dist/SubsystemHealthRegistry.d.ts.map +1 -0
  31. package/dist/SubsystemHealthRegistry.js +230 -0
  32. package/dist/SubsystemHealthRegistry.js.map +1 -0
  33. package/dist/TelemetryScheduler.d.ts +56 -0
  34. package/dist/TelemetryScheduler.d.ts.map +1 -0
  35. package/dist/TelemetryScheduler.js +99 -0
  36. package/dist/TelemetryScheduler.js.map +1 -0
  37. package/dist/VASWeightConfig.d.ts +26 -0
  38. package/dist/VASWeightConfig.d.ts.map +1 -0
  39. package/dist/VASWeightConfig.js +83 -0
  40. package/dist/VASWeightConfig.js.map +1 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +44 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/types.d.ts +530 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +96 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +40 -0
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ /**
3
+ * RecoveryLadder — 6-level autonomous recovery system.
4
+ * Port of RecoveryLadder.kt. Platform-specific recovery actions via callbacks.
5
+ *
6
+ * Levels:
7
+ * 1. Network Drop → Socket.io handles (infinite reconnect with exp backoff)
8
+ * 2. Player Stuck → Reload (3x) → Recreate (3x)
9
+ * 3. Camera/Mic Failure → Unbind/rebind hardware
10
+ * 4. Memory Pressure → Attenuate (shed models, restore)
11
+ * 5. Full Service Restart → Stop + restart service (3x per 24h)
12
+ * 6. OTA Rollback → Server-initiated install of previous version
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RecoveryLadder = void 0;
16
+ const types_1 = require("./types");
17
+ const MAX_RELOAD_ATTEMPTS = 3;
18
+ const MAX_RECREATE_ATTEMPTS = 3;
19
+ const MAX_REBIND_ATTEMPTS = 3;
20
+ const MAX_SERVICE_RESTARTS_PER_DAY = 3;
21
+ const RECOVERY_COOLDOWN_MS = 30000;
22
+ class RecoveryLadder {
23
+ constructor(healthRegistry, callbacks, maxRecoveryLevel = 6) {
24
+ this.recoveryHistory = [];
25
+ this.checkInterval = null;
26
+ this.healthRegistry = healthRegistry;
27
+ this.callbacks = callbacks;
28
+ this.maxRecoveryLevel = maxRecoveryLevel;
29
+ this.state = {
30
+ reloadAttempts: 0,
31
+ recreateAttempts: 0,
32
+ rebindAttempts: 0,
33
+ serviceRestarts: 0,
34
+ serviceRestartDay: this.getDayOfYear(),
35
+ lastRecoveryTimestamp: 0,
36
+ };
37
+ }
38
+ /**
39
+ * Start periodic recovery checks (every 30s, matching Kotlin).
40
+ */
41
+ start() {
42
+ if (this.checkInterval)
43
+ return;
44
+ this.checkInterval = setInterval(() => this.checkAndRecover(), 30000);
45
+ }
46
+ /**
47
+ * Stop periodic checks.
48
+ */
49
+ stop() {
50
+ if (this.checkInterval) {
51
+ clearInterval(this.checkInterval);
52
+ this.checkInterval = null;
53
+ }
54
+ }
55
+ /**
56
+ * Check subsystem health and trigger recovery if needed.
57
+ * Called every 30s by the interval, or manually.
58
+ */
59
+ async checkAndRecover() {
60
+ const now = Date.now();
61
+ if (now - this.state.lastRecoveryTimestamp < RECOVERY_COOLDOWN_MS) {
62
+ return; // Cooldown active
63
+ }
64
+ const failed = this.healthRegistry.getFailedSubsystems();
65
+ if (failed.length === 0) {
66
+ this.resetAttempts();
67
+ return;
68
+ }
69
+ for (const subsystem of failed) {
70
+ await this.handleFailure(subsystem);
71
+ }
72
+ }
73
+ /**
74
+ * Handle a specific subsystem failure with escalating recovery.
75
+ */
76
+ async handleFailure(subsystem) {
77
+ const now = Date.now();
78
+ this.state.lastRecoveryTimestamp = now;
79
+ try {
80
+ switch (subsystem) {
81
+ case types_1.Subsystem.SOCKET:
82
+ // Level 1: Socket.io handles reconnection internally
83
+ this.recordAction(types_1.RecoveryLevel.RECONNECT, subsystem, 'auto_reconnect', true);
84
+ break;
85
+ case types_1.Subsystem.PLAYER:
86
+ await this.recoverPlayer();
87
+ break;
88
+ case types_1.Subsystem.CAMERA:
89
+ await this.recoverHardware(subsystem, () => this.callbacks.onRebindCamera());
90
+ break;
91
+ case types_1.Subsystem.MICROPHONE:
92
+ await this.recoverHardware(subsystem, () => this.callbacks.onRebindMicrophone());
93
+ break;
94
+ case types_1.Subsystem.ML_PIPELINE:
95
+ // Level 4: Memory attenuation handles ML pipeline issues
96
+ if (this.maxRecoveryLevel >= types_1.RecoveryLevel.ATTENUATE_MEMORY) {
97
+ this.recordAction(types_1.RecoveryLevel.ATTENUATE_MEMORY, subsystem, 'attenuate', true);
98
+ this.callbacks.onEmitRecoveryEvent('ml_pipeline_attenuation', { subsystem });
99
+ }
100
+ break;
101
+ case types_1.Subsystem.MEMORY:
102
+ if (this.maxRecoveryLevel >= types_1.RecoveryLevel.ATTENUATE_MEMORY) {
103
+ this.recordAction(types_1.RecoveryLevel.ATTENUATE_MEMORY, subsystem, 'memory_attenuate', true);
104
+ this.callbacks.onEmitRecoveryEvent('memory_attenuation', { subsystem });
105
+ }
106
+ break;
107
+ }
108
+ }
109
+ catch (error) {
110
+ this.callbacks.onEmitRecoveryEvent('recovery_error', {
111
+ subsystem,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ });
114
+ }
115
+ }
116
+ async recoverPlayer() {
117
+ // Level 2: Reload → Recreate
118
+ if (this.state.reloadAttempts < MAX_RELOAD_ATTEMPTS && this.maxRecoveryLevel >= types_1.RecoveryLevel.RELOAD_PLAYER) {
119
+ this.state.reloadAttempts++;
120
+ await this.callbacks.onReloadPlayer();
121
+ this.recordAction(types_1.RecoveryLevel.RELOAD_PLAYER, types_1.Subsystem.PLAYER, 'reload', true);
122
+ return;
123
+ }
124
+ if (this.state.recreateAttempts < MAX_RECREATE_ATTEMPTS && this.maxRecoveryLevel >= types_1.RecoveryLevel.RELOAD_PLAYER) {
125
+ this.state.recreateAttempts++;
126
+ await this.callbacks.onRecreatePlayer();
127
+ this.recordAction(types_1.RecoveryLevel.RELOAD_PLAYER, types_1.Subsystem.PLAYER, 'recreate', true);
128
+ return;
129
+ }
130
+ // Level 5: Service restart
131
+ await this.attemptServiceRestart(types_1.Subsystem.PLAYER);
132
+ }
133
+ async recoverHardware(subsystem, rebindFn) {
134
+ // Level 3: Rebind hardware
135
+ if (this.state.rebindAttempts < MAX_REBIND_ATTEMPTS && this.maxRecoveryLevel >= types_1.RecoveryLevel.REBIND_HARDWARE) {
136
+ this.state.rebindAttempts++;
137
+ await rebindFn();
138
+ this.recordAction(types_1.RecoveryLevel.REBIND_HARDWARE, subsystem, 'rebind', true);
139
+ return;
140
+ }
141
+ // Escalate to sensing service restart
142
+ if (this.maxRecoveryLevel >= types_1.RecoveryLevel.RESTART_SERVICE) {
143
+ await this.callbacks.onRestartSensingService();
144
+ this.recordAction(types_1.RecoveryLevel.RESTART_SERVICE, subsystem, 'restart_sensing', true);
145
+ }
146
+ }
147
+ async attemptServiceRestart(subsystem) {
148
+ if (this.maxRecoveryLevel < types_1.RecoveryLevel.RESTART_SERVICE)
149
+ return;
150
+ // Reset daily counter if day changed
151
+ const today = this.getDayOfYear();
152
+ if (this.state.serviceRestartDay !== today) {
153
+ this.state.serviceRestarts = 0;
154
+ this.state.serviceRestartDay = today;
155
+ }
156
+ if (this.state.serviceRestarts < MAX_SERVICE_RESTARTS_PER_DAY) {
157
+ this.state.serviceRestarts++;
158
+ await this.callbacks.onRestartDeviceService();
159
+ this.recordAction(types_1.RecoveryLevel.RESTART_SERVICE, subsystem, 'service_restart', true);
160
+ }
161
+ else {
162
+ this.callbacks.onEmitRecoveryEvent('max_restarts_exceeded', {
163
+ subsystem,
164
+ restarts: this.state.serviceRestarts,
165
+ });
166
+ }
167
+ }
168
+ recordAction(level, subsystem, action, success) {
169
+ const record = {
170
+ level,
171
+ subsystem,
172
+ action,
173
+ timestamp: Date.now(),
174
+ success,
175
+ };
176
+ this.recoveryHistory.push(record);
177
+ // Keep last 100 actions
178
+ if (this.recoveryHistory.length > 100) {
179
+ this.recoveryHistory = this.recoveryHistory.slice(-100);
180
+ }
181
+ this.callbacks.onEmitRecoveryEvent('recovery_action', record);
182
+ }
183
+ resetAttempts() {
184
+ this.state.reloadAttempts = 0;
185
+ this.state.recreateAttempts = 0;
186
+ this.state.rebindAttempts = 0;
187
+ }
188
+ getDayOfYear() {
189
+ const now = new Date();
190
+ const start = new Date(now.getFullYear(), 0, 0);
191
+ const diff = now.getTime() - start.getTime();
192
+ return Math.floor(diff / (1000 * 60 * 60 * 24));
193
+ }
194
+ getRecoveryHistory() {
195
+ return [...this.recoveryHistory];
196
+ }
197
+ }
198
+ exports.RecoveryLadder = RecoveryLadder;
199
+ //# sourceMappingURL=RecoveryLadder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecoveryLadder.js","sourceRoot":"","sources":["../src/RecoveryLadder.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAEH,mCAIiB;AAuBjB,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,4BAA4B,GAAG,CAAC,CAAC;AACvC,MAAM,oBAAoB,GAAG,KAAM,CAAC;AAEpC,MAAa,cAAc;IAQzB,YACE,cAAuC,EACvC,SAA4B,EAC5B,mBAA2B,CAAC;QANtB,oBAAe,GAAqB,EAAE,CAAC;QACvC,kBAAa,GAA0C,IAAI,CAAC;QAOlE,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG;YACX,cAAc,EAAE,CAAC;YACjB,gBAAgB,EAAE,CAAC;YACnB,cAAc,EAAE,CAAC;YACjB,eAAe,EAAE,CAAC;YAClB,iBAAiB,EAAE,IAAI,CAAC,YAAY,EAAE;YACtC,qBAAqB,EAAE,CAAC;SACzB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAC/B,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,KAAM,CAAC,CAAC;IACzE,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAClC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,GAAG,oBAAoB,EAAE,CAAC;YAClE,OAAO,CAAC,kBAAkB;QAC5B,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,mBAAmB,EAAE,CAAC;QACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,MAAM,EAAE,CAAC;YAC/B,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,SAAoB;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,qBAAqB,GAAG,GAAG,CAAC;QAEvC,IAAI,CAAC;YACH,QAAQ,SAAS,EAAE,CAAC;gBAClB,KAAK,iBAAS,CAAC,MAAM;oBACnB,qDAAqD;oBACrD,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;oBAC9E,MAAM;gBAER,KAAK,iBAAS,CAAC,MAAM;oBACnB,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;oBAC3B,MAAM;gBAER,KAAK,iBAAS,CAAC,MAAM;oBACnB,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC;oBAC7E,MAAM;gBAER,KAAK,iBAAS,CAAC,UAAU;oBACvB,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,EAAE,CAAC,CAAC;oBACjF,MAAM;gBAER,KAAK,iBAAS,CAAC,WAAW;oBACxB,yDAAyD;oBACzD,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,gBAAgB,EAAE,CAAC;wBAC5D,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;wBAChF,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,yBAAyB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;oBAC/E,CAAC;oBACD,MAAM;gBAER,KAAK,iBAAS,CAAC,MAAM;oBACnB,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,gBAAgB,EAAE,CAAC;wBAC5D,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,gBAAgB,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAC;wBACvF,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;oBAC1E,CAAC;oBACD,MAAM;YACV,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,gBAAgB,EAAE;gBACnD,SAAS;gBACT,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,6BAA6B;QAC7B,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,mBAAmB,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,aAAa,EAAE,CAAC;YAC5G,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC;YACtC,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,aAAa,EAAE,iBAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,qBAAqB,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,aAAa,EAAE,CAAC;YAChH,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;YAC9B,MAAM,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC;YACxC,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,aAAa,EAAE,iBAAS,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;YACnF,OAAO;QACT,CAAC;QAED,2BAA2B;QAC3B,MAAM,IAAI,CAAC,qBAAqB,CAAC,iBAAS,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,eAAe,CAC3B,SAAoB,EACpB,QAA6B;QAE7B,2BAA2B;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,mBAAmB,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,eAAe,EAAE,CAAC;YAC9G,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC5B,MAAM,QAAQ,EAAE,CAAC;YACjB,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,IAAI,IAAI,CAAC,gBAAgB,IAAI,qBAAa,CAAC,eAAe,EAAE,CAAC;YAC3D,MAAM,IAAI,CAAC,SAAS,CAAC,uBAAuB,EAAE,CAAC;YAC/C,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,SAAoB;QACtD,IAAI,IAAI,CAAC,gBAAgB,GAAG,qBAAa,CAAC,eAAe;YAAE,OAAO;QAElE,qCAAqC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,KAAK,KAAK,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC;QACvC,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,4BAA4B,EAAE,CAAC;YAC9D,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,SAAS,CAAC,sBAAsB,EAAE,CAAC;YAC9C,IAAI,CAAC,YAAY,CAAC,qBAAa,CAAC,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;QACvF,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,uBAAuB,EAAE;gBAC1D,SAAS;gBACT,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe;aACrC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,YAAY,CAClB,KAAoB,EACpB,SAAoB,EACpB,MAAc,EACd,OAAgB;QAEhB,MAAM,MAAM,GAAmB;YAC7B,KAAK;YACL,SAAS;YACT,MAAM;YACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,OAAO;SACR,CAAC;QACF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,wBAAwB;QACxB,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACtC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,iBAAiB,EAAE,MAA4C,CAAC,CAAC;IACtG,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;IAChC,CAAC;IAEO,YAAY;QAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,kBAAkB;QAChB,OAAO,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;CACF;AApND,wCAoNC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SensingConfig — All 70+ server-tunable sensing parameters.
3
+ * Port of SensingConfig.kt. Thread-safe singleton with server-push merging.
4
+ *
5
+ * Default values match the Kotlin hardcoded behavior.
6
+ * Server can update ANY parameter via config.push event without recompile.
7
+ */
8
+ import type { AudioConfig, AttentionConfig, EngagementConfig, GazeConfig, BodyConfig, EmotionConfig, DetectionConfig, ShadowConfig, ViewabilityConfig, InsightConfig, CaptureConfig, SensingConfigData } from './types';
9
+ export declare class SensingConfig {
10
+ private config;
11
+ private hasServerConfig_;
12
+ constructor();
13
+ /**
14
+ * Merge server-pushed config update. Only overwrites fields present in the update.
15
+ * Called when a config.push event of type "sensing_config_update" arrives.
16
+ */
17
+ mergeWithServerUpdate(json: Record<string, unknown>): void;
18
+ get audio(): Readonly<AudioConfig>;
19
+ get attention(): Readonly<AttentionConfig>;
20
+ get engagement(): Readonly<EngagementConfig>;
21
+ get gaze(): Readonly<GazeConfig>;
22
+ get body(): Readonly<BodyConfig>;
23
+ get emotion(): Readonly<EmotionConfig>;
24
+ get detection(): Readonly<DetectionConfig>;
25
+ get shadow(): Readonly<ShadowConfig>;
26
+ get viewability(): Readonly<ViewabilityConfig>;
27
+ get insight(): Readonly<InsightConfig>;
28
+ get capture(): Readonly<CaptureConfig>;
29
+ hasServerConfig(): boolean;
30
+ /** Snapshot of all config for serialization. */
31
+ toJSON(): SensingConfigData;
32
+ }
33
+ //# sourceMappingURL=SensingConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SensingConfig.d.ts","sourceRoot":"","sources":["../src/SensingConfig.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAqGjB,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,gBAAgB,CAAS;;IAkBjC;;;OAGG;IACH,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAoB1D,IAAI,KAAK,IAAI,QAAQ,CAAC,WAAW,CAAC,CAEjC;IACD,IAAI,SAAS,IAAI,QAAQ,CAAC,eAAe,CAAC,CAEzC;IACD,IAAI,UAAU,IAAI,QAAQ,CAAC,gBAAgB,CAAC,CAE3C;IACD,IAAI,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,CAE/B;IACD,IAAI,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,CAE/B;IACD,IAAI,OAAO,IAAI,QAAQ,CAAC,aAAa,CAAC,CAErC;IACD,IAAI,SAAS,IAAI,QAAQ,CAAC,eAAe,CAAC,CAEzC;IACD,IAAI,MAAM,IAAI,QAAQ,CAAC,YAAY,CAAC,CAEnC;IACD,IAAI,WAAW,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CAE7C;IACD,IAAI,OAAO,IAAI,QAAQ,CAAC,aAAa,CAAC,CAErC;IACD,IAAI,OAAO,IAAI,QAAQ,CAAC,aAAa,CAAC,CAErC;IAED,eAAe,IAAI,OAAO;IAI1B,gDAAgD;IAChD,MAAM,IAAI,iBAAiB;CAG5B"}
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ /**
3
+ * SensingConfig — All 70+ server-tunable sensing parameters.
4
+ * Port of SensingConfig.kt. Thread-safe singleton with server-push merging.
5
+ *
6
+ * Default values match the Kotlin hardcoded behavior.
7
+ * Server can update ANY parameter via config.push event without recompile.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.SensingConfig = void 0;
11
+ const DEFAULT_AUDIO = {
12
+ minClassificationConfidence: 0.3,
13
+ speechAudienceSizeFactor: 1.5,
14
+ musicAudienceSizeFactor: 1.2,
15
+ crowdAudienceSizeFactor: 2.0,
16
+ speechAttentionFactor: 0.6,
17
+ musicAttentionFactor: 0.4,
18
+ highSpeechReceptivity: 0.8,
19
+ lowSpeechReceptivity: 0.3,
20
+ musicReceptivity: 0.5,
21
+ };
22
+ const DEFAULT_ATTENTION = {
23
+ yawDivisor: 45.0,
24
+ pitchDivisor: 30.0,
25
+ eyeWeight: 0.3,
26
+ yawWeight: 0.4,
27
+ pitchWeight: 0.3,
28
+ };
29
+ const DEFAULT_ENGAGEMENT = {
30
+ poseWeight: 0.3,
31
+ emotionWeight: 0.4,
32
+ gazeWeight: 0.3,
33
+ highEngagementThreshold: 0.7,
34
+ };
35
+ const DEFAULT_GAZE = {
36
+ yawDirect: 15.0,
37
+ yawPartial: 35.0,
38
+ yawAway: 60.0,
39
+ pitchDirect: 10.0,
40
+ pitchPartial: 25.0,
41
+ pitchAway: 45.0,
42
+ stabilityWindow: 5,
43
+ directScore: 1.0,
44
+ partialScore: 0.5,
45
+ awayScore: 0.1,
46
+ focusDirectScore: 1.0,
47
+ focusPartialScore: 0.6,
48
+ focusAwayScore: 0.2,
49
+ minStabilityFrames: 3,
50
+ };
51
+ const DEFAULT_BODY = {
52
+ facingAngleThreshold: 30.0,
53
+ leanMagnitudeThreshold: 0.15,
54
+ movementSpeedThreshold: 50.0,
55
+ facingScore: 0.4,
56
+ leanScore: 0.3,
57
+ movementScore: 0.3,
58
+ };
59
+ const DEFAULT_EMOTION = {
60
+ confidenceThreshold: 0.5,
61
+ reactionThreshold: 0.6,
62
+ positiveSentimentFactor: 1.2,
63
+ negativeSentimentFactor: 0.8,
64
+ neutralSentimentFactor: 1.0,
65
+ };
66
+ const DEFAULT_DETECTION = {
67
+ personConfidenceThreshold: 0.5,
68
+ };
69
+ const DEFAULT_SHADOW = {
70
+ luxRatioHigh: 3.0,
71
+ luxRatioLow: 0.3,
72
+ cooldownMs: 30000,
73
+ baselineUpdateAlpha: 0.05,
74
+ tamperingThreshold: 10.0,
75
+ };
76
+ const DEFAULT_VIEWABILITY = {
77
+ darkLux: 10,
78
+ dimLux: 50,
79
+ goodLux: 200,
80
+ brightLux: 1000,
81
+ darkViewability: 0.2,
82
+ dimViewability: 0.5,
83
+ goodViewability: 1.0,
84
+ brightViewability: 0.8,
85
+ overbrightViewability: 0.4,
86
+ };
87
+ const DEFAULT_INSIGHT = {
88
+ baselineConfidence: 0.5,
89
+ brandBoost: 0.15,
90
+ productBoost: 0.1,
91
+ interestBoost: 0.2,
92
+ purchaseIntentBoost: 0.25,
93
+ };
94
+ const DEFAULT_CAPTURE = {
95
+ periodicIntervalMs: 300000, // 5 minutes (idle / no faces)
96
+ faceTriggeredIntervalMs: 30000, // 30 seconds (when faces detected)
97
+ jpegQuality: 70,
98
+ };
99
+ class SensingConfig {
100
+ constructor() {
101
+ this.hasServerConfig_ = false;
102
+ this.config = {
103
+ audio: { ...DEFAULT_AUDIO },
104
+ attention: { ...DEFAULT_ATTENTION },
105
+ engagement: { ...DEFAULT_ENGAGEMENT },
106
+ gaze: { ...DEFAULT_GAZE },
107
+ body: { ...DEFAULT_BODY },
108
+ emotion: { ...DEFAULT_EMOTION },
109
+ detection: { ...DEFAULT_DETECTION },
110
+ shadow: { ...DEFAULT_SHADOW },
111
+ viewability: { ...DEFAULT_VIEWABILITY },
112
+ insight: { ...DEFAULT_INSIGHT },
113
+ capture: { ...DEFAULT_CAPTURE },
114
+ };
115
+ }
116
+ /**
117
+ * Merge server-pushed config update. Only overwrites fields present in the update.
118
+ * Called when a config.push event of type "sensing_config_update" arrives.
119
+ */
120
+ mergeWithServerUpdate(json) {
121
+ for (const section of Object.keys(json)) {
122
+ const sectionData = json[section];
123
+ if (typeof sectionData === 'object' &&
124
+ sectionData !== null &&
125
+ section in this.config) {
126
+ const target = this.config[section];
127
+ const source = sectionData;
128
+ for (const key of Object.keys(source)) {
129
+ if (key in target) {
130
+ target[key] = source[key];
131
+ }
132
+ }
133
+ }
134
+ }
135
+ this.hasServerConfig_ = true;
136
+ }
137
+ get audio() {
138
+ return this.config.audio;
139
+ }
140
+ get attention() {
141
+ return this.config.attention;
142
+ }
143
+ get engagement() {
144
+ return this.config.engagement;
145
+ }
146
+ get gaze() {
147
+ return this.config.gaze;
148
+ }
149
+ get body() {
150
+ return this.config.body;
151
+ }
152
+ get emotion() {
153
+ return this.config.emotion;
154
+ }
155
+ get detection() {
156
+ return this.config.detection;
157
+ }
158
+ get shadow() {
159
+ return this.config.shadow;
160
+ }
161
+ get viewability() {
162
+ return this.config.viewability;
163
+ }
164
+ get insight() {
165
+ return this.config.insight;
166
+ }
167
+ get capture() {
168
+ return this.config.capture;
169
+ }
170
+ hasServerConfig() {
171
+ return this.hasServerConfig_;
172
+ }
173
+ /** Snapshot of all config for serialization. */
174
+ toJSON() {
175
+ return JSON.parse(JSON.stringify(this.config));
176
+ }
177
+ }
178
+ exports.SensingConfig = SensingConfig;
179
+ //# sourceMappingURL=SensingConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SensingConfig.js","sourceRoot":"","sources":["../src/SensingConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * SignalBuffer — Offline signal buffering using better-sqlite3.
3
+ * Port of SignalBuffer.kt (Room database) to cross-platform SQLite.
4
+ *
5
+ * Capacity: 8,640 signals (24h at 6 signals/min)
6
+ * Eviction: oldest 10% when at capacity
7
+ * Flush: batch of 50, marks with _buffered and _bufferedAt
8
+ * Pruning: signals older than 24h auto-deleted
9
+ */
10
+ import { SocketManager } from './SocketManager';
11
+ export interface BufferedSignal {
12
+ id: number;
13
+ payload: string;
14
+ timestamp: number;
15
+ retryCount: number;
16
+ }
17
+ export declare class SignalBuffer {
18
+ static readonly MAX_CAPACITY = 8640;
19
+ static readonly FLUSH_BATCH_SIZE = 50;
20
+ static readonly MAX_AGE_MS: number;
21
+ static readonly MAX_RETRIES = 5;
22
+ static readonly EVICT_RATIO = 0.1;
23
+ private db;
24
+ private insertStmt;
25
+ private getOldestStmt;
26
+ private deleteByIdStmt;
27
+ private countStmt;
28
+ private pruneStmt;
29
+ private deleteOldestNStmt;
30
+ private incrementRetryStmt;
31
+ private deleteMaxRetriesStmt;
32
+ constructor(dbPath: string);
33
+ /**
34
+ * Buffer a signal for later sending.
35
+ * If at capacity, evicts oldest 10% first.
36
+ */
37
+ bufferSignal(payload: Record<string, unknown>): void;
38
+ /**
39
+ * Flush buffered signals via socket.
40
+ * Returns number of successfully sent signals.
41
+ */
42
+ flushBuffer(socketManager: SocketManager, limit?: number): number;
43
+ /**
44
+ * Prune signals older than 24 hours.
45
+ */
46
+ pruneOldSignals(): number;
47
+ /**
48
+ * Current number of buffered signals.
49
+ */
50
+ count(): number;
51
+ /**
52
+ * Close the database connection.
53
+ */
54
+ shutdown(): void;
55
+ }
56
+ //# sourceMappingURL=SignalBuffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignalBuffer.d.ts","sourceRoot":"","sources":["../src/SignalBuffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,MAAM,CAAC,QAAQ,CAAC,YAAY,QAAQ;IACpC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,MAAM;IACtC,MAAM,CAAC,QAAQ,CAAC,UAAU,SAAuB;IACjD,MAAM,CAAC,QAAQ,CAAC,WAAW,KAAK;IAChC,MAAM,CAAC,QAAQ,CAAC,WAAW,OAAO;IAElC,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,aAAa,CAAsC;IAC3D,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,OAAO,CAAC,kBAAkB,CAAsC;IAChE,OAAO,CAAC,oBAAoB,CAAsC;gBAEtD,MAAM,EAAE,MAAM;IAkD1B;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAYpD;;;OAGG;IACH,WAAW,CACT,aAAa,EAAE,aAAa,EAC5B,KAAK,GAAE,MAAsC,GAC5C,MAAM;IA8BT;;OAEG;IACH,eAAe,IAAI,MAAM;IAMzB;;OAEG;IACH,KAAK,IAAI,MAAM;IAKf;;OAEG;IACH,QAAQ,IAAI,IAAI;CAGjB"}
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * SignalBuffer — Offline signal buffering using better-sqlite3.
4
+ * Port of SignalBuffer.kt (Room database) to cross-platform SQLite.
5
+ *
6
+ * Capacity: 8,640 signals (24h at 6 signals/min)
7
+ * Eviction: oldest 10% when at capacity
8
+ * Flush: batch of 50, marks with _buffered and _bufferedAt
9
+ * Pruning: signals older than 24h auto-deleted
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.SignalBuffer = void 0;
39
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
40
+ const path = __importStar(require("path"));
41
+ const fs = __importStar(require("fs"));
42
+ class SignalBuffer {
43
+ constructor(dbPath) {
44
+ const dir = path.dirname(dbPath);
45
+ if (!fs.existsSync(dir)) {
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ }
48
+ this.db = new better_sqlite3_1.default(dbPath);
49
+ this.db.pragma('journal_mode = WAL');
50
+ this.db.pragma('synchronous = NORMAL');
51
+ this.db.exec(`
52
+ CREATE TABLE IF NOT EXISTS buffered_signals (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ payload TEXT NOT NULL,
55
+ timestamp INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000),
56
+ retry_count INTEGER NOT NULL DEFAULT 0
57
+ )
58
+ `);
59
+ this.db.exec(`
60
+ CREATE INDEX IF NOT EXISTS idx_signals_timestamp ON buffered_signals(timestamp)
61
+ `);
62
+ // Prepared statements for performance
63
+ this.insertStmt = this.db.prepare('INSERT INTO buffered_signals (payload, timestamp, retry_count) VALUES (?, ?, 0)');
64
+ this.getOldestStmt = this.db.prepare('SELECT id, payload, timestamp, retry_count FROM buffered_signals ORDER BY timestamp ASC LIMIT ?');
65
+ this.deleteByIdStmt = this.db.prepare('DELETE FROM buffered_signals WHERE id = ?');
66
+ this.countStmt = this.db.prepare('SELECT COUNT(*) as count FROM buffered_signals');
67
+ this.pruneStmt = this.db.prepare('DELETE FROM buffered_signals WHERE timestamp < ?');
68
+ this.deleteOldestNStmt = this.db.prepare('DELETE FROM buffered_signals WHERE id IN (SELECT id FROM buffered_signals ORDER BY timestamp ASC LIMIT ?)');
69
+ this.incrementRetryStmt = this.db.prepare('UPDATE buffered_signals SET retry_count = retry_count + 1 WHERE id = ?');
70
+ this.deleteMaxRetriesStmt = this.db.prepare('DELETE FROM buffered_signals WHERE retry_count >= ?');
71
+ }
72
+ /**
73
+ * Buffer a signal for later sending.
74
+ * If at capacity, evicts oldest 10% first.
75
+ */
76
+ bufferSignal(payload) {
77
+ const count = this.count();
78
+ if (count >= SignalBuffer.MAX_CAPACITY) {
79
+ const evictCount = Math.ceil(SignalBuffer.MAX_CAPACITY * SignalBuffer.EVICT_RATIO);
80
+ this.deleteOldestNStmt.run(evictCount);
81
+ }
82
+ this.insertStmt.run(JSON.stringify(payload), Date.now());
83
+ }
84
+ /**
85
+ * Flush buffered signals via socket.
86
+ * Returns number of successfully sent signals.
87
+ */
88
+ flushBuffer(socketManager, limit = SignalBuffer.FLUSH_BATCH_SIZE) {
89
+ const signals = this.getOldestStmt.all(limit);
90
+ let sent = 0;
91
+ for (const signal of signals) {
92
+ try {
93
+ const payload = JSON.parse(signal.payload);
94
+ // Mark as buffered signal with metadata
95
+ payload._buffered = true;
96
+ payload._bufferedAt = signal.timestamp;
97
+ const success = socketManager.emitAudienceSignal(payload);
98
+ if (success) {
99
+ this.deleteByIdStmt.run(signal.id);
100
+ sent++;
101
+ }
102
+ else {
103
+ this.incrementRetryStmt.run(signal.id);
104
+ }
105
+ }
106
+ catch {
107
+ // Corrupted payload — remove it
108
+ this.deleteByIdStmt.run(signal.id);
109
+ }
110
+ }
111
+ // Clean up signals that have exceeded max retries
112
+ this.deleteMaxRetriesStmt.run(SignalBuffer.MAX_RETRIES);
113
+ return sent;
114
+ }
115
+ /**
116
+ * Prune signals older than 24 hours.
117
+ */
118
+ pruneOldSignals() {
119
+ const cutoff = Date.now() - SignalBuffer.MAX_AGE_MS;
120
+ const result = this.pruneStmt.run(cutoff);
121
+ return result.changes;
122
+ }
123
+ /**
124
+ * Current number of buffered signals.
125
+ */
126
+ count() {
127
+ const row = this.countStmt.get();
128
+ return row.count;
129
+ }
130
+ /**
131
+ * Close the database connection.
132
+ */
133
+ shutdown() {
134
+ this.db.close();
135
+ }
136
+ }
137
+ exports.SignalBuffer = SignalBuffer;
138
+ SignalBuffer.MAX_CAPACITY = 8640;
139
+ SignalBuffer.FLUSH_BATCH_SIZE = 50;
140
+ SignalBuffer.MAX_AGE_MS = 24 * 60 * 60 * 1000;
141
+ SignalBuffer.MAX_RETRIES = 5;
142
+ SignalBuffer.EVICT_RATIO = 0.1;
143
+ //# sourceMappingURL=SignalBuffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignalBuffer.js","sourceRoot":"","sources":["../src/SignalBuffer.ts"],"names":[],"mappings":"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"}