@camstack/server 0.2.2 → 1.0.1
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/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
- package/dist/api/addon-upload.js +441 -0
- package/dist/api/addons-custom.router.js +91 -0
- package/dist/api/auth-whoami.js +55 -0
- package/dist/api/bridge-addons.router.js +109 -0
- package/dist/api/capabilities.router.js +229 -0
- package/dist/api/core/addon-settings.router.js +117 -0
- package/dist/api/core/agents.router.js +73 -0
- package/dist/api/core/auth.router.js +286 -0
- package/dist/api/core/bulk-update-coordinator.js +229 -0
- package/dist/api/core/cap-providers.js +1124 -0
- package/dist/api/core/capabilities.router.js +138 -0
- package/dist/api/core/collection-preference.js +17 -0
- package/dist/api/core/event-bus-proxy.router.js +45 -0
- package/dist/api/core/hwaccel.router.js +91 -0
- package/dist/api/core/live-events.router.js +61 -0
- package/dist/api/core/logs.router.js +172 -0
- package/dist/api/core/notifications.router.js +67 -0
- package/dist/api/core/repl.router.js +35 -0
- package/dist/api/core/settings-backend.router.js +121 -0
- package/dist/api/core/stream-probe.router.js +58 -0
- package/dist/api/core/system-events.router.js +100 -0
- package/dist/api/health/health.routes.js +68 -0
- package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
- package/dist/api/oauth2/oauth2-routes.js +219 -0
- package/dist/api/trpc/cap-mount-helpers.js +194 -0
- package/dist/api/trpc/cap-route-error-formatter.js +133 -0
- package/dist/api/trpc/client-ip.js +147 -0
- package/dist/api/trpc/core-cap-bridge.js +115 -0
- package/dist/api/trpc/generated-cap-mounts.js +388 -0
- package/dist/api/trpc/generated-cap-routers.js +7635 -0
- package/dist/api/trpc/scope-access.js +93 -0
- package/dist/api/trpc/trpc.context.js +184 -0
- package/dist/api/trpc/trpc.middleware.js +139 -0
- package/dist/api/trpc/trpc.router.js +188 -0
- package/dist/auth/session-cookie.js +47 -0
- package/dist/boot/boot-config.js +241 -0
- package/dist/boot/integration-id-backfill.js +76 -0
- package/dist/boot/post-boot.service.js +85 -0
- package/dist/core/addon/addon-call-gateway.js +99 -0
- package/dist/core/addon/addon-package.service.js +1560 -0
- package/dist/core/addon/addon-registry.service.js +2739 -0
- package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
- package/dist/core/addon/addon-search.service.js +62 -0
- package/dist/core/addon/addon-settings-provider.js +102 -0
- package/dist/core/addon/addon.tokens.js +5 -0
- package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
- package/dist/core/addon-pages/addon-pages.service.js +107 -0
- package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
- package/dist/core/agent/agent-registry.service.js +477 -0
- package/dist/core/auth/auth.service.js +10 -0
- package/dist/core/capability/capability.service.js +58 -0
- package/dist/core/config/config.schema.js +7 -0
- package/dist/core/config/config.service.js +10 -0
- package/dist/core/events/event-bus.service.js +83 -0
- package/dist/core/feature/feature.service.js +10 -0
- package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
- package/dist/core/logging/log-ring-buffer.js +6 -0
- package/dist/core/logging/logging.service.js +130 -0
- package/dist/core/logging/scoped-logger.js +6 -0
- package/dist/core/moleculer/cap-call-fn.js +50 -0
- package/dist/core/moleculer/cap-route-authority.js +122 -0
- package/dist/core/moleculer/moleculer.service.js +898 -0
- package/dist/core/network/network-quality.service.js +7 -0
- package/dist/core/notification/notification-wrapper.service.js +33 -0
- package/dist/core/notification/toast-wrapper.service.js +25 -0
- package/dist/core/provider/provider.tokens.js +4 -0
- package/dist/core/repl/repl-engine.service.js +140 -0
- package/dist/core/storage/fs-storage-backend.js +6 -0
- package/dist/core/storage/storage-location-manager.js +6 -0
- package/dist/core/storage/storage.service.js +7 -0
- package/dist/core/streaming/stream-probe.service.js +209 -0
- package/dist/core/topology/topology-emitter.service.js +106 -0
- package/dist/launcher.js +325 -0
- package/dist/main.js +1098 -0
- package/dist/manual-boot.js +227 -0
- package/package.json +5 -1
- package/src/__tests__/addon-install-e2e.test.ts +0 -74
- package/src/__tests__/addon-pages-e2e.test.ts +0 -200
- package/src/__tests__/addon-route-session.test.ts +0 -17
- package/src/__tests__/addon-settings-router.spec.ts +0 -67
- package/src/__tests__/addon-upload.spec.ts +0 -475
- package/src/__tests__/agent-registry.spec.ts +0 -179
- package/src/__tests__/agent-status-page.spec.ts +0 -82
- package/src/__tests__/auth-session-cookie.test.ts +0 -48
- package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
- package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
- package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
- package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
- package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
- package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
- package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
- package/src/__tests__/cap-route-adapter.spec.ts +0 -302
- package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
- package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
- package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
- package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
- package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
- package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
- package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
- package/src/__tests__/cap-routers/harness.ts +0 -163
- package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
- package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
- package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
- package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
- package/src/__tests__/capability-e2e.test.ts +0 -384
- package/src/__tests__/cli-e2e.test.ts +0 -150
- package/src/__tests__/core-cap-bridge.spec.ts +0 -91
- package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
- package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
- package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
- package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
- package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
- package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
- package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
- package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
- package/src/__tests__/framework-allowlist.spec.ts +0 -96
- package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
- package/src/__tests__/https-e2e.test.ts +0 -124
- package/src/__tests__/lifecycle-e2e.test.ts +0 -189
- package/src/__tests__/live-events-subscription.spec.ts +0 -149
- package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
- package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
- package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
- package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
- package/src/__tests__/native-cap-route.spec.ts +0 -427
- package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
- package/src/__tests__/post-boot-restart.spec.ts +0 -161
- package/src/__tests__/singleton-contention.test.ts +0 -499
- package/src/__tests__/streaming-diagnostic.test.ts +0 -615
- package/src/__tests__/streaming-scale.test.ts +0 -314
- package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
- package/src/__tests__/uds-log-ingest.spec.ts +0 -183
- package/src/api/__tests__/addons-custom.spec.ts +0 -148
- package/src/api/__tests__/capabilities.router.test.ts +0 -56
- package/src/api/addon-upload.ts +0 -529
- package/src/api/addons-custom.router.ts +0 -101
- package/src/api/auth-whoami.ts +0 -101
- package/src/api/bridge-addons.router.ts +0 -122
- package/src/api/capabilities.router.ts +0 -265
- package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
- package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
- package/src/api/core/addon-settings.router.ts +0 -127
- package/src/api/core/agents.router.ts +0 -86
- package/src/api/core/auth.router.ts +0 -322
- package/src/api/core/bulk-update-coordinator.ts +0 -305
- package/src/api/core/cap-providers.ts +0 -1339
- package/src/api/core/capabilities.router.ts +0 -149
- package/src/api/core/collection-preference.ts +0 -40
- package/src/api/core/event-bus-proxy.router.ts +0 -45
- package/src/api/core/hwaccel.router.ts +0 -108
- package/src/api/core/live-events.router.ts +0 -67
- package/src/api/core/logs.router.ts +0 -195
- package/src/api/core/notifications.router.ts +0 -66
- package/src/api/core/repl.router.ts +0 -39
- package/src/api/core/settings-backend.router.ts +0 -140
- package/src/api/core/stream-probe.router.ts +0 -57
- package/src/api/core/system-events.router.ts +0 -125
- package/src/api/health/health.routes.ts +0 -117
- package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
- package/src/api/oauth2/oauth2-routes.ts +0 -281
- package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
- package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
- package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
- package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
- package/src/api/trpc/cap-mount-helpers.ts +0 -245
- package/src/api/trpc/cap-route-error-formatter.ts +0 -171
- package/src/api/trpc/client-ip.ts +0 -147
- package/src/api/trpc/core-cap-bridge.ts +0 -154
- package/src/api/trpc/generated-cap-mounts.ts +0 -1240
- package/src/api/trpc/generated-cap-routers.ts +0 -11523
- package/src/api/trpc/scope-access.ts +0 -110
- package/src/api/trpc/trpc.context.ts +0 -258
- package/src/api/trpc/trpc.middleware.ts +0 -146
- package/src/api/trpc/trpc.router.ts +0 -389
- package/src/auth/session-cookie.ts +0 -54
- package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
- package/src/boot/boot-config.ts +0 -259
- package/src/boot/integration-id-backfill.ts +0 -109
- package/src/boot/post-boot.service.ts +0 -105
- package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
- package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
- package/src/core/addon/addon-call-gateway.ts +0 -171
- package/src/core/addon/addon-package.service.ts +0 -1787
- package/src/core/addon/addon-registry.service.ts +0 -3130
- package/src/core/addon/addon-search.service.ts +0 -91
- package/src/core/addon/addon-settings-provider.ts +0 -220
- package/src/core/addon/addon.tokens.ts +0 -2
- package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
- package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
- package/src/core/addon-pages/addon-pages.service.ts +0 -82
- package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
- package/src/core/agent/agent-registry.service.ts +0 -529
- package/src/core/auth/auth.service.spec.ts +0 -86
- package/src/core/auth/auth.service.ts +0 -8
- package/src/core/capability/capability.service.ts +0 -66
- package/src/core/config/config.schema.ts +0 -3
- package/src/core/config/config.service.spec.ts +0 -175
- package/src/core/config/config.service.ts +0 -7
- package/src/core/events/event-bus.service.spec.ts +0 -235
- package/src/core/events/event-bus.service.ts +0 -89
- package/src/core/feature/feature.service.spec.ts +0 -99
- package/src/core/feature/feature.service.ts +0 -8
- package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
- package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
- package/src/core/logging/log-ring-buffer.ts +0 -3
- package/src/core/logging/logging.service.spec.ts +0 -287
- package/src/core/logging/logging.service.ts +0 -143
- package/src/core/logging/scoped-logger.ts +0 -3
- package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
- package/src/core/moleculer/cap-call-fn.ts +0 -107
- package/src/core/moleculer/cap-route-authority.ts +0 -194
- package/src/core/moleculer/moleculer.service.ts +0 -1072
- package/src/core/network/network-quality.service.spec.ts +0 -53
- package/src/core/network/network-quality.service.ts +0 -5
- package/src/core/notification/notification-wrapper.service.ts +0 -34
- package/src/core/notification/toast-wrapper.service.ts +0 -27
- package/src/core/provider/provider.tokens.ts +0 -1
- package/src/core/repl/repl-engine.service.spec.ts +0 -444
- package/src/core/repl/repl-engine.service.ts +0 -155
- package/src/core/storage/fs-storage-backend.spec.ts +0 -70
- package/src/core/storage/fs-storage-backend.ts +0 -3
- package/src/core/storage/storage-location-manager.spec.ts +0 -130
- package/src/core/storage/storage-location-manager.ts +0 -3
- package/src/core/storage/storage.service.spec.ts +0 -73
- package/src/core/storage/storage.service.ts +0 -3
- package/src/core/streaming/stream-probe.service.ts +0 -221
- package/src/core/topology/topology-emitter.service.ts +0 -105
- package/src/launcher.ts +0 -314
- package/src/main.ts +0 -1245
- package/src/manual-boot.ts +0 -301
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -33
- package/vitest.config.ts +0 -26
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* device-settings-contribution-dispatch.spec.ts
|
|
3
|
-
*
|
|
4
|
-
* Regression tests for Bug-3: snapshot `getDeviceSettingsContribution` not
|
|
5
|
-
* found on native provider.
|
|
6
|
-
*
|
|
7
|
-
* Root cause: device-manager.addon.ts used `getProviderForDevice` for the
|
|
8
|
-
* three `exposesDeviceSettings` contribution methods. `getProviderForDevice`
|
|
9
|
-
* returns the native first when present (e.g. Reolink/ONVIF); the native
|
|
10
|
-
* only implements camera-specific operations (getSnapshot, invalidateCache),
|
|
11
|
-
* NOT the contribution methods. The cross-process dispatch then throws
|
|
12
|
-
* "native-provider: method 'getDeviceSettingsContribution' not found on
|
|
13
|
-
* 'snapshot'".
|
|
14
|
-
*
|
|
15
|
-
* Fix: device-manager now uses `getProvider(capName)` — the active system
|
|
16
|
-
* singleton (the wrapper, e.g. SnapshotAddon) — for all contribution method
|
|
17
|
-
* calls. The native is never touched for these methods.
|
|
18
|
-
*
|
|
19
|
-
* Spec: docs/superpowers/plans/2026-05-21-addons-bulk-update-progress.md (Bug-3)
|
|
20
|
-
*/
|
|
21
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
22
|
-
import { DeviceManagerAddon } from '@camstack/core'
|
|
23
|
-
import { CapabilityRegistry, DeviceRegistry } from '@camstack/kernel'
|
|
24
|
-
import { snapshotCapability } from '@camstack/types'
|
|
25
|
-
import type {
|
|
26
|
-
AddonContext,
|
|
27
|
-
ConfigUISchemaWithValues,
|
|
28
|
-
IScopedLogger,
|
|
29
|
-
IEventBus,
|
|
30
|
-
ProviderRegistration,
|
|
31
|
-
IDeviceManagerProvider,
|
|
32
|
-
} from '@camstack/types'
|
|
33
|
-
|
|
34
|
-
// ── Fakes ────────────────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
function makeLogger(): IScopedLogger {
|
|
37
|
-
const l: IScopedLogger = {
|
|
38
|
-
info: vi.fn(),
|
|
39
|
-
warn: vi.fn(),
|
|
40
|
-
error: vi.fn(),
|
|
41
|
-
debug: vi.fn(),
|
|
42
|
-
child: vi.fn(() => l),
|
|
43
|
-
withTags: vi.fn(() => l),
|
|
44
|
-
}
|
|
45
|
-
return l
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function makeEventBus(): IEventBus {
|
|
49
|
-
return { emit: vi.fn(), subscribe: () => () => {}, getRecent: () => [] } as unknown as IEventBus
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function makeSettings() {
|
|
53
|
-
return {
|
|
54
|
-
readAddonStore: vi.fn(async () => ({})),
|
|
55
|
-
writeAddonStore: vi.fn(async () => {}),
|
|
56
|
-
readDeviceStore: vi.fn(async () => ({})),
|
|
57
|
-
writeDeviceStore: vi.fn(async () => {}),
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Wrapper provider (SnapshotAddon equivalent): registered as a system
|
|
63
|
-
* singleton via `registerProvider`. Implements ALL three contribution
|
|
64
|
-
* methods plus the cap-specific operations (getSnapshot, invalidateCache).
|
|
65
|
-
*/
|
|
66
|
-
function makeSnapshotWrapperProvider() {
|
|
67
|
-
return {
|
|
68
|
-
// Cap-specific methods
|
|
69
|
-
getSnapshot: vi.fn(async () => null),
|
|
70
|
-
invalidateCache: vi.fn(async () => undefined),
|
|
71
|
-
|
|
72
|
-
// Contribution methods — owned by the wrapper
|
|
73
|
-
getDeviceSettingsContribution: vi.fn(
|
|
74
|
-
async (_input: { deviceId: number }): Promise<ConfigUISchemaWithValues | null> => ({
|
|
75
|
-
sections: [
|
|
76
|
-
{
|
|
77
|
-
id: 'snapshot-settings',
|
|
78
|
-
title: 'Snapshot',
|
|
79
|
-
tab: 'general',
|
|
80
|
-
order: 99,
|
|
81
|
-
fields: [
|
|
82
|
-
{
|
|
83
|
-
type: 'text' as const,
|
|
84
|
-
key: 'preferredStreamId',
|
|
85
|
-
label: 'Preferred Stream',
|
|
86
|
-
default: '',
|
|
87
|
-
value: '',
|
|
88
|
-
},
|
|
89
|
-
],
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
}),
|
|
93
|
-
),
|
|
94
|
-
getDeviceLiveContribution: vi.fn(
|
|
95
|
-
async (_input: { deviceId: number }): Promise<ConfigUISchemaWithValues | null> => null,
|
|
96
|
-
),
|
|
97
|
-
applyDeviceSettingsPatch: vi.fn(async () => ({ success: true as const })),
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Native provider (Reolink/ONVIF/Hikvision equivalent): registered per-device
|
|
103
|
-
* via `registerNativeProvider`. Implements ONLY the camera-specific operations
|
|
104
|
-
* (getSnapshot, invalidateCache). Does NOT implement contribution methods.
|
|
105
|
-
*
|
|
106
|
-
* Calling any of the three contribution methods on this object simulates the
|
|
107
|
-
* "method not found" scenario that was Bug-3.
|
|
108
|
-
*/
|
|
109
|
-
function makeSnapshotNativeProvider() {
|
|
110
|
-
return {
|
|
111
|
-
getSnapshot: vi.fn(async () => ({
|
|
112
|
-
base64: 'abc123',
|
|
113
|
-
contentType: 'image/jpeg',
|
|
114
|
-
})),
|
|
115
|
-
invalidateCache: vi.fn(async () => undefined),
|
|
116
|
-
// Deliberately omit getDeviceSettingsContribution / getDeviceLiveContribution /
|
|
117
|
-
// applyDeviceSettingsPatch — these don't exist on native providers.
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ── Scenario setup ────────────────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
async function setupBug3Scenario() {
|
|
124
|
-
const addon = new DeviceManagerAddon()
|
|
125
|
-
const settings = makeSettings()
|
|
126
|
-
const deviceRegistry = new DeviceRegistry()
|
|
127
|
-
const capabilityRegistry = new CapabilityRegistry(makeLogger())
|
|
128
|
-
|
|
129
|
-
const wrapperProvider = makeSnapshotWrapperProvider()
|
|
130
|
-
const nativeProvider = makeSnapshotNativeProvider()
|
|
131
|
-
|
|
132
|
-
// Declare the snapshot capability (kind:'wrapper', defaultActive:true,
|
|
133
|
-
// exposesDeviceSettings:true, mode:'singleton').
|
|
134
|
-
capabilityRegistry.declareCapability(snapshotCapability)
|
|
135
|
-
|
|
136
|
-
// Register the WRAPPER as the system singleton — this is what SnapshotAddon does.
|
|
137
|
-
capabilityRegistry.registerProvider(snapshotCapability.name, 'snapshot-addon', wrapperProvider)
|
|
138
|
-
|
|
139
|
-
// Register the default wrapper so auto-bind picks it up in getBindings step 2.
|
|
140
|
-
capabilityRegistry.registerWrapper(snapshotCapability.name, 'snapshot-addon', {
|
|
141
|
-
defaultActive: true,
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
const ctx = {
|
|
145
|
-
id: 'device-manager',
|
|
146
|
-
logger: makeLogger(),
|
|
147
|
-
eventBus: makeEventBus(),
|
|
148
|
-
addonConfig: {},
|
|
149
|
-
dataDir: '/tmp',
|
|
150
|
-
settings,
|
|
151
|
-
kernel: { deviceRegistry, capabilityRegistry },
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const initResult = await addon.initialize(ctx as unknown as AddonContext)
|
|
155
|
-
let providers: readonly ProviderRegistration[] = []
|
|
156
|
-
if (Array.isArray(initResult)) {
|
|
157
|
-
providers = initResult as readonly ProviderRegistration[]
|
|
158
|
-
} else if (initResult && typeof initResult === 'object' && 'providers' in initResult) {
|
|
159
|
-
providers = (initResult.providers ?? []) as readonly ProviderRegistration[]
|
|
160
|
-
}
|
|
161
|
-
const provider = providers[0]!.provider as IDeviceManagerProvider
|
|
162
|
-
|
|
163
|
-
// Allocate and register the device.
|
|
164
|
-
const { id: deviceId } = await provider.allocateDeviceId({
|
|
165
|
-
addonId: 'reolink-addon',
|
|
166
|
-
stableId: 'cam-reolink-1',
|
|
167
|
-
})
|
|
168
|
-
await provider.registerDevice({
|
|
169
|
-
addonId: 'reolink-addon',
|
|
170
|
-
stableId: 'cam-reolink-1',
|
|
171
|
-
id: deviceId,
|
|
172
|
-
type: 'camera',
|
|
173
|
-
name: 'Reolink Camera',
|
|
174
|
-
parentDeviceId: null,
|
|
175
|
-
config: {},
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
// Register the NATIVE provider for the device — this is what a Reolink/ONVIF
|
|
179
|
-
// driver does. The native does NOT implement contribution methods.
|
|
180
|
-
capabilityRegistry.registerNativeProvider(
|
|
181
|
-
snapshotCapability.name,
|
|
182
|
-
deviceId,
|
|
183
|
-
'reolink-addon',
|
|
184
|
-
nativeProvider,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
capabilityRegistry.ready()
|
|
188
|
-
|
|
189
|
-
return { provider, wrapperProvider, nativeProvider, deviceId }
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
193
|
-
|
|
194
|
-
describe('device-manager contribution dispatch — Bug-3 regression (snapshot wrapper vs native)', () => {
|
|
195
|
-
it('routes getDeviceSettingsContribution to the wrapper, never to the native', async () => {
|
|
196
|
-
const { provider, wrapperProvider, nativeProvider, deviceId } = await setupBug3Scenario()
|
|
197
|
-
|
|
198
|
-
// This MUST NOT throw "method not found on native" — the fix routes to the wrapper.
|
|
199
|
-
const aggregate = await provider.getDeviceSettingsAggregate({ deviceId })
|
|
200
|
-
|
|
201
|
-
expect(aggregate).not.toBeNull()
|
|
202
|
-
// The wrapper's contribution section must be present.
|
|
203
|
-
const snapshotSection = aggregate!.sections.find((s) => s.id === 'snapshot-settings')
|
|
204
|
-
expect(snapshotSection).toBeDefined()
|
|
205
|
-
expect(snapshotSection!.title).toBe('Snapshot')
|
|
206
|
-
|
|
207
|
-
// Wrapper was called exactly once.
|
|
208
|
-
expect(wrapperProvider.getDeviceSettingsContribution).toHaveBeenCalledTimes(1)
|
|
209
|
-
expect(wrapperProvider.getDeviceSettingsContribution).toHaveBeenCalledWith({ deviceId })
|
|
210
|
-
|
|
211
|
-
// Native was NOT called — it doesn't implement contribution methods.
|
|
212
|
-
expect(nativeProvider.getSnapshot).not.toHaveBeenCalled()
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
it('routes getDeviceLiveContribution to the wrapper, never to the native', async () => {
|
|
216
|
-
const { provider, wrapperProvider, nativeProvider, deviceId } = await setupBug3Scenario()
|
|
217
|
-
|
|
218
|
-
// This MUST NOT throw "method not found on native".
|
|
219
|
-
const liveAggregate = await provider.getDeviceLiveInfoAggregate({ deviceId })
|
|
220
|
-
|
|
221
|
-
// The wrapper returns null for live contributions in our fake (acceptable).
|
|
222
|
-
// Key assertion: the wrapper was invoked, and no error was thrown.
|
|
223
|
-
expect(wrapperProvider.getDeviceLiveContribution).toHaveBeenCalledTimes(1)
|
|
224
|
-
expect(wrapperProvider.getDeviceLiveContribution).toHaveBeenCalledWith({ deviceId })
|
|
225
|
-
|
|
226
|
-
// Native was NOT called for live contributions.
|
|
227
|
-
expect(nativeProvider.getSnapshot).not.toHaveBeenCalled()
|
|
228
|
-
// liveAggregate may be null (wrapper returned null for live) — that's fine.
|
|
229
|
-
// The important thing is no throw occurred.
|
|
230
|
-
expect(() => liveAggregate).not.toThrow()
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it('routes applyDeviceSettingsPatch to the wrapper, never to the native', async () => {
|
|
234
|
-
const { provider, wrapperProvider, nativeProvider, deviceId } = await setupBug3Scenario()
|
|
235
|
-
|
|
236
|
-
// First get the aggregate to confirm the contribution fields are tagged correctly.
|
|
237
|
-
const aggregate = await provider.getDeviceSettingsAggregate({ deviceId })
|
|
238
|
-
expect(aggregate).not.toBeNull()
|
|
239
|
-
|
|
240
|
-
const field = aggregate!.sections
|
|
241
|
-
.find((s) => s.id === 'snapshot-settings')
|
|
242
|
-
?.fields.find((f) => (f as Record<string, unknown>)['key'] === 'preferredStreamId') as
|
|
243
|
-
| Record<string, unknown>
|
|
244
|
-
| undefined
|
|
245
|
-
|
|
246
|
-
expect(field).toBeDefined()
|
|
247
|
-
expect(field!['writerCapName']).toBe('snapshot')
|
|
248
|
-
|
|
249
|
-
// Now apply a settings patch — must go to the wrapper, NOT the native.
|
|
250
|
-
await provider.updateDeviceField({
|
|
251
|
-
deviceId,
|
|
252
|
-
writerCapName: 'snapshot',
|
|
253
|
-
writerAddonId: field!['writerAddonId'] as string,
|
|
254
|
-
key: 'preferredStreamId',
|
|
255
|
-
value: 'high',
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
// Wrapper's applyDeviceSettingsPatch was called with the correct patch.
|
|
259
|
-
expect(wrapperProvider.applyDeviceSettingsPatch).toHaveBeenCalledWith({
|
|
260
|
-
deviceId,
|
|
261
|
-
patch: { preferredStreamId: 'high' },
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
// Native was NOT called for the settings write.
|
|
265
|
-
expect(nativeProvider.getSnapshot).not.toHaveBeenCalled()
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
it('does not throw when both a native and a wrapper provider are registered for the same cap', async () => {
|
|
269
|
-
// This is the exact Bug-3 scenario: native registered for device, wrapper
|
|
270
|
-
// registered as system singleton. Before the fix, calling
|
|
271
|
-
// getDeviceSettingsAggregate would throw because getProviderForDevice
|
|
272
|
-
// returned the native which lacks contribution methods.
|
|
273
|
-
const { provider, deviceId } = await setupBug3Scenario()
|
|
274
|
-
|
|
275
|
-
// Must complete without throwing.
|
|
276
|
-
await expect(provider.getDeviceSettingsAggregate({ deviceId })).resolves.not.toBeNull()
|
|
277
|
-
|
|
278
|
-
await expect(provider.getDeviceLiveInfoAggregate({ deviceId })).resolves.toBeDefined()
|
|
279
|
-
})
|
|
280
|
-
})
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Embedded dependencies E2E — verify ffmpeg/python resolution and download.
|
|
3
|
-
*
|
|
4
|
-
* These tests check the resolution logic (PATH detection, embedded binary check)
|
|
5
|
-
* without actually downloading large binaries (unless CAMSTACK_TEST_DOWNLOAD=true).
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
8
|
-
import * as fs from 'node:fs'
|
|
9
|
-
import * as path from 'node:path'
|
|
10
|
-
import * as os from 'node:os'
|
|
11
|
-
// Import directly from source submodules because vitest+swc doesn't resolve
|
|
12
|
-
// `export * from './deps/index.js'` barrel re-exports in the @camstack/core index.
|
|
13
|
-
import {
|
|
14
|
-
findInPath,
|
|
15
|
-
getPlatformInfo,
|
|
16
|
-
buildBinaryPath,
|
|
17
|
-
} from '../../../../packages/core/src/deps/binary-downloader'
|
|
18
|
-
import {
|
|
19
|
-
getFfmpegDownloadUrl,
|
|
20
|
-
ensureFfmpeg,
|
|
21
|
-
} from '../../../../packages/core/src/deps/ffmpeg-downloader'
|
|
22
|
-
import {
|
|
23
|
-
getPythonDownloadUrl,
|
|
24
|
-
ensurePython,
|
|
25
|
-
} from '../../../../packages/core/src/deps/python-downloader'
|
|
26
|
-
import type { IScopedLogger } from '@camstack/types'
|
|
27
|
-
|
|
28
|
-
function createMockLogger(): IScopedLogger {
|
|
29
|
-
const logger: IScopedLogger = {
|
|
30
|
-
info: () => {},
|
|
31
|
-
warn: () => {},
|
|
32
|
-
error: () => {},
|
|
33
|
-
debug: () => {},
|
|
34
|
-
child: () => createMockLogger(),
|
|
35
|
-
}
|
|
36
|
-
return logger
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const mockLogger = createMockLogger()
|
|
40
|
-
|
|
41
|
-
describe('Embedded Dependencies E2E', () => {
|
|
42
|
-
let tmpDir: string
|
|
43
|
-
|
|
44
|
-
beforeAll(() => {
|
|
45
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'camstack-deps-e2e-'))
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
afterAll(() => {
|
|
49
|
-
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
describe('Platform detection', () => {
|
|
53
|
-
it('returns valid platform and arch', () => {
|
|
54
|
-
const info = getPlatformInfo()
|
|
55
|
-
expect(['darwin', 'linux', 'win32']).toContain(info.platform)
|
|
56
|
-
expect(['x64', 'arm64', 'arm', 'ia32']).toContain(info.arch)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('Binary path resolution', () => {
|
|
61
|
-
it('builds correct path for deps directory', () => {
|
|
62
|
-
const p = buildBinaryPath('/data', 'ffmpeg')
|
|
63
|
-
expect(p).toBe('/data/deps/ffmpeg')
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
describe('FFmpeg', () => {
|
|
68
|
-
it('generates valid download URL for current platform', () => {
|
|
69
|
-
const url = getFfmpegDownloadUrl(process.platform, process.arch)
|
|
70
|
-
expect(url).toMatch(/^https:\/\//)
|
|
71
|
-
expect(url.length).toBeGreaterThan(20)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('finds ffmpeg in PATH or reports not found', () => {
|
|
75
|
-
const result = findInPath('ffmpeg')
|
|
76
|
-
// Either found (returns the name) or null — both valid
|
|
77
|
-
expect(result === null || typeof result === 'string').toBe(true)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
// Only run download test if explicitly enabled (downloads ~80MB)
|
|
81
|
-
const downloadTest = process.env.CAMSTACK_TEST_DOWNLOAD === 'true' ? it : it.skip
|
|
82
|
-
downloadTest(
|
|
83
|
-
'downloads ffmpeg binary',
|
|
84
|
-
async () => {
|
|
85
|
-
const ffmpegPath = await ensureFfmpeg(tmpDir, mockLogger)
|
|
86
|
-
expect(ffmpegPath).toBeTruthy()
|
|
87
|
-
expect(fs.existsSync(ffmpegPath)).toBe(true)
|
|
88
|
-
},
|
|
89
|
-
120000,
|
|
90
|
-
)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('Python', () => {
|
|
94
|
-
it('generates valid download URL for current platform', () => {
|
|
95
|
-
const url = getPythonDownloadUrl(process.platform, process.arch)
|
|
96
|
-
expect(url).toContain('python-headless')
|
|
97
|
-
if (process.platform === 'darwin') {
|
|
98
|
-
expect(url).toContain('universal2')
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('finds python in PATH or reports not found', () => {
|
|
103
|
-
const result = findInPath('python3') ?? findInPath('python')
|
|
104
|
-
// Either found or null — both valid
|
|
105
|
-
expect(result === null || typeof result === 'string').toBe(true)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// Only run download test if explicitly enabled (downloads ~25MB)
|
|
109
|
-
const downloadTest = process.env.CAMSTACK_TEST_DOWNLOAD === 'true' ? it : it.skip
|
|
110
|
-
downloadTest(
|
|
111
|
-
'downloads portable Python',
|
|
112
|
-
async () => {
|
|
113
|
-
const pythonPath = await ensurePython(tmpDir, mockLogger)
|
|
114
|
-
expect(pythonPath).toBeTruthy()
|
|
115
|
-
expect(fs.existsSync(pythonPath!)).toBe(true)
|
|
116
|
-
|
|
117
|
-
// Verify it actually runs
|
|
118
|
-
const { execFileSync } = await import('node:child_process')
|
|
119
|
-
const version = execFileSync(pythonPath!, ['--version'], { encoding: 'utf8' }).trim()
|
|
120
|
-
expect(version).toMatch(/Python 3\.12/)
|
|
121
|
-
},
|
|
122
|
-
120000,
|
|
123
|
-
)
|
|
124
|
-
})
|
|
125
|
-
})
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment -- test mock typing */
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
3
|
-
import { createEventBusProxyRouter } from '../api/core/event-bus-proxy.router.js'
|
|
4
|
-
import { makeCtx } from './cap-routers/harness.js'
|
|
5
|
-
import type { IEventBus } from '@camstack/types'
|
|
6
|
-
|
|
7
|
-
function createMockEventBus() {
|
|
8
|
-
return {
|
|
9
|
-
emit: vi.fn(),
|
|
10
|
-
} as unknown as IEventBus & { emit: ReturnType<typeof vi.fn> }
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
describe('event-bus-proxy router', () => {
|
|
14
|
-
it('emit calls eventBus.emit with the correct shape', async () => {
|
|
15
|
-
const bus = createMockEventBus()
|
|
16
|
-
const router = createEventBusProxyRouter(bus)
|
|
17
|
-
const caller = router.createCaller(makeCtx('admin'))
|
|
18
|
-
|
|
19
|
-
await caller.emit({
|
|
20
|
-
id: 'evt-1',
|
|
21
|
-
timestamp: '2026-01-15T10:00:00.000Z',
|
|
22
|
-
source: { type: 'addon', id: 'my-addon' },
|
|
23
|
-
// biome-ignore lint/plugin: synthetic test-only event category
|
|
24
|
-
category: 'motion',
|
|
25
|
-
data: { zone: 'front' },
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
expect(bus.emit).toHaveBeenCalledOnce()
|
|
29
|
-
expect(bus.emit).toHaveBeenCalledWith({
|
|
30
|
-
id: 'evt-1',
|
|
31
|
-
timestamp: expect.any(Date),
|
|
32
|
-
source: { type: 'addon', id: 'my-addon' },
|
|
33
|
-
category: 'motion',
|
|
34
|
-
data: { zone: 'front' },
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('emit returns { ok: true }', async () => {
|
|
39
|
-
const bus = createMockEventBus()
|
|
40
|
-
const router = createEventBusProxyRouter(bus)
|
|
41
|
-
const caller = router.createCaller(makeCtx('admin'))
|
|
42
|
-
|
|
43
|
-
const result = await caller.emit({
|
|
44
|
-
id: 'evt-2',
|
|
45
|
-
timestamp: '2026-01-15T10:00:00.000Z',
|
|
46
|
-
source: { type: 'device', id: 'cam-1' },
|
|
47
|
-
// biome-ignore lint/plugin: synthetic test-only event category
|
|
48
|
-
category: 'alert',
|
|
49
|
-
data: {},
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
expect(result).toEqual({ ok: true })
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('timestamp string is converted to a Date object', async () => {
|
|
56
|
-
const bus = createMockEventBus()
|
|
57
|
-
const router = createEventBusProxyRouter(bus)
|
|
58
|
-
const caller = router.createCaller(makeCtx('admin'))
|
|
59
|
-
|
|
60
|
-
const isoString = '2026-06-20T14:30:00.000Z'
|
|
61
|
-
|
|
62
|
-
await caller.emit({
|
|
63
|
-
id: 'evt-3',
|
|
64
|
-
timestamp: isoString,
|
|
65
|
-
source: { type: 'addon', id: 'test' },
|
|
66
|
-
// biome-ignore lint/plugin: synthetic test-only event category
|
|
67
|
-
category: 'info',
|
|
68
|
-
data: {},
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const emittedEvent = bus.emit.mock.calls[0][0] as { timestamp: unknown }
|
|
72
|
-
expect(emittedEvent.timestamp).toBeInstanceOf(Date)
|
|
73
|
-
expect((emittedEvent.timestamp as Date).toISOString()).toBe(isoString)
|
|
74
|
-
})
|
|
75
|
-
})
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// server/backend/src/__tests__/fixtures/mock-analysis-addon-a.ts
|
|
2
|
-
import type { ICamstackAddon, AddonDeclaration, AddonContext } from '@camstack/types'
|
|
3
|
-
|
|
4
|
-
export class MockAnalysisAddonA implements ICamstackAddon {
|
|
5
|
-
readonly manifest: AddonDeclaration = {
|
|
6
|
-
id: 'mock-analysis-a',
|
|
7
|
-
name: 'Mock Analysis A',
|
|
8
|
-
version: '1.0.0',
|
|
9
|
-
capabilities: ['object-detector'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
private initialized = false
|
|
13
|
-
readonly provider = { id: 'analysis-a', processFrame: async () => [] }
|
|
14
|
-
|
|
15
|
-
async initialize(_context: AddonContext) {
|
|
16
|
-
this.initialized = true
|
|
17
|
-
return [{ capability: 'object-detector', provider: this.provider }]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async shutdown(): Promise<void> {
|
|
21
|
-
this.initialized = false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.initialized
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getConfigSchema() {
|
|
29
|
-
return { sections: [] }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getConfig() {
|
|
33
|
-
return {}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async onConfigChange() {}
|
|
37
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// server/backend/src/__tests__/fixtures/mock-analysis-addon-b.ts
|
|
2
|
-
import type { ICamstackAddon, AddonDeclaration, AddonContext } from '@camstack/types'
|
|
3
|
-
|
|
4
|
-
export class MockAnalysisAddonB implements ICamstackAddon {
|
|
5
|
-
readonly manifest: AddonDeclaration = {
|
|
6
|
-
id: 'mock-analysis-b',
|
|
7
|
-
name: 'Mock Analysis B',
|
|
8
|
-
version: '1.0.0',
|
|
9
|
-
capabilities: ['object-detector'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
private initialized = false
|
|
13
|
-
readonly provider = { id: 'analysis-b', processFrame: async () => [] }
|
|
14
|
-
|
|
15
|
-
async initialize(_context: AddonContext) {
|
|
16
|
-
this.initialized = true
|
|
17
|
-
return [{ capability: 'object-detector', provider: this.provider }]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async shutdown(): Promise<void> {
|
|
21
|
-
this.initialized = false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.initialized
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getConfigSchema() {
|
|
29
|
-
return { sections: [] }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getConfig() {
|
|
33
|
-
return {}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async onConfigChange() {}
|
|
37
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// server/backend/src/__tests__/fixtures/mock-log-addon.ts
|
|
2
|
-
import type { ICamstackAddon, AddonDeclaration, AddonContext } from '@camstack/types'
|
|
3
|
-
|
|
4
|
-
export class MockLogAddon implements ICamstackAddon {
|
|
5
|
-
readonly manifest: AddonDeclaration = {
|
|
6
|
-
id: 'mock-log-addon',
|
|
7
|
-
name: 'Mock Log Destination',
|
|
8
|
-
version: '1.0.0',
|
|
9
|
-
capabilities: ['log-destination'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
private initialized = false
|
|
13
|
-
readonly provider = { id: 'mock-log', write: async () => {} }
|
|
14
|
-
|
|
15
|
-
async initialize(_context: AddonContext) {
|
|
16
|
-
this.initialized = true
|
|
17
|
-
return [{ capability: 'log-destination', provider: this.provider }]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async shutdown(): Promise<void> {
|
|
21
|
-
this.initialized = false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.initialized
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
getConfigSchema() {
|
|
29
|
-
return { sections: [] }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getConfig() {
|
|
33
|
-
return {}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async onConfigChange() {}
|
|
37
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// server/backend/src/__tests__/fixtures/mock-storage-addon.ts
|
|
2
|
-
import type { ICamstackAddon, AddonDeclaration, AddonContext } from '@camstack/types'
|
|
3
|
-
|
|
4
|
-
export class MockStorageAddon implements ICamstackAddon {
|
|
5
|
-
readonly manifest: AddonDeclaration = {
|
|
6
|
-
id: 'mock-storage',
|
|
7
|
-
name: 'Mock Storage',
|
|
8
|
-
version: '1.0.0',
|
|
9
|
-
capabilities: ['storage', 'settings-store'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
private initialized = false
|
|
13
|
-
readonly provider = { id: 'mock-storage', type: 'mock' }
|
|
14
|
-
|
|
15
|
-
async initialize(_context: AddonContext) {
|
|
16
|
-
this.initialized = true
|
|
17
|
-
return [
|
|
18
|
-
{ capability: 'storage', provider: this.provider },
|
|
19
|
-
{ capability: 'settings-store', provider: this.provider },
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async shutdown(): Promise<void> {
|
|
24
|
-
this.initialized = false
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
isInitialized(): boolean {
|
|
28
|
-
return this.initialized
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getConfigSchema() {
|
|
32
|
-
return { sections: [] }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
getConfig() {
|
|
36
|
-
return {}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async onConfigChange() {}
|
|
40
|
-
}
|