@camstack/core 0.1.15 → 0.1.16
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/addon/addon-api-factory.d.ts +36 -0
- package/dist/addon/addon-api-factory.d.ts.map +1 -0
- package/dist/addon-routes/addon-route-registry.d.ts +38 -0
- package/dist/addon-routes/addon-route-registry.d.ts.map +1 -0
- package/dist/auth/api-key-manager.d.ts +27 -0
- package/dist/auth/api-key-manager.d.ts.map +1 -0
- package/dist/auth/auth-manager.d.ts +47 -0
- package/dist/auth/auth-manager.d.ts.map +1 -0
- package/dist/auth/parse-record.d.ts +19 -0
- package/dist/auth/parse-record.d.ts.map +1 -0
- package/dist/auth/scoped-token-manager.d.ts +18 -0
- package/dist/auth/scoped-token-manager.d.ts.map +1 -0
- package/dist/auth/user-manager.d.ts +34 -0
- package/dist/auth/user-manager.d.ts.map +1 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts +54 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.d.ts.map +1 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +223 -217
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js.map +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +216 -7
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs.map +1 -1
- package/dist/builtins/addon-pages-aggregator/index.d.ts +2 -0
- package/dist/builtins/addon-pages-aggregator/index.d.ts.map +1 -0
- package/dist/builtins/addon-pages-aggregator/index.js +6 -221
- package/dist/builtins/addon-pages-aggregator/index.mjs +2 -9
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts +33 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.d.ts.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +199 -197
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js.map +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +192 -7
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs.map +1 -1
- package/dist/builtins/addon-widgets-aggregator/index.d.ts +2 -0
- package/dist/builtins/addon-widgets-aggregator/index.d.ts.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/index.js +6 -201
- package/dist/builtins/addon-widgets-aggregator/index.mjs +2 -9
- package/dist/builtins/alerts/alerts.addon.d.ts +82 -0
- package/dist/builtins/alerts/alerts.addon.d.ts.map +1 -0
- package/dist/builtins/alerts/alerts.addon.js +590 -430
- package/dist/builtins/alerts/alerts.addon.js.map +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +595 -7
- package/dist/builtins/alerts/alerts.addon.mjs.map +1 -1
- package/dist/builtins/alerts/index.d.ts +2 -0
- package/dist/builtins/alerts/index.d.ts.map +1 -0
- package/dist/builtins/alerts/index.js +3 -443
- package/dist/builtins/alerts/index.mjs +2 -8
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts +8 -0
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js +56 -0
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.js.map +1 -0
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs +50 -0
- package/dist/builtins/auth-orchestrator/auth-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/auth-orchestrator/index.d.ts +2 -0
- package/dist/builtins/auth-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/auth-orchestrator/index.js +7 -0
- package/dist/builtins/auth-orchestrator/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +148 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +7639 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js.map +1 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +7627 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/backup-orchestrator/cron-helpers.d.ts +24 -0
- package/dist/builtins/backup-orchestrator/cron-helpers.d.ts.map +1 -0
- package/dist/builtins/backup-orchestrator/destination-policy.d.ts +73 -0
- package/dist/builtins/backup-orchestrator/destination-policy.d.ts.map +1 -0
- package/dist/builtins/backup-orchestrator/download-helpers.d.ts +13 -0
- package/dist/builtins/backup-orchestrator/download-helpers.d.ts.map +1 -0
- package/dist/builtins/backup-orchestrator/index.d.ts +3 -0
- package/dist/builtins/backup-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/backup-orchestrator/index.js +7 -0
- package/dist/builtins/backup-orchestrator/index.mjs +2 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts +78 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts.map +1 -0
- package/dist/builtins/console-logging/console-destination.d.ts +14 -0
- package/dist/builtins/console-logging/console-destination.d.ts.map +1 -0
- package/dist/builtins/console-logging/console-logging.addon.d.ts +26 -0
- package/dist/builtins/console-logging/console-logging.addon.d.ts.map +1 -0
- package/dist/builtins/console-logging/index.d.ts +4 -0
- package/dist/builtins/console-logging/index.d.ts.map +1 -0
- package/dist/builtins/console-logging/index.js +99 -235
- package/dist/builtins/console-logging/index.js.map +1 -1
- package/dist/builtins/console-logging/index.mjs +95 -9
- package/dist/builtins/console-logging/index.mjs.map +1 -1
- package/dist/builtins/device-manager/device-event-propagator.d.ts +27 -0
- package/dist/builtins/device-manager/device-event-propagator.d.ts.map +1 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts +259 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts.map +1 -0
- package/dist/builtins/device-manager/device-manager.addon.js +2125 -2127
- package/dist/builtins/device-manager/device-manager.addon.js.map +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +2145 -7
- package/dist/builtins/device-manager/device-manager.addon.mjs.map +1 -1
- package/dist/builtins/device-manager/index.d.ts +3 -0
- package/dist/builtins/device-manager/index.d.ts.map +1 -0
- package/dist/builtins/device-manager/index.js +6 -2156
- package/dist/builtins/device-manager/index.mjs +2 -10
- package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +45 -0
- package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts.map +1 -0
- package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts +16 -0
- package/dist/builtins/hub-forwarder/hub-forwarder.addon.d.ts.map +1 -0
- package/dist/builtins/hub-forwarder/index.d.ts +4 -0
- package/dist/builtins/hub-forwarder/index.d.ts.map +1 -0
- package/dist/builtins/hub-forwarder/index.js +150 -291
- package/dist/builtins/hub-forwarder/index.js.map +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +145 -9
- package/dist/builtins/hub-forwarder/index.mjs.map +1 -1
- package/dist/builtins/local-auth/auth-schema.d.ts +12 -0
- package/dist/builtins/local-auth/auth-schema.d.ts.map +1 -0
- package/dist/builtins/local-auth/index.d.ts +2 -0
- package/dist/builtins/local-auth/index.d.ts.map +1 -0
- package/dist/builtins/local-auth/index.js +3 -623
- package/dist/builtins/local-auth/index.mjs +2 -8
- package/dist/builtins/local-auth/local-auth.addon.d.ts +17 -0
- package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -0
- package/dist/builtins/local-auth/local-auth.addon.js +6861 -589
- package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +6883 -7
- package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
- package/dist/builtins/local-network/index.d.ts +3 -0
- package/dist/builtins/local-network/index.d.ts.map +1 -0
- package/dist/builtins/local-network/index.js +9 -0
- package/dist/builtins/local-network/index.mjs +2 -0
- package/dist/builtins/local-network/local-network.addon.d.ts +102 -0
- package/dist/builtins/local-network/local-network.addon.d.ts.map +1 -0
- package/dist/builtins/local-network/local-network.addon.js +404 -0
- package/dist/builtins/local-network/local-network.addon.js.map +1 -0
- package/dist/builtins/local-network/local-network.addon.mjs +392 -0
- package/dist/builtins/local-network/local-network.addon.mjs.map +1 -0
- package/dist/builtins/mesh-orchestrator/index.d.ts +2 -0
- package/dist/builtins/mesh-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/mesh-orchestrator/index.js +7 -0
- package/dist/builtins/mesh-orchestrator/index.mjs +2 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts +9 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js +83 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js.map +1 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs +77 -0
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/native-metrics/index.d.ts +3 -0
- package/dist/builtins/native-metrics/index.d.ts.map +1 -0
- package/dist/builtins/native-metrics/native-metrics-provider.d.ts +49 -0
- package/dist/builtins/native-metrics/native-metrics-provider.d.ts.map +1 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +74 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts.map +1 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +887 -861
- package/dist/builtins/native-metrics/native-metrics.addon.js.map +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +914 -5
- package/dist/builtins/native-metrics/native-metrics.addon.mjs.map +1 -1
- package/dist/builtins/platform-probe/index.d.ts +12 -0
- package/dist/builtins/platform-probe/index.d.ts.map +1 -0
- package/dist/builtins/platform-probe/index.js +539 -0
- package/dist/builtins/platform-probe/index.js.map +1 -0
- package/dist/builtins/platform-probe/index.mjs +530 -0
- package/dist/builtins/platform-probe/index.mjs.map +1 -0
- package/dist/builtins/platform-probe/inference-config-resolver.d.ts +30 -0
- package/dist/builtins/platform-probe/inference-config-resolver.d.ts.map +1 -0
- package/dist/builtins/platform-probe/platform-scorer.d.ts +22 -0
- package/dist/builtins/platform-probe/platform-scorer.d.ts.map +1 -0
- package/dist/builtins/remote-access-orchestrator/index.d.ts +2 -0
- package/dist/builtins/remote-access-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/remote-access-orchestrator/index.js +7 -0
- package/dist/builtins/remote-access-orchestrator/index.mjs +2 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts +9 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +72 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js.map +1 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +66 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/snapshot/index.d.ts +3 -0
- package/dist/builtins/snapshot/index.d.ts.map +1 -0
- package/dist/builtins/snapshot/index.js +481 -491
- package/dist/builtins/snapshot/index.js.map +1 -1
- package/dist/builtins/snapshot/index.mjs +475 -464
- package/dist/builtins/snapshot/index.mjs.map +1 -1
- package/dist/builtins/snapshot/snapshot.addon.d.ts +121 -0
- package/dist/builtins/snapshot/snapshot.addon.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/config-store.d.ts +9 -0
- package/dist/builtins/sqlite-storage/config-store.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/device-store.d.ts +24 -0
- package/dist/builtins/sqlite-storage/device-store.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts +87 -0
- package/dist/builtins/sqlite-storage/filesystem-storage-provider.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +32 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +312 -56
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js.map +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +305 -7
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs.map +1 -1
- package/dist/builtins/sqlite-storage/index.d.ts +12 -0
- package/dist/builtins/sqlite-storage/index.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/index.js +229 -1001
- package/dist/builtins/sqlite-storage/index.js.map +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +268 -26
- package/dist/builtins/sqlite-storage/index.mjs.map +1 -1
- package/dist/builtins/sqlite-storage/integration-registry.d.ts +28 -0
- package/dist/builtins/sqlite-storage/integration-registry.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/settings-store.d.ts +40 -0
- package/dist/builtins/sqlite-storage/settings-store.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/sql-schema.d.ts +33 -0
- package/dist/builtins/sqlite-storage/sql-schema.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +94 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +15 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts.map +1 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +586 -653
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js.map +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +582 -7
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs.map +1 -1
- package/dist/builtins/storage-orchestrator/index.d.ts +7 -0
- package/dist/builtins/storage-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/storage-orchestrator/index.js +9 -0
- package/dist/builtins/storage-orchestrator/index.mjs +2 -0
- package/dist/builtins/storage-orchestrator/location-store.d.ts +50 -0
- package/dist/builtins/storage-orchestrator/location-store.d.ts.map +1 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +60 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +755 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js.map +1 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +746 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +121 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts.map +1 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts +138 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts.map +1 -0
- package/dist/builtins/system-config/index.d.ts +2 -0
- package/dist/builtins/system-config/index.d.ts.map +1 -0
- package/dist/builtins/system-config/index.js +6 -188
- package/dist/builtins/system-config/index.mjs +2 -10
- package/dist/builtins/system-config/system-config.addon.d.ts +11 -0
- package/dist/builtins/system-config/system-config.addon.d.ts.map +1 -0
- package/dist/builtins/system-config/system-config.addon.js +227 -180
- package/dist/builtins/system-config/system-config.addon.js.map +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +226 -7
- package/dist/builtins/system-config/system-config.addon.mjs.map +1 -1
- package/dist/builtins/turn-orchestrator/index.d.ts +2 -0
- package/dist/builtins/turn-orchestrator/index.d.ts.map +1 -0
- package/dist/builtins/turn-orchestrator/index.js +7 -0
- package/dist/builtins/turn-orchestrator/index.mjs +2 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts +10 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.d.ts.map +1 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js +78 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.js.map +1 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs +72 -0
- package/dist/builtins/turn-orchestrator/turn-orchestrator.addon.mjs.map +1 -0
- package/dist/builtins/winston-logging/index.d.ts +4 -0
- package/dist/builtins/winston-logging/index.d.ts.map +1 -0
- package/dist/builtins/winston-logging/index.js +153 -300
- package/dist/builtins/winston-logging/index.js.map +1 -1
- package/dist/builtins/winston-logging/index.mjs +144 -9
- package/dist/builtins/winston-logging/index.mjs.map +1 -1
- package/dist/builtins/winston-logging/winston-destination.d.ts +22 -0
- package/dist/builtins/winston-logging/winston-destination.d.ts.map +1 -0
- package/dist/builtins/winston-logging/winston-logging.addon.d.ts +20 -0
- package/dist/builtins/winston-logging/winston-logging.addon.d.ts.map +1 -0
- package/dist/chunk-C13QxCFV.js +50 -0
- package/dist/chunk-hT5z_Zn9.mjs +35 -0
- package/dist/download/model-download-service.d.ts +42 -0
- package/dist/download/model-download-service.d.ts.map +1 -0
- package/dist/download/model-downloader.d.ts +32 -0
- package/dist/download/model-downloader.d.ts.map +1 -0
- package/dist/events/event-bus.d.ts +11 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/system-event-bus.d.ts +15 -0
- package/dist/events/system-event-bus.d.ts.map +1 -0
- package/dist/feature/feature-manager.d.ts +12 -0
- package/dist/feature/feature-manager.d.ts.map +1 -0
- package/dist/formatter-C-5An4Bl.mjs +164 -0
- package/dist/formatter-C-5An4Bl.mjs.map +1 -0
- package/dist/formatter-Dr_6NNZc.js +169 -0
- package/dist/formatter-Dr_6NNZc.js.map +1 -0
- package/dist/index.d.ts +76 -1696
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7780 -8035
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7707 -2148
- package/dist/index.mjs.map +1 -1
- package/dist/lifecycle/lifecycle-state-machine.d.ts +29 -0
- package/dist/lifecycle/lifecycle-state-machine.d.ts.map +1 -0
- package/dist/logging/formatter.d.ts +31 -0
- package/dist/logging/formatter.d.ts.map +1 -0
- package/dist/logging/log-manager.d.ts +52 -0
- package/dist/logging/log-manager.d.ts.map +1 -0
- package/dist/logging/log-ring-buffer.d.ts +48 -0
- package/dist/logging/log-ring-buffer.d.ts.map +1 -0
- package/dist/logging/scoped-logger.d.ts +18 -0
- package/dist/logging/scoped-logger.d.ts.map +1 -0
- package/dist/network/network-quality.d.ts +12 -0
- package/dist/network/network-quality.d.ts.map +1 -0
- package/dist/notification/notification-service.d.ts +38 -0
- package/dist/notification/notification-service.d.ts.map +1 -0
- package/dist/notification/toast-service.d.ts +23 -0
- package/dist/notification/toast-service.d.ts.map +1 -0
- package/dist/pipeline/engine-manager-resolver.d.ts +16 -0
- package/dist/pipeline/engine-manager-resolver.d.ts.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +9 -0
- package/dist/pipeline/pipeline-runner.d.ts.map +1 -0
- package/dist/pipeline/pipeline-validator.d.ts +14 -0
- package/dist/pipeline/pipeline-validator.d.ts.map +1 -0
- package/dist/process/resource-monitor.d.ts +12 -0
- package/dist/process/resource-monitor.d.ts.map +1 -0
- package/dist/python/python-env-manager.d.ts +13 -0
- package/dist/python/python-env-manager.d.ts.map +1 -0
- package/dist/repl/interfaces.d.ts +32 -0
- package/dist/repl/interfaces.d.ts.map +1 -0
- package/dist/repl/repl-engine.d.ts +9 -0
- package/dist/repl/repl-engine.d.ts.map +1 -0
- package/dist/resource-monitor-CmuWlmap.js +76 -0
- package/dist/resource-monitor-CmuWlmap.js.map +1 -0
- package/dist/resource-monitor-DcQdKGYU.mjs +59 -0
- package/dist/resource-monitor-DcQdKGYU.mjs.map +1 -0
- package/dist/storage/fs-storage-backend.d.ts +41 -0
- package/dist/storage/fs-storage-backend.d.ts.map +1 -0
- package/dist/storage/storage-location-manager.d.ts +24 -0
- package/dist/storage/storage-location-manager.d.ts.map +1 -0
- package/dist/storage/storage-manager.d.ts +77 -0
- package/dist/storage/storage-manager.d.ts.map +1 -0
- package/dist/tls/cert-manager.d.ts +27 -0
- package/dist/tls/cert-manager.d.ts.map +1 -0
- package/dist/tls/index.d.ts +2 -0
- package/dist/tls/index.d.ts.map +1 -0
- package/package.json +119 -23
- package/dist/builtins/addon-pages-aggregator/index.js.map +0 -1
- package/dist/builtins/addon-pages-aggregator/index.mjs.map +0 -1
- package/dist/builtins/addon-widgets-aggregator/index.js.map +0 -1
- package/dist/builtins/addon-widgets-aggregator/index.mjs.map +0 -1
- package/dist/builtins/alerts/index.js.map +0 -1
- package/dist/builtins/alerts/index.mjs.map +0 -1
- package/dist/builtins/device-manager/index.js.map +0 -1
- package/dist/builtins/device-manager/index.mjs.map +0 -1
- package/dist/builtins/local-auth/index.js.map +0 -1
- package/dist/builtins/local-auth/index.mjs.map +0 -1
- package/dist/builtins/local-backup/index.js +0 -173
- package/dist/builtins/local-backup/index.js.map +0 -1
- package/dist/builtins/local-backup/index.mjs +0 -10
- package/dist/builtins/local-backup/index.mjs.map +0 -1
- package/dist/builtins/system-config/index.js.map +0 -1
- package/dist/builtins/system-config/index.mjs.map +0 -1
- package/dist/chunk-2CIYKDRN.mjs +0 -1
- package/dist/chunk-2CIYKDRN.mjs.map +0 -1
- package/dist/chunk-2F76X6NL.mjs +0 -136
- package/dist/chunk-2F76X6NL.mjs.map +0 -1
- package/dist/chunk-2QUFBZ7M.mjs +0 -1
- package/dist/chunk-2QUFBZ7M.mjs.map +0 -1
- package/dist/chunk-3BK2Y7GY.mjs +0 -593
- package/dist/chunk-3BK2Y7GY.mjs.map +0 -1
- package/dist/chunk-4OOHFJHT.mjs +0 -421
- package/dist/chunk-4OOHFJHT.mjs.map +0 -1
- package/dist/chunk-4XHB7IHT.mjs +0 -809
- package/dist/chunk-4XHB7IHT.mjs.map +0 -1
- package/dist/chunk-6M2HSSTQ.mjs +0 -98
- package/dist/chunk-6M2HSSTQ.mjs.map +0 -1
- package/dist/chunk-7FI7SQS7.mjs +0 -135
- package/dist/chunk-7FI7SQS7.mjs.map +0 -1
- package/dist/chunk-ED57RCQE.mjs +0 -171
- package/dist/chunk-ED57RCQE.mjs.map +0 -1
- package/dist/chunk-FZN56HGQ.mjs +0 -626
- package/dist/chunk-FZN56HGQ.mjs.map +0 -1
- package/dist/chunk-GL4OOB25.mjs +0 -51
- package/dist/chunk-GL4OOB25.mjs.map +0 -1
- package/dist/chunk-KDG2NTDB.mjs +0 -137
- package/dist/chunk-KDG2NTDB.mjs.map +0 -1
- package/dist/chunk-NRBQWBDM.mjs +0 -191
- package/dist/chunk-NRBQWBDM.mjs.map +0 -1
- package/dist/chunk-O4V246GG.mjs +0 -2137
- package/dist/chunk-O4V246GG.mjs.map +0 -1
- package/dist/chunk-QT57H266.mjs +0 -163
- package/dist/chunk-QT57H266.mjs.map +0 -1
- package/dist/chunk-QX4RH25I.mjs +0 -141
- package/dist/chunk-QX4RH25I.mjs.map +0 -1
- package/dist/chunk-TB562PZX.mjs +0 -86
- package/dist/chunk-TB562PZX.mjs.map +0 -1
- package/dist/chunk-TDYPZXK5.mjs +0 -1
- package/dist/chunk-TDYPZXK5.mjs.map +0 -1
- package/dist/chunk-UJI4LN5P.mjs +0 -36
- package/dist/chunk-UJI4LN5P.mjs.map +0 -1
- package/dist/chunk-W6RTHQGP.mjs +0 -1
- package/dist/chunk-W6RTHQGP.mjs.map +0 -1
- package/dist/chunk-ZELBCPDC.mjs +0 -369
- package/dist/chunk-ZELBCPDC.mjs.map +0 -1
- package/dist/index.d.mts +0 -1696
- package/dist/resource-monitor-UZUGPIAU.mjs +0 -9
- package/dist/resource-monitor-UZUGPIAU.mjs.map +0 -1
- package/dist/storage-location-manager-HFNB3PCS.mjs +0 -7
- package/dist/storage-location-manager-HFNB3PCS.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/builtins/sqlite-storage/sqlite-settings.addon.ts","../src/builtins/sqlite-storage/sqlite-settings-backend.ts"],"sourcesContent":["import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, RUNTIME_DEFAULTS, errMsg, settingsStoreCapability } from '@camstack/types'\nimport { SqliteSettingsBackend } from './sqlite-settings-backend.js'\n\n/**\n * SQLite Settings addon — provides persistent settings storage.\n * Capability: 'settings-store' (singleton)\n */\nexport class SqliteSettingsAddon extends BaseAddon {\n private backend: SqliteSettingsBackend | null = null\n\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const addonId = this.ctx.id.replace('addon:', '')\n let pathSource = 'fallback (hardcoded)'\n const dbPath = await this.ctx.api.storage.resolve.query({ location: 'addons-data', relativePath: `${addonId}/camstack.db` })\n .then((p: string) => { pathSource = 'storage capability (addons-data)'; return p })\n .catch(() => {\n if (this.ctx.dataDir) { pathSource = 'dataDir'; return `${this.ctx.dataDir}/camstack.db` }\n return 'camstack-data/addons-data/sqlite-settings/camstack.db'\n })\n\n let dbExists = false\n try {\n const fs = await import('node:fs')\n dbExists = fs.existsSync(dbPath)\n } catch (err) { this.ctx.logger.warn('Failed to check DB file existence', { meta: { error: errMsg(err) } }) }\n\n this.ctx.logger.info('DB path resolved', { meta: { pathSource, dbPath } })\n this.ctx.logger.info('DB file status', { meta: { dbExists } })\n\n this.backend = new SqliteSettingsBackend(dbPath, { ...RUNTIME_DEFAULTS })\n await this.backend.initialize()\n this.ctx.logger.info('Initialized successfully')\n return [{ capability: settingsStoreCapability, provider: this.backend }]\n }\n\n protected async onShutdown(): Promise<void> {\n await this.backend?.shutdown()\n }\n\n getBackend(): SqliteSettingsBackend | null {\n return this.backend\n }\n}\n\nexport default SqliteSettingsAddon\n","import Database from 'better-sqlite3'\nimport { randomUUID } from 'node:crypto'\nimport type {\n ISettingsBackend,\n SettingsRecord,\n TableSchema,\n TableQueryOptions,\n SettingsGetInput,\n SettingsSetInput,\n SettingsQueryInput,\n SettingsInsertInput,\n SettingsUpdateInput,\n SettingsDeleteInput,\n SettingsCountInput,\n SettingsIsEmptyInput,\n CollectionColumn,\n CollectionIndex,\n} from '@camstack/types'\nimport { asJsonObject, parseJsonUnknown } from '@camstack/types'\n\nfunction parseRowData(raw: string): Record<string, unknown> {\n return asJsonObject(parseJsonUnknown(raw)) ?? {}\n}\n\n/**\n * SQLite implementation of ISettingsBackend.\n *\n * Every collection is structured: declared at boot (canonical KV\n * collections) or via `declareCollection` (typed schemas like\n * `pipeline-analytics:tracks`). The legacy auto-create-on-first-access\n * path was removed — see commit history for the migration. Operations\n * on undeclared collections throw at runtime so callers fail fast.\n *\n * WAL mode for concurrent reads.\n */\nexport class SqliteSettingsBackend implements ISettingsBackend {\n private db: Database.Database | null = null\n private readonly structuredTables = new Set<string>()\n /** Map from scoped collection name → set of column names (non-id) that\n * the structured schema owns. Routes set/get/insert/update/query to\n * typed columns. Every collection MUST be declared here before use. */\n private readonly declaredCollections = new Map<string, { readonly primaryKey: string; readonly columns: ReadonlySet<string> }>()\n private readonly runtimeDefaults: Record<string, unknown>\n\n /**\n * Canonical key/value collections — declared with a `(id TEXT PK,\n * data TEXT NOT NULL)` schema at boot so existing JSON-blob rows\n * keep working through the structured path. Generates SQL identical\n * to the previous legacy path; only the routing is unified.\n */\n private static readonly CANONICAL_KV_COLLECTIONS: readonly string[] = [\n 'system-settings',\n 'addon-settings',\n 'addon-device-settings',\n 'addon-devices',\n 'device-runtime-state',\n 'sections',\n 'provider-settings',\n 'device-settings',\n 'alerts',\n ]\n\n // Domain schemas (auth, analytics events, …) live in the addons that\n // own them — same pattern as `pipeline-analytics` (`event-store.ts`,\n // `track-store.ts`, `media-store.ts`) and `local-auth` (`auth-schema.ts`).\n // The backend exposes `declareCollection` and stays out of domain\n // knowledge.\n\n constructor(private readonly dbPath: string, runtimeDefaults?: Record<string, unknown>) {\n this.runtimeDefaults = runtimeDefaults ?? {}\n }\n\n async initialize(): Promise<void> {\n // Ensure parent directory exists\n const dir = this.dbPath.substring(0, this.dbPath.lastIndexOf('/'))\n if (dir) {\n const fs = await import('node:fs')\n fs.mkdirSync(dir, { recursive: true })\n }\n this.db = new Database(this.dbPath)\n this.db.pragma('journal_mode = WAL')\n this.db.pragma('foreign_keys = ON')\n\n // Declare canonical KV collections so set/get/insert/update/query\n // reach the structured path uniformly. Schema is the same shape\n // legacy created on demand — `(id TEXT PK, data TEXT NOT NULL)` —\n // so existing rows continue to work with no migration.\n for (const collection of SqliteSettingsBackend.CANONICAL_KV_COLLECTIONS) {\n await this.ensureTable(collection, {\n columns: [\n { name: 'id', type: 'TEXT', primaryKey: true, notNull: true },\n { name: 'data', type: 'TEXT', notNull: true },\n ],\n })\n this.declaredCollections.set(collection, {\n primaryKey: 'id',\n columns: new Set(['data']),\n })\n }\n\n // Seed system-settings on first boot\n const isEmpty = await this.isEmpty({ collection: 'system-settings' })\n if (isEmpty) {\n await this.seedDefaults()\n }\n }\n\n private requireDeclared(scoped: string): { readonly primaryKey: string; readonly columns: ReadonlySet<string> } {\n const decl = this.declaredCollections.get(scoped)\n if (!decl) {\n throw new Error(`SqliteSettingsBackend: collection \"${scoped}\" is not declared. Call declareCollection() first or add it to CANONICAL_KV_COLLECTIONS.`)\n }\n return decl\n }\n\n async shutdown(): Promise<void> {\n this.db?.close()\n this.db = null\n }\n\n // ---------------------------------------------------------------------------\n // ISettingsBackend implementation\n // ---------------------------------------------------------------------------\n\n async get({ namespace, collection, key }: SettingsGetInput): Promise<unknown> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n // Canonical KV collections store the value as a single `data`\n // JSON-blob column; their declared columns are exactly `['data']`\n // so we read it back, parse, and return. Structured collections\n // (multi-column) return the row map untouched.\n const cols = [`\"${decl.primaryKey}\"`, ...[...decl.columns].map(c => `\"${c}\"`)].join(', ')\n const row = this.getDb()\n .prepare<[string], Record<string, unknown>>(`SELECT ${cols} FROM \"${scoped}\" WHERE \"${decl.primaryKey}\" = ?`)\n .get(key)\n if (!row) return undefined\n if (decl.columns.size === 1 && decl.columns.has('data')) {\n const raw = row['data']\n return typeof raw === 'string' ? JSON.parse(raw) : raw\n }\n const data: Record<string, unknown> = {}\n for (const c of decl.columns) {\n const v = row[c]\n if (typeof v === 'string' && (v.startsWith('{') || v.startsWith('['))) {\n try { data[c] = JSON.parse(v) } catch { data[c] = v }\n } else {\n data[c] = v ?? null\n }\n }\n return data\n }\n\n async set({ namespace, collection, key, value }: SettingsSetInput): Promise<void> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n // Build the column row. KV-shape collections (`['data']`) store\n // `value` as a JSON blob in `data`; structured collections\n // destructure `value` into its declared columns.\n const row: Record<string, unknown> = { [decl.primaryKey]: key }\n if (decl.columns.size === 1 && decl.columns.has('data')) {\n row['data'] = JSON.stringify(value)\n } else {\n const valueObj = (value !== null && typeof value === 'object' ? value : {}) as Record<string, unknown>\n for (const [k, v] of Object.entries(valueObj)) {\n if (decl.columns.has(k)) row[k] = this.serializeColumnValue(v)\n }\n }\n const keys = Object.keys(row)\n const cols = keys.map(k => `\"${k}\"`).join(', ')\n const placeholders = keys.map(() => '?').join(', ')\n const updates = keys\n .filter(k => k !== decl.primaryKey)\n .map(k => `\"${k}\" = excluded.\"${k}\"`)\n .join(', ')\n const values = keys.map(k => row[k])\n const sql = updates.length > 0\n ? `INSERT INTO \"${scoped}\" (${cols}) VALUES (${placeholders}) ON CONFLICT(\"${decl.primaryKey}\") DO UPDATE SET ${updates}`\n : `INSERT INTO \"${scoped}\" (${cols}) VALUES (${placeholders}) ON CONFLICT(\"${decl.primaryKey}\") DO NOTHING`\n this.getDb().prepare(sql).run(...values)\n }\n\n async query<T extends object = Record<string, unknown>>(\n { namespace, collection, filter }: SettingsQueryInput,\n ): Promise<readonly SettingsRecord<T>[]> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n return this.queryDeclared<T>(scoped, decl, filter)\n }\n\n async insert<T extends object = Record<string, unknown>>({ namespace, collection, record }: SettingsInsertInput<T>): Promise<void> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n const id = record.id || randomUUID()\n const row: Record<string, unknown> = { [decl.primaryKey]: id }\n if (decl.columns.size === 1 && decl.columns.has('data')) {\n row['data'] = JSON.stringify(record.data)\n } else {\n for (const [k, v] of Object.entries(record.data)) {\n if (decl.columns.has(k)) row[k] = this.serializeColumnValue(v)\n }\n }\n await this.tableInsert(scoped, row)\n }\n\n async update({ namespace, collection, id, data }: SettingsUpdateInput): Promise<void> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n const updates: Record<string, unknown> = {}\n if (decl.columns.size === 1 && decl.columns.has('data')) {\n updates['data'] = JSON.stringify(data)\n } else {\n for (const [k, v] of Object.entries(data)) {\n if (decl.columns.has(k)) updates[k] = this.serializeColumnValue(v)\n }\n }\n if (Object.keys(updates).length > 0) {\n await this.tableUpdate(scoped, { [decl.primaryKey]: id }, updates)\n }\n }\n\n async delete({ namespace, collection, key }: SettingsDeleteInput): Promise<void> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n await this.tableDelete(scoped, { [decl.primaryKey]: key })\n }\n\n async count({ namespace, collection, filter }: SettingsCountInput): Promise<number> {\n const scoped = this.scopedName(namespace, collection)\n const decl = this.requireDeclared(scoped)\n const isKvShape = decl.columns.size === 1 && decl.columns.has('data')\n const isColumn = (f: string): boolean => f === decl.primaryKey || decl.columns.has(f)\n const fieldExpr = (f: string): string => {\n if (isColumn(f)) return `\"${f}\"`\n if (isKvShape) return `json_extract(\"data\", '$.${f}')`\n return ''\n }\n let sql = `SELECT COUNT(*) AS cnt FROM \"${scoped}\"`\n const params: unknown[] = []\n if (filter?.where) {\n const clauses: string[] = []\n for (const [field, value] of Object.entries(filter.where)) {\n const expr = fieldExpr(field)\n if (!expr) continue\n clauses.push(`${expr} = ?`)\n params.push(this.serializeColumnValue(value))\n }\n if (clauses.length > 0) sql += ` WHERE ${clauses.join(' AND ')}`\n }\n const row = this.getDb()\n .prepare<unknown[], { cnt: number }>(sql)\n .get(...params)\n return row?.cnt ?? 0\n }\n\n async isEmpty({ namespace, collection }: SettingsIsEmptyInput): Promise<boolean> {\n const scoped = this.scopedName(namespace, collection)\n this.requireDeclared(scoped)\n return (await this.tableCount(scoped)) === 0\n }\n\n private async queryDeclared<T extends object = Record<string, unknown>>(\n table: string,\n decl: { readonly primaryKey: string; readonly columns: ReadonlySet<string> },\n filter: import('@camstack/types').QueryFilter | undefined,\n ): Promise<readonly SettingsRecord<T>[]> {\n // KV-shape detection: a single `data` JSON-blob column. Canonical\n // collections (users, alerts, api-keys, …) and the catalogue of\n // \"legacy\" collections fall in this branch — the structured per-\n // column path is reserved for explicitly declared schemas.\n const isKvShape = decl.columns.size === 1 && decl.columns.has('data')\n const cols = [`\"${decl.primaryKey}\"`, ...[...decl.columns].map(c => `\"${c}\"`)].join(', ')\n let sql = `SELECT ${cols} FROM \"${table}\"`\n const params: unknown[] = []\n const whereClauses: string[] = []\n\n const isColumn = (f: string): boolean => f === decl.primaryKey || decl.columns.has(f)\n /**\n * SQL expression for `field` on this table.\n * - real column → `\"field\"`\n * - KV blob field (e.g. `username` inside `data`) → `json_extract`\n * - structured table + non-column → empty string (filter dropped,\n * matches legacy structured-table behaviour)\n */\n const fieldExpr = (f: string): string => {\n if (isColumn(f)) return `\"${f}\"`\n if (isKvShape) return `json_extract(\"data\", '$.${f}')`\n return ''\n }\n\n if (filter?.where) {\n for (const [field, value] of Object.entries(filter.where)) {\n const expr = fieldExpr(field)\n if (!expr) continue\n whereClauses.push(`${expr} = ?`)\n params.push(this.serializeColumnValue(value))\n }\n }\n if (filter?.whereIn) {\n for (const [field, values] of Object.entries(filter.whereIn)) {\n const expr = fieldExpr(field)\n if (!expr) continue\n const placeholders = values.map(() => '?').join(', ')\n whereClauses.push(`${expr} IN (${placeholders})`)\n for (const v of values) params.push(this.serializeColumnValue(v))\n }\n }\n if (filter?.whereBetween) {\n for (const [field, [low, high]] of Object.entries(filter.whereBetween)) {\n const expr = fieldExpr(field)\n if (!expr) continue\n whereClauses.push(`${expr} BETWEEN ? AND ?`)\n params.push(this.serializeColumnValue(low), this.serializeColumnValue(high))\n }\n }\n if (whereClauses.length > 0) sql += ` WHERE ${whereClauses.join(' AND ')}`\n\n if (filter?.orderBy) {\n const expr = fieldExpr(filter.orderBy.field)\n if (expr) {\n const dir = filter.orderBy.direction === 'desc' ? 'DESC' : 'ASC'\n sql += ` ORDER BY ${expr} ${dir}`\n }\n }\n if (filter?.limit !== undefined) { sql += ` LIMIT ?`; params.push(filter.limit) }\n if (filter?.offset !== undefined) { sql += ` OFFSET ?`; params.push(filter.offset) }\n\n const rows = this.getDb()\n .prepare<unknown[], Record<string, unknown>>(sql)\n .all(...params)\n return rows.map(r => {\n const id = String(r[decl.primaryKey] ?? '')\n // KV-shape: unwrap the JSON blob so consumers receive\n // `{ id, data: <parsedJSON> }` directly. Without this every caller\n // in the auth chain (UserManager, ScopedTokenManager, …) gets a\n // double-wrapped record (`{ data: { data: <real> } }`) and the\n // schema parse throws on undefined fields.\n if (isKvShape) {\n const v = r['data']\n if (typeof v === 'string' && (v.startsWith('{') || v.startsWith('['))) {\n try {\n return { id, data: JSON.parse(v) as T }\n } catch {\n return { id, data: { value: v } as unknown as T }\n }\n }\n return { id, data: (v == null ? {} : { value: v }) as T }\n }\n // Structured tables: reassemble the record from typed columns and\n // include the primary key field so callers (e.g. `UserRecordSchema.parse`)\n // see the same shape they originally inserted.\n const data: Record<string, unknown> = { [decl.primaryKey]: r[decl.primaryKey] ?? null }\n for (const c of decl.columns) {\n // Auto-deserialize JSON strings back into objects. Heuristic:\n // values starting with `{` or `[` are attempted as JSON.parse,\n // fallback to the raw string on parse failure. This preserves\n // primitive column types (TEXT/INTEGER/REAL) as-is.\n const v = r[c]\n if (typeof v === 'string' && (v.startsWith('{') || v.startsWith('['))) {\n try { data[c] = JSON.parse(v) } catch { data[c] = v }\n } else {\n data[c] = v ?? null\n }\n }\n return { id, data: data as T }\n })\n }\n\n // ---------------------------------------------------------------------------\n // Legacy SettingsStore compatibility (used by ConfigManager.setSettingsStore)\n // ---------------------------------------------------------------------------\n\n /** Get a system setting by dot-notation key */\n getSystem(key: string): unknown {\n this.requireDeclared('system-settings')\n const row = this.getDb()\n .prepare<[string], { data: string }>('SELECT data FROM \"system-settings\" WHERE id = ?')\n .get(key)\n if (!row) return undefined\n return JSON.parse(row.data)\n }\n\n /** Set a system setting */\n setSystem(key: string, value: unknown): void {\n this.requireDeclared('system-settings')\n this.getDb()\n .prepare('INSERT INTO \"system-settings\" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data')\n .run(key, JSON.stringify(value))\n }\n\n /** Get all system settings as flat key-value */\n getAllSystem(): Record<string, unknown> {\n this.requireDeclared('system-settings')\n const rows = this.getDb()\n .prepare<[], { id: string; data: string }>('SELECT id, data FROM \"system-settings\"')\n .all()\n return Object.fromEntries(rows.map(r => [r.id, JSON.parse(r.data)]))\n }\n\n /** Get all settings for an addon */\n getAllAddon(addonId: string): Record<string, unknown> {\n this.requireDeclared('addon-settings')\n const rows = this.getDb()\n .prepare<[string], { id: string; data: string }>('SELECT id, data FROM \"addon-settings\" WHERE json_extract(data, \\'$.addonId\\') = ?')\n .all(addonId)\n if (rows.length === 0) return {}\n // addon-settings stores all keys for an addon as separate rows with id\n // = \"addonId.key\" and data = `{ addonId, key, value }`. Detect the\n // wrapper shape explicitly: a corrupted legacy row that lacks the\n // `value` field would otherwise be returned as the wrapper itself\n // (the previous `parsed.value ?? parsed` fallback masked the bug as a\n // valid value, breaking downstream type-narrowed reads like\n // `pipelineDefaults`). Skip those rows entirely so the caller sees\n // them as missing.\n const result: Record<string, unknown> = {}\n for (const row of rows) {\n const parsed = parseRowData(row.data)\n const key = row.id.startsWith(`${addonId}.`) ? row.id.slice(addonId.length + 1) : row.id\n const isWrapper = parsed !== null\n && typeof parsed === 'object'\n && 'addonId' in parsed\n && 'key' in (parsed as Record<string, unknown>)\n if (isWrapper) {\n const wrapper = parsed as { readonly addonId: string; readonly key: string; readonly value?: unknown }\n if ('value' in wrapper) {\n result[key] = wrapper.value\n }\n // else: corrupted wrapper-without-value — skip\n } else {\n result[key] = parsed\n }\n }\n return result\n }\n\n /** Bulk-set all settings for an addon */\n setAllAddon(addonId: string, config: Record<string, unknown>): void {\n this.requireDeclared('addon-settings')\n const db = this.getDb()\n const deleteStmt = db.prepare('DELETE FROM \"addon-settings\" WHERE id LIKE ? || \\'%\\'')\n const insertStmt = db.prepare('INSERT INTO \"addon-settings\" (id, data) VALUES (?, ?)')\n db.transaction(() => {\n deleteStmt.run(`${addonId}.`)\n for (const [key, value] of Object.entries(config)) {\n // Skip undefined values: the JSON wrapper `{addonId, key, value}`\n // serializes as `{addonId, key}` when value is undefined, which\n // round-trips as a corrupted \"wrapper without value\" row.\n if (value === undefined) continue\n insertStmt.run(`${addonId}.${key}`, JSON.stringify({ addonId, key, value }))\n }\n })()\n }\n\n /** Get all settings for a provider */\n getAllProvider(providerId: string): Record<string, unknown> {\n return this.getAllScoped('provider-settings', providerId)\n }\n\n /** Set a provider setting */\n setProvider(providerId: string, key: string, value: unknown): void {\n this.setScopedKey('provider-settings', providerId, key, value)\n }\n\n /** Get all settings for a device */\n getAllDevice(deviceId: string): Record<string, unknown> {\n return this.getAllScoped('device-settings', deviceId)\n }\n\n /** Set a device setting */\n setDevice(deviceId: string, key: string, value: unknown): void {\n this.setScopedKey('device-settings', deviceId, key, value)\n }\n\n // ── Addon-device settings (per-device overrides of an addon's config) ──\n //\n // Storage key format: \"<addonId>:<deviceId>.<field>\" inside the\n // \"addon-device-settings\" collection. Uses the same JSON-blob layout\n // as the other scoped collections so no schema change is required.\n\n getAddonDevice(addonId: string, deviceId: string): Record<string, unknown> {\n return this.getAllScoped('addon-device-settings', `${addonId}:${deviceId}`)\n }\n\n setAddonDevice(addonId: string, deviceId: string, values: Record<string, unknown>): void {\n this.requireDeclared('addon-device-settings')\n const db = this.getDb()\n const prefix = `${addonId}:${deviceId}.`\n const deleteStmt = db.prepare(`DELETE FROM \"addon-device-settings\" WHERE id LIKE ? || '%'`)\n const insertStmt = db.prepare(\n `INSERT INTO \"addon-device-settings\" (id, data) VALUES (?, ?)\n ON CONFLICT(id) DO UPDATE SET data = excluded.data`,\n )\n db.transaction(() => {\n deleteStmt.run(prefix)\n for (const [key, value] of Object.entries(values)) {\n insertStmt.run(\n `${prefix}${key}`,\n JSON.stringify({ addonId, deviceId, key, value }),\n )\n }\n })()\n }\n\n clearAddonDevice(addonId: string, deviceId: string): void {\n this.requireDeclared('addon-device-settings')\n const prefix = `${addonId}:${deviceId}.`\n this.getDb()\n .prepare(`DELETE FROM \"addon-device-settings\" WHERE id LIKE ? || '%'`)\n .run(prefix)\n }\n\n /** Seed system-settings with runtime defaults (first boot) */\n private async seedDefaults(): Promise<void> {\n this.requireDeclared('system-settings')\n const insert = this.getDb().prepare(\n 'INSERT OR IGNORE INTO \"system-settings\" (id, data) VALUES (?, ?)',\n )\n this.getDb().transaction(() => {\n for (const [key, value] of Object.entries(this.runtimeDefaults)) {\n insert.run(key, JSON.stringify(value))\n }\n })()\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Expose the raw better-sqlite3 Database instance for components that\n * need direct SQL access (e.g. DeviceStore, ConfigStore).\n * Returns null if the backend has not been initialized yet.\n */\n getDatabase(): Database.Database | null {\n return this.db\n }\n\n private getDb(): Database.Database {\n if (!this.db) throw new Error('SqliteSettingsBackend not initialized — call initialize() first')\n return this.db\n }\n\n private getAllScoped(collection: string, scopeId: string): Record<string, unknown> {\n this.requireDeclared(collection)\n const rows = this.getDb()\n .prepare<[string], { id: string; data: string }>(`SELECT id, data FROM \"${collection}\" WHERE id LIKE ? || '.%'`)\n .all(scopeId)\n const result: Record<string, unknown> = {}\n for (const row of rows) {\n const key = row.id.slice(scopeId.length + 1)\n const parsed = parseRowData(row.data)\n result[key] = parsed.value ?? parsed\n }\n return result\n }\n\n private setScopedKey(collection: string, scopeId: string, key: string, value: unknown): void {\n this.requireDeclared(collection)\n this.getDb()\n .prepare(`INSERT INTO \"${collection}\" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`)\n .run(`${scopeId}.${key}`, JSON.stringify({ scopeId, key, value }))\n }\n\n // ── Declared collections (typed SQL tables behind the generic cap) ──\n\n private scopedName(namespace: string | undefined, collection: string): string {\n return namespace ? `${namespace}:${collection}` : collection\n }\n\n async declareCollection(input: {\n namespace?: string\n collection: string\n columns: readonly CollectionColumn[]\n indexes?: readonly CollectionIndex[]\n }): Promise<void> {\n const table = this.scopedName(input.namespace, input.collection)\n if (this.declaredCollections.has(table)) return\n\n // Ensure id column is explicit — every declared collection needs a\n // primary key that maps to the cap's record.id.\n const hasId = input.columns.some(c => c.primaryKey === true)\n const columns = hasId\n ? input.columns\n : [{ name: 'id', type: 'TEXT' as const, primaryKey: true, notNull: true }, ...input.columns]\n\n // Translate to the TableSchema shape ensureTable expects.\n const schema: TableSchema = {\n columns: columns.map(c => ({\n name: c.name,\n // SQLite treats JSON as TEXT; we serialize object values on write\n // and parse on read.\n type: c.type === 'JSON' ? 'TEXT' : c.type,\n ...(c.primaryKey !== undefined ? { primaryKey: c.primaryKey } : {}),\n ...(c.notNull !== undefined ? { notNull: c.notNull } : {}),\n ...(c.unique !== undefined ? { unique: c.unique } : {}),\n })),\n ...(input.indexes ? { indexes: input.indexes.map(i => ({\n name: i.name,\n columns: i.columns,\n ...(i.unique !== undefined ? { unique: i.unique } : {}),\n })) } : {}),\n }\n await this.ensureTable(table, schema)\n\n const pkCol = columns.find(c => c.primaryKey === true)\n const primaryKey = pkCol ? pkCol.name : 'id'\n const columnNames = new Set(columns.filter(c => c.name !== primaryKey).map(c => c.name))\n this.declaredCollections.set(table, { primaryKey, columns: columnNames })\n }\n\n /** Serialise per-column values for SQL binding: objects → JSON, booleans → 0/1. */\n private serializeColumnValue(v: unknown): unknown {\n if (v === null || v === undefined) return v ?? null\n if (typeof v === 'boolean') return v ? 1 : 0\n if (typeof v === 'object') return JSON.stringify(v)\n return v\n }\n\n // ── Structured table operations ────────────────────────────────────\n\n async ensureTable(table: string, schema: TableSchema): Promise<void> {\n if (this.structuredTables.has(table)) return\n\n const colDefs = schema.columns.map(col => {\n const parts = [`\"${col.name}\" ${col.type}`]\n if (col.primaryKey) parts.push('PRIMARY KEY')\n if (col.notNull) parts.push('NOT NULL')\n if (col.unique) parts.push('UNIQUE')\n if (col.defaultValue !== undefined) {\n parts.push(`DEFAULT ${typeof col.defaultValue === 'string' ? `'${col.defaultValue}'` : col.defaultValue === null ? 'NULL' : String(col.defaultValue)}`)\n }\n return parts.join(' ')\n })\n\n // Drop a pre-existing legacy `(id, data)` shape when the caller\n // is now declaring a structured schema. Without this, an earlier\n // `ensureTable(table)` call (no schema → legacy KV table) wins\n // because `CREATE TABLE IF NOT EXISTS` is a no-op when the table\n // is already there with a different shape, and every subsequent\n // typed query throws \"no such column: deviceId\" / \"...id\".\n // Destructive — the caller (analytics tracks/events/media) is the\n // only consumer of the table, so the operator only sees a one-time\n // wipe of yet-unmigrated observability rings.\n type ColInfoRow = { name: string }\n const existingCols = this.getDb()\n .prepare(`PRAGMA table_info(\"${table}\")`)\n .all() as ColInfoRow[]\n if (existingCols.length > 0) {\n const existingNames = new Set(existingCols.map(c => c.name))\n const declaredNames = new Set(schema.columns.map(c => c.name))\n const missingDeclared = schema.columns.some(c => !existingNames.has(c.name))\n const isLegacyKv =\n existingCols.length <= 2 &&\n existingNames.has('data') &&\n !declaredNames.has('data')\n if (isLegacyKv || missingDeclared) {\n try { this.getDb().exec(`DROP TABLE \"${table}\"`) } catch { /* best-effort */ }\n }\n }\n\n this.getDb().exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs.join(', ')})`)\n\n if (schema.indexes) {\n for (const idx of schema.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : ''\n const cols = idx.columns.map(c => `\"${c}\"`).join(', ')\n this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS \"${idx.name}\" ON \"${table}\" (${cols})`)\n }\n }\n\n this.structuredTables.add(table)\n }\n\n async tableInsert(table: string, row: Record<string, unknown>): Promise<void> {\n const keys = Object.keys(row)\n const cols = keys.map(k => `\"${k}\"`).join(', ')\n const placeholders = keys.map(() => '?').join(', ')\n const values = keys.map(k => {\n const v = row[k]\n return typeof v === 'object' && v !== null ? JSON.stringify(v) : v\n })\n this.getDb().prepare(`INSERT INTO \"${table}\" (${cols}) VALUES (${placeholders})`).run(...values)\n }\n\n async tableUpdate(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number> {\n const setClauses: string[] = []\n const setValues: unknown[] = []\n for (const [k, v] of Object.entries(updates)) {\n setClauses.push(`\"${k}\" = ?`)\n setValues.push(typeof v === 'object' && v !== null ? JSON.stringify(v) : v)\n }\n\n const { whereSql, whereValues } = this.buildWhere(filter)\n const result = this.getDb()\n .prepare(`UPDATE \"${table}\" SET ${setClauses.join(', ')}${whereSql}`)\n .run(...setValues, ...whereValues)\n return result.changes\n }\n\n async tableDelete(table: string, filter: Record<string, unknown>): Promise<number> {\n const { whereSql, whereValues } = this.buildWhere(filter)\n const result = this.getDb().prepare(`DELETE FROM \"${table}\"${whereSql}`).run(...whereValues)\n return result.changes\n }\n\n async tableQuery(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]> {\n let sql = `SELECT * FROM \"${table}\"`\n const values: unknown[] = []\n\n if (options?.where) {\n const { whereSql, whereValues } = this.buildWhere(options.where)\n sql += whereSql\n values.push(...whereValues)\n }\n\n if (options?.orderBy) {\n sql += ` ORDER BY \"${options.orderBy.field}\" ${options.orderBy.direction === 'desc' ? 'DESC' : 'ASC'}`\n }\n\n if (options?.limit !== undefined) {\n sql += ` LIMIT ?`\n values.push(options.limit)\n }\n\n if (options?.offset !== undefined) {\n sql += ` OFFSET ?`\n values.push(options.offset)\n }\n\n const rows = this.getDb().prepare(sql).all(...values)\n return rows.flatMap((r) => asJsonObject(r) ? [asJsonObject(r)!] : [])\n }\n\n async tableGet(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null> {\n const { whereSql, whereValues } = this.buildWhere(filter)\n const row = this.getDb().prepare(`SELECT * FROM \"${table}\"${whereSql} LIMIT 1`).get(...whereValues)\n return asJsonObject(row)\n }\n\n async tableCount(table: string, filter?: Record<string, unknown>): Promise<number> {\n let sql = `SELECT COUNT(*) as count FROM \"${table}\"`\n const values: unknown[] = []\n\n if (filter) {\n const { whereSql, whereValues } = this.buildWhere(filter)\n sql += whereSql\n values.push(...whereValues)\n }\n\n const row = asJsonObject(this.getDb().prepare(sql).get(...values))\n return typeof row?.count === 'number' ? row.count : 0\n }\n\n private buildWhere(filter: Record<string, unknown>): { whereSql: string; whereValues: unknown[] } {\n const conditions: string[] = []\n const values: unknown[] = []\n for (const [k, v] of Object.entries(filter)) {\n conditions.push(`\"${k}\" = ?`)\n values.push(typeof v === 'object' && v !== null ? JSON.stringify(v) : v)\n }\n const whereSql = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : ''\n return { whereSql, whereValues: values }\n }\n}\n\nexport default SqliteSettingsBackend\n"],"mappings":"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","names":[]}
|
package/dist/chunk-GL4OOB25.mjs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// src/process/resource-monitor.ts
|
|
2
|
-
import { execFile } from "child_process";
|
|
3
|
-
function execPsForPids(pids) {
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
execFile("ps", ["-o", "pid=,pcpu=,rss=", "-p", pids.join(",")], {
|
|
6
|
-
timeout: 5e3
|
|
7
|
-
}, (err, stdout) => {
|
|
8
|
-
if (err) return reject(err);
|
|
9
|
-
resolve(stdout);
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
function parsePsOutput(stdout) {
|
|
14
|
-
const result = /* @__PURE__ */ new Map();
|
|
15
|
-
for (const line of stdout.trim().split("\n")) {
|
|
16
|
-
const trimmed = line.trim();
|
|
17
|
-
if (!trimmed) continue;
|
|
18
|
-
const parts = trimmed.split(/\s+/);
|
|
19
|
-
if (parts.length < 3) continue;
|
|
20
|
-
const pid = parseInt(parts[0], 10);
|
|
21
|
-
const cpu = parseFloat(parts[1]);
|
|
22
|
-
const rssKb = parseInt(parts[2], 10);
|
|
23
|
-
if (isNaN(pid) || isNaN(cpu) || isNaN(rssKb)) continue;
|
|
24
|
-
result.set(pid, {
|
|
25
|
-
cpu: Math.round(cpu * 10) / 10,
|
|
26
|
-
memory: rssKb * 1024
|
|
27
|
-
// KB → bytes
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return result;
|
|
31
|
-
}
|
|
32
|
-
async function getPidStats(pids) {
|
|
33
|
-
const pidArray = typeof pids === "number" ? [pids] : [...pids];
|
|
34
|
-
if (pidArray.length === 0) return /* @__PURE__ */ new Map();
|
|
35
|
-
try {
|
|
36
|
-
const stdout = await execPsForPids(pidArray);
|
|
37
|
-
return parsePsOutput(stdout);
|
|
38
|
-
} catch {
|
|
39
|
-
return /* @__PURE__ */ new Map();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
async function getSinglePidStats(pid) {
|
|
43
|
-
const stats = await getPidStats([pid]);
|
|
44
|
-
return stats.get(pid) ?? null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export {
|
|
48
|
-
getPidStats,
|
|
49
|
-
getSinglePidStats
|
|
50
|
-
};
|
|
51
|
-
//# sourceMappingURL=chunk-GL4OOB25.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/process/resource-monitor.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\n\n// ===========================================================================\n// Per-PID resource usage — zero dependencies, uses ps (macOS/Linux).\n//\n// These are process-management utilities used by:\n// - native-metrics-provider.ts (system metrics collection)\n// - addon-registry.service.ts (addon process stats)\n//\n// System-wide metrics (CPU, memory, disk, network, GPU, temp, PSI)\n// have moved to @camstack/addon-metrics-native via the IMetricsProvider\n// interface defined in @camstack/types.\n// ===========================================================================\n\nexport interface PidStats {\n cpu: number // 0–100 percentage\n memory: number // RSS in bytes\n}\n\nfunction execPsForPids(pids: readonly number[]): Promise<string> {\n return new Promise((resolve, reject) => {\n execFile('ps', ['-o', 'pid=,pcpu=,rss=', '-p', pids.join(',')], {\n timeout: 5000,\n }, (err, stdout) => {\n if (err) return reject(err)\n resolve(stdout)\n })\n })\n}\n\nfunction parsePsOutput(stdout: string): ReadonlyMap<number, PidStats> {\n const result = new Map<number, PidStats>()\n for (const line of stdout.trim().split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n const parts = trimmed.split(/\\s+/)\n if (parts.length < 3) continue\n const pid = parseInt(parts[0]!, 10)\n const cpu = parseFloat(parts[1]!)\n const rssKb = parseInt(parts[2]!, 10)\n if (isNaN(pid) || isNaN(cpu) || isNaN(rssKb)) continue\n result.set(pid, {\n cpu: Math.round(cpu * 10) / 10,\n memory: rssKb * 1024, // KB → bytes\n })\n }\n return result\n}\n\n/**\n * Get CPU% and memory RSS for one or more PIDs.\n * Returns a map of pid → stats. Missing/dead PIDs are silently omitted.\n */\nexport async function getPidStats(pids: number | readonly number[]): Promise<ReadonlyMap<number, PidStats>> {\n const pidArray = typeof pids === 'number' ? [pids] : [...pids]\n if (pidArray.length === 0) return new Map()\n\n try {\n const stdout = await execPsForPids(pidArray)\n return parsePsOutput(stdout)\n } catch {\n return new Map()\n }\n}\n\n/** Get CPU% and memory RSS for a single PID. Returns null if dead. */\nexport async function getSinglePidStats(pid: number): Promise<PidStats | null> {\n const stats = await getPidStats([pid])\n return stats.get(pid) ?? null\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AAmBzB,SAAS,cAAc,MAA0C;AAC/D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,aAAS,MAAM,CAAC,MAAM,mBAAmB,MAAM,KAAK,KAAK,GAAG,CAAC,GAAG;AAAA,MAC9D,SAAS;AAAA,IACX,GAAG,CAAC,KAAK,WAAW;AAClB,UAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,cAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,cAAc,QAA+C;AACpE,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,QAAQ,OAAO,KAAK,EAAE,MAAM,IAAI,GAAG;AAC5C,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,QAAI,MAAM,SAAS,EAAG;AACtB,UAAM,MAAM,SAAS,MAAM,CAAC,GAAI,EAAE;AAClC,UAAM,MAAM,WAAW,MAAM,CAAC,CAAE;AAChC,UAAM,QAAQ,SAAS,MAAM,CAAC,GAAI,EAAE;AACpC,QAAI,MAAM,GAAG,KAAK,MAAM,GAAG,KAAK,MAAM,KAAK,EAAG;AAC9C,WAAO,IAAI,KAAK;AAAA,MACd,KAAK,KAAK,MAAM,MAAM,EAAE,IAAI;AAAA,MAC5B,QAAQ,QAAQ;AAAA;AAAA,IAClB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAMA,eAAsB,YAAY,MAA0E;AAC1G,QAAM,WAAW,OAAO,SAAS,WAAW,CAAC,IAAI,IAAI,CAAC,GAAG,IAAI;AAC7D,MAAI,SAAS,WAAW,EAAG,QAAO,oBAAI,IAAI;AAE1C,MAAI;AACF,UAAM,SAAS,MAAM,cAAc,QAAQ;AAC3C,WAAO,cAAc,MAAM;AAAA,EAC7B,QAAQ;AACN,WAAO,oBAAI,IAAI;AAAA,EACjB;AACF;AAGA,eAAsB,kBAAkB,KAAuC;AAC7E,QAAM,QAAQ,MAAM,YAAY,CAAC,GAAG,CAAC;AACrC,SAAO,MAAM,IAAI,GAAG,KAAK;AAC3B;","names":[]}
|
package/dist/chunk-KDG2NTDB.mjs
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatLogLine
|
|
3
|
-
} from "./chunk-2F76X6NL.mjs";
|
|
4
|
-
|
|
5
|
-
// src/builtins/winston-logging/winston-logging.addon.ts
|
|
6
|
-
import { BaseAddon, logDestinationCapability } from "@camstack/types";
|
|
7
|
-
|
|
8
|
-
// src/builtins/winston-logging/winston-destination.ts
|
|
9
|
-
import * as winston from "winston";
|
|
10
|
-
import DailyRotateFile from "winston-daily-rotate-file";
|
|
11
|
-
import * as path from "path";
|
|
12
|
-
var WinstonDestination = class {
|
|
13
|
-
logger = null;
|
|
14
|
-
async initialize(config) {
|
|
15
|
-
const {
|
|
16
|
-
level = "info",
|
|
17
|
-
retentionDays = 30,
|
|
18
|
-
logsDir = path.join("camstack-data", "logs")
|
|
19
|
-
} = config ?? {};
|
|
20
|
-
const consoleFormat = winston.format.printf((info) => {
|
|
21
|
-
const rendered = info._rendered;
|
|
22
|
-
return typeof rendered === "string" ? rendered : String(info.message);
|
|
23
|
-
});
|
|
24
|
-
const stripRendered = winston.format((info) => {
|
|
25
|
-
delete info["_rendered"];
|
|
26
|
-
return info;
|
|
27
|
-
});
|
|
28
|
-
const fileFormat = winston.format.combine(
|
|
29
|
-
winston.format.timestamp(),
|
|
30
|
-
stripRendered(),
|
|
31
|
-
winston.format.json()
|
|
32
|
-
);
|
|
33
|
-
void consoleFormat;
|
|
34
|
-
this.logger = winston.createLogger({
|
|
35
|
-
level,
|
|
36
|
-
transports: [
|
|
37
|
-
new DailyRotateFile({
|
|
38
|
-
dirname: logsDir,
|
|
39
|
-
filename: "camstack-%DATE%.log",
|
|
40
|
-
datePattern: "YYYY-MM-DD",
|
|
41
|
-
maxFiles: `${retentionDays}d`,
|
|
42
|
-
format: fileFormat
|
|
43
|
-
})
|
|
44
|
-
]
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
write(entry) {
|
|
48
|
-
if (!this.logger) return;
|
|
49
|
-
const ts = entry.timestamp instanceof Date ? entry.timestamp.toISOString() : String(entry.timestamp);
|
|
50
|
-
const rendered = formatLogLine(entry, { colorize: true });
|
|
51
|
-
this.logger.log({
|
|
52
|
-
level: entry.level,
|
|
53
|
-
message: entry.message,
|
|
54
|
-
_rendered: rendered,
|
|
55
|
-
timestamp: ts,
|
|
56
|
-
scope: entry.scope,
|
|
57
|
-
...entry.tags ? { tags: entry.tags } : {},
|
|
58
|
-
...entry.meta ? { meta: entry.meta } : {}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
async query(_filter) {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
async shutdown() {
|
|
65
|
-
if (!this.logger) return;
|
|
66
|
-
await new Promise((resolve) => {
|
|
67
|
-
this.logger.on("finish", resolve);
|
|
68
|
-
this.logger.end();
|
|
69
|
-
});
|
|
70
|
-
this.logger = null;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// src/builtins/winston-logging/winston-logging.addon.ts
|
|
75
|
-
var WinstonLoggingAddon = class extends BaseAddon {
|
|
76
|
-
destination = null;
|
|
77
|
-
constructor() {
|
|
78
|
-
super({ level: "info", retentionDays: 30 });
|
|
79
|
-
}
|
|
80
|
-
async onInitialize() {
|
|
81
|
-
const logsDir = await this.ctx.api.storage.resolve.query({ location: "logs", relativePath: "" }).catch(() => "camstack-data/logs");
|
|
82
|
-
this.destination = new WinstonDestination();
|
|
83
|
-
await this.destination.initialize({ ...this.config, logsDir });
|
|
84
|
-
this.ctx.logger.info("Winston logging initialized");
|
|
85
|
-
return [{ capability: logDestinationCapability, provider: this.destination }];
|
|
86
|
-
}
|
|
87
|
-
async onShutdown() {
|
|
88
|
-
await this.destination?.shutdown();
|
|
89
|
-
}
|
|
90
|
-
getDestination() {
|
|
91
|
-
if (!this.destination) throw new Error("Winston not initialized");
|
|
92
|
-
return this.destination;
|
|
93
|
-
}
|
|
94
|
-
globalSettingsSchema() {
|
|
95
|
-
return this.schema({
|
|
96
|
-
sections: [
|
|
97
|
-
{
|
|
98
|
-
id: "winston-logging",
|
|
99
|
-
title: "Logging",
|
|
100
|
-
description: "Global log verbosity + retention for rotated log files on this node.",
|
|
101
|
-
columns: 2,
|
|
102
|
-
fields: [
|
|
103
|
-
this.field({
|
|
104
|
-
type: "select",
|
|
105
|
-
key: "level",
|
|
106
|
-
label: "Log Level",
|
|
107
|
-
default: "info",
|
|
108
|
-
options: [
|
|
109
|
-
{ value: "debug", label: "Debug" },
|
|
110
|
-
{ value: "info", label: "Info" },
|
|
111
|
-
{ value: "warn", label: "Warn" },
|
|
112
|
-
{ value: "error", label: "Error" }
|
|
113
|
-
]
|
|
114
|
-
}),
|
|
115
|
-
this.field({
|
|
116
|
-
type: "number",
|
|
117
|
-
key: "retentionDays",
|
|
118
|
-
label: "Retention (days)",
|
|
119
|
-
description: "Number of days to keep rotated log files before deletion",
|
|
120
|
-
min: 1,
|
|
121
|
-
max: 365,
|
|
122
|
-
step: 1,
|
|
123
|
-
default: 30,
|
|
124
|
-
unit: "days"
|
|
125
|
-
})
|
|
126
|
-
]
|
|
127
|
-
}
|
|
128
|
-
]
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export {
|
|
134
|
-
WinstonDestination,
|
|
135
|
-
WinstonLoggingAddon
|
|
136
|
-
};
|
|
137
|
-
//# sourceMappingURL=chunk-KDG2NTDB.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/builtins/winston-logging/winston-logging.addon.ts","../src/builtins/winston-logging/winston-destination.ts"],"sourcesContent":["import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, logDestinationCapability } from '@camstack/types'\nimport { WinstonDestination } from './winston-destination.js'\n\ninterface WinstonConfig {\n readonly level: string\n readonly retentionDays: number\n}\n\n/**\n * Winston logging addon — rotated log files + console output.\n * Settings appear under Cluster → NodeDetail → Settings.\n */\nexport class WinstonLoggingAddon extends BaseAddon<WinstonConfig> {\n private destination: WinstonDestination | null = null\n\n constructor() {\n super({ level: 'info', retentionDays: 30 })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const logsDir = await this.ctx.api.storage.resolve.query({ location: 'logs', relativePath: '' })\n .catch(() => 'camstack-data/logs')\n this.destination = new WinstonDestination()\n await this.destination.initialize({ ...this.config, logsDir })\n this.ctx.logger.info('Winston logging initialized')\n return [{ capability: logDestinationCapability, provider: this.destination }]\n }\n\n protected async onShutdown(): Promise<void> {\n await this.destination?.shutdown()\n }\n\n getDestination(): WinstonDestination {\n if (!this.destination) throw new Error('Winston not initialized')\n return this.destination\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'winston-logging',\n title: 'Logging',\n description: 'Global log verbosity + retention for rotated log files on this node.',\n columns: 2,\n fields: [\n this.field({\n type: 'select',\n key: 'level',\n label: 'Log Level',\n default: 'info',\n options: [\n { value: 'debug', label: 'Debug' },\n { value: 'info', label: 'Info' },\n { value: 'warn', label: 'Warn' },\n { value: 'error', label: 'Error' },\n ],\n }),\n this.field({\n type: 'number',\n key: 'retentionDays',\n label: 'Retention (days)',\n description: 'Number of days to keep rotated log files before deletion',\n min: 1,\n max: 365,\n step: 1,\n default: 30,\n unit: 'days',\n }),\n ],\n },\n ],\n })\n }\n}\n","import * as winston from 'winston'\nimport DailyRotateFile from 'winston-daily-rotate-file'\nimport * as path from 'node:path'\nimport type { ILogDestination, LogEntry, LogFilter } from '@camstack/types'\nimport { formatLogLine } from '../../logging/formatter.js'\n\ninterface WinstonConfig {\n readonly level: string\n readonly retentionDays: number\n /** Resolved absolute path to the logs directory (replaces the old dataPath field). */\n readonly logsDir: string\n}\n\n/**\n * Rotated-file log destination. Console output goes through the shared\n * `formatLogLine` so every console sink (Winston, ConsoleDestination,\n * HubForwarderDestination) emits identical lines. File output keeps a\n * structured JSON shape so log tooling can query tags/meta.\n */\nexport class WinstonDestination implements ILogDestination {\n private logger: winston.Logger | null = null\n\n async initialize(config?: WinstonConfig): Promise<void> {\n const {\n level = 'info',\n retentionDays = 30,\n logsDir = path.join('camstack-data', 'logs'),\n } = config ?? {}\n\n // Console: canonical single-line formatter — `_rendered` already\n // carries level-based ANSI colour codes (see `formatLogLine` in\n // `@camstack/core/logging/formatter.ts`). Using Winston's colorize\n // here would just decorate unused `info.level` and leave the\n // rendered line uncoloured.\n const consoleFormat = winston.format.printf((info) => {\n const rendered = (info as unknown as { _rendered?: string })._rendered\n return typeof rendered === 'string' ? rendered : String(info.message)\n })\n\n // File: structured JSON. Strip `_rendered` first — it carries ANSI\n // escape codes intended for the terminal and has no place in a\n // rotated-file store used for ops queries.\n const stripRendered = winston.format((info) => {\n delete (info as Record<string, unknown>)['_rendered']\n return info\n })\n const fileFormat = winston.format.combine(\n winston.format.timestamp(),\n stripRendered(),\n winston.format.json(),\n )\n\n // File-only winston. Stdout is owned by `ConsoleDestination`\n // (the `console-logging` builtin addon) — adding a Winston console\n // transport here would re-render every log line a second time on\n // the hub terminal, which is the historic cause of the duplicate\n // output operators have been seeing. The `consoleFormat` printf\n // above stays wired in so a future opt-in transport can use it\n // without re-deriving the layout.\n void consoleFormat\n this.logger = winston.createLogger({\n level,\n transports: [\n new DailyRotateFile({\n dirname: logsDir,\n filename: 'camstack-%DATE%.log',\n datePattern: 'YYYY-MM-DD',\n maxFiles: `${retentionDays}d`,\n format: fileFormat,\n }),\n ],\n })\n }\n\n write(entry: LogEntry): void {\n if (!this.logger) return\n\n // Moleculer JSON serialization converts Date → string; normalize back\n const ts = entry.timestamp instanceof Date\n ? entry.timestamp.toISOString()\n : String(entry.timestamp)\n // Render once with colours for the console transport; file transport\n // strips `_rendered` via the format chain above so disk stays plain.\n const rendered = formatLogLine(entry, { colorize: true })\n\n this.logger.log({\n level: entry.level,\n message: entry.message,\n _rendered: rendered,\n timestamp: ts,\n scope: entry.scope,\n ...(entry.tags ? { tags: entry.tags } : {}),\n ...(entry.meta ? { meta: entry.meta } : {}),\n })\n }\n\n async query(_filter: LogFilter): Promise<readonly LogEntry[]> {\n // File-based log querying is not implemented; use structured storage for log queries\n return []\n }\n\n async shutdown(): Promise<void> {\n if (!this.logger) return\n\n await new Promise<void>((resolve) => {\n this.logger!.on('finish', resolve)\n this.logger!.end()\n })\n this.logger = null\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-NRBQWBDM.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
// src/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.ts
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
5
|
-
import {
|
|
6
|
-
BaseAddon,
|
|
7
|
-
EventCategory,
|
|
8
|
-
addonPagesCapability,
|
|
9
|
-
errMsg
|
|
10
|
-
} from "@camstack/types";
|
|
11
|
-
var RETRY_BACKOFF_MS = [500, 1500, 4e3];
|
|
12
|
-
var AddonPagesAggregatorAddon = class extends BaseAddon {
|
|
13
|
-
id = "addon-pages-aggregator";
|
|
14
|
-
resolvedPaths = null;
|
|
15
|
-
/**
|
|
16
|
-
* Last successful `listPages()` snapshot per source. Used as the
|
|
17
|
-
* "stale-but-valid" fallback when a source transiently fails — drops
|
|
18
|
-
* happen often enough during boot (Moleculer service-discovery
|
|
19
|
-
* window) that swallowing the error and returning empty leaves the
|
|
20
|
-
* sidebar with nothing for several seconds. Keeping the previous
|
|
21
|
-
* good entry means a flake is invisible to the operator.
|
|
22
|
-
*/
|
|
23
|
-
lastGood = /* @__PURE__ */ new Map();
|
|
24
|
-
/** In-flight retry guards keyed by sourceId. Avoids double-scheduling. */
|
|
25
|
-
retryTimers = /* @__PURE__ */ new Map();
|
|
26
|
-
constructor() {
|
|
27
|
-
super({});
|
|
28
|
-
}
|
|
29
|
-
async onInitialize() {
|
|
30
|
-
this.resolvedPaths = await this.resolvePaths();
|
|
31
|
-
const provider = {
|
|
32
|
-
listPages: async () => this.aggregate()
|
|
33
|
-
};
|
|
34
|
-
this.ctx.logger.info("Initialized \u2014 aggregating addon-pages-source providers");
|
|
35
|
-
return [{ capability: addonPagesCapability, provider }];
|
|
36
|
-
}
|
|
37
|
-
async onShutdown() {
|
|
38
|
-
for (const t of this.retryTimers.values()) clearTimeout(t);
|
|
39
|
-
this.retryTimers.clear();
|
|
40
|
-
this.lastGood.clear();
|
|
41
|
-
}
|
|
42
|
-
// ── Aggregation ───────────────────────────────────────────────────
|
|
43
|
-
async aggregate() {
|
|
44
|
-
const sources = this.capabilities?.getCollection("addon-pages-source") ?? [];
|
|
45
|
-
const out = [];
|
|
46
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
47
|
-
for (const source of sources) {
|
|
48
|
-
seenIds.add(source.id);
|
|
49
|
-
try {
|
|
50
|
-
const pages = await Promise.resolve(source.listPages());
|
|
51
|
-
const enriched = pages.map((page) => ({
|
|
52
|
-
addonId: source.id,
|
|
53
|
-
page,
|
|
54
|
-
bundleUrl: this.makeBundleUrl(source.id, page.bundle)
|
|
55
|
-
}));
|
|
56
|
-
for (const item of enriched) out.push(item);
|
|
57
|
-
this.lastGood.set(source.id, enriched);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
const message = errMsg(err);
|
|
60
|
-
this.ctx.logger.warn("addon-pages-source provider failed", {
|
|
61
|
-
meta: { sourceId: source.id, error: message }
|
|
62
|
-
});
|
|
63
|
-
const cached = this.lastGood.get(source.id);
|
|
64
|
-
if (cached !== void 0) {
|
|
65
|
-
for (const item of cached) out.push(item);
|
|
66
|
-
this.ctx.logger.info("addon-pages-source falling back to cached snapshot", {
|
|
67
|
-
meta: { sourceId: source.id, cachedPages: cached.length }
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
this.scheduleRetry(source.id);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
for (const cachedId of this.lastGood.keys()) {
|
|
74
|
-
if (!seenIds.has(cachedId)) this.lastGood.delete(cachedId);
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
}
|
|
78
|
-
// ── Retry on transient Moleculer race ─────────────────────────────
|
|
79
|
-
/**
|
|
80
|
-
* Schedule background re-call of `listPages()` on a source that just
|
|
81
|
-
* failed. Walks `RETRY_BACKOFF_MS` from index 0 — each successful
|
|
82
|
-
* call updates the cache and emits `AddonPageReady` so the admin UI
|
|
83
|
-
* invalidates its query. Stops on first success or after the schedule
|
|
84
|
-
* is exhausted.
|
|
85
|
-
*/
|
|
86
|
-
scheduleRetry(sourceId, attempt = 0) {
|
|
87
|
-
if (attempt >= RETRY_BACKOFF_MS.length) return;
|
|
88
|
-
if (this.retryTimers.has(sourceId)) return;
|
|
89
|
-
const delayMs = RETRY_BACKOFF_MS[attempt] ?? RETRY_BACKOFF_MS[RETRY_BACKOFF_MS.length - 1];
|
|
90
|
-
const timer = setTimeout(() => {
|
|
91
|
-
this.retryTimers.delete(sourceId);
|
|
92
|
-
void this.retrySource(sourceId, attempt);
|
|
93
|
-
}, delayMs);
|
|
94
|
-
this.retryTimers.set(sourceId, timer);
|
|
95
|
-
}
|
|
96
|
-
async retrySource(sourceId, attempt) {
|
|
97
|
-
const sources = this.capabilities?.getCollection("addon-pages-source") ?? [];
|
|
98
|
-
const source = sources.find((s) => s.id === sourceId);
|
|
99
|
-
if (!source) return;
|
|
100
|
-
try {
|
|
101
|
-
const pages = await Promise.resolve(source.listPages());
|
|
102
|
-
const enriched = pages.map((page) => ({
|
|
103
|
-
addonId: source.id,
|
|
104
|
-
page,
|
|
105
|
-
bundleUrl: this.makeBundleUrl(source.id, page.bundle)
|
|
106
|
-
}));
|
|
107
|
-
this.lastGood.set(source.id, enriched);
|
|
108
|
-
this.ctx.logger.info("addon-pages-source recovered after retry", {
|
|
109
|
-
meta: { sourceId, attempt: attempt + 1, pages: enriched.length }
|
|
110
|
-
});
|
|
111
|
-
this.ctx.eventBus.emit({
|
|
112
|
-
id: randomUUID(),
|
|
113
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
114
|
-
source: { type: "addon", id: this.id },
|
|
115
|
-
category: EventCategory.AddonPageReady,
|
|
116
|
-
data: { addonId: sourceId, recovered: true }
|
|
117
|
-
});
|
|
118
|
-
} catch (err) {
|
|
119
|
-
this.ctx.logger.debug("addon-pages-source retry failed", {
|
|
120
|
-
meta: { sourceId, attempt: attempt + 1, error: errMsg(err) }
|
|
121
|
-
});
|
|
122
|
-
this.scheduleRetry(sourceId, attempt + 1);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// ── Bundle URL stamping ──────────────────────────────────────────
|
|
126
|
-
/**
|
|
127
|
-
* Build `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. Falls back
|
|
128
|
-
* to `Date.now()` when the bundle path can't be stat'd (remote addon
|
|
129
|
-
* with no local file, addon not yet on disk, etc.) — the browser
|
|
130
|
-
* just gets a fresh URL on each call instead of cache-friendly mtime.
|
|
131
|
-
*/
|
|
132
|
-
makeBundleUrl(addonId, bundle) {
|
|
133
|
-
const bundlePath = this.resolveBundlePath(addonId, bundle);
|
|
134
|
-
let mtime = Date.now();
|
|
135
|
-
if (bundlePath !== null) {
|
|
136
|
-
try {
|
|
137
|
-
mtime = fs.statSync(bundlePath).mtimeMs;
|
|
138
|
-
} catch {
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
const v = Math.floor(mtime);
|
|
142
|
-
return `/api/addon-pages/${addonId}/${bundle}?v=${v}`;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Resolve the local filesystem path for an addon's bundle. Mirrors
|
|
146
|
-
* `AddonPagesService.resolveBundle` (server-side), but without the
|
|
147
|
-
* existence / traversal checks — those still live on the server's
|
|
148
|
-
* static file route. We only need a stat-able path here for the
|
|
149
|
-
* `mtime` cache-buster.
|
|
150
|
-
*/
|
|
151
|
-
resolveBundlePath(addonId, bundle) {
|
|
152
|
-
const paths = this.resolvedPaths;
|
|
153
|
-
if (!paths) return null;
|
|
154
|
-
const addonDistPath = path.join(paths.addonsDir, "@camstack", `addon-${addonId}`, "dist");
|
|
155
|
-
const resolvedBase = path.resolve(addonDistPath);
|
|
156
|
-
const resolvedFile = path.resolve(addonDistPath, bundle);
|
|
157
|
-
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
return resolvedFile;
|
|
161
|
-
}
|
|
162
|
-
// ── Path resolution ──────────────────────────────────────────────
|
|
163
|
-
/**
|
|
164
|
-
* Read `server.dataPath` from the cluster yml-backed sections (same
|
|
165
|
-
* source the server uses at boot), then derive the addons directory
|
|
166
|
-
* from it. Falls back to `camstack-data/addons` when no settings API
|
|
167
|
-
* is available — agents and isolated tests don't see this addon, so
|
|
168
|
-
* the fallback is purely defensive.
|
|
169
|
-
*/
|
|
170
|
-
async resolvePaths() {
|
|
171
|
-
const fallback = { addonsDir: path.resolve("camstack-data", "addons") };
|
|
172
|
-
if (!this.ctx.settings) return fallback;
|
|
173
|
-
try {
|
|
174
|
-
const server = await this.ctx.settings.getSection("server");
|
|
175
|
-
const dataPath = typeof server["dataPath"] === "string" && server["dataPath"] ? server["dataPath"] : "camstack-data";
|
|
176
|
-
return { addonsDir: path.resolve(dataPath, "addons") };
|
|
177
|
-
} catch (err) {
|
|
178
|
-
this.ctx.logger.debug("Failed to read server.dataPath \u2014 falling back", {
|
|
179
|
-
meta: { error: errMsg(err) }
|
|
180
|
-
});
|
|
181
|
-
return fallback;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
var addon_pages_aggregator_addon_default = AddonPagesAggregatorAddon;
|
|
186
|
-
|
|
187
|
-
export {
|
|
188
|
-
AddonPagesAggregatorAddon,
|
|
189
|
-
addon_pages_aggregator_addon_default
|
|
190
|
-
};
|
|
191
|
-
//# sourceMappingURL=chunk-NRBQWBDM.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.ts"],"sourcesContent":["/**\n * Addon Pages Aggregator — hub-local builtin that owns the singleton\n * `addon-pages` cap.\n *\n * Walks every registered `addon-pages-source` (collection) provider and\n * emits an enriched `AddonPageInfo[]` list with versioned `bundleUrl`s\n * pointing at `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. The\n * filesystem `mtime` cache-buster lets the browser pick up addon\n * rebuilds without manual reload — same scheme the original\n * hand-written `AddonPagesService.listPages` used before this split.\n *\n * The static file endpoint (`/api/addon-pages/:addonId/*`) is still\n * served by `AddonPagesService.resolveBundle()` on the server side; this\n * addon only owns the listing surface.\n *\n * Why a builtin rather than a cap-providers helper:\n * - The aggregator is conceptually the \"addon-pages provider\" — addons\n * own caps, not the server. Living in `@camstack/core/builtins` keeps\n * the surface symmetrical with `system-config`, `local-auth`, etc.\n * - Lets the aggregator subscribe to source readiness without piping a\n * CapabilityRegistry handle through `RouterServices`.\n */\nimport * as path from 'node:path'\nimport * as fs from 'node:fs'\nimport { randomUUID } from 'node:crypto'\nimport {\n BaseAddon,\n EventCategory,\n addonPagesCapability,\n errMsg,\n type AddonPageInfo,\n type IAddonPageProvider,\n type IAddonPagesAggregatorProvider,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface ResolvedPaths {\n readonly addonsDir: string\n}\n\n/**\n * Backoff schedule (ms) used to retry sources that failed during a\n * `listPages()` round-trip — typically because the cap was just\n * registered (provider connected via Moleculer) but the worker-side\n * action registration hadn't propagated yet, so `Service '...listPages'\n * is not found on '<node>'` raced ahead of the call.\n *\n * We schedule one retry per entry; if every retry fails we stop\n * (the next aggregate() run will pick the source up again). On\n * success we re-emit `AddonPageReady` so admin-ui invalidates its\n * `addonPages.listPages` query and the sidebar populates without a\n * page reload.\n */\nconst RETRY_BACKOFF_MS: readonly number[] = [500, 1500, 4000]\n\nexport class AddonPagesAggregatorAddon extends BaseAddon {\n readonly id = 'addon-pages-aggregator'\n\n private resolvedPaths: ResolvedPaths | null = null\n\n /**\n * Last successful `listPages()` snapshot per source. Used as the\n * \"stale-but-valid\" fallback when a source transiently fails — drops\n * happen often enough during boot (Moleculer service-discovery\n * window) that swallowing the error and returning empty leaves the\n * sidebar with nothing for several seconds. Keeping the previous\n * good entry means a flake is invisible to the operator.\n */\n private readonly lastGood = new Map<string, readonly AddonPageInfo[]>()\n\n /** In-flight retry guards keyed by sourceId. Avoids double-scheduling. */\n private readonly retryTimers = new Map<string, NodeJS.Timeout>()\n\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.resolvedPaths = await this.resolvePaths()\n\n const provider: IAddonPagesAggregatorProvider = {\n listPages: async (): Promise<readonly AddonPageInfo[]> => this.aggregate(),\n }\n\n this.ctx.logger.info('Initialized — aggregating addon-pages-source providers')\n return [{ capability: addonPagesCapability, provider }]\n }\n\n protected async onShutdown(): Promise<void> {\n for (const t of this.retryTimers.values()) clearTimeout(t)\n this.retryTimers.clear()\n this.lastGood.clear()\n }\n\n // ── Aggregation ───────────────────────────────────────────────────\n\n private async aggregate(): Promise<readonly AddonPageInfo[]> {\n const sources = this.capabilities?.getCollection<IAddonPageProvider>('addon-pages-source') ?? []\n const out: AddonPageInfo[] = []\n const seenIds = new Set<string>()\n\n for (const source of sources) {\n seenIds.add(source.id)\n try {\n // listPages() may be async for remote Moleculer providers.\n const pages = await Promise.resolve(source.listPages())\n const enriched: AddonPageInfo[] = pages.map((page) => ({\n addonId: source.id,\n page,\n bundleUrl: this.makeBundleUrl(source.id, page.bundle),\n }))\n for (const item of enriched) out.push(item)\n // Cache successful snapshot — used as fallback on next failure.\n this.lastGood.set(source.id, enriched)\n } catch (err: unknown) {\n const message = errMsg(err)\n this.ctx.logger.warn('addon-pages-source provider failed', {\n meta: { sourceId: source.id, error: message },\n })\n // Fall back to the last-good snapshot for this source so a\n // transient Moleculer service-discovery race (cap registered\n // but action not yet callable) doesn't blank the sidebar.\n const cached = this.lastGood.get(source.id)\n if (cached !== undefined) {\n for (const item of cached) out.push(item)\n this.ctx.logger.info('addon-pages-source falling back to cached snapshot', {\n meta: { sourceId: source.id, cachedPages: cached.length },\n })\n }\n // Schedule a background retry. On success we re-emit\n // `AddonPageReady` so admin-ui's queryClient invalidates and\n // any newly-loaded pages show up without a manual reload.\n this.scheduleRetry(source.id)\n }\n }\n\n // Drop cache entries for sources that have disappeared from the\n // registry — keeps the fallback aligned with the live collection.\n for (const cachedId of this.lastGood.keys()) {\n if (!seenIds.has(cachedId)) this.lastGood.delete(cachedId)\n }\n\n return out\n }\n\n // ── Retry on transient Moleculer race ─────────────────────────────\n\n /**\n * Schedule background re-call of `listPages()` on a source that just\n * failed. Walks `RETRY_BACKOFF_MS` from index 0 — each successful\n * call updates the cache and emits `AddonPageReady` so the admin UI\n * invalidates its query. Stops on first success or after the schedule\n * is exhausted.\n */\n private scheduleRetry(sourceId: string, attempt = 0): void {\n if (attempt >= RETRY_BACKOFF_MS.length) return\n if (this.retryTimers.has(sourceId)) return // already pending\n\n const delayMs = RETRY_BACKOFF_MS[attempt] ?? RETRY_BACKOFF_MS[RETRY_BACKOFF_MS.length - 1]!\n const timer = setTimeout(() => {\n this.retryTimers.delete(sourceId)\n void this.retrySource(sourceId, attempt)\n }, delayMs)\n this.retryTimers.set(sourceId, timer)\n }\n\n private async retrySource(sourceId: string, attempt: number): Promise<void> {\n const sources = this.capabilities?.getCollection<IAddonPageProvider>('addon-pages-source') ?? []\n const source = sources.find((s) => s.id === sourceId)\n if (!source) return // provider went away; nothing to retry\n\n try {\n const pages = await Promise.resolve(source.listPages())\n const enriched: AddonPageInfo[] = pages.map((page) => ({\n addonId: source.id,\n page,\n bundleUrl: this.makeBundleUrl(source.id, page.bundle),\n }))\n this.lastGood.set(source.id, enriched)\n this.ctx.logger.info('addon-pages-source recovered after retry', {\n meta: { sourceId, attempt: attempt + 1, pages: enriched.length },\n })\n // Re-emit AddonPageReady so admin-ui invalidates and refetches.\n this.ctx.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: this.id },\n category: EventCategory.AddonPageReady,\n data: { addonId: sourceId, recovered: true },\n })\n } catch (err: unknown) {\n this.ctx.logger.debug('addon-pages-source retry failed', {\n meta: { sourceId, attempt: attempt + 1, error: errMsg(err) },\n })\n this.scheduleRetry(sourceId, attempt + 1)\n }\n }\n\n // ── Bundle URL stamping ──────────────────────────────────────────\n\n /**\n * Build `/api/addon-pages/<addonId>/<bundle>?v=<mtime>`. Falls back\n * to `Date.now()` when the bundle path can't be stat'd (remote addon\n * with no local file, addon not yet on disk, etc.) — the browser\n * just gets a fresh URL on each call instead of cache-friendly mtime.\n */\n private makeBundleUrl(addonId: string, bundle: string): string {\n const bundlePath = this.resolveBundlePath(addonId, bundle)\n let mtime = Date.now()\n if (bundlePath !== null) {\n try { mtime = fs.statSync(bundlePath).mtimeMs }\n catch { /* remote addon — no local file */ }\n }\n const v = Math.floor(mtime)\n return `/api/addon-pages/${addonId}/${bundle}?v=${v}`\n }\n\n /**\n * Resolve the local filesystem path for an addon's bundle. Mirrors\n * `AddonPagesService.resolveBundle` (server-side), but without the\n * existence / traversal checks — those still live on the server's\n * static file route. We only need a stat-able path here for the\n * `mtime` cache-buster.\n */\n private resolveBundlePath(addonId: string, bundle: string): string | null {\n const paths = this.resolvedPaths\n if (!paths) return null\n const addonDistPath = path.join(paths.addonsDir, '@camstack', `addon-${addonId}`, 'dist')\n const resolvedBase = path.resolve(addonDistPath)\n const resolvedFile = path.resolve(addonDistPath, bundle)\n if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {\n return null\n }\n return resolvedFile\n }\n\n // ── Path resolution ──────────────────────────────────────────────\n\n /**\n * Read `server.dataPath` from the cluster yml-backed sections (same\n * source the server uses at boot), then derive the addons directory\n * from it. Falls back to `camstack-data/addons` when no settings API\n * is available — agents and isolated tests don't see this addon, so\n * the fallback is purely defensive.\n */\n private async resolvePaths(): Promise<ResolvedPaths> {\n const fallback: ResolvedPaths = { addonsDir: path.resolve('camstack-data', 'addons') }\n if (!this.ctx.settings) return fallback\n try {\n const server = await this.ctx.settings.getSection('server')\n const dataPath = typeof server['dataPath'] === 'string' && server['dataPath']\n ? server['dataPath']\n : 'camstack-data'\n return { addonsDir: path.resolve(dataPath, 'addons') }\n } catch (err: unknown) {\n this.ctx.logger.debug('Failed to read server.dataPath — falling back', {\n meta: { error: errMsg(err) },\n })\n return fallback\n }\n }\n}\n\nexport default AddonPagesAggregatorAddon\n"],"mappings":"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","names":[]}
|