@camstack/core 0.1.7 → 0.1.10

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 (61) hide show
  1. package/dist/builtins/local-backup/index.d.mts +42 -0
  2. package/dist/builtins/local-backup/index.d.ts +42 -0
  3. package/dist/builtins/local-backup/index.mjs +2 -10
  4. package/dist/builtins/local-backup/index.mjs.map +1 -1
  5. package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.mts +2 -0
  6. package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +2 -0
  7. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +2 -5
  8. package/dist/builtins/sqlite-storage/index.d.mts +4 -0
  9. package/dist/builtins/sqlite-storage/index.d.ts +4 -0
  10. package/dist/builtins/sqlite-storage/index.js +95 -197
  11. package/dist/builtins/sqlite-storage/index.js.map +1 -1
  12. package/dist/builtins/sqlite-storage/index.mjs +6 -26
  13. package/dist/builtins/sqlite-storage/index.mjs.map +1 -1
  14. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.mts +2 -0
  15. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +2 -0
  16. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -4
  17. package/dist/builtins/winston-logging/index.d.mts +30 -0
  18. package/dist/builtins/winston-logging/index.d.ts +30 -0
  19. package/dist/builtins/winston-logging/index.mjs +2 -10
  20. package/dist/builtins/winston-logging/index.mjs.map +1 -1
  21. package/dist/chunk-2F3XZYRW.mjs +89 -0
  22. package/dist/chunk-2F3XZYRW.mjs.map +1 -0
  23. package/dist/chunk-LQFPAEQF.mjs +147 -0
  24. package/dist/{chunk-XSLBW5C2.mjs.map → chunk-LQFPAEQF.mjs.map} +1 -1
  25. package/dist/chunk-R3DIIBBX.mjs +532 -0
  26. package/dist/chunk-R3DIIBBX.mjs.map +1 -0
  27. package/dist/chunk-SO4LROOT.mjs +150 -0
  28. package/dist/{chunk-GBWW3JU4.mjs.map → chunk-SO4LROOT.mjs.map} +1 -1
  29. package/dist/chunk-SPA4JBKN.mjs +175 -0
  30. package/dist/{chunk-4YD6WMO6.mjs.map → chunk-SPA4JBKN.mjs.map} +1 -1
  31. package/dist/chunk-YXNXYYHL.mjs +282 -0
  32. package/dist/{chunk-CHFIH4G6.mjs.map → chunk-YXNXYYHL.mjs.map} +1 -1
  33. package/dist/dist-3BY63UQ5.mjs +2151 -0
  34. package/dist/dist-3BY63UQ5.mjs.map +1 -0
  35. package/dist/filesystem-storage.addon-C42r589X.d.mts +57 -0
  36. package/dist/filesystem-storage.addon-C42r589X.d.ts +57 -0
  37. package/dist/index.d.mts +1110 -0
  38. package/dist/index.d.ts +1110 -0
  39. package/dist/index.js +6002 -17264
  40. package/dist/index.js.map +1 -1
  41. package/dist/index.mjs +3408 -16481
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/sql-schema-CKz78rId.d.mts +97 -0
  44. package/dist/sql-schema-CKz78rId.d.ts +97 -0
  45. package/dist/sqlite-settings.addon-DigoKwpZ.d.mts +70 -0
  46. package/dist/sqlite-settings.addon-DigoKwpZ.d.ts +70 -0
  47. package/dist/storage-location-manager-KKDQNAKA.mjs +7 -0
  48. package/package.json +1 -1
  49. package/dist/chunk-4JEXNFZZ.mjs +0 -57
  50. package/dist/chunk-4YD6WMO6.mjs +0 -207
  51. package/dist/chunk-CHFIH4G6.mjs +0 -314
  52. package/dist/chunk-EFQ25JFE.mjs +0 -689
  53. package/dist/chunk-EFQ25JFE.mjs.map +0 -1
  54. package/dist/chunk-GBWW3JU4.mjs +0 -180
  55. package/dist/chunk-XSLBW5C2.mjs +0 -177
  56. package/dist/onnxruntime_binding-6Q6HXASN.node +0 -0
  57. package/dist/onnxruntime_binding-EKZT2NRK.node +0 -0
  58. package/dist/onnxruntime_binding-P6S7V3CI.node +0 -0
  59. package/dist/onnxruntime_binding-PJNNIIUO.node +0 -0
  60. package/dist/onnxruntime_binding-UN6SPTQK.node +0 -0
  61. /package/dist/{chunk-4JEXNFZZ.mjs.map → storage-location-manager-KKDQNAKA.mjs.map} +0 -0
