@trillboards/edge-core 0.2.0 → 0.2.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Trillboards, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * JsonFileStore — Zero-dependency signal store using Node.js fs.
3
+ *
4
+ * Storage: configurable path (default: ~/.trillboards/signal_buffer.json)
5
+ * Persistence: debounced writes (every 5s max) + sync write on close()
6
+ * Crash safety: write to .tmp then fs.renameSync (atomic on all platforms)
7
+ * Priority: FACE_BACKED > PERSON_BACKED > SPEECH_ONLY > PROXY_ONLY > EMPTY
8
+ */
9
+ import type { SignalStore, StoredSignal, SignalStoreStats } from './SignalStore';
10
+ export declare class JsonFileStore implements SignalStore {
11
+ private signals;
12
+ private nextId;
13
+ private readonly filePath;
14
+ private dirty;
15
+ private flushTimer;
16
+ private static readonly FLUSH_INTERVAL_MS;
17
+ constructor(filePath?: string);
18
+ private loadFromDisk;
19
+ private schedulePersist;
20
+ private persistSync;
21
+ private extractEvidenceGrade;
22
+ insert(payload: string, timestamp: number): void;
23
+ getOldest(limit: number): StoredSignal[];
24
+ deleteById(id: number): void;
25
+ deleteOlderThan(cutoffMs: number): number;
26
+ deleteOldestN(n: number): void;
27
+ deleteExceedingRetries(maxRetries: number): void;
28
+ incrementRetry(id: number): void;
29
+ count(): number;
30
+ stats(): SignalStoreStats;
31
+ close(): void;
32
+ }
33
+ //# sourceMappingURL=JsonFileStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonFileStore.d.ts","sourceRoot":"","sources":["../src/JsonFileStore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAejF,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,QAAQ,CAAC,EAAE,MAAM;IAiB7B,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,oBAAoB;IAW5B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAYhD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE;IAWxC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ5B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAQzC,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ9B,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAMhD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQhC,KAAK,IAAI,MAAM;IAIf,KAAK,IAAI,gBAAgB;IAmBzB,KAAK,IAAI,IAAI;CASd"}
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * JsonFileStore — Zero-dependency signal store using Node.js fs.
4
+ *
5
+ * Storage: configurable path (default: ~/.trillboards/signal_buffer.json)
6
+ * Persistence: debounced writes (every 5s max) + sync write on close()
7
+ * Crash safety: write to .tmp then fs.renameSync (atomic on all platforms)
8
+ * Priority: FACE_BACKED > PERSON_BACKED > SPEECH_ONLY > PROXY_ONLY > EMPTY
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || function (mod) {
27
+ if (mod && mod.__esModule) return mod;
28
+ var result = {};
29
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
30
+ __setModuleDefault(result, mod);
31
+ return result;
32
+ };
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.JsonFileStore = void 0;
35
+ const fs = __importStar(require("fs"));
36
+ const path = __importStar(require("path"));
37
+ const EVIDENCE_GRADE_PRIORITY = {
38
+ FACE_BACKED: 1,
39
+ PERSON_BACKED: 2,
40
+ SPEECH_ONLY: 3,
41
+ PROXY_ONLY: 4,
42
+ EMPTY: 5,
43
+ };
44
+ class JsonFileStore {
45
+ constructor(filePath) {
46
+ this.signals = [];
47
+ this.nextId = 1;
48
+ this.dirty = false;
49
+ this.flushTimer = null;
50
+ this.filePath =
51
+ filePath ||
52
+ path.join(process.env.HOME || process.env.USERPROFILE || '/tmp', '.trillboards', 'signal_buffer.json');
53
+ const dir = path.dirname(this.filePath);
54
+ if (!fs.existsSync(dir)) {
55
+ fs.mkdirSync(dir, { recursive: true });
56
+ }
57
+ this.loadFromDisk();
58
+ }
59
+ loadFromDisk() {
60
+ try {
61
+ if (fs.existsSync(this.filePath)) {
62
+ const raw = fs.readFileSync(this.filePath, 'utf-8');
63
+ const data = JSON.parse(raw);
64
+ if (Array.isArray(data.signals)) {
65
+ this.signals = data.signals;
66
+ this.nextId = typeof data.nextId === 'number' ? data.nextId : 1;
67
+ // Ensure nextId is higher than any existing signal id
68
+ for (const s of this.signals) {
69
+ if (s.id >= this.nextId) {
70
+ this.nextId = s.id + 1;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ catch {
77
+ // Corrupt file — start fresh, log warning
78
+ console.warn(`[JsonFileStore] Corrupt signal buffer at ${this.filePath}, starting fresh`);
79
+ this.signals = [];
80
+ this.nextId = 1;
81
+ }
82
+ }
83
+ schedulePersist() {
84
+ this.dirty = true;
85
+ if (this.flushTimer)
86
+ return;
87
+ this.flushTimer = setTimeout(() => {
88
+ this.flushTimer = null;
89
+ if (this.dirty) {
90
+ this.persistSync();
91
+ }
92
+ }, JsonFileStore.FLUSH_INTERVAL_MS);
93
+ }
94
+ persistSync() {
95
+ this.dirty = false;
96
+ const data = {
97
+ nextId: this.nextId,
98
+ signals: this.signals,
99
+ };
100
+ const json = JSON.stringify(data);
101
+ const tmpPath = this.filePath + '.tmp';
102
+ try {
103
+ fs.writeFileSync(tmpPath, json, 'utf-8');
104
+ fs.renameSync(tmpPath, this.filePath);
105
+ }
106
+ catch (err) {
107
+ console.error(`[JsonFileStore] Failed to persist signal buffer:`, err);
108
+ }
109
+ }
110
+ extractEvidenceGrade(payload) {
111
+ try {
112
+ const parsed = JSON.parse(payload);
113
+ return typeof parsed.evidenceGrade === 'string'
114
+ ? parsed.evidenceGrade
115
+ : 'EMPTY';
116
+ }
117
+ catch {
118
+ return 'EMPTY';
119
+ }
120
+ }
121
+ insert(payload, timestamp) {
122
+ const signal = {
123
+ id: this.nextId++,
124
+ payload,
125
+ timestamp,
126
+ retryCount: 0,
127
+ evidenceGrade: this.extractEvidenceGrade(payload),
128
+ };
129
+ this.signals.push(signal);
130
+ this.schedulePersist();
131
+ }
132
+ getOldest(limit) {
133
+ // Sort by priority (evidence grade), then by timestamp (oldest first)
134
+ const sorted = [...this.signals].sort((a, b) => {
135
+ const prioA = EVIDENCE_GRADE_PRIORITY[a.evidenceGrade || 'EMPTY'] || 5;
136
+ const prioB = EVIDENCE_GRADE_PRIORITY[b.evidenceGrade || 'EMPTY'] || 5;
137
+ if (prioA !== prioB)
138
+ return prioA - prioB;
139
+ return a.timestamp - b.timestamp;
140
+ });
141
+ return sorted.slice(0, limit);
142
+ }
143
+ deleteById(id) {
144
+ const idx = this.signals.findIndex((s) => s.id === id);
145
+ if (idx !== -1) {
146
+ this.signals.splice(idx, 1);
147
+ this.schedulePersist();
148
+ }
149
+ }
150
+ deleteOlderThan(cutoffMs) {
151
+ const before = this.signals.length;
152
+ this.signals = this.signals.filter((s) => s.timestamp >= cutoffMs);
153
+ const deleted = before - this.signals.length;
154
+ if (deleted > 0)
155
+ this.schedulePersist();
156
+ return deleted;
157
+ }
158
+ deleteOldestN(n) {
159
+ if (n <= 0 || this.signals.length === 0)
160
+ return;
161
+ // Sort by timestamp ascending, remove the oldest N
162
+ this.signals.sort((a, b) => a.timestamp - b.timestamp);
163
+ this.signals.splice(0, n);
164
+ this.schedulePersist();
165
+ }
166
+ deleteExceedingRetries(maxRetries) {
167
+ const before = this.signals.length;
168
+ this.signals = this.signals.filter((s) => s.retryCount < maxRetries);
169
+ if (this.signals.length < before)
170
+ this.schedulePersist();
171
+ }
172
+ incrementRetry(id) {
173
+ const signal = this.signals.find((s) => s.id === id);
174
+ if (signal) {
175
+ signal.retryCount++;
176
+ this.schedulePersist();
177
+ }
178
+ }
179
+ count() {
180
+ return this.signals.length;
181
+ }
182
+ stats() {
183
+ const byGrade = {};
184
+ let oldest = null;
185
+ for (const s of this.signals) {
186
+ const grade = s.evidenceGrade || 'EMPTY';
187
+ byGrade[grade] = (byGrade[grade] || 0) + 1;
188
+ if (oldest === null || s.timestamp < oldest) {
189
+ oldest = s.timestamp;
190
+ }
191
+ }
192
+ return {
193
+ total: this.signals.length,
194
+ byEvidenceGrade: byGrade,
195
+ oldestTimestampMs: oldest,
196
+ };
197
+ }
198
+ close() {
199
+ if (this.flushTimer) {
200
+ clearTimeout(this.flushTimer);
201
+ this.flushTimer = null;
202
+ }
203
+ if (this.dirty) {
204
+ this.persistSync();
205
+ }
206
+ }
207
+ }
208
+ exports.JsonFileStore = JsonFileStore;
209
+ JsonFileStore.FLUSH_INTERVAL_MS = 5000;
210
+ //# sourceMappingURL=JsonFileStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonFileStore.js","sourceRoot":"","sources":["../src/JsonFileStore.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,22 @@
1
1
  /**
2
- * SignalBuffer — Offline signal buffering using better-sqlite3.
3
- * Port of SignalBuffer.kt (Room database) to cross-platform SQLite.
2
+ * SignalBuffer — Offline signal buffering with auto-detecting backend.
3
+ * Port of SignalBuffer.kt (Room database) to cross-platform storage.
4
+ *
5
+ * Backends:
6
+ * - SqliteStore (better-sqlite3 installed → WAL mode, prepared stmts)
7
+ * - JsonFileStore (fallback → zero deps, fs.renameSync atomic writes)
8
+ *
9
+ * Auto-detection: tries require('better-sqlite3') at construction time.
10
+ * If unavailable (native compilation failed, not installed), falls back
11
+ * to JsonFileStore — guaranteed to work on any Node.js >=18 platform.
4
12
  *
5
13
  * Capacity: 8,640 signals (24h at 6 signals/min)
6
14
  * Eviction: oldest 10% when at capacity
7
15
  * Flush: batch of 50, marks with _buffered and _bufferedAt
8
16
  * Pruning: signals older than 24h auto-deleted
17
+ * Priority: FACE_BACKED > PERSON_BACKED > SPEECH_ONLY > PROXY_ONLY > EMPTY
9
18
  */
19
+ import type { SignalStoreStats } from './SignalStore';
10
20
  import { SocketManager } from './SocketManager';
11
21
  export interface BufferedSignal {
12
22
  id: number;
@@ -20,15 +30,8 @@ export declare class SignalBuffer {
20
30
  static readonly MAX_AGE_MS: number;
21
31
  static readonly MAX_RETRIES = 5;
22
32
  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;
33
+ private store;
34
+ readonly backend: 'sqlite' | 'json';
32
35
  constructor(dbPath: string);
33
36
  /**
34
37
  * Buffer a signal for later sending.
@@ -38,6 +41,7 @@ export declare class SignalBuffer {
38
41
  /**
39
42
  * Flush buffered signals via socket.
40
43
  * Returns number of successfully sent signals.
44
+ * Signals are flushed in priority order — highest evidence grade first.
41
45
  */
42
46
  flushBuffer(socketManager: SocketManager, limit?: number): number;
43
47
  /**
@@ -49,7 +53,11 @@ export declare class SignalBuffer {
49
53
  */
50
54
  count(): number;
51
55
  /**
52
- * Close the database connection.
56
+ * Signal buffer statistics — count by evidence grade, oldest timestamp.
57
+ */
58
+ stats(): SignalStoreStats;
59
+ /**
60
+ * Close the store connection.
53
61
  */
54
62
  shutdown(): void;
55
63
  }
@@ -1 +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"}
1
+ {"version":3,"file":"SignalBuffer.d.ts","sourceRoot":"","sources":["../src/SignalBuffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEnE,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,KAAK,CAAc;IAC3B,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,CAAC;gBAExB,MAAM,EAAE,MAAM;IAiC1B;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAYpD;;;;OAIG;IACH,WAAW,CACT,aAAa,EAAE,aAAa,EAC5B,KAAK,GAAE,MAAsC,GAC5C,MAAM;IA8BT;;OAEG;IACH,eAAe,IAAI,MAAM;IAKzB;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,KAAK,IAAI,gBAAgB;IAIzB;;OAEG;IACH,QAAQ,IAAI,IAAI;CAGjB"}
@@ -1,92 +1,73 @@
1
1
  "use strict";
2
2
  /**
3
- * SignalBuffer — Offline signal buffering using better-sqlite3.
4
- * Port of SignalBuffer.kt (Room database) to cross-platform SQLite.
3
+ * SignalBuffer — Offline signal buffering with auto-detecting backend.
4
+ * Port of SignalBuffer.kt (Room database) to cross-platform storage.
5
+ *
6
+ * Backends:
7
+ * - SqliteStore (better-sqlite3 installed → WAL mode, prepared stmts)
8
+ * - JsonFileStore (fallback → zero deps, fs.renameSync atomic writes)
9
+ *
10
+ * Auto-detection: tries require('better-sqlite3') at construction time.
11
+ * If unavailable (native compilation failed, not installed), falls back
12
+ * to JsonFileStore — guaranteed to work on any Node.js >=18 platform.
5
13
  *
6
14
  * Capacity: 8,640 signals (24h at 6 signals/min)
7
15
  * Eviction: oldest 10% when at capacity
8
16
  * Flush: batch of 50, marks with _buffered and _bufferedAt
9
17
  * Pruning: signals older than 24h auto-deleted
18
+ * Priority: FACE_BACKED > PERSON_BACKED > SPEECH_ONLY > PROXY_ONLY > EMPTY
10
19
  */
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
20
  Object.defineProperty(exports, "__esModule", { value: true });
38
21
  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"));
22
+ const JsonFileStore_1 = require("./JsonFileStore");
42
23
  class SignalBuffer {
43
24
  constructor(dbPath) {
44
- const dir = path.dirname(dbPath);
45
- if (!fs.existsSync(dir)) {
46
- fs.mkdirSync(dir, { recursive: true });
25
+ let store = null;
26
+ let backend = 'json';
27
+ // Try SQLite first (high-performance, optional)
28
+ try {
29
+ // Dynamic require — only resolves if better-sqlite3 is installed + compiled
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
31
+ const Database = require('better-sqlite3');
32
+ const { SqliteStore } = require('./SqliteStore');
33
+ store = new SqliteStore(dbPath, Database);
34
+ backend = 'sqlite';
35
+ }
36
+ catch {
37
+ // better-sqlite3 not available — fall back to JSON file store
47
38
  }
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 >= ?');
39
+ if (!store) {
40
+ // Use JSON file store — zero dependencies, universal
41
+ const jsonPath = dbPath.replace(/\.db$/, '.json');
42
+ store = new JsonFileStore_1.JsonFileStore(jsonPath);
43
+ backend = 'json';
44
+ }
45
+ this.store = store;
46
+ this.backend = backend;
47
+ const label = backend === 'sqlite'
48
+ ? 'SQLite (disk-persistent, WAL mode)'
49
+ : 'JSON file (disk-persistent, atomic writes)';
50
+ console.log(`[SignalBuffer] Initialized: ${label}`);
71
51
  }
72
52
  /**
73
53
  * Buffer a signal for later sending.
74
54
  * If at capacity, evicts oldest 10% first.
75
55
  */
76
56
  bufferSignal(payload) {
77
- const count = this.count();
57
+ const count = this.store.count();
78
58
  if (count >= SignalBuffer.MAX_CAPACITY) {
79
59
  const evictCount = Math.ceil(SignalBuffer.MAX_CAPACITY * SignalBuffer.EVICT_RATIO);
80
- this.deleteOldestNStmt.run(evictCount);
60
+ this.store.deleteOldestN(evictCount);
81
61
  }
82
- this.insertStmt.run(JSON.stringify(payload), Date.now());
62
+ this.store.insert(JSON.stringify(payload), Date.now());
83
63
  }
84
64
  /**
85
65
  * Flush buffered signals via socket.
86
66
  * Returns number of successfully sent signals.
67
+ * Signals are flushed in priority order — highest evidence grade first.
87
68
  */
88
69
  flushBuffer(socketManager, limit = SignalBuffer.FLUSH_BATCH_SIZE) {
89
- const signals = this.getOldestStmt.all(limit);
70
+ const signals = this.store.getOldest(limit);
90
71
  let sent = 0;
91
72
  for (const signal of signals) {
92
73
  try {
@@ -96,20 +77,20 @@ class SignalBuffer {
96
77
  payload._bufferedAt = signal.timestamp;
97
78
  const success = socketManager.emitAudienceSignal(payload);
98
79
  if (success) {
99
- this.deleteByIdStmt.run(signal.id);
80
+ this.store.deleteById(signal.id);
100
81
  sent++;
101
82
  }
102
83
  else {
103
- this.incrementRetryStmt.run(signal.id);
84
+ this.store.incrementRetry(signal.id);
104
85
  }
105
86
  }
106
87
  catch {
107
88
  // Corrupted payload — remove it
108
- this.deleteByIdStmt.run(signal.id);
89
+ this.store.deleteById(signal.id);
109
90
  }
110
91
  }
111
92
  // Clean up signals that have exceeded max retries
112
- this.deleteMaxRetriesStmt.run(SignalBuffer.MAX_RETRIES);
93
+ this.store.deleteExceedingRetries(SignalBuffer.MAX_RETRIES);
113
94
  return sent;
114
95
  }
115
96
  /**
@@ -117,21 +98,25 @@ class SignalBuffer {
117
98
  */
118
99
  pruneOldSignals() {
119
100
  const cutoff = Date.now() - SignalBuffer.MAX_AGE_MS;
120
- const result = this.pruneStmt.run(cutoff);
121
- return result.changes;
101
+ return this.store.deleteOlderThan(cutoff);
122
102
  }
123
103
  /**
124
104
  * Current number of buffered signals.
125
105
  */
126
106
  count() {
127
- const row = this.countStmt.get();
128
- return row.count;
107
+ return this.store.count();
108
+ }
109
+ /**
110
+ * Signal buffer statistics — count by evidence grade, oldest timestamp.
111
+ */
112
+ stats() {
113
+ return this.store.stats();
129
114
  }
130
115
  /**
131
- * Close the database connection.
116
+ * Close the store connection.
132
117
  */
133
118
  shutdown() {
134
- this.db.close();
119
+ this.store.close();
135
120
  }
136
121
  }
137
122
  exports.SignalBuffer = SignalBuffer;
@@ -1 +1 @@
1
- {"version":3,"file":"SignalBuffer.js","sourceRoot":"","sources":["../src/SignalBuffer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SignalBuffer.js","sourceRoot":"","sources":["../src/SignalBuffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * SignalStore — Internal interface for signal-aware persistence backends.
3
+ *
4
+ * The SignalBuffer delegates all storage operations to a SignalStore implementation.
5
+ * Two built-in implementations:
6
+ * - JsonFileStore (zero dependencies, universal default)
7
+ * - SqliteStore (optional, high-throughput upgrade via better-sqlite3)
8
+ *
9
+ * Advanced users can implement this interface for custom backends (Redis, IndexedDB, etc.)
10
+ */
11
+ export interface StoredSignal {
12
+ id: number;
13
+ payload: string;
14
+ timestamp: number;
15
+ retryCount: number;
16
+ evidenceGrade?: string;
17
+ }
18
+ export interface SignalStoreStats {
19
+ total: number;
20
+ byEvidenceGrade: Record<string, number>;
21
+ oldestTimestampMs: number | null;
22
+ }
23
+ export interface SignalStore {
24
+ insert(payload: string, timestamp: number): void;
25
+ getOldest(limit: number): StoredSignal[];
26
+ deleteById(id: number): void;
27
+ deleteOlderThan(cutoffMs: number): number;
28
+ deleteOldestN(n: number): void;
29
+ deleteExceedingRetries(maxRetries: number): void;
30
+ incrementRetry(id: number): void;
31
+ count(): number;
32
+ stats(): SignalStoreStats;
33
+ close(): void;
34
+ }
35
+ //# sourceMappingURL=SignalStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignalStore.d.ts","sourceRoot":"","sources":["../src/SignalStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CAAC;IACzC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,KAAK,IAAI,MAAM,CAAC;IAChB,KAAK,IAAI,gBAAgB,CAAC;IAC1B,KAAK,IAAI,IAAI,CAAC;CACf"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ /**
3
+ * SignalStore — Internal interface for signal-aware persistence backends.
4
+ *
5
+ * The SignalBuffer delegates all storage operations to a SignalStore implementation.
6
+ * Two built-in implementations:
7
+ * - JsonFileStore (zero dependencies, universal default)
8
+ * - SqliteStore (optional, high-throughput upgrade via better-sqlite3)
9
+ *
10
+ * Advanced users can implement this interface for custom backends (Redis, IndexedDB, etc.)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ //# sourceMappingURL=SignalStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignalStore.js","sourceRoot":"","sources":["../src/SignalStore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * SqliteStore — High-performance signal store using better-sqlite3.
3
+ * Extracted from the original SignalBuffer implementation.
4
+ *
5
+ * WAL journal mode, prepared statements, evidence_grade column + index
6
+ * for priority-aware flushing (FACE_BACKED sent first).
7
+ *
8
+ * Only loaded when better-sqlite3 is available (optional dependency).
9
+ */
10
+ import type { SignalStore, StoredSignal, SignalStoreStats } from './SignalStore';
11
+ import type { Database as DatabaseType } from 'better-sqlite3';
12
+ export declare class SqliteStore implements SignalStore {
13
+ private db;
14
+ private insertStmt;
15
+ private getOldestStmt;
16
+ private deleteByIdStmt;
17
+ private countStmt;
18
+ private pruneStmt;
19
+ private deleteOldestNStmt;
20
+ private incrementRetryStmt;
21
+ private deleteMaxRetriesStmt;
22
+ private statsStmt;
23
+ private oldestStmt;
24
+ /**
25
+ * @param dbPath - Path to the SQLite database file
26
+ * @param DatabaseConstructor - The better-sqlite3 Database constructor (injected to avoid top-level require)
27
+ */
28
+ constructor(dbPath: string, DatabaseConstructor: new (filename: string) => DatabaseType);
29
+ private extractEvidenceGrade;
30
+ insert(payload: string, timestamp: number): void;
31
+ getOldest(limit: number): StoredSignal[];
32
+ deleteById(id: number): void;
33
+ deleteOlderThan(cutoffMs: number): number;
34
+ deleteOldestN(n: number): void;
35
+ deleteExceedingRetries(maxRetries: number): void;
36
+ incrementRetry(id: number): void;
37
+ count(): number;
38
+ stats(): SignalStoreStats;
39
+ close(): void;
40
+ }
41
+ //# sourceMappingURL=SqliteStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteStore.d.ts","sourceRoot":"","sources":["../src/SqliteStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGjF,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE/D,qBAAa,WAAY,YAAW,WAAW;IAC7C,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;IAClE,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAsC;IAExD;;;OAGG;gBAED,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,KAAK,QAAQ,EAAE,MAAM,KAAK,YAAY;IA4F7D,OAAO,CAAC,oBAAoB;IAW5B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKhD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE;IAIxC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI5B,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKzC,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAI9B,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIhD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIhC,KAAK,IAAI,MAAM;IAKf,KAAK,IAAI,gBAAgB;IAoBzB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * SqliteStore — High-performance signal store using better-sqlite3.
4
+ * Extracted from the original SignalBuffer implementation.
5
+ *
6
+ * WAL journal mode, prepared statements, evidence_grade column + index
7
+ * for priority-aware flushing (FACE_BACKED sent first).
8
+ *
9
+ * Only loaded when better-sqlite3 is available (optional dependency).
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
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.SqliteStore = void 0;
36
+ const path = __importStar(require("path"));
37
+ const fs = __importStar(require("fs"));
38
+ class SqliteStore {
39
+ /**
40
+ * @param dbPath - Path to the SQLite database file
41
+ * @param DatabaseConstructor - The better-sqlite3 Database constructor (injected to avoid top-level require)
42
+ */
43
+ constructor(dbPath, DatabaseConstructor) {
44
+ const dir = path.dirname(dbPath);
45
+ if (!fs.existsSync(dir)) {
46
+ fs.mkdirSync(dir, { recursive: true });
47
+ }
48
+ this.db = new DatabaseConstructor(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
+ evidence_grade TEXT NOT NULL DEFAULT 'EMPTY'
58
+ )
59
+ `);
60
+ this.db.exec(`
61
+ CREATE INDEX IF NOT EXISTS idx_signals_timestamp ON buffered_signals(timestamp)
62
+ `);
63
+ this.db.exec(`
64
+ CREATE INDEX IF NOT EXISTS idx_signals_evidence_grade ON buffered_signals(evidence_grade)
65
+ `);
66
+ // Migrate existing rows that lack evidence_grade (from pre-0.2.2 databases)
67
+ try {
68
+ // Check if evidence_grade column exists by querying pragma
69
+ const columns = this.db
70
+ .prepare("PRAGMA table_info('buffered_signals')")
71
+ .all();
72
+ const hasGradeCol = columns.some((c) => c.name === 'evidence_grade');
73
+ if (!hasGradeCol) {
74
+ this.db.exec("ALTER TABLE buffered_signals ADD COLUMN evidence_grade TEXT NOT NULL DEFAULT 'EMPTY'");
75
+ this.db.exec("CREATE INDEX IF NOT EXISTS idx_signals_evidence_grade ON buffered_signals(evidence_grade)");
76
+ }
77
+ }
78
+ catch {
79
+ // Column already exists or migration not needed
80
+ }
81
+ // Prepared statements
82
+ this.insertStmt = this.db.prepare('INSERT INTO buffered_signals (payload, timestamp, retry_count, evidence_grade) VALUES (?, ?, 0, ?)');
83
+ this.getOldestStmt = this.db.prepare(`
84
+ SELECT id, payload, timestamp, retry_count as retryCount, evidence_grade as evidenceGrade
85
+ FROM buffered_signals
86
+ ORDER BY
87
+ CASE evidence_grade
88
+ WHEN 'FACE_BACKED' THEN 1
89
+ WHEN 'PERSON_BACKED' THEN 2
90
+ WHEN 'SPEECH_ONLY' THEN 3
91
+ WHEN 'PROXY_ONLY' THEN 4
92
+ ELSE 5
93
+ END,
94
+ timestamp ASC
95
+ LIMIT ?
96
+ `);
97
+ this.deleteByIdStmt = this.db.prepare('DELETE FROM buffered_signals WHERE id = ?');
98
+ this.countStmt = this.db.prepare('SELECT COUNT(*) as count FROM buffered_signals');
99
+ this.pruneStmt = this.db.prepare('DELETE FROM buffered_signals WHERE timestamp < ?');
100
+ this.deleteOldestNStmt = this.db.prepare('DELETE FROM buffered_signals WHERE id IN (SELECT id FROM buffered_signals ORDER BY timestamp ASC LIMIT ?)');
101
+ this.incrementRetryStmt = this.db.prepare('UPDATE buffered_signals SET retry_count = retry_count + 1 WHERE id = ?');
102
+ this.deleteMaxRetriesStmt = this.db.prepare('DELETE FROM buffered_signals WHERE retry_count >= ?');
103
+ this.statsStmt = this.db.prepare('SELECT evidence_grade as grade, COUNT(*) as cnt FROM buffered_signals GROUP BY evidence_grade');
104
+ this.oldestStmt = this.db.prepare('SELECT MIN(timestamp) as oldest FROM buffered_signals');
105
+ }
106
+ extractEvidenceGrade(payload) {
107
+ try {
108
+ const parsed = JSON.parse(payload);
109
+ return typeof parsed.evidenceGrade === 'string'
110
+ ? parsed.evidenceGrade
111
+ : 'EMPTY';
112
+ }
113
+ catch {
114
+ return 'EMPTY';
115
+ }
116
+ }
117
+ insert(payload, timestamp) {
118
+ const grade = this.extractEvidenceGrade(payload);
119
+ this.insertStmt.run(payload, timestamp, grade);
120
+ }
121
+ getOldest(limit) {
122
+ return this.getOldestStmt.all(limit);
123
+ }
124
+ deleteById(id) {
125
+ this.deleteByIdStmt.run(id);
126
+ }
127
+ deleteOlderThan(cutoffMs) {
128
+ const result = this.pruneStmt.run(cutoffMs);
129
+ return result.changes;
130
+ }
131
+ deleteOldestN(n) {
132
+ this.deleteOldestNStmt.run(n);
133
+ }
134
+ deleteExceedingRetries(maxRetries) {
135
+ this.deleteMaxRetriesStmt.run(maxRetries);
136
+ }
137
+ incrementRetry(id) {
138
+ this.incrementRetryStmt.run(id);
139
+ }
140
+ count() {
141
+ const row = this.countStmt.get();
142
+ return row.count;
143
+ }
144
+ stats() {
145
+ const rows = this.statsStmt.all();
146
+ const byGrade = {};
147
+ for (const row of rows) {
148
+ byGrade[row.grade] = row.cnt;
149
+ }
150
+ const oldestRow = this.oldestStmt.get();
151
+ return {
152
+ total: this.count(),
153
+ byEvidenceGrade: byGrade,
154
+ oldestTimestampMs: oldestRow.oldest,
155
+ };
156
+ }
157
+ close() {
158
+ this.db.close();
159
+ }
160
+ }
161
+ exports.SqliteStore = SqliteStore;
162
+ //# sourceMappingURL=SqliteStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqliteStore.js","sourceRoot":"","sources":["../src/SqliteStore.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,2CAA6B;AAC7B,uCAAyB;AAMzB,MAAa,WAAW;IAatB;;;OAGG;IACH,YACE,MAAc,EACd,mBAA2D;QAE3D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,CAAC,EAAE,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAEvC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;;;;;;;KAQZ,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;KAEZ,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC;;KAEZ,CAAC,CAAC;QAEH,4EAA4E;QAC5E,IAAI,CAAC;YACH,2DAA2D;YAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE;iBACpB,OAAO,CAAC,uCAAuC,CAAC;iBAChD,GAAG,EAA6B,CAAC;YACpC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;YACrE,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,IAAI,CAAC,EAAE,CAAC,IAAI,CACV,sFAAsF,CACvF,CAAC;gBACF,IAAI,CAAC,EAAE,CAAC,IAAI,CACV,2FAA2F,CAC5F,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,gDAAgD;QAClD,CAAC;QAED,sBAAsB;QACtB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC/B,oGAAoG,CACrG,CAAC;QACF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;;;;;;;;;KAapC,CAAC,CAAC;QACH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACnC,2CAA2C,CAC5C,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC9B,gDAAgD,CACjD,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC9B,kDAAkD,CACnD,CAAC;QACF,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACtC,2GAA2G,CAC5G,CAAC;QACF,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACvC,wEAAwE,CACzE,CAAC;QACF,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CACzC,qDAAqD,CACtD,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC9B,+FAA+F,CAChG,CAAC;QACF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAC/B,uDAAuD,CACxD,CAAC;IACJ,CAAC;IAEO,oBAAoB,CAAC,OAAe;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ;gBAC7C,CAAC,CAAC,MAAM,CAAC,aAAa;gBACtB,CAAC,CAAC,OAAO,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,SAAiB;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,UAAkB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAmB,CAAC;IACzD,CAAC;IAED,UAAU,CAAC,EAAU;QACnB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,aAAa,CAAC,CAAS;QACrB,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,sBAAsB,CAAC,UAAkB;QACvC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAED,cAAc,CAAC,EAAU;QACvB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,KAAK;QACH,MAAM,GAAG,GAAI,IAAI,CAAC,SAAiB,CAAC,GAAG,EAAuB,CAAC;QAC/D,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IAED,KAAK;QACH,MAAM,IAAI,GAAI,IAAI,CAAC,SAAiB,CAAC,GAAG,EAGtC,CAAC;QACH,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;QAC/B,CAAC;QACD,MAAM,SAAS,GAAI,IAAI,CAAC,UAAkB,CAAC,GAAG,EAE7C,CAAC;QAEF,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;YACnB,eAAe,EAAE,OAAO;YACxB,iBAAiB,EAAE,SAAS,CAAC,MAAM;SACpC,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF;AApLD,kCAoLC"}
package/dist/index.d.ts CHANGED
@@ -5,6 +5,9 @@ export { SocketManager } from './SocketManager';
5
5
  export type { SocketManagerOptions } from './SocketManager';
6
6
  export { VASWeightConfig } from './VASWeightConfig';
7
7
  export { SignalBuffer } from './SignalBuffer';
8
+ export type { SignalStore, StoredSignal, SignalStoreStats } from './SignalStore';
9
+ export { JsonFileStore } from './JsonFileStore';
10
+ export { SqliteStore } from './SqliteStore';
8
11
  export { SubsystemHealthRegistry } from './SubsystemHealthRegistry';
9
12
  export { RecoveryLadder } from './RecoveryLadder';
10
13
  export type { RecoveryCallbacks } from './RecoveryLadder';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.getAttenuationTier = exports.getShedOrder = exports.isModelActive = exports.getCapabilityMatrix = exports.computeTier = exports.classifyObservationSignal = exports.TelemetryScheduler = exports.RecoveryLadder = exports.SubsystemHealthRegistry = exports.SignalBuffer = exports.VASWeightConfig = exports.SocketManager = exports.SensingConfig = exports.DeviceIdentity = void 0;
18
+ exports.getAttenuationTier = exports.getShedOrder = exports.isModelActive = exports.getCapabilityMatrix = exports.computeTier = exports.classifyObservationSignal = exports.TelemetryScheduler = exports.RecoveryLadder = exports.SubsystemHealthRegistry = exports.SqliteStore = exports.JsonFileStore = exports.SignalBuffer = exports.VASWeightConfig = exports.SocketManager = exports.SensingConfig = exports.DeviceIdentity = void 0;
19
19
  __exportStar(require("./types"), exports);
20
20
  var DeviceIdentity_1 = require("./DeviceIdentity");
21
21
  Object.defineProperty(exports, "DeviceIdentity", { enumerable: true, get: function () { return DeviceIdentity_1.DeviceIdentity; } });
@@ -27,6 +27,10 @@ var VASWeightConfig_1 = require("./VASWeightConfig");
27
27
  Object.defineProperty(exports, "VASWeightConfig", { enumerable: true, get: function () { return VASWeightConfig_1.VASWeightConfig; } });
28
28
  var SignalBuffer_1 = require("./SignalBuffer");
29
29
  Object.defineProperty(exports, "SignalBuffer", { enumerable: true, get: function () { return SignalBuffer_1.SignalBuffer; } });
30
+ var JsonFileStore_1 = require("./JsonFileStore");
31
+ Object.defineProperty(exports, "JsonFileStore", { enumerable: true, get: function () { return JsonFileStore_1.JsonFileStore; } });
32
+ var SqliteStore_1 = require("./SqliteStore");
33
+ Object.defineProperty(exports, "SqliteStore", { enumerable: true, get: function () { return SqliteStore_1.SqliteStore; } });
30
34
  var SubsystemHealthRegistry_1 = require("./SubsystemHealthRegistry");
31
35
  Object.defineProperty(exports, "SubsystemHealthRegistry", { enumerable: true, get: function () { return SubsystemHealthRegistry_1.SubsystemHealthRegistry; } });
32
36
  var RecoveryLadder_1 = require("./RecoveryLadder");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;;;;;;;;;;;;AAEtF,0CAAwB;AACxB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AAEvB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAE3B,6EAGuC;AAFrC,wIAAA,yBAAyB,OAAA;AAG3B,iDAMyB;AALvB,4GAAA,WAAW,OAAA;AACX,oHAAA,mBAAmB,OAAA;AACnB,8GAAA,aAAa,OAAA;AACb,6GAAA,YAAY,OAAA;AACZ,mHAAA,kBAAkB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;;;;;;;;;;;;AAEtF,0CAAwB;AACxB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AAEvB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAE3B,6EAGuC;AAFrC,wIAAA,yBAAyB,OAAA;AAG3B,iDAMyB;AALvB,4GAAA,WAAW,OAAA;AACX,oHAAA,mBAAmB,OAAA;AACnB,8GAAA,aAAa,OAAA;AACb,6GAAA,YAAY,OAAA;AACZ,mHAAA,kBAAkB,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trillboards/edge-core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Core identity, heartbeat, Socket.io, config, and signal buffer for Trillboards Edge AI SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,10 +11,12 @@
11
11
  "prepublishOnly": "npm run build"
12
12
  },
13
13
  "dependencies": {
14
- "better-sqlite3": "^11.0.0",
15
14
  "socket.io-client": "^4.7.0",
16
15
  "uuid": "^9.0.0"
17
16
  },
17
+ "optionalDependencies": {
18
+ "better-sqlite3": "^11.0.0"
19
+ },
18
20
  "devDependencies": {
19
21
  "@types/better-sqlite3": "^7.6.0",
20
22
  "@types/jest": "^29.5.0",
@@ -24,8 +26,9 @@
24
26
  "ts-jest": "^29.1.0",
25
27
  "typescript": "~5.3.0"
26
28
  },
27
- "files": ["dist"],
28
- "license": "UNLICENSED",
29
+ "files": ["dist", "LICENSE", "README.md"],
30
+ "author": "Trillboards <engineering@trillboards.com>",
31
+ "license": "MIT",
29
32
  "publishConfig": {
30
33
  "access": "public"
31
34
  },