@camstack/core 0.1.11 → 0.1.13

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.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  addonTableToDdl
8
8
  } from "./chunk-R3DIIBBX.mjs";
9
9
  import "./chunk-SPA4JBKN.mjs";
10
- import "./chunk-YXNXYYHL.mjs";
10
+ import "./chunk-SMNR44VG.mjs";
11
11
  import {
12
12
  WinstonDestination,
13
13
  WinstonLoggingAddon
@@ -1935,7 +1935,13 @@ var SystemEventBus = class {
1935
1935
  this.ringBuffer.push(event);
1936
1936
  for (const subscriber of this.subscribers) {
1937
1937
  if (matchesFilter(event, subscriber.filter)) {
1938
- subscriber.callback(event);
1938
+ queueMicrotask(() => {
1939
+ try {
1940
+ subscriber.callback(event);
1941
+ } catch (err) {
1942
+ console.error(`[EventBus] Subscriber error for ${event.category}:`, err);
1943
+ }
1944
+ });
1939
1945
  }
1940
1946
  }
1941
1947
  }
@@ -3372,6 +3378,320 @@ var InferenceConfigResolver = class {
3372
3378
  }
3373
3379
  };
3374
3380
 
3381
+ // src/builtins/sqlite-storage/integration-registry.ts
3382
+ function serializeSetting(value) {
3383
+ if (typeof value === "number") return { value: String(value), valueType: "number" };
3384
+ if (typeof value === "boolean") return { value: String(value), valueType: "boolean" };
3385
+ if (typeof value === "string") return { value, valueType: "string" };
3386
+ return { value: JSON.stringify(value), valueType: "json" };
3387
+ }
3388
+ function deserializeSetting(value, valueType) {
3389
+ switch (valueType) {
3390
+ case "number":
3391
+ return Number(value);
3392
+ case "boolean":
3393
+ return value === "true";
3394
+ case "json":
3395
+ try {
3396
+ return JSON.parse(value);
3397
+ } catch {
3398
+ return value;
3399
+ }
3400
+ default:
3401
+ return value;
3402
+ }
3403
+ }
3404
+ var integrationCounter = 0;
3405
+ var deviceCounter = 0;
3406
+ function nextIntegrationId() {
3407
+ return `int_${String(++integrationCounter).padStart(4, "0")}`;
3408
+ }
3409
+ function nextDeviceId() {
3410
+ return `dev_${String(++deviceCounter).padStart(4, "0")}`;
3411
+ }
3412
+ var INTEGRATIONS_SCHEMA = {
3413
+ columns: [
3414
+ { name: "id", type: "TEXT", primaryKey: true },
3415
+ { name: "addon_id", type: "TEXT", notNull: true },
3416
+ { name: "name", type: "TEXT", notNull: true },
3417
+ { name: "enabled", type: "INTEGER", notNull: true, defaultValue: 1 },
3418
+ { name: "info", type: "TEXT", notNull: true, defaultValue: "{}" },
3419
+ { name: "created_at", type: "INTEGER", notNull: true },
3420
+ { name: "updated_at", type: "INTEGER", notNull: true }
3421
+ ],
3422
+ indexes: [
3423
+ { name: "idx_integrations_addon", columns: ["addon_id"] }
3424
+ ]
3425
+ };
3426
+ var INTEGRATION_SETTINGS_SCHEMA = {
3427
+ columns: [
3428
+ { name: "integration_id", type: "TEXT", notNull: true },
3429
+ { name: "key", type: "TEXT", notNull: true },
3430
+ { name: "value", type: "TEXT", notNull: true },
3431
+ { name: "value_type", type: "TEXT", notNull: true, defaultValue: "string" }
3432
+ ]
3433
+ };
3434
+ var DEVICES_SCHEMA = {
3435
+ columns: [
3436
+ { name: "id", type: "TEXT", primaryKey: true },
3437
+ { name: "integration_id", type: "TEXT", notNull: true },
3438
+ { name: "stable_id", type: "TEXT", notNull: true, unique: true },
3439
+ { name: "type", type: "TEXT", notNull: true, defaultValue: "camera" },
3440
+ { name: "name", type: "TEXT", notNull: true },
3441
+ { name: "enabled", type: "INTEGER", notNull: true, defaultValue: 1 },
3442
+ { name: "info", type: "TEXT", notNull: true, defaultValue: "{}" },
3443
+ { name: "created_at", type: "INTEGER", notNull: true },
3444
+ { name: "updated_at", type: "INTEGER", notNull: true }
3445
+ ],
3446
+ indexes: [
3447
+ { name: "idx_devices_integration", columns: ["integration_id"] },
3448
+ { name: "idx_devices_stable", columns: ["stable_id"], unique: true },
3449
+ { name: "idx_devices_type", columns: ["type"] }
3450
+ ]
3451
+ };
3452
+ var DEVICE_SETTINGS_SCHEMA = {
3453
+ columns: [
3454
+ { name: "device_id", type: "TEXT", notNull: true },
3455
+ { name: "key", type: "TEXT", notNull: true },
3456
+ { name: "value", type: "TEXT", notNull: true },
3457
+ { name: "value_type", type: "TEXT", notNull: true, defaultValue: "string" }
3458
+ ]
3459
+ };
3460
+ var IntegrationRegistry = class {
3461
+ backend;
3462
+ constructor(backend) {
3463
+ this.backend = backend;
3464
+ }
3465
+ async initialize() {
3466
+ await this.backend.ensureTable?.("integrations", INTEGRATIONS_SCHEMA);
3467
+ await this.backend.ensureTable?.("integration_settings", INTEGRATION_SETTINGS_SCHEMA);
3468
+ await this.backend.ensureTable?.("devices", DEVICES_SCHEMA);
3469
+ await this.backend.ensureTable?.("device_settings_kv", DEVICE_SETTINGS_SCHEMA);
3470
+ const ints = await this.backend.tableQuery?.("integrations", { orderBy: { field: "id", direction: "desc" }, limit: 1 }) ?? [];
3471
+ if (ints[0]) {
3472
+ const num = parseInt(String(ints[0]["id"]).replace("int_", ""), 10);
3473
+ if (!isNaN(num)) integrationCounter = num;
3474
+ }
3475
+ const devs = await this.backend.tableQuery?.("devices", { orderBy: { field: "id", direction: "desc" }, limit: 1 }) ?? [];
3476
+ if (devs[0]) {
3477
+ const num = parseInt(String(devs[0]["id"]).replace("dev_", ""), 10);
3478
+ if (!isNaN(num)) deviceCounter = num;
3479
+ }
3480
+ }
3481
+ // --- Integrations ---
3482
+ createIntegration(input) {
3483
+ const id = nextIntegrationId();
3484
+ const now = Math.floor(Date.now() / 1e3);
3485
+ void this.backend.tableInsert?.("integrations", {
3486
+ id,
3487
+ addon_id: input.addonId,
3488
+ name: input.name,
3489
+ enabled: input.enabled !== false ? 1 : 0,
3490
+ info: JSON.stringify(input.info ?? {}),
3491
+ created_at: now,
3492
+ updated_at: now
3493
+ });
3494
+ if (input.settings) {
3495
+ this.setIntegrationSettings(id, input.settings);
3496
+ }
3497
+ return {
3498
+ id,
3499
+ addonId: input.addonId,
3500
+ name: input.name,
3501
+ enabled: input.enabled !== false,
3502
+ info: input.info ?? {},
3503
+ createdAt: now,
3504
+ updatedAt: now
3505
+ };
3506
+ }
3507
+ getIntegration(id) {
3508
+ let result = null;
3509
+ void this.backend.tableGet?.("integrations", { id }).then((row) => {
3510
+ if (row) result = this.mapIntegration(row);
3511
+ });
3512
+ return result;
3513
+ }
3514
+ getIntegrationByAddonId(addonId) {
3515
+ let result = null;
3516
+ void this.backend.tableGet?.("integrations", { addon_id: addonId }).then((row) => {
3517
+ if (row) result = this.mapIntegration(row);
3518
+ });
3519
+ return result;
3520
+ }
3521
+ listIntegrations() {
3522
+ let result = [];
3523
+ void this.backend.tableQuery?.("integrations", { orderBy: { field: "created_at", direction: "asc" } }).then((rows) => {
3524
+ result = rows.map((r) => this.mapIntegration(r));
3525
+ });
3526
+ return result;
3527
+ }
3528
+ updateIntegration(id, updates) {
3529
+ const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
3530
+ if (updates.name !== void 0) updateRow["name"] = updates.name;
3531
+ if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
3532
+ if (updates.info !== void 0) updateRow["info"] = JSON.stringify(updates.info);
3533
+ void this.backend.tableUpdate?.("integrations", { id }, updateRow);
3534
+ return this.getIntegration(id);
3535
+ }
3536
+ deleteIntegration(id) {
3537
+ const devices = this.listDevices(id);
3538
+ for (const d of devices) {
3539
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: d.id });
3540
+ }
3541
+ void this.backend.tableDelete?.("devices", { integration_id: id });
3542
+ void this.backend.tableDelete?.("integration_settings", { integration_id: id });
3543
+ void this.backend.tableDelete?.("integrations", { id });
3544
+ return true;
3545
+ }
3546
+ // --- Integration Settings ---
3547
+ getIntegrationSettings(integrationId) {
3548
+ const result = {};
3549
+ void this.backend.tableQuery?.("integration_settings", { where: { integration_id: integrationId } }).then((rows) => {
3550
+ for (const row of rows) {
3551
+ result[String(row["key"])] = deserializeSetting(String(row["value"]), String(row["value_type"]));
3552
+ }
3553
+ });
3554
+ return result;
3555
+ }
3556
+ setIntegrationSetting(integrationId, key, value) {
3557
+ const s = serializeSetting(value);
3558
+ void this.backend.tableDelete?.("integration_settings", { integration_id: integrationId, key });
3559
+ void this.backend.tableInsert?.("integration_settings", {
3560
+ integration_id: integrationId,
3561
+ key,
3562
+ value: s.value,
3563
+ value_type: s.valueType
3564
+ });
3565
+ }
3566
+ setIntegrationSettings(integrationId, settings) {
3567
+ for (const [key, value] of Object.entries(settings)) {
3568
+ this.setIntegrationSetting(integrationId, key, value);
3569
+ }
3570
+ }
3571
+ // --- Devices ---
3572
+ createDevice(input) {
3573
+ const id = nextDeviceId();
3574
+ const now = Math.floor(Date.now() / 1e3);
3575
+ void this.backend.tableInsert?.("devices", {
3576
+ id,
3577
+ integration_id: input.integrationId,
3578
+ stable_id: input.stableId,
3579
+ type: input.type,
3580
+ name: input.name,
3581
+ enabled: input.enabled !== false ? 1 : 0,
3582
+ info: JSON.stringify(input.info ?? {}),
3583
+ created_at: now,
3584
+ updated_at: now
3585
+ });
3586
+ if (input.settings) {
3587
+ this.setDeviceSettings(id, input.settings);
3588
+ }
3589
+ return {
3590
+ id,
3591
+ integrationId: input.integrationId,
3592
+ stableId: input.stableId,
3593
+ type: input.type,
3594
+ name: input.name,
3595
+ enabled: input.enabled !== false,
3596
+ info: input.info ?? {},
3597
+ createdAt: now,
3598
+ updatedAt: now
3599
+ };
3600
+ }
3601
+ getDevice(id) {
3602
+ let result = null;
3603
+ void this.backend.tableGet?.("devices", { id }).then((row) => {
3604
+ if (row) result = this.mapDevice(row);
3605
+ });
3606
+ return result;
3607
+ }
3608
+ getDeviceByStableId(stableId) {
3609
+ let result = null;
3610
+ void this.backend.tableGet?.("devices", { stable_id: stableId }).then((row) => {
3611
+ if (row) result = this.mapDevice(row);
3612
+ });
3613
+ return result;
3614
+ }
3615
+ listDevices(integrationId) {
3616
+ let result = [];
3617
+ const options = integrationId ? { where: { integration_id: integrationId }, orderBy: { field: "created_at", direction: "asc" } } : { orderBy: { field: "created_at", direction: "asc" } };
3618
+ void this.backend.tableQuery?.("devices", options).then((rows) => {
3619
+ result = rows.map((r) => this.mapDevice(r));
3620
+ });
3621
+ return result;
3622
+ }
3623
+ listCameras() {
3624
+ let result = [];
3625
+ void this.backend.tableQuery?.("devices", { where: { type: "camera" }, orderBy: { field: "created_at", direction: "asc" } }).then((rows) => {
3626
+ result = rows.map((r) => this.mapDevice(r));
3627
+ });
3628
+ return result;
3629
+ }
3630
+ updateDevice(id, updates) {
3631
+ const updateRow = { updated_at: Math.floor(Date.now() / 1e3) };
3632
+ if (updates.name !== void 0) updateRow["name"] = updates.name;
3633
+ if (updates.enabled !== void 0) updateRow["enabled"] = updates.enabled ? 1 : 0;
3634
+ if (updates.info !== void 0) updateRow["info"] = JSON.stringify(updates.info);
3635
+ void this.backend.tableUpdate?.("devices", { id }, updateRow);
3636
+ return this.getDevice(id);
3637
+ }
3638
+ deleteDevice(id) {
3639
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: id });
3640
+ void this.backend.tableDelete?.("devices", { id });
3641
+ return true;
3642
+ }
3643
+ // --- Device Settings ---
3644
+ getDeviceSettings(deviceId) {
3645
+ const result = {};
3646
+ void this.backend.tableQuery?.("device_settings_kv", { where: { device_id: deviceId } }).then((rows) => {
3647
+ for (const row of rows) {
3648
+ result[String(row["key"])] = deserializeSetting(String(row["value"]), String(row["value_type"]));
3649
+ }
3650
+ });
3651
+ return result;
3652
+ }
3653
+ setDeviceSetting(deviceId, key, value) {
3654
+ const s = serializeSetting(value);
3655
+ void this.backend.tableDelete?.("device_settings_kv", { device_id: deviceId, key });
3656
+ void this.backend.tableInsert?.("device_settings_kv", {
3657
+ device_id: deviceId,
3658
+ key,
3659
+ value: s.value,
3660
+ value_type: s.valueType
3661
+ });
3662
+ }
3663
+ setDeviceSettings(deviceId, settings) {
3664
+ for (const [key, value] of Object.entries(settings)) {
3665
+ this.setDeviceSetting(deviceId, key, value);
3666
+ }
3667
+ }
3668
+ // --- Mappers ---
3669
+ mapIntegration(row) {
3670
+ return {
3671
+ id: String(row["id"]),
3672
+ addonId: String(row["addon_id"]),
3673
+ name: String(row["name"]),
3674
+ enabled: row["enabled"] === 1,
3675
+ info: typeof row["info"] === "string" ? JSON.parse(row["info"]) : {},
3676
+ createdAt: Number(row["created_at"]),
3677
+ updatedAt: Number(row["updated_at"])
3678
+ };
3679
+ }
3680
+ mapDevice(row) {
3681
+ return {
3682
+ id: String(row["id"]),
3683
+ integrationId: String(row["integration_id"]),
3684
+ stableId: String(row["stable_id"]),
3685
+ type: String(row["type"]),
3686
+ name: String(row["name"]),
3687
+ enabled: row["enabled"] === 1,
3688
+ info: typeof row["info"] === "string" ? JSON.parse(row["info"]) : {},
3689
+ createdAt: Number(row["created_at"]),
3690
+ updatedAt: Number(row["updated_at"])
3691
+ };
3692
+ }
3693
+ };
3694
+
3375
3695
  // src/provider/provider-manager.ts
3376
3696
  import { randomUUID as randomUUID7 } from "crypto";
3377
3697
  var ProviderManager = class {
@@ -3526,6 +3846,7 @@ export {
3526
3846
  FileSystemStorage,
3527
3847
  FsStorageBackend,
3528
3848
  InferenceConfigResolver,
3849
+ IntegrationRegistry,
3529
3850
  LifecycleStateMachine,
3530
3851
  LocalBackupAddon,
3531
3852
  LocalBackupService,