@@ -1,689 +0,0 @@
1
- import {
2
- __commonJS,
3
- __esm,
4
- __export,
5
- __toESM
6
- } from "./chunk-4JEXNFZZ.mjs";
7
-
8
- // src/builtins/sqlite-storage/sqlite-storage.provider.ts
9
- var sqlite_storage_provider_exports = {};
10
- __export(sqlite_storage_provider_exports, {
11
- FileSystemStorage: () => FileSystemStorage,
12
- SqliteStorageProvider: () => SqliteStorageProvider
13
- });
14
- import Database from "better-sqlite3";
15
- import * as fs from "fs";
16
- import * as path from "path";
17
- import { randomUUID } from "crypto";
18
- var LOCATION_TYPES, SqliteStructuredStorage, FileSystemStorage, SqliteStorageProvider;
19
- var init_sqlite_storage_provider = __esm({
20
- "src/builtins/sqlite-storage/sqlite-storage.provider.ts"() {
21
- "use strict";
22
- LOCATION_TYPES = {
23
- // New location names (from StorageLocationManager)
24
- data: "structured",
25
- // settings, events, trails — SQL only
26
- media: "files",
27
- // crops, snapshots, thumbnails — files only
28
- recordings: "files",
29
- // video segments — files only
30
- models: "files",
31
- // ONNX/TFLite models — files only
32
- cache: "files",
33
- // temp files — files only
34
- logs: "files",
35
- // Winston log files — files only
36
- // Legacy location names (backward compat)
37
- config: "structured",
38
- events: "structured",
39
- addon: "both"
40
- };
41
- SqliteStructuredStorage = class {
42
- constructor(db) {
43
- this.db = db;
44
- }
45
- ensuredTables = /* @__PURE__ */ new Set();
46
- ensureTable(collection) {
47
- if (this.ensuredTables.has(collection)) return;
48
- this.db.exec(
49
- `CREATE TABLE IF NOT EXISTS "${collection}" (id TEXT PRIMARY KEY, data TEXT)`
50
- );
51
- this.ensuredTables.add(collection);
52
- }
53
- async insert(record) {
54
- this.ensureTable(record.collection);
55
- const id = record.id || randomUUID();
56
- const newRecord = {
57
- collection: record.collection,
58
- id,
59
- data: record.data
60
- };
61
- this.db.prepare(`INSERT INTO "${record.collection}" (id, data) VALUES (?, ?)`).run(id, JSON.stringify(newRecord.data));
62
- return newRecord;
63
- }
64
- async query(collection, filter) {
65
- this.ensureTable(collection);
66
- const { sql, params } = this.buildSelect(collection, filter);
67
- const rows = this.db.prepare(sql).all(...params);
68
- return rows.map((row) => ({
69
- collection,
70
- id: row.id,
71
- data: JSON.parse(row.data)
72
- }));
73
- }
74
- async update(collection, id, data) {
75
- this.ensureTable(collection);
76
- this.db.prepare(`UPDATE "${collection}" SET data = ? WHERE id = ?`).run(JSON.stringify(data), id);
77
- return { collection, id, data };
78
- }
79
- async delete(collection, id) {
80
- this.ensureTable(collection);
81
- this.db.prepare(`DELETE FROM "${collection}" WHERE id = ?`).run(id);
82
- }
83
- async count(collection, filter) {
84
- this.ensureTable(collection);
85
- const { sql, params } = this.buildCount(collection, filter);
86
- const row = this.db.prepare(sql).get(...params);
87
- return row.cnt;
88
- }
89
- buildWhereClause(filter) {
90
- if (!filter) return { clause: "", params: [] };
91
- const conditions = [];
92
- const params = [];
93
- if (filter.where) {
94
- for (const [field, value] of Object.entries(filter.where)) {
95
- if (field === "id") {
96
- conditions.push("id = ?");
97
- params.push(value);
98
- } else {
99
- conditions.push(`json_extract(data, '$.${field}') = ?`);
100
- params.push(value);
101
- }
102
- }
103
- }
104
- if (filter.whereIn) {
105
- for (const [field, values] of Object.entries(filter.whereIn)) {
106
- const placeholders = values.map(() => "?").join(", ");
107
- if (field === "id") {
108
- conditions.push(`id IN (${placeholders})`);
109
- } else {
110
- conditions.push(`json_extract(data, '$.${field}') IN (${placeholders})`);
111
- }
112
- params.push(...values);
113
- }
114
- }
115
- if (filter.whereBetween) {
116
- for (const [field, [low, high]] of Object.entries(filter.whereBetween)) {
117
- if (field === "id") {
118
- conditions.push("id BETWEEN ? AND ?");
119
- } else {
120
- conditions.push(`json_extract(data, '$.${field}') BETWEEN ? AND ?`);
121
- }
122
- params.push(low, high);
123
- }
124
- }
125
- const clause = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
126
- return { clause, params };
127
- }
128
- buildSelect(collection, filter) {
129
- const { clause, params } = this.buildWhereClause(filter);
130
- let sql = `SELECT id, data FROM "${collection}"${clause}`;
131
- if (filter?.orderBy) {
132
- const dir = filter.orderBy.direction === "desc" ? "DESC" : "ASC";
133
- if (filter.orderBy.field === "id") {
134
- sql += ` ORDER BY id ${dir}`;
135
- } else {
136
- sql += ` ORDER BY json_extract(data, '$.${filter.orderBy.field}') ${dir}`;
137
- }
138
- }
139
- if (filter?.limit !== void 0) {
140
- sql += ` LIMIT ?`;
141
- params.push(filter.limit);
142
- }
143
- if (filter?.offset !== void 0) {
144
- sql += ` OFFSET ?`;
145
- params.push(filter.offset);
146
- }
147
- return { sql, params };
148
- }
149
- buildCount(collection, filter) {
150
- const { clause, params } = this.buildWhereClause(filter);
151
- return { sql: `SELECT COUNT(*) as cnt FROM "${collection}"${clause}`, params };
152
- }
153
- };
154
- FileSystemStorage = class {
155
- constructor(basePath) {
156
- this.basePath = basePath;
157
- }
158
- async readFile(filePath) {
159
- const fullPath = path.join(this.basePath, filePath);
160
- return fs.promises.readFile(fullPath);
161
- }
162
- async writeFile(filePath, data) {
163
- const fullPath = path.join(this.basePath, filePath);
164
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
165
- await fs.promises.writeFile(fullPath, data);
166
- }
167
- async deleteFile(filePath) {
168
- const fullPath = path.join(this.basePath, filePath);
169
- await fs.promises.unlink(fullPath);
170
- }
171
- async listFiles(prefix) {
172
- const searchDir = prefix ? path.join(this.basePath, prefix) : this.basePath;
173
- try {
174
- const entries = await fs.promises.readdir(searchDir, { recursive: true });
175
- const files = [];
176
- for (const entry of entries) {
177
- const entryStr = String(entry);
178
- const relative = prefix ? path.join(prefix, entryStr) : entryStr;
179
- const fullPath = path.join(this.basePath, relative);
180
- try {
181
- const stat = await fs.promises.stat(fullPath);
182
- if (stat.isFile()) {
183
- files.push(relative);
184
- }
185
- } catch {
186
- }
187
- }
188
- return files;
189
- } catch {
190
- return [];
191
- }
192
- }
193
- async getFileUrl(_path) {
194
- return null;
195
- }
196
- async exists(filePath) {
197
- const fullPath = path.join(this.basePath, filePath);
198
- try {
199
- await fs.promises.access(fullPath, fs.constants.F_OK);
200
- return true;
201
- } catch {
202
- return false;
203
- }
204
- }
205
- };
206
- SqliteStorageProvider = class {
207
- mainDb = null;
208
- sharedStructured = null;
209
- locations = /* @__PURE__ */ new Map();
210
- async initialize() {
211
- }
212
- /**
213
- * Configure all storage locations.
214
- * ONE single SQLite database (camstack.db) is used for ALL structured storage.
215
- * File-based locations use the filesystem at their configured path.
216
- */
217
- async configure(config) {
218
- const dataPath = config.locations["data"] ?? config.locations["config"] ?? Object.values(config.locations)[0];
219
- if (!dataPath) throw new Error("No data path configured for SQLite storage");
220
- fs.mkdirSync(dataPath, { recursive: true });
221
- const dbPath = path.join(dataPath, "camstack.db");
222
- this.mainDb = new Database(dbPath);
223
- this.mainDb.pragma("journal_mode = WAL");
224
- this.sharedStructured = new SqliteStructuredStorage(this.mainDb);
225
- for (const [name, dirPath] of Object.entries(config.locations)) {
226
- const locationName = name;
227
- const locationType = LOCATION_TYPES[name] ?? "files";
228
- fs.mkdirSync(dirPath, { recursive: true });
229
- const location = {};
230
- if (locationType === "structured" || locationType === "both") {
231
- location.structured = this.sharedStructured;
232
- }
233
- if (locationType === "files" || locationType === "both") {
234
- location.files = new FileSystemStorage(dirPath);
235
- }
236
- this.locations.set(locationName, location);
237
- }
238
- }
239
- getLocation(name) {
240
- const location = this.locations.get(name);
241
- if (!location) {
242
- throw new Error(`Storage location "${name}" not found`);
243
- }
244
- return location;
245
- }
246
- async shutdown() {
247
- if (this.mainDb) {
248
- this.mainDb.close();
249
- this.mainDb = null;
250
- this.sharedStructured = null;
251
- }
252
- this.locations.clear();
253
- }
254
- async export(_locationName) {
255
- throw new Error("Export not yet implemented");
256
- }
257
- async import(_locationName, _data) {
258
- throw new Error("Import not yet implemented");
259
- }
260
- };
261
- }
262
- });
263
-
264
- // src/builtins/sqlite-storage/sqlite-storage.addon.ts
265
- var sqlite_storage_addon_exports = {};
266
- __export(sqlite_storage_addon_exports, {
267
- SqliteStorageAddon: () => SqliteStorageAddon
268
- });
269
- var SqliteStorageAddon;
270
- var init_sqlite_storage_addon = __esm({
271
- "src/builtins/sqlite-storage/sqlite-storage.addon.ts"() {
272
- "use strict";
273
- init_sqlite_storage_provider();
274
- SqliteStorageAddon = class {
275
- manifest = {
276
- id: "sqlite-storage",
277
- name: "SQLite Storage",
278
- version: "1.0.0",
279
- capabilities: ["storage"]
280
- };
281
- provider = null;
282
- async initialize(context) {
283
- const storageConfig = {
284
- locations: { ...context.locationPaths }
285
- };
286
- this.provider = new SqliteStorageProvider();
287
- await this.provider.configure(storageConfig);
288
- context.logger.info("SQLite storage initialized");
289
- }
290
- async shutdown() {
291
- await this.provider?.shutdown();
292
- }
293
- getProvider() {
294
- if (!this.provider) throw new Error("SQLite storage not initialized");
295
- return this.provider;
296
- }
297
- getCapabilityProvider(name) {
298
- if (name === "storage" && this.provider) {
299
- return this.provider;
300
- }
301
- return null;
302
- }
303
- getConfigSchema() {
304
- return {
305
- sections: []
306
- };
307
- }
308
- getConfig() {
309
- return {};
310
- }
311
- async onConfigChange(_config) {
312
- }
313
- };
314
- }
315
- });
316
-
317
- // src/builtins/sqlite-storage/sql-schema.js
318
- var require_sql_schema = __commonJS({
319
- "src/builtins/sqlite-storage/sql-schema.js"(exports) {
320
- "use strict";
321
- Object.defineProperty(exports, "__esModule", { value: true });
322
- exports.CORE_TABLE_DDL = void 0;
323
- exports.addonTableToDdl = addonTableToDdl2;
324
- exports.CORE_TABLE_DDL = [
325
- // Settings tables
326
- `CREATE TABLE IF NOT EXISTS system_settings (
327
- key TEXT PRIMARY KEY,
328
- value JSON NOT NULL,
329
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
330
- )`,
331
- `CREATE TABLE IF NOT EXISTS addon_settings (
332
- addon_id TEXT NOT NULL,
333
- key TEXT NOT NULL,
334
- value JSON NOT NULL,
335
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
336
- PRIMARY KEY (addon_id, key)
337
- )`,
338
- `CREATE TABLE IF NOT EXISTS provider_settings (
339
- provider_id TEXT NOT NULL,
340
- key TEXT NOT NULL,
341
- value JSON NOT NULL,
342
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
343
- PRIMARY KEY (provider_id, key)
344
- )`,
345
- `CREATE TABLE IF NOT EXISTS device_settings (
346
- device_id TEXT NOT NULL,
347
- key TEXT NOT NULL,
348
- value JSON NOT NULL,
349
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
350
- PRIMARY KEY (device_id, key)
351
- )`,
352
- // Detection events
353
- `CREATE TABLE IF NOT EXISTS detection_events (
354
- id TEXT PRIMARY KEY,
355
- timestamp INTEGER NOT NULL,
356
- device_id TEXT NOT NULL,
357
- class_name TEXT NOT NULL,
358
- score REAL NOT NULL,
359
- severity TEXT NOT NULL,
360
- track_id TEXT,
361
- zones JSON,
362
- recognition JSON,
363
- media_files JSON,
364
- data JSON
365
- )`,
366
- `CREATE INDEX IF NOT EXISTS idx_det_device_ts ON detection_events(device_id, timestamp)`,
367
- `CREATE INDEX IF NOT EXISTS idx_det_class_ts ON detection_events(class_name, timestamp)`,
368
- // Audio levels
369
- `CREATE TABLE IF NOT EXISTS audio_levels (
370
- id TEXT PRIMARY KEY,
371
- timestamp INTEGER NOT NULL,
372
- device_id TEXT NOT NULL,
373
- dbfs REAL NOT NULL,
374
- rms REAL NOT NULL,
375
- state TEXT NOT NULL
376
- )`,
377
- `CREATE INDEX IF NOT EXISTS idx_audio_device_ts ON audio_levels(device_id, timestamp)`,
378
- // Track trails
379
- `CREATE TABLE IF NOT EXISTS track_trails (
380
- track_id TEXT PRIMARY KEY,
381
- device_id TEXT NOT NULL,
382
- class_name TEXT NOT NULL,
383
- first_seen INTEGER NOT NULL,
384
- last_seen INTEGER NOT NULL,
385
- positions JSON NOT NULL,
386
- snapshots JSON,
387
- total_distance REAL,
388
- zones_visited JSON
389
- )`,
390
- `CREATE INDEX IF NOT EXISTS idx_trails_device_ts ON track_trails(device_id, first_seen)`
391
- ];
392
- function addonTableToDdl2(schema) {
393
- const pks = schema.columns.filter((c) => c.primaryKey).map((c) => c.name);
394
- const colDefs = schema.columns.map((c) => {
395
- const parts = [c.name, c.type];
396
- if (c.notNull)
397
- parts.push("NOT NULL");
398
- return parts.join(" ");
399
- });
400
- let ddl = `CREATE TABLE IF NOT EXISTS ${schema.name} (
401
- ${colDefs.join(",\n ")}`;
402
- if (pks.length > 0) {
403
- ddl += `,
404
- PRIMARY KEY (${pks.join(", ")})`;
405
- }
406
- ddl += "\n)";
407
- const stmts = [ddl];
408
- for (const idx of schema.indexes ?? []) {
409
- const unique = idx.unique ? "UNIQUE " : "";
410
- stmts.push(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name}(${idx.columns.join(", ")})`);
411
- }
412
- return stmts;
413
- }
414
- }
415
- });
416
-
417
- // src/builtins/sqlite-storage/settings-store.ts
418
- var settings_store_exports = {};
419
- __export(settings_store_exports, {
420
- SettingsStore: () => SettingsStore
421
- });
422
- import Database2 from "better-sqlite3";
423
- import { RUNTIME_DEFAULTS } from "@camstack/kernel";
424
- var import_sql_schema, SettingsStore;
425
- var init_settings_store = __esm({
426
- "src/builtins/sqlite-storage/settings-store.ts"() {
427
- "use strict";
428
- import_sql_schema = __toESM(require_sql_schema());
429
- SettingsStore = class {
430
- db;
431
- constructor(dbPath) {
432
- this.db = new Database2(dbPath);
433
- this.db.pragma("journal_mode = WAL");
434
- this.db.pragma("foreign_keys = ON");
435
- this.initTables();
436
- }
437
- // ---------------------------------------------------------------------------
438
- // System settings
439
- // ---------------------------------------------------------------------------
440
- getSystem(key) {
441
- const row = this.db.prepare("SELECT value FROM system_settings WHERE key = ?").get(key);
442
- if (row === void 0) return void 0;
443
- return JSON.parse(row.value);
444
- }
445
- setSystem(key, value) {
446
- this.db.prepare(
447
- `INSERT INTO system_settings (key, value, updated_at) VALUES (?, json(?), unixepoch())
448
- ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
449
- ).run(key, JSON.stringify(value));
450
- }
451
- getAllSystem() {
452
- const rows = this.db.prepare("SELECT key, value FROM system_settings").all();
453
- return Object.fromEntries(rows.map((r) => [r.key, JSON.parse(r.value)]));
454
- }
455
- // ---------------------------------------------------------------------------
456
- // Addon settings
457
- // ---------------------------------------------------------------------------
458
- getAddon(addonId, key) {
459
- const row = this.db.prepare(
460
- "SELECT value FROM addon_settings WHERE addon_id = ? AND key = ?"
461
- ).get(addonId, key);
462
- if (row === void 0) return void 0;
463
- return JSON.parse(row.value);
464
- }
465
- setAddon(addonId, key, value) {
466
- this.db.prepare(
467
- `INSERT INTO addon_settings (addon_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())
468
- ON CONFLICT(addon_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
469
- ).run(addonId, key, JSON.stringify(value));
470
- }
471
- getAllAddon(addonId) {
472
- const rows = this.db.prepare(
473
- "SELECT key, value FROM addon_settings WHERE addon_id = ?"
474
- ).all(addonId);
475
- return Object.fromEntries(rows.map((r) => [r.key, JSON.parse(r.value)]));
476
- }
477
- /** Bulk-replace all keys for an addon (within a transaction). */
478
- setAllAddon(addonId, config) {
479
- const deleteStmt = this.db.prepare("DELETE FROM addon_settings WHERE addon_id = ?");
480
- const insertStmt = this.db.prepare(
481
- `INSERT INTO addon_settings (addon_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())`
482
- );
483
- this.db.transaction(() => {
484
- deleteStmt.run(addonId);
485
- for (const [key, value] of Object.entries(config)) {
486
- insertStmt.run(addonId, key, JSON.stringify(value));
487
- }
488
- })();
489
- }
490
- // ---------------------------------------------------------------------------
491
- // Provider settings
492
- // ---------------------------------------------------------------------------
493
- getProvider(providerId, key) {
494
- const row = this.db.prepare(
495
- "SELECT value FROM provider_settings WHERE provider_id = ? AND key = ?"
496
- ).get(providerId, key);
497
- if (row === void 0) return void 0;
498
- return JSON.parse(row.value);
499
- }
500
- setProvider(providerId, key, value) {
501
- this.db.prepare(
502
- `INSERT INTO provider_settings (provider_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())
503
- ON CONFLICT(provider_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
504
- ).run(providerId, key, JSON.stringify(value));
505
- }
506
- getAllProvider(providerId) {
507
- const rows = this.db.prepare(
508
- "SELECT key, value FROM provider_settings WHERE provider_id = ?"
509
- ).all(providerId);
510
- return Object.fromEntries(rows.map((r) => [r.key, JSON.parse(r.value)]));
511
- }
512
- // ---------------------------------------------------------------------------
513
- // Device settings
514
- // ---------------------------------------------------------------------------
515
- getDevice(deviceId, key) {
516
- const row = this.db.prepare(
517
- "SELECT value FROM device_settings WHERE device_id = ? AND key = ?"
518
- ).get(deviceId, key);
519
- if (row === void 0) return void 0;
520
- return JSON.parse(row.value);
521
- }
522
- setDevice(deviceId, key, value) {
523
- this.db.prepare(
524
- `INSERT INTO device_settings (device_id, key, value, updated_at) VALUES (?, ?, json(?), unixepoch())
525
- ON CONFLICT(device_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
526
- ).run(deviceId, key, JSON.stringify(value));
527
- }
528
- getAllDevice(deviceId) {
529
- const rows = this.db.prepare(
530
- "SELECT key, value FROM device_settings WHERE device_id = ?"
531
- ).all(deviceId);
532
- return Object.fromEntries(rows.map((r) => [r.key, JSON.parse(r.value)]));
533
- }
534
- // ---------------------------------------------------------------------------
535
- // Lifecycle
536
- // ---------------------------------------------------------------------------
537
- /** Close the SQLite connection (call on shutdown). */
538
- close() {
539
- this.db.close();
540
- }
541
- /** Check if system_settings is empty (used for first-boot seeding). */
542
- isSystemSettingsEmpty() {
543
- const row = this.db.prepare("SELECT COUNT(*) AS cnt FROM system_settings").get();
544
- return (row?.cnt ?? 0) === 0;
545
- }
546
- /** Seed system_settings with RUNTIME_DEFAULTS (only on first boot). */
547
- seedDefaults() {
548
- const insert = this.db.prepare(
549
- `INSERT OR IGNORE INTO system_settings (key, value, updated_at) VALUES (?, json(?), unixepoch())`
550
- );
551
- this.db.transaction(() => {
552
- for (const [key, value] of Object.entries(RUNTIME_DEFAULTS)) {
553
- insert.run(key, JSON.stringify(value));
554
- }
555
- })();
556
- }
557
- // ---------------------------------------------------------------------------
558
- // Private helpers
559
- // ---------------------------------------------------------------------------
560
- initTables() {
561
- this.db.transaction(() => {
562
- for (const stmt of import_sql_schema.CORE_TABLE_DDL) {
563
- this.db.prepare(stmt).run();
564
- }
565
- })();
566
- }
567
- };
568
- }
569
- });
570
-
571
- // src/builtins/sqlite-storage/sql-schema.ts
572
- var sql_schema_exports = {};
573
- __export(sql_schema_exports, {
574
- CORE_TABLE_DDL: () => CORE_TABLE_DDL2,
575
- addonTableToDdl: () => addonTableToDdl
576
- });
577
- function addonTableToDdl(schema) {
578
- const pks = schema.columns.filter((c) => c.primaryKey).map((c) => c.name);
579
- const colDefs = schema.columns.map((c) => {
580
- const parts = [c.name, c.type];
581
- if (c.notNull) parts.push("NOT NULL");
582
- return parts.join(" ");
583
- });
584
- let ddl = `CREATE TABLE IF NOT EXISTS ${schema.name} (
585
- ${colDefs.join(",\n ")}`;
586
- if (pks.length > 0) {
587
- ddl += `,
588
- PRIMARY KEY (${pks.join(", ")})`;
589
- }
590
- ddl += "\n)";
591
- const stmts = [ddl];
592
- for (const idx of schema.indexes ?? []) {
593
- const unique = idx.unique ? "UNIQUE " : "";
594
- stmts.push(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name}(${idx.columns.join(", ")})`);
595
- }
596
- return stmts;
597
- }
598
- var CORE_TABLE_DDL2;
599
- var init_sql_schema = __esm({
600
- "src/builtins/sqlite-storage/sql-schema.ts"() {
601
- "use strict";
602
- CORE_TABLE_DDL2 = [
603
- // Settings tables
604
- `CREATE TABLE IF NOT EXISTS system_settings (
605
- key TEXT PRIMARY KEY,
606
- value JSON NOT NULL,
607
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
608
- )`,
609
- `CREATE TABLE IF NOT EXISTS addon_settings (
610
- addon_id TEXT NOT NULL,
611
- key TEXT NOT NULL,
612
- value JSON NOT NULL,
613
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
614
- PRIMARY KEY (addon_id, key)
615
- )`,
616
- `CREATE TABLE IF NOT EXISTS provider_settings (
617
- provider_id TEXT NOT NULL,
618
- key TEXT NOT NULL,
619
- value JSON NOT NULL,
620
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
621
- PRIMARY KEY (provider_id, key)
622
- )`,
623
- `CREATE TABLE IF NOT EXISTS device_settings (
624
- device_id TEXT NOT NULL,
625
- key TEXT NOT NULL,
626
- value JSON NOT NULL,
627
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
628
- PRIMARY KEY (device_id, key)
629
- )`,
630
- // Detection events
631
- `CREATE TABLE IF NOT EXISTS detection_events (
632
- id TEXT PRIMARY KEY,
633
- timestamp INTEGER NOT NULL,
634
- device_id TEXT NOT NULL,
635
- class_name TEXT NOT NULL,
636
- score REAL NOT NULL,
637
- severity TEXT NOT NULL,
638
- track_id TEXT,
639
- zones JSON,
640
- recognition JSON,
641
- media_files JSON,
642
- data JSON
643
- )`,
644
- `CREATE INDEX IF NOT EXISTS idx_det_device_ts ON detection_events(device_id, timestamp)`,
645
- `CREATE INDEX IF NOT EXISTS idx_det_class_ts ON detection_events(class_name, timestamp)`,
646
- // Audio levels
647
- `CREATE TABLE IF NOT EXISTS audio_levels (
648
- id TEXT PRIMARY KEY,
649
- timestamp INTEGER NOT NULL,
650
- device_id TEXT NOT NULL,
651
- dbfs REAL NOT NULL,
652
- rms REAL NOT NULL,
653
- state TEXT NOT NULL
654
- )`,
655
- `CREATE INDEX IF NOT EXISTS idx_audio_device_ts ON audio_levels(device_id, timestamp)`,
656
- // Track trails
657
- `CREATE TABLE IF NOT EXISTS track_trails (
658
- track_id TEXT PRIMARY KEY,
659
- device_id TEXT NOT NULL,
660
- class_name TEXT NOT NULL,
661
- first_seen INTEGER NOT NULL,
662
- last_seen INTEGER NOT NULL,
663
- positions JSON NOT NULL,
664
- snapshots JSON,
665
- total_distance REAL,
666
- zones_visited JSON
667
- )`,
668
- `CREATE INDEX IF NOT EXISTS idx_trails_device_ts ON track_trails(device_id, first_seen)`
669
- ];
670
- }
671
- });
672
-
673
- export {
674
- FileSystemStorage,
675
- SqliteStorageProvider,
676
- sqlite_storage_provider_exports,
677
- init_sqlite_storage_provider,
678
- SqliteStorageAddon,
679
- sqlite_storage_addon_exports,
680
- init_sqlite_storage_addon,
681
- SettingsStore,
682
- settings_store_exports,
683
- init_settings_store,
684
- CORE_TABLE_DDL2 as CORE_TABLE_DDL,
685
- addonTableToDdl,
686
- sql_schema_exports,
687
- init_sql_schema
688
- };
689
- //# sourceMappingURL=chunk-EFQ25JFE.mjs.map