@openrfid/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.
package/dist/index.js ADDED
@@ -0,0 +1,787 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BRAND: () => BRAND,
34
+ ComponentState: () => ComponentState,
35
+ ConfigManager: () => ConfigManager,
36
+ Container: () => Container,
37
+ EventBus: () => EventBus,
38
+ Logger: () => Logger,
39
+ MemoryStorageDriver: () => MemoryStorageDriver,
40
+ SqliteStorageDriver: () => SqliteStorageDriver,
41
+ defaultConfig: () => defaultConfig
42
+ });
43
+ module.exports = __toCommonJS(index_exports);
44
+
45
+ // src/lifecycle.ts
46
+ var ComponentState = /* @__PURE__ */ ((ComponentState2) => {
47
+ ComponentState2["UNINITIALIZED"] = "UNINITIALIZED";
48
+ ComponentState2["INITIALIZED"] = "INITIALIZED";
49
+ ComponentState2["RUNNING"] = "RUNNING";
50
+ ComponentState2["STOPPED"] = "STOPPED";
51
+ ComponentState2["DISPOSED"] = "DISPOSED";
52
+ ComponentState2["ERROR"] = "ERROR";
53
+ return ComponentState2;
54
+ })(ComponentState || {});
55
+
56
+ // src/event-bus.ts
57
+ var EventBus = class {
58
+ listeners = /* @__PURE__ */ new Map();
59
+ on(event, handler) {
60
+ const eventName = String(event);
61
+ if (!this.listeners.has(eventName)) {
62
+ this.listeners.set(eventName, /* @__PURE__ */ new Set());
63
+ }
64
+ this.listeners.get(eventName).add(handler);
65
+ return () => this.off(event, handler);
66
+ }
67
+ off(event, handler) {
68
+ const eventName = String(event);
69
+ const handlers = this.listeners.get(eventName);
70
+ if (handlers) {
71
+ handlers.delete(handler);
72
+ if (handlers.size === 0) {
73
+ this.listeners.delete(eventName);
74
+ }
75
+ }
76
+ }
77
+ async emit(event, payload) {
78
+ const eventName = String(event);
79
+ const handlers = this.listeners.get(eventName);
80
+ if (handlers) {
81
+ const promises = [];
82
+ for (const handler of handlers) {
83
+ try {
84
+ promises.push(handler(payload));
85
+ } catch (err) {
86
+ console.error(`Error in event handler for event '${eventName}':`, err);
87
+ }
88
+ }
89
+ await Promise.all(promises);
90
+ }
91
+ }
92
+ removeAllListeners(event) {
93
+ if (event) {
94
+ this.listeners.delete(String(event));
95
+ } else {
96
+ this.listeners.clear();
97
+ }
98
+ }
99
+ listenerCount(event) {
100
+ return this.listeners.get(String(event))?.size ?? 0;
101
+ }
102
+ };
103
+
104
+ // src/di-container.ts
105
+ var Container = class {
106
+ services = /* @__PURE__ */ new Map();
107
+ register(key, factory, singleton = true) {
108
+ this.services.set(key, { factory, singleton });
109
+ }
110
+ registerInstance(key, instance) {
111
+ this.services.set(key, { factory: () => instance, singleton: true, instance });
112
+ }
113
+ resolve(key) {
114
+ const service = this.services.get(key);
115
+ if (!service) {
116
+ throw new Error(`Service '${key}' is not registered in the DI Container.`);
117
+ }
118
+ if (service.singleton) {
119
+ if (!service.instance) {
120
+ service.instance = service.factory(this);
121
+ }
122
+ return service.instance;
123
+ }
124
+ return service.factory(this);
125
+ }
126
+ has(key) {
127
+ return this.services.has(key);
128
+ }
129
+ reset() {
130
+ this.services.clear();
131
+ }
132
+ };
133
+
134
+ // src/config-manager.ts
135
+ var defaultConfig = {
136
+ server: {
137
+ port: 3e3,
138
+ host: "0.0.0.0"
139
+ },
140
+ mqtt: {
141
+ enabled: false,
142
+ brokerUrl: "mqtt://localhost:1883",
143
+ topicPrefix: "openrfid"
144
+ },
145
+ websocket: {
146
+ port: 3001
147
+ },
148
+ storage: {
149
+ dbPath: "./openrfid.db",
150
+ memoryEventLimit: 1e4,
151
+ eventWarningThreshold: 8e3,
152
+ autoTrimEnabled: true
153
+ },
154
+ simulator: {
155
+ defaultReadIntervalMs: 500,
156
+ defaultRssi: -55
157
+ }
158
+ };
159
+ var ConfigManager = class {
160
+ config;
161
+ listeners = /* @__PURE__ */ new Set();
162
+ constructor(initialConfig = {}) {
163
+ this.config = this.deepMerge({ ...defaultConfig }, initialConfig);
164
+ }
165
+ get(path) {
166
+ const keys = path.split(".");
167
+ let current = this.config;
168
+ for (const key of keys) {
169
+ if (current === void 0 || current === null) return void 0;
170
+ current = current[key];
171
+ }
172
+ return current;
173
+ }
174
+ set(path, value) {
175
+ const keys = path.split(".");
176
+ let current = this.config;
177
+ for (let i = 0; i < keys.length - 1; i++) {
178
+ const key = keys[i];
179
+ if (!current[key] || typeof current[key] !== "object") {
180
+ current[key] = {};
181
+ }
182
+ current = current[key];
183
+ }
184
+ current[keys[keys.length - 1]] = value;
185
+ this.notify();
186
+ }
187
+ getAll() {
188
+ return { ...this.config };
189
+ }
190
+ onChange(listener) {
191
+ this.listeners.add(listener);
192
+ return () => this.listeners.delete(listener);
193
+ }
194
+ notify() {
195
+ const freezeCopy = Object.freeze({ ...this.config });
196
+ for (const listener of this.listeners) {
197
+ listener(freezeCopy);
198
+ }
199
+ }
200
+ deepMerge(target, source) {
201
+ const output = { ...target };
202
+ for (const key of Object.keys(source)) {
203
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
204
+ output[key] = this.deepMerge(target[key] ?? {}, source[key]);
205
+ } else {
206
+ output[key] = source[key];
207
+ }
208
+ }
209
+ return output;
210
+ }
211
+ };
212
+
213
+ // src/logger.ts
214
+ var Logger = class {
215
+ constructor(context = "App", minLevel = "info") {
216
+ this.context = context;
217
+ this.minLevel = minLevel;
218
+ }
219
+ context;
220
+ minLevel;
221
+ levelWeights = {
222
+ debug: 0,
223
+ info: 1,
224
+ warn: 2,
225
+ error: 3
226
+ };
227
+ debug(message, data) {
228
+ this.log("debug", message, data);
229
+ }
230
+ info(message, data) {
231
+ this.log("info", message, data);
232
+ }
233
+ warn(message, data) {
234
+ this.log("warn", message, data);
235
+ }
236
+ error(message, data) {
237
+ this.log("error", message, data);
238
+ }
239
+ log(level, message, data) {
240
+ if (this.levelWeights[level] < this.levelWeights[this.minLevel]) {
241
+ return;
242
+ }
243
+ const entry = {
244
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
245
+ level,
246
+ context: this.context,
247
+ message,
248
+ data
249
+ };
250
+ const formatted = `[${entry.timestamp}] [${level.toUpperCase()}] [${entry.context}]: ${message}`;
251
+ if (level === "error") {
252
+ console.error(formatted, data !== void 0 ? data : "");
253
+ } else if (level === "warn") {
254
+ console.warn(formatted, data !== void 0 ? data : "");
255
+ } else {
256
+ console.log(formatted, data !== void 0 ? data : "");
257
+ }
258
+ }
259
+ };
260
+
261
+ // src/storage-memory.ts
262
+ var MemoryStorageDriver = class {
263
+ constructor(config) {
264
+ this.config = config;
265
+ this.maxEvents = config?.get("storage.memoryEventLimit") ?? 1e4;
266
+ this.warningThreshold = config?.get("storage.eventWarningThreshold") ?? 8e3;
267
+ this.autoTrim = config?.get("storage.autoTrimEnabled") ?? true;
268
+ if (config) {
269
+ this.configUnsubscribe = config.onChange((cfg) => {
270
+ this.maxEvents = cfg.storage?.memoryEventLimit ?? 1e4;
271
+ this.warningThreshold = cfg.storage?.eventWarningThreshold ?? 8e3;
272
+ this.autoTrim = cfg.storage?.autoTrimEnabled ?? true;
273
+ if (this.events.length > this.maxEvents) {
274
+ this.events.length = this.maxEvents;
275
+ }
276
+ });
277
+ }
278
+ }
279
+ config;
280
+ readers = /* @__PURE__ */ new Map();
281
+ tags = /* @__PURE__ */ new Map();
282
+ antennas = /* @__PURE__ */ new Map();
283
+ events = [];
284
+ connected = false;
285
+ maxEvents;
286
+ warningThreshold;
287
+ autoTrim;
288
+ configUnsubscribe;
289
+ async connect() {
290
+ this.connected = true;
291
+ }
292
+ async disconnect() {
293
+ this.connected = false;
294
+ this.configUnsubscribe?.();
295
+ }
296
+ // Reader CRUD
297
+ async getReaders() {
298
+ return Array.from(this.readers.values());
299
+ }
300
+ async getReaderById(id) {
301
+ return this.readers.get(id) ?? null;
302
+ }
303
+ async saveReader(reader) {
304
+ this.readers.set(reader.id, reader);
305
+ }
306
+ async deleteReader(id) {
307
+ this.readers.delete(id);
308
+ for (const key of Array.from(this.antennas.keys())) {
309
+ if (this.antennas.get(key)?.readerId === id) {
310
+ this.antennas.delete(key);
311
+ }
312
+ }
313
+ }
314
+ // Antenna Persistence
315
+ async saveAntenna(antenna) {
316
+ this.antennas.set(antenna.id, antenna);
317
+ }
318
+ async getAntennasByReaderId(readerId) {
319
+ return Array.from(this.antennas.values()).filter((a) => a.readerId === readerId);
320
+ }
321
+ // Tag CRUD
322
+ async getTags() {
323
+ return Array.from(this.tags.values());
324
+ }
325
+ async getTagByEpc(epc) {
326
+ for (const tag of this.tags.values()) {
327
+ if (tag.epc === epc) return tag;
328
+ }
329
+ return null;
330
+ }
331
+ async saveTag(tag) {
332
+ this.tags.set(tag.id, tag);
333
+ }
334
+ async deleteTag(id) {
335
+ this.tags.delete(id);
336
+ }
337
+ // Event Log — basic
338
+ async logEvent(event) {
339
+ this.events.unshift(event);
340
+ if (this.autoTrim && this.events.length > this.maxEvents) {
341
+ this.events.length = this.maxEvents;
342
+ }
343
+ }
344
+ // Event Log — advanced queries
345
+ async getEvents(options = {}) {
346
+ let results = [...this.events];
347
+ if (options.date) results = results.filter((e) => e.date === options.date);
348
+ if (options.from) results = results.filter((e) => e.timestamp >= options.from);
349
+ if (options.to) results = results.filter((e) => e.timestamp <= options.to);
350
+ if (options.readerId) results = results.filter((e) => e.readerId === options.readerId);
351
+ if (options.epc) results = results.filter((e) => e.epc === options.epc);
352
+ if (options.protocol) results = results.filter((e) => e.protocol === options.protocol);
353
+ const offset = options.offset ?? 0;
354
+ const limit = options.limit ?? 1e3;
355
+ return results.slice(offset, offset + limit);
356
+ }
357
+ async getEventsByDate(date, limit = 1e3, offset = 0) {
358
+ const filtered = this.events.filter((e) => e.date === date);
359
+ return filtered.slice(offset, offset + limit);
360
+ }
361
+ async getEventDates() {
362
+ const map = /* @__PURE__ */ new Map();
363
+ for (const e of this.events) {
364
+ map.set(e.date, (map.get(e.date) ?? 0) + 1);
365
+ }
366
+ return Array.from(map.entries()).map(([date, count]) => ({ date, count })).sort((a, b) => b.date.localeCompare(a.date));
367
+ }
368
+ async getEventStats() {
369
+ const byDate = await this.getEventDates();
370
+ const dates = byDate.map((d) => d.date).sort();
371
+ return {
372
+ totalEvents: this.events.length,
373
+ totalDays: byDate.length,
374
+ oldestEventDate: dates[0] ?? null,
375
+ newestEventDate: dates[dates.length - 1] ?? null,
376
+ eventsByDate: byDate
377
+ };
378
+ }
379
+ async getEventCount(options = {}) {
380
+ const results = await this.getEvents({ ...options, limit: Number.MAX_SAFE_INTEGER, offset: 0 });
381
+ return results.length;
382
+ }
383
+ // Event Log — management
384
+ async deleteEventsByDate(date) {
385
+ const before = this.events.length;
386
+ this.events = this.events.filter((e) => e.date !== date);
387
+ return { deleted: before - this.events.length };
388
+ }
389
+ async clearAllEvents() {
390
+ const deleted = this.events.length;
391
+ this.events = [];
392
+ return { deleted };
393
+ }
394
+ // Nuclear clear
395
+ async clearAllData() {
396
+ this.readers.clear();
397
+ this.tags.clear();
398
+ this.antennas.clear();
399
+ this.events = [];
400
+ }
401
+ // Memory stats for dashboard UI
402
+ getMemoryUsage() {
403
+ const percent = Math.round(this.events.length / this.maxEvents * 100);
404
+ return {
405
+ current: this.events.length,
406
+ limit: this.maxEvents,
407
+ percent,
408
+ isWarning: this.events.length >= this.warningThreshold
409
+ };
410
+ }
411
+ setMemoryLimit(limit) {
412
+ const clamped = Math.max(100, Math.min(5e5, limit));
413
+ this.maxEvents = clamped;
414
+ if (this.events.length > clamped) {
415
+ this.events.length = clamped;
416
+ }
417
+ this.config?.set("storage.memoryEventLimit", clamped);
418
+ }
419
+ };
420
+
421
+ // src/storage-sqlite.ts
422
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"));
423
+ var SqliteStorageDriver = class {
424
+ constructor(dbPath = ":memory:") {
425
+ this.dbPath = dbPath;
426
+ }
427
+ dbPath;
428
+ db = null;
429
+ async connect() {
430
+ this.db = new import_better_sqlite3.default(this.dbPath);
431
+ this.db.pragma("journal_mode = WAL");
432
+ this.initTables();
433
+ }
434
+ async disconnect() {
435
+ if (this.db) {
436
+ this.db.close();
437
+ this.db = null;
438
+ }
439
+ }
440
+ initTables() {
441
+ if (!this.db) return;
442
+ this.db.exec(`
443
+ CREATE TABLE IF NOT EXISTS readers (
444
+ id TEXT PRIMARY KEY,
445
+ name TEXT NOT NULL,
446
+ vendor TEXT NOT NULL,
447
+ model TEXT NOT NULL,
448
+ ip TEXT NOT NULL,
449
+ port INTEGER NOT NULL,
450
+ protocol TEXT NOT NULL,
451
+ status TEXT NOT NULL,
452
+ createdAt TEXT NOT NULL,
453
+ readRate INTEGER DEFAULT 0,
454
+ readMode TEXT DEFAULT 'continuous',
455
+ readIntervalValue INTEGER DEFAULT 1,
456
+ readIntervalUnit TEXT DEFAULT 'seconds',
457
+ epcFilterStart TEXT DEFAULT '',
458
+ epcFilterEnd TEXT DEFAULT '',
459
+ epcFilterPrefix TEXT DEFAULT ''
460
+ );
461
+
462
+ CREATE TABLE IF NOT EXISTS antennas (
463
+ id TEXT PRIMARY KEY,
464
+ reader_id TEXT NOT NULL,
465
+ antenna_index INTEGER NOT NULL,
466
+ gain REAL NOT NULL,
467
+ power REAL NOT NULL,
468
+ frequency REAL NOT NULL,
469
+ rssi_offset REAL NOT NULL,
470
+ read_zone TEXT NOT NULL,
471
+ enabled INTEGER NOT NULL DEFAULT 1,
472
+ FOREIGN KEY (reader_id) REFERENCES readers(id) ON DELETE CASCADE
473
+ );
474
+
475
+ CREATE TABLE IF NOT EXISTS tags (
476
+ id TEXT PRIMARY KEY,
477
+ epc TEXT UNIQUE NOT NULL,
478
+ tid TEXT,
479
+ userMemory TEXT,
480
+ accessPassword TEXT,
481
+ killPassword TEXT,
482
+ protocol TEXT
483
+ );
484
+
485
+ CREATE TABLE IF NOT EXISTS events (
486
+ id TEXT PRIMARY KEY,
487
+ date TEXT NOT NULL,
488
+ timestamp TEXT NOT NULL,
489
+ readerId TEXT NOT NULL,
490
+ antennaId INTEGER NOT NULL,
491
+ epc TEXT NOT NULL,
492
+ rssi REAL NOT NULL,
493
+ protocol TEXT NOT NULL,
494
+ payload TEXT
495
+ );
496
+
497
+ CREATE TABLE IF NOT EXISTS settings (
498
+ key TEXT PRIMARY KEY,
499
+ value TEXT NOT NULL
500
+ );
501
+
502
+ CREATE TABLE IF NOT EXISTS plugins (
503
+ id TEXT PRIMARY KEY,
504
+ name TEXT NOT NULL,
505
+ version TEXT NOT NULL,
506
+ status TEXT NOT NULL,
507
+ config TEXT
508
+ );
509
+
510
+ CREATE INDEX IF NOT EXISTS idx_events_date ON events(date);
511
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
512
+ CREATE INDEX IF NOT EXISTS idx_events_epc ON events(epc);
513
+ CREATE INDEX IF NOT EXISTS idx_events_reader ON events(readerId);
514
+ CREATE INDEX IF NOT EXISTS idx_tags_epc ON tags(epc);
515
+ `);
516
+ }
517
+ // ─── Readers ─────────────────────────────────────────────────────────────
518
+ async getReaders() {
519
+ this.checkConnection();
520
+ return this.db.prepare("SELECT * FROM readers").all();
521
+ }
522
+ async getReaderById(id) {
523
+ this.checkConnection();
524
+ const row = this.db.prepare("SELECT * FROM readers WHERE id = ?").get(id);
525
+ return row ? row : null;
526
+ }
527
+ async saveReader(reader) {
528
+ this.checkConnection();
529
+ this.db.prepare(`
530
+ INSERT INTO readers (id, name, vendor, model, ip, port, protocol, status, createdAt,
531
+ readRate, readMode, readIntervalValue, readIntervalUnit,
532
+ epcFilterStart, epcFilterEnd, epcFilterPrefix)
533
+ VALUES (@id, @name, @vendor, @model, @ip, @port, @protocol, @status, @createdAt,
534
+ @readRate, @readMode, @readIntervalValue, @readIntervalUnit,
535
+ @epcFilterStart, @epcFilterEnd, @epcFilterPrefix)
536
+ ON CONFLICT(id) DO UPDATE SET
537
+ name=excluded.name, vendor=excluded.vendor, model=excluded.model,
538
+ ip=excluded.ip, port=excluded.port, protocol=excluded.protocol,
539
+ status=excluded.status, readRate=excluded.readRate, readMode=excluded.readMode,
540
+ readIntervalValue=excluded.readIntervalValue, readIntervalUnit=excluded.readIntervalUnit,
541
+ epcFilterStart=excluded.epcFilterStart, epcFilterEnd=excluded.epcFilterEnd,
542
+ epcFilterPrefix=excluded.epcFilterPrefix
543
+ `).run({
544
+ ...reader,
545
+ readRate: reader.readRate ?? 0,
546
+ readMode: reader.readMode ?? "continuous",
547
+ readIntervalValue: reader.readIntervalValue ?? 1,
548
+ readIntervalUnit: reader.readIntervalUnit ?? "seconds",
549
+ epcFilterStart: reader.epcFilterStart ?? "",
550
+ epcFilterEnd: reader.epcFilterEnd ?? "",
551
+ epcFilterPrefix: reader.epcFilterPrefix ?? ""
552
+ });
553
+ }
554
+ async deleteReader(id) {
555
+ this.checkConnection();
556
+ this.db.prepare("DELETE FROM readers WHERE id = ?").run(id);
557
+ }
558
+ // ─── Antennas ─────────────────────────────────────────────────────────────
559
+ async saveAntenna(antenna) {
560
+ this.checkConnection();
561
+ this.db.prepare(`
562
+ INSERT INTO antennas (id, reader_id, antenna_index, gain, power, frequency, rssi_offset, read_zone, enabled)
563
+ VALUES (@id, @readerId, @index, @gain, @power, @frequency, @rssiOffset, @readZone, @enabled)
564
+ ON CONFLICT(id) DO UPDATE SET
565
+ gain=excluded.gain, power=excluded.power, frequency=excluded.frequency,
566
+ rssi_offset=excluded.rssi_offset, read_zone=excluded.read_zone, enabled=excluded.enabled
567
+ `).run({
568
+ id: antenna.id,
569
+ readerId: antenna.readerId,
570
+ index: antenna.index,
571
+ gain: antenna.gain,
572
+ power: antenna.power,
573
+ frequency: antenna.frequency,
574
+ rssiOffset: antenna.rssiOffset,
575
+ readZone: antenna.readZone,
576
+ enabled: antenna.enabled ? 1 : 0
577
+ });
578
+ }
579
+ async getAntennasByReaderId(readerId) {
580
+ this.checkConnection();
581
+ const rows = this.db.prepare("SELECT * FROM antennas WHERE reader_id = ?").all(readerId);
582
+ return rows.map((r) => ({
583
+ id: r.id,
584
+ readerId: r.reader_id,
585
+ index: r.antenna_index,
586
+ gain: r.gain,
587
+ power: r.power,
588
+ frequency: r.frequency,
589
+ rssiOffset: r.rssi_offset,
590
+ readZone: r.read_zone,
591
+ enabled: r.enabled === 1
592
+ }));
593
+ }
594
+ // ─── Tags ─────────────────────────────────────────────────────────────────
595
+ async getTags() {
596
+ this.checkConnection();
597
+ return this.db.prepare("SELECT * FROM tags").all();
598
+ }
599
+ async getTagByEpc(epc) {
600
+ this.checkConnection();
601
+ const row = this.db.prepare("SELECT * FROM tags WHERE epc = ?").get(epc);
602
+ return row ? row : null;
603
+ }
604
+ async saveTag(tag) {
605
+ this.checkConnection();
606
+ this.db.prepare(`
607
+ INSERT INTO tags (id, epc, tid, userMemory, accessPassword, killPassword, protocol)
608
+ VALUES (@id, @epc, @tid, @userMemory, @accessPassword, @killPassword, @protocol)
609
+ ON CONFLICT(epc) DO UPDATE SET
610
+ tid=excluded.tid, userMemory=excluded.userMemory,
611
+ accessPassword=excluded.accessPassword, killPassword=excluded.killPassword,
612
+ protocol=excluded.protocol
613
+ `).run({
614
+ id: tag.id,
615
+ epc: tag.epc,
616
+ tid: tag.tid ?? null,
617
+ userMemory: tag.userMemory ?? null,
618
+ accessPassword: tag.accessPassword ?? null,
619
+ killPassword: tag.killPassword ?? null,
620
+ protocol: tag.protocol ?? null
621
+ });
622
+ }
623
+ async deleteTag(id) {
624
+ this.checkConnection();
625
+ this.db.prepare("DELETE FROM tags WHERE id = ?").run(id);
626
+ }
627
+ // ─── Events — basic ───────────────────────────────────────────────────────
628
+ async logEvent(event) {
629
+ this.checkConnection();
630
+ this.db.prepare(`
631
+ INSERT INTO events (id, date, timestamp, readerId, antennaId, epc, rssi, protocol, payload)
632
+ VALUES (@id, @date, @timestamp, @readerId, @antennaId, @epc, @rssi, @protocol, @payload)
633
+ `).run({
634
+ id: event.id,
635
+ date: event.date,
636
+ timestamp: event.timestamp,
637
+ readerId: event.readerId,
638
+ antennaId: event.antennaId,
639
+ epc: event.epc,
640
+ rssi: event.rssi,
641
+ protocol: event.protocol,
642
+ payload: event.payload ?? null
643
+ });
644
+ }
645
+ // ─── Events — advanced queries ────────────────────────────────────────────
646
+ async getEvents(options = {}) {
647
+ this.checkConnection();
648
+ let sql = "SELECT * FROM events WHERE 1=1";
649
+ const params = [];
650
+ if (options.date) {
651
+ sql += " AND date = ?";
652
+ params.push(options.date);
653
+ }
654
+ if (options.from) {
655
+ sql += " AND timestamp >= ?";
656
+ params.push(options.from);
657
+ }
658
+ if (options.to) {
659
+ sql += " AND timestamp <= ?";
660
+ params.push(options.to);
661
+ }
662
+ if (options.readerId) {
663
+ sql += " AND readerId = ?";
664
+ params.push(options.readerId);
665
+ }
666
+ if (options.epc) {
667
+ sql += " AND epc = ?";
668
+ params.push(options.epc);
669
+ }
670
+ if (options.protocol) {
671
+ sql += " AND protocol = ?";
672
+ params.push(options.protocol);
673
+ }
674
+ sql += " ORDER BY timestamp DESC";
675
+ sql += ` LIMIT ${options.limit ?? 1e3} OFFSET ${options.offset ?? 0}`;
676
+ return this.db.prepare(sql).all(...params);
677
+ }
678
+ async getEventsByDate(date, limit = 1e3, offset = 0) {
679
+ this.checkConnection();
680
+ return this.db.prepare(
681
+ "SELECT * FROM events WHERE date = ? ORDER BY timestamp DESC LIMIT ? OFFSET ?"
682
+ ).all(date, limit, offset);
683
+ }
684
+ async getEventDates() {
685
+ this.checkConnection();
686
+ return this.db.prepare(
687
+ "SELECT date, COUNT(*) as count FROM events GROUP BY date ORDER BY date DESC"
688
+ ).all();
689
+ }
690
+ async getEventStats() {
691
+ this.checkConnection();
692
+ const total = this.db.prepare("SELECT COUNT(*) as n FROM events").get().n;
693
+ const oldest = this.db.prepare("SELECT MIN(date) as d FROM events").get().d;
694
+ const newest = this.db.prepare("SELECT MAX(date) as d FROM events").get().d;
695
+ const byDate = await this.getEventDates();
696
+ return {
697
+ totalEvents: total,
698
+ totalDays: byDate.length,
699
+ oldestEventDate: oldest,
700
+ newestEventDate: newest,
701
+ eventsByDate: byDate
702
+ };
703
+ }
704
+ async getEventCount(options = {}) {
705
+ this.checkConnection();
706
+ let sql = "SELECT COUNT(*) as n FROM events WHERE 1=1";
707
+ const params = [];
708
+ if (options.date) {
709
+ sql += " AND date = ?";
710
+ params.push(options.date);
711
+ }
712
+ if (options.from) {
713
+ sql += " AND timestamp >= ?";
714
+ params.push(options.from);
715
+ }
716
+ if (options.to) {
717
+ sql += " AND timestamp <= ?";
718
+ params.push(options.to);
719
+ }
720
+ if (options.readerId) {
721
+ sql += " AND readerId = ?";
722
+ params.push(options.readerId);
723
+ }
724
+ if (options.epc) {
725
+ sql += " AND epc = ?";
726
+ params.push(options.epc);
727
+ }
728
+ if (options.protocol) {
729
+ sql += " AND protocol = ?";
730
+ params.push(options.protocol);
731
+ }
732
+ return this.db.prepare(sql).get(...params).n;
733
+ }
734
+ // ─── Events — management ─────────────────────────────────────────────────
735
+ async deleteEventsByDate(date) {
736
+ this.checkConnection();
737
+ const result = this.db.prepare("DELETE FROM events WHERE date = ?").run(date);
738
+ return { deleted: result.changes };
739
+ }
740
+ async clearAllEvents() {
741
+ this.checkConnection();
742
+ const result = this.db.prepare("DELETE FROM events").run();
743
+ return { deleted: result.changes };
744
+ }
745
+ // Nuclear clear — wipes all data
746
+ async clearAllData() {
747
+ this.checkConnection();
748
+ this.db.exec("DELETE FROM events; DELETE FROM tags; DELETE FROM readers; DELETE FROM antennas;");
749
+ }
750
+ // ─── Internal ────────────────────────────────────────────────────────────
751
+ checkConnection() {
752
+ if (!this.db) {
753
+ throw new Error("Database is not connected. Call connect() first.");
754
+ }
755
+ }
756
+ };
757
+
758
+ // src/branding.ts
759
+ var BRAND = {
760
+ name: "OpenRFID Simulator",
761
+ tagline: "The Open Source RFID Reader Simulator for Developers",
762
+ organization: "RFID Software India Private Limited",
763
+ website: "https://rfidsoftwares.com",
764
+ docsUrl: "https://rfidsoftwares.com/docs",
765
+ githubUrl: "https://github.com/rfidsoftwares/openrfid-simulator",
766
+ license: "MIT",
767
+ copyright: "Copyright (c) 2026 RFID Software India Private Limited - https://rfidsoftwares.com",
768
+ headerBanner: `/**
769
+ * OpenRFID Simulator - The Open Source RFID Reader Simulator for Developers.
770
+ * Copyright (c) 2026 RFID Software India Private Limited - https://rfidsoftwares.com
771
+ *
772
+ * Licensed under the MIT License.
773
+ * For RFID software, enterprise tools, and hardware drivers, visit https://rfidsoftwares.com
774
+ */`
775
+ };
776
+ // Annotate the CommonJS export names for ESM import in node:
777
+ 0 && (module.exports = {
778
+ BRAND,
779
+ ComponentState,
780
+ ConfigManager,
781
+ Container,
782
+ EventBus,
783
+ Logger,
784
+ MemoryStorageDriver,
785
+ SqliteStorageDriver,
786
+ defaultConfig
787
+ });