From ed226015ec7f7051a1d576c25d0c9987aa445df1 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 12:09:20 +0300 Subject: [PATCH 1/7] build(strict): add strictNullChecks ratchet via betterer Enabling strictNullChecks surfaces 959 latent null-safety issues across the app sources (issue #6). Rather than flip the flag globally and fix all at once, lock today's error set as a betterer baseline and fail CI only on new issues, so the count ratchets down incrementally per PR. - tsconfig.strict.json: base config + strictNullChecks for local `npm run snc` - tsconfig.betterer.json: commonjs config so ts-node can load .betterer.ts (the app's bundler moduleResolution is incompatible with commonjs) - .betterer.results: committed baseline snapshot (959 issues) - CI: strict-null-checks job runs `betterer:ci` Refs #6 Co-Authored-By: Claude Opus 4.8 (1M context) --- .betterer.results | 1116 ++++++++++++++++++++++++++++++++++++++ .betterer.ts | 11 + .github/workflows/ci.yml | 21 + .gitignore | 1 + package-lock.json | 535 ++++++++++++++++++ package.json | 5 + tsconfig.betterer.json | 10 + tsconfig.strict.json | 9 + 8 files changed, 1708 insertions(+) create mode 100644 .betterer.results create mode 100644 .betterer.ts create mode 100644 tsconfig.betterer.json create mode 100644 tsconfig.strict.json diff --git a/.betterer.results b/.betterer.results new file mode 100644 index 00000000..f504f6c1 --- /dev/null +++ b/.betterer.results @@ -0,0 +1,1116 @@ +// BETTERER RESULTS V2. +// +// If this file contains merge conflicts, use `betterer merge` to automatically resolve them: +// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge +// +exports[`strictNullChecks`] = { + value: `{ + "src/app/app.component.ts:1462426010": [ + [112, 19, 25, "Object is possibly \'undefined\'.", "255143637"] + ], + "src/app/core/components/dashboard/dashboard.component.ts:1605211129": [ + [106, 12, 22, "Object is possibly \'undefined\'.", "2187394131"], + [116, 27, 9, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2171020380"], + [150, 8, 22, "Object is possibly \'undefined\'.", "2187394131"], + [156, 14, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [242, 25, 23, "Argument of type \'[] | NgGridStackWidget[] | undefined\' is not assignable to parameter of type \'GridStackWidget[]\'.\\n Type \'undefined\' is not assignable to type \'GridStackWidget[]\'.", "90124799"], + [262, 27, 22, "Object is possibly \'undefined\'.", "2187394131"], + [264, 39, 32, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "730256750"], + [274, 23, 32, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "730256750"], + [285, 21, 22, "Object is possibly \'undefined\'.", "2187394131"], + [286, 9, 22, "Object is possibly \'undefined\'.", "2187394131"], + [511, 6, 4, "\'grid\' is possibly \'undefined\'.", "2087632733"], + [512, 6, 4, "\'grid\' is possibly \'undefined\'.", "2087632733"], + [515, 49, 4, "Argument of type \'GridStack | undefined\' is not assignable to parameter of type \'{ isAreaEmpty: (x: number, y: number, w: number, h: number) => boolean; willItFit: (w: NgGridStackWidget) => boolean; }\'.\\n Type \'undefined\' is not assignable to type \'{ isAreaEmpty: (x: number, y: number, w: number, h: number) => boolean; willItFit: (w: NgGridStackWidget) => boolean; }\'.", "2087632733"], + [526, 17, 22, "Object is possibly \'undefined\'.", "2187394131"], + [527, 8, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [528, 6, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [528, 6, 30, "\'item.gridstackNode.subGridOpts\' is possibly \'undefined\'.", "514924546"], + [528, 43, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [528, 43, 30, "\'item.gridstackNode.subGridOpts\' is possibly \'undefined\'.", "514924546"], + [528, 83, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [528, 83, 30, "\'item.gridstackNode.subGridOpts\' is possibly \'undefined\'.", "514924546"], + [528, 123, 18, "\'item.gridstackNode\' is possibly \'undefined\'.", "697090056"], + [567, 10, 22, "Object is possibly \'undefined\'.", "2187394131"], + [578, 14, 22, "Object is possibly \'undefined\'.", "2187394131"], + [585, 10, 6, "Type \'GridStackNode | undefined\' is not assignable to type \'NgGridStackWidget\'.\\n Type \'undefined\' is not assignable to type \'NgGridStackWidget\'.", "2169993400"], + [592, 16, 12, "\'source.input\' is possibly \'undefined\'.", "908845952"], + [594, 28, 12, "\'source.input\' is possibly \'undefined\'.", "908845952"], + [600, 8, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [601, 6, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [605, 10, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [606, 8, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [641, 8, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [642, 6, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [647, 10, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [648, 8, 15, "\'_gridstack.grid\' is possibly \'undefined\'.", "3391422426"], + [657, 4, 22, "Object is possibly \'undefined\'.", "2187394131"] + ], + "src/app/core/components/dashboards-editor/dashboards-editor.component.ts:976168273": [ + [80, 39, 40, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1983996063"], + [94, 6, 4, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2087876002"], + [140, 17, 13, "\'currentActive\' is possibly \'null\'.", "194687392"], + [140, 56, 13, "\'currentActive\' is possibly \'null\'.", "194687392"], + [142, 44, 13, "\'currentActive\' is possibly \'null\'.", "194687392"], + [143, 17, 13, "\'currentActive\' is possibly \'null\'.", "194687392"], + [143, 56, 13, "\'currentActive\' is possibly \'null\'.", "194687392"], + [145, 44, 13, "\'currentActive\' is possibly \'null\'.", "194687392"] + ], + "src/app/core/components/data-inspector-row/data-inspector-row.component.ts:3525188709": [ + [25, 34, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [34, 4, 10, "Type \'IConversionPathList\' is not assignable to type \'null\'.", "4013178488"], + [35, 24, 10, "Object is possibly \'null\'.", "4013178488"] + ], + "src/app/core/components/data-inspector/data-inspector.component.ts:3426964144": [ + [71, 4, 20, "Type \'Signal\' is not assignable to type \'Signal\'.\\n Type \'Signal\' is not assignable to type \'() => BreakpointState\'.\\n Type \'BreakpointState | undefined\' is not assignable to type \'BreakpointState\'.\\n Type \'undefined\' is not assignable to type \'BreakpointState\'.", "1507091964"] + ], + "src/app/core/components/group-widget/group-widget.component.ts:2421937404": [ + [85, 8, 6, "Type \'IWidgetSvcConfig | undefined\' is not assignable to type \'object\'.\\n Type \'undefined\' is not assignable to type \'object\'.", "1544266319"] + ], + "src/app/core/components/options/notifications/notifications.component.ts:3253454142": [ + [41, 4, 20, "Type \'Signal\' is not assignable to type \'Signal\'.\\n Type \'Signal\' is not assignable to type \'() => BreakpointState\'.\\n Type \'BreakpointState | undefined\' is not assignable to type \'BreakpointState\'.\\n Type \'undefined\' is not assignable to type \'BreakpointState\'.", "1507091964"], + [47, 4, 24, "Object is possibly \'undefined\'.", "2222700196"], + [54, 6, 17, "Object is possibly \'undefined\'.", "2944426029"], + [55, 6, 17, "Object is possibly \'undefined\'.", "27783321"] + ], + "src/app/core/components/options/signalk/signalk.component.ts:3522798646": [ + [101, 10, 6, "Type \'null\' is not assignable to type \'Chart\'.", "3491622934"], + [110, 45, 20, "Object is possibly \'undefined\'.", "235433357"], + [171, 28, 20, "Object is possibly \'undefined\'.", "235433357"], + [171, 28, 51, "Argument of type \'CanvasRenderingContext2D | null\' is not assignable to parameter of type \'ChartItem\'.\\n Type \'null\' is not assignable to type \'ChartItem\'.", "38422540"] + ], + "src/app/core/components/options/units/units.component.ts:2220224509": [ + [36, 19, 4, "Argument of type \'IUnit\' is not assignable to parameter of type \'never\'.", "2087995779"] + ], + "src/app/core/components/settings/settings.component.ts:794917258": [ + [23, 45, 23, "Object is possibly \'undefined\'.", "214257314"], + [24, 46, 24, "Object is possibly \'undefined\'.", "3441589046"] + ], + "src/app/core/components/widget-embedded/widget-embedded.component.ts:3928020917": [ + [114, 4, 13, "Type \'Type | undefined\' is not assignable to type \'Type\'.\\n Type \'undefined\' is not assignable to type \'Type\'.", "2689238020"] + ], + "src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts:4108070123": [ + [221, 12, 1, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "177628"], + [284, 6, 12, "Object is possibly \'null\'.", "765689629"], + [285, 6, 12, "Object is possibly \'null\'.", "765689629"], + [286, 6, 12, "Object is possibly \'null\'.", "765689629"], + [287, 6, 12, "Object is possibly \'null\'.", "765689629"], + [288, 6, 12, "Object is possibly \'null\'.", "765689629"], + [289, 6, 12, "Object is possibly \'null\'.", "765689629"], + [290, 6, 12, "Object is possibly \'null\'.", "765689629"], + [291, 6, 12, "Object is possibly \'null\'.", "765689629"], + [305, 23, 12, "Object is possibly \'null\'.", "765689629"], + [306, 56, 6, "Property \'length\' does not exist on type \'never\'.", "1433765721"], + [418, 4, 6, "Type \'({ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; trackedDevices?: null | undefined; ownerWidgetUuid: string; ownerWidgetSelector: string | null; ... 8 more ...; reconcileTs?: number | undefined; } | { ...; })[]\' is not assignable to type \'IKipSeriesDefinition[]\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; trackedDevices?: null | undefined; ownerWidgetUuid: string; ownerWidgetSelector: string | null; ... 8 more ...; reconcileTs?: number | undefined; } | { ...; }\' is not assignable to type \'IKipSeriesDefinition\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; ownerWidgetSelector: \\"widget-bms\\" | \\"widget-solar-charger\\" | \\"widget-charger\\" | \\"widget-inverter\\" | \\"widget-alternator\\" | \\"widget-ac\\"; ... 10 more ...; reconcileTs?: number; }\' is not assignable to type \'IKipSeriesDefinition\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; ownerWidgetSelector: \\"widget-bms\\" | \\"widget-solar-charger\\" | \\"widget-charger\\" | \\"widget-inverter\\" | \\"widget-alternator\\" | \\"widget-ac\\"; ... 10 more ...; reconcileTs?: number; }\' is not assignable to type \'IKipConcreteSeriesDefinition\'.\\n Types of property \'trackedDevices\' are incompatible.\\n Type \'readonly IElectricalTrackedDeviceRef[] | null | undefined\' is not assignable to type \'null | undefined\'.\\n Type \'readonly IElectricalTrackedDeviceRef[]\' is not assignable to type \'null | undefined\'.", "2123913871"], + [560, 21, 12, "Object is possibly \'null\'.", "765689629"], + [563, 21, 12, "Object is possibly \'null\'.", "765689629"], + [571, 21, 12, "Object is possibly \'null\'.", "765689629"], + [590, 59, 12, "Object is possibly \'null\'.", "765689629"], + [643, 19, 12, "Object is possibly \'null\'.", "765689629"], + [646, 19, 12, "Object is possibly \'null\'.", "765689629"], + [651, 19, 12, "Object is possibly \'null\'.", "765689629"], + [664, 10, 12, "Object is possibly \'null\'.", "765689629"], + [672, 17, 12, "Object is possibly \'null\'.", "765689629"], + [676, 17, 12, "Object is possibly \'null\'.", "765689629"], + [705, 4, 6, "Type \'{ fillStyle: Color; lineDash: _DeepPartialArray; text: string; borderRadius?: number | BorderRadius | undefined; datasetIndex?: number | undefined; ... 10 more ...; textAlign?: TextAlign | undefined; }[]\' is not assignable to type \'LegendItem[]\'.\\n Type \'{ fillStyle: Color; lineDash: _DeepPartialArray; text: string; borderRadius?: number | BorderRadius; datasetIndex?: number; ... 10 more ...; textAlign?: TextAlign; }\' is not assignable to type \'LegendItem\'.\\n Types of property \'lineDash\' are incompatible.\\n Type \'_DeepPartialArray\' is not assignable to type \'number[]\'.\\n Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "2123913871"] + ], + "src/app/core/components/widget-host2/widget-host2.component.ts:867588761": [ + [137, 4, 13, "Type \'Type | undefined\' is not assignable to type \'Type\'.\\n Type \'undefined\' is not assignable to type \'Type\'.", "2689238020"], + [253, 32, 9, "Type \'null\' cannot be used as an index type.", "2171020380"] + ], + "src/app/core/components/widget-title/widget-title.component.ts:2551944470": [ + [25, 4, 14, "Type \'CanvasRenderingContext2D | null\' is not assignable to type \'CanvasRenderingContext2D\'.\\n Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], + [54, 4, 14, "Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], + [55, 4, 18, "Type \'null\' is not assignable to type \'HTMLCanvasElement\'.", "1193423419"] + ], + "src/app/core/services/app-initNetwork.service.ts:3963658519": [ + [40, 10, 10, "Type \'null\' is not assignable to type \'boolean\'.", "4095117658"], + [41, 10, 20, "Type \'null\' is not assignable to type \'Subscription\'.", "1129182691"], + [340, 29, 43, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2187486198"] + ], + "src/app/core/services/app-service.ts:2341917173": [ + [67, 10, 19, "Type \'null\' is not assignable to type \'ITheme\'.", "1256751570"], + [100, 19, 23, "Object is possibly \'undefined\'.", "1034891909"], + [127, 4, 24, "Type \'ITheme | null\' is not assignable to type \'ITheme\'.\\n Type \'null\' is not assignable to type \'ITheme\'.", "3614169146"], + [223, 4, 24, "Type \'ITheme | null\' is not assignable to type \'ITheme\'.\\n Type \'null\' is not assignable to type \'ITheme\'.", "3614169146"] + ], + "src/app/core/services/canvas.service.ts:2103067572": [ + [76, 8, 13, "Cannot invoke an object which is possibly \'undefined\'.", "3459502336"] + ], + "src/app/core/services/configuration-upgrade.service.ts:1608134649": [ + [181, 47, 3, "Type \'null\' is not assignable to type \'v10IAppConfig\'.", "193410244"], + [181, 58, 6, "Type \'null\' is not assignable to type \'v10IWidgetConfig\'.", "2016758345"], + [181, 72, 6, "Type \'null\' is not assignable to type \'v10ILayoutConfig\'.", "1429788639"], + [181, 86, 5, "Type \'null\' is not assignable to type \'v10IThemeConfig\'.", "183206964"], + [218, 12, 20, "\'oldConfiguration.app\' is possibly \'null\'.", "103008637"], + [239, 55, 10, "Type \'null\' is not assignable to type \'Dashboard[]\'.", "1337325010"], + [242, 6, 22, "\'localStorageConfig.app\' is possibly \'null\'.", "1972111892"], + [243, 6, 22, "\'localStorageConfig.app\' is possibly \'null\'.", "1972111892"], + [244, 6, 24, "\'localStorageConfig.theme\' is possibly \'null\'.", "122221156"], + [304, 14, 6, "Type \'null\' is not assignable to type \'IAppConfig\'.", "2123913871"], + [313, 16, 6, "Type \'null\' is not assignable to type \'IThemeConfig\'.", "2123913871"], + [320, 10, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [321, 59, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [324, 38, 10, "Argument of type \'IAppConfig | null\' is not assignable to parameter of type \'IAppConfig\'.\\n Type \'null\' is not assignable to type \'IAppConfig\'.", "3800660544"], + [377, 6, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [384, 58, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [396, 10, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [397, 59, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], + [417, 6, 10, "\'config.app\' is possibly \'null\'.", "3800660544"] + ], + "src/app/core/services/dashboard.service.ts:89333634": [ + [32, 63, 4, "Argument of type \'null\' is not assignable to parameter of type \'widgetOperation\'.", "2087897566"], + [286, 9, 22, "Object is possibly \'null\'.", "2213989156"], + [289, 31, 22, "Object is possibly \'null\'.", "2213989156"], + [299, 9, 22, "Object is possibly \'null\'.", "2213989156"], + [302, 31, 22, "Object is possibly \'null\'.", "2213989156"], + [333, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "1592152070"], + [334, 9, 22, "Object is possibly \'null\'.", "2213989156"], + [337, 22, 22, "Object is possibly \'null\'.", "2213989156"], + [348, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "1592152070"], + [349, 9, 22, "Object is possibly \'null\'.", "2213989156"], + [352, 22, 22, "Object is possibly \'null\'.", "2213989156"] + ], + "src/app/core/services/data.service.ts:1120858683": [ + [241, 8, 12, "Type \'null\' is not assignable to type \'string\'.", "2101405159"], + [242, 8, 16, "Type \'null\' is not assignable to type \'string\'.", "4266228162"], + [252, 8, 10, "Type \'null\' is not assignable to type \'ISkMetadata\'.", "2623107177"], + [270, 6, 10, "Type \'ISkMetadata | null\' is not assignable to type \'ISkMetadata\'.\\n Type \'null\' is not assignable to type \'ISkMetadata\'.", "2623107177"], + [402, 8, 4, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2087944093"], + [412, 35, 8, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "1214764093"], + [438, 37, 8, "\'pathItem\' is possibly \'undefined\'.", "1214764093"], + [438, 57, 8, "\'pathItem\' is possibly \'undefined\'.", "1214764093"], + [453, 29, 8, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "1214764093"], + [477, 10, 13, "Type \'undefined\' is not assignable to type \'string\'.", "3344503334"], + [478, 10, 4, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2087944093"], + [482, 10, 4, "Type \'ISkMetadata | null\' is not assignable to type \'ISkMetadata | undefined\'.\\n Type \'null\' is not assignable to type \'ISkMetadata | undefined\'.", "2087701304"], + [484, 35, 10, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "384538813"], + [489, 33, 10, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "384538813"], + [494, 10, 15, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1816235499"], + [505, 31, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"], + [505, 59, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"], + [511, 74, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"] + ], + "src/app/core/services/notifications.service.ts:131921706": [ + [65, 10, 33, "Type \'null\' is not assignable to type \'Subscription\'.", "1613706193"], + [66, 10, 35, "Type \'null\' is not assignable to type \'Subscription\'.", "1439588120"], + [67, 10, 35, "Type \'null\' is not assignable to type \'Subscription\'.", "4279004213"], + [68, 10, 25, "Type \'null\' is not assignable to type \'Subscription\'.", "3596131985"], + [80, 10, 22, "Type \'null\' is not assignable to type \'number\'.", "1453628105"], + [83, 10, 17, "Type \'null\' is not assignable to type \'IAlarmInfo\'.", "2736621211"], + [252, 18, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"], + [262, 9, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"], + [267, 9, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"] + ], + "src/app/core/services/remote-dashboards.service.ts:2849282820": [ + [74, 43, 4, "Argument of type \'null\' is not assignable to parameter of type \'IScreensPayload\'.", "2087897566"], + [87, 12, 14, "Type \'undefined\' is not assignable to type \'IScreensPayload\'.", "2447800916"], + [90, 10, 14, "Type \'null\' is not assignable to type \'IScreensPayload\'.", "2447800916"], + [134, 12, 8, "\'changeTo\' is possibly \'undefined\'.", "2212884184"], + [135, 27, 8, "\'changeTo\' is possibly \'undefined\'.", "2212884184"] + ], + "src/app/core/services/settings.service.ts:3918426213": [ + [227, 27, 21, "Object is possibly \'null\'.", "1182065060"], + [228, 33, 21, "Object is possibly \'null\'.", "1182065060"], + [230, 8, 21, "Object is possibly \'null\'.", "1182065060"], + [233, 30, 21, "Object is possibly \'null\'.", "1182065060"], + [236, 8, 21, "Object is possibly \'null\'.", "1182065060"], + [239, 29, 21, "Object is possibly \'null\'.", "1182065060"], + [242, 8, 21, "Object is possibly \'null\'.", "1182065060"], + [245, 36, 21, "Object is possibly \'null\'.", "1182065060"], + [254, 8, 21, "Object is possibly \'null\'.", "1182065060"], + [257, 32, 21, "Object is possibly \'null\'.", "1182065060"] + ], + "src/app/core/services/signalk-connection.service.ts:1697882337": [ + [66, 4, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], + [67, 4, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], + [68, 4, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], + [74, 54, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"], + [175, 6, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], + [176, 6, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], + [177, 6, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], + [206, 73, 16, "Argument of type \'HttpResponse\' is not assignable to parameter of type \'{ body: ISignalKEndpointResponse; status: number; }\'.\\n Types of property \'body\' are incompatible.\\n Type \'ISignalKEndpointResponse | null\' is not assignable to type \'ISignalKEndpointResponse\'.\\n Type \'null\' is not assignable to type \'ISignalKEndpointResponse\'.", "920189509"], + [268, 66, 16, "Argument of type \'HttpResponse\' is not assignable to parameter of type \'{ body: ISignalKEndpointResponse; status: number; }\'.\\n Types of property \'body\' are incompatible.\\n Type \'ISignalKEndpointResponse | null\' is not assignable to type \'ISignalKEndpointResponse\'.\\n Type \'null\' is not assignable to type \'ISignalKEndpointResponse\'.", "920189509"], + [279, 8, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], + [280, 8, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], + [281, 8, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], + [316, 6, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], + [317, 6, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], + [322, 79, 7, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string | URL\'.\\n Type \'undefined\' is not assignable to type \'string | URL\'.", "1210482486"], + [323, 131, 5, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string | URL\'.\\n Type \'undefined\' is not assignable to type \'string | URL\'.", "190446474"], + [331, 6, 37, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4248587053"], + [334, 6, 35, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "299693937"], + [334, 58, 5, "\'wsUrl\' is possibly \'undefined\'.", "190446474"] + ], + "src/app/core/services/signalk-delta.service.ts:3304767793": [ + [61, 10, 8, "Type \'undefined\' is not assignable to type \'string\'.", "2634452559"], + [75, 10, 10, "Type \'null\' is not assignable to type \'string\'.", "1874862562"], + [334, 41, 15, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "876287967"], + [351, 32, 12, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "3845597927"] + ], + "src/app/core/services/signalk-requests.service.ts:2835987348": [ + [92, 6, 5, "Type \'null\' is not assignable to type \'string\'.", "195031250"], + [93, 6, 10, "Type \'null\' is not assignable to type \'number\'.", "3382497788"], + [109, 6, 26, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2950021774"], + [110, 6, 31, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3535017376"], + [113, 49, 16, "Type \'undefined\' cannot be used as an index type.", "1603728970"], + [124, 26, 28, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4291747378"], + [139, 26, 6, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1121335604"] + ], + "src/app/core/services/storage.service.ts:1590506663": [ + [54, 10, 14, "Type \'null\' is not assignable to type \'string\'.", "2483034339"], + [56, 10, 17, "Type \'null\' is not assignable to type \'number\'.", "1298990515"], + [58, 10, 10, "Type \'null\' is not assignable to type \'IConfig\'.", "710858837"], + [62, 10, 14, "Type \'undefined\' is not assignable to type \'IEndpointStatus\'.", "2899844880"], + [282, 6, 6, "Type \'null\' is not assignable to type \'Config[]\'.", "2123913871"], + [347, 6, 6, "Type \'null\' is not assignable to type \'IConfig\'.", "2123913871"] + ], + "src/app/core/utils/dataScales.util.ts:2208923770": [ + [54, 6, 12, "Type \'null\' is not assignable to type \'number\'.", "2083421902"] + ], + "src/app/core/utils/zones-highlight.utils.ts:1634660371": [ + [42, 8, 5, "Type \'null\' is not assignable to type \'number\'.", "173263814"], + [43, 8, 5, "Type \'null\' is not assignable to type \'number\'.", "183473191"], + [72, 6, 5, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "173263814"], + [78, 6, 5, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "183473191"] + ], + "src/app/widget-config/boolean-control-config/boolean-control-config.component.ts:3033536446": [ + [25, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [26, 39, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [33, 4, 11, "Type \'{ label: string; value: string; }[]\' is not assignable to type \'never[]\'.\\n Type \'{ label: string; value: string; }\' is not assignable to type \'never\'.", "3027418051"], + [37, 79, 34, "Object is possibly \'null\'.", "3704244789"] + ], + "src/app/widget-config/boolean-multicontrol-options/boolean-multicontrol-options.component.ts:1795746450": [ + [35, 9, 14, "Type \'null\' is not assignable to type \'UntypedFormGroup\'.", "3522031013"], + [37, 10, 26, "Type \'null\' is not assignable to type \'Subscription\'.", "273497119"], + [79, 8, 13, "Type \'null\' is not assignable to type \'string | undefined\'.", "2466344431"] + ], + "src/app/widget-config/dataset-chart-options/dataset-chart-options.component.ts:4252167719": [ + [82, 26, 10, "Argument of type \'ISkPathData | null\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'null\' is not assignable to type \'ISkPathData\'.", "384538813"] + ], + "src/app/widget-config/display-chart-options/display-chart-options.component.ts:333710162": [ + [48, 4, 11, "Type \'{ label: string; value: string; }[]\' is not assignable to type \'never[]\'.\\n Type \'{ label: string; value: string; }\' is not assignable to type \'never\'.", "3027418051"] + ], + "src/app/widget-config/display-datetime/display-datetime.component.ts:1505324315": [ + [45, 50, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'UntypedFormControl\'.", "2620553983"], + [46, 52, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'UntypedFormControl\'.", "2620553983"], + [49, 10, 22, "Type \'null\' is not assignable to type \'Subscription\'.", "29842089"] + ], + "src/app/widget-config/electrical-family-setup/electrical-family-setup.component.ts:4115844403": [ + [60, 48, 28, "Object is possibly \'undefined\'.", "758680035"], + [114, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [115, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [119, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [120, 4, 20, "Object is possibly \'undefined\'.", "2522653710"] + ], + "src/app/widget-config/paths-options/paths-options.component.ts:3600643100": [ + [31, 51, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'IAddNewPathObject\'.", "2620553983"], + [32, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [33, 54, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'IDynamicControl[]\'.", "2620553983"], + [42, 28, 49, "Object is possibly \'null\'.", "3725617003"], + [66, 26, 49, "Object is possibly \'null\'.", "3725617003"], + [70, 26, 49, "Object is possibly \'null\'.", "3725617003"] + ], + "src/app/widget-config/root-modal-widget-config/root-modal-widget-config.component.ts:2942117481": [ + [74, 4, 11, "Type \'{ label: string; value: string; }[]\' is not assignable to type \'never[]\'.\\n Type \'{ label: string; value: string; }\' is not assignable to type \'never\'.", "3027418051"], + [181, 8, 4, "Type \'null\' is not assignable to type \'UntypedFormControl\'.", "2087777580"], + [202, 4, 9, "\'ctrlLabel\' is possibly \'null\'.", "2892925034"] + ], + "src/app/widget-config/solar-charger-setup/solar-charger-setup.component.ts:3993417046": [ + [47, 48, 28, "Object is possibly \'undefined\'.", "758680035"], + [151, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [152, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [156, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], + [157, 4, 20, "Object is possibly \'undefined\'.", "2522653710"] + ], + "src/app/widgets/gauge-steel/gauge-steel.component.ts:3394617356": [ + [74, 38, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [75, 35, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [76, 37, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'boolean\'.", "2620553983"], + [77, 38, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [78, 43, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [79, 38, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [80, 36, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [81, 36, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [82, 36, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [83, 36, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'ISkZone[]\'.", "2620553983"], + [84, 33, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [85, 33, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [86, 33, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [127, 52, 7, "\'a.lower\' is possibly \'undefined\'.", "2967727561"], + [127, 62, 7, "\'b.lower\' is possibly \'undefined\'.", "4173952970"], + [130, 12, 5, "Type \'null\' is not assignable to type \'number\'.", "173263814"], + [131, 12, 5, "Type \'null\' is not assignable to type \'number\'.", "183473191"], + [136, 20, 12, "Object is possibly \'undefined\'.", "765689629"], + [139, 20, 12, "Object is possibly \'undefined\'.", "765689629"], + [142, 20, 12, "Object is possibly \'undefined\'.", "765689629"], + [145, 20, 12, "Object is possibly \'undefined\'.", "765689629"], + [148, 20, 12, "Object is possibly \'undefined\'.", "765689629"], + [157, 10, 5, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "173263814"], + [158, 10, 5, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "183473191"], + [160, 10, 5, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "173263814"], + [160, 57, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "467412886"], + [161, 10, 5, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "183473191"], + [161, 57, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "440544247"], + [179, 24, 40, "Argument of type \'any\' is not assignable to parameter of type \'never\'.", "1277859535"], + [183, 22, 40, "Argument of type \'any\' is not assignable to parameter of type \'never\'.", "1277859535"] + ], + "src/app/widgets/minichart/minichart.component.ts:15445999": [ + [43, 9, 5, "Type \'null\' is not assignable to type \'string\'.", "176948952"], + [44, 9, 8, "Type \'null\' is not assignable to type \'string\'.", "806402680"], + [45, 9, 10, "Type \'null\' is not assignable to type \'string\'.", "1967398792"], + [46, 9, 13, "Type \'null\' is not assignable to type \'string\'.", "2466344431"], + [47, 9, 10, "Type \'null\' is not assignable to type \'number\'.", "2101602424"], + [48, 9, 9, "Type \'null\' is not assignable to type \'number\'.", "212862030"], + [49, 9, 9, "Type \'null\' is not assignable to type \'number\'.", "212862288"], + [77, 10, 9, "Type \'null\' is not assignable to type \'Subscription\'.", "4113745821"], + [78, 10, 14, "Type \'null\' is not assignable to type \'IChartDataSourceInfo\'.", "2985720646"], + [152, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [153, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [155, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [158, 6, 21, "Object is possibly \'undefined\'.", "406043295"], + [162, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], + [163, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], + [164, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], + [165, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], + [219, 6, 21, "Object is possibly \'undefined\'.", "406043295"], + [254, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], + [255, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], + [256, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], + [257, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], + [281, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [294, 26, 21, "Object is possibly \'undefined\'.", "406043295"], + [339, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], + [340, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], + [341, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], + [342, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], + [343, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], + [344, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], + [345, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], + [546, 4, 14, "Type \'null\' is not assignable to type \'Subscription\'.", "2516429621"], + [558, 4, 6, "Type \'({ x: number | null; y: number; } | { x: number; y: number | null; })[]\' is not assignable to type \'IDataSetRow[]\'.\\n Type \'{ x: number | null; y: number; } | { x: number; y: number | null; }\' is not assignable to type \'IDataSetRow\'.\\n Type \'{ x: number | null; y: number; }\' is not assignable to type \'IDataSetRow\'.\\n Types of property \'x\' are incompatible.\\n Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2123913871"] + ], + "src/app/widgets/svg-boolean-button/svg-boolean-button.component.ts:1543445686": [ + [14, 41, 4, "No overload matches this call.\\n Overload 1 of 5, \'(initialValue: IDynamicControl, opts?: InputOptionsWithoutTransform | undefined): InputSignal<...>\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'IDynamicControl\'.\\n Overload 2 of 5, \'(initialValue: undefined, opts: InputOptionsWithoutTransform): InputSignal\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'undefined\'.", "2087897566"], + [15, 33, 4, "Argument of type \'null\' is not assignable to parameter of type \'ITheme\'.", "2087897566"], + [21, 10, 8, "Type \'null\' is not assignable to type \'ITheme\'.", "2459887219"], + [114, 8, 22, "Type \'\\"black\\"\' is not assignable to type \'null\'.", "807528371"], + [115, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [116, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [119, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [120, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [121, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [124, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [125, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [126, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [129, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [130, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [131, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [134, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [135, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [136, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [139, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [140, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [141, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [144, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [145, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [146, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [149, 8, 22, "Type \'string\' is not assignable to type \'null\'.", "807528371"], + [150, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [151, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [154, 8, 22, "Type \'\\"black\\"\' is not assignable to type \'null\'.", "807528371"], + [155, 8, 23, "Type \'string\' is not assignable to type \'null\'.", "2686791206"], + [156, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"] + ], + "src/app/widgets/svg-boolean-light/svg-boolean-light.component.ts:520976916": [ + [15, 41, 4, "No overload matches this call.\\n Overload 1 of 5, \'(initialValue: IDynamicControl, opts?: InputOptionsWithoutTransform | undefined): InputSignal<...>\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'IDynamicControl\'.\\n Overload 2 of 5, \'(initialValue: undefined, opts: InputOptionsWithoutTransform): InputSignal\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'undefined\'.", "2087897566"], + [16, 33, 4, "Argument of type \'null\' is not assignable to parameter of type \'ITheme\'.", "2087897566"], + [22, 10, 9, "Type \'null\' is not assignable to type \'boolean\'.", "2931300859"], + [24, 10, 8, "Type \'null\' is not assignable to type \'ITheme\'.", "2459887219"], + [59, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [60, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [63, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [64, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [67, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [68, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [71, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [72, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [75, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [76, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [79, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [80, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [83, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [84, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [87, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [88, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [91, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [92, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"] + ], + "src/app/widgets/svg-boolean-switch/svg-boolean-switch.component.ts:1706226148": [ + [13, 41, 4, "No overload matches this call.\\n Overload 1 of 5, \'(initialValue: IDynamicControl, opts?: InputOptionsWithoutTransform | undefined): InputSignal<...>\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'IDynamicControl\'.\\n Overload 2 of 5, \'(initialValue: undefined, opts: InputOptionsWithoutTransform): InputSignal\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'undefined\'.", "2087897566"], + [14, 33, 4, "Argument of type \'null\' is not assignable to parameter of type \'ITheme\'.", "2087897566"], + [20, 10, 9, "Type \'null\' is not assignable to type \'boolean\'.", "2931300859"], + [21, 10, 8, "Type \'null\' is not assignable to type \'ITheme\'.", "2459887219"], + [75, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [76, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [79, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [80, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [83, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [84, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [87, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [88, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [91, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [92, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [95, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [96, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [99, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [100, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [103, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [104, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"], + [107, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "2145130966"], + [108, 8, 15, "Type \'string\' is not assignable to type \'null\'.", "1525246139"] + ], + "src/app/widgets/svg-racesteer/svg-racesteer.component.ts:3776013780": [ + [36, 46, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [37, 47, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [39, 51, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [40, 51, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [41, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [42, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [43, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"] + ], + "src/app/widgets/svg-windsteer/svg-windsteer.component.ts:3669162095": [ + [28, 59, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [39, 50, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [44, 46, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [45, 47, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [46, 51, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [48, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [49, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], + [50, 57, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"] + ], + "src/app/widgets/svg-zone-states/svg-zone-states.component.ts:1762718998": [ + [17, 41, 4, "No overload matches this call.\\n Overload 1 of 5, \'(initialValue: IDynamicControl, opts?: InputOptionsWithoutTransform | undefined): InputSignal<...>\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'IDynamicControl\'.\\n Overload 2 of 5, \'(initialValue: undefined, opts: InputOptionsWithoutTransform): InputSignal\', gave the following error.\\n Argument of type \'null\' is not assignable to parameter of type \'undefined\'.", "2087897566"], + [18, 33, 4, "Argument of type \'null\' is not assignable to parameter of type \'ITheme\'.", "2087897566"], + [22, 41, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"], + [23, 41, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"], + [24, 42, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"] + ], + "src/app/widgets/widget-ais-radar/dialog-ais-target/dialog-ais-target.component.ts:2335594053": [ + [41, 11, 38, "Object is possibly \'null\'.", "3959613788"], + [46, 14, 37, "Object is possibly \'null\'.", "1171285017"], + [85, 11, 34, "Object is possibly \'null\'.", "4280675262"] + ], + "src/app/widgets/widget-ais-radar/widget-ais-radar.component.ts:4016173371": [ + [416, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [543, 29, 17, "Object is possibly \'undefined\'.", "671087258"], + [738, 49, 14, "Argument of type \'Position | undefined\' is not assignable to parameter of type \'Position\'.\\n Type \'undefined\' is not assignable to type \'Position\'.", "3448801501"], + [739, 56, 14, "Argument of type \'Position | undefined\' is not assignable to parameter of type \'Position\'.\\n Type \'undefined\' is not assignable to type \'Position\'.", "3448801501"], + [762, 8, 6, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2141063537"], + [774, 8, 6, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2141063537"], + [1350, 17, 10, "\'a.latitude\' is possibly \'undefined\'.", "3625974138"], + [1351, 17, 10, "\'b.latitude\' is possibly \'undefined\'.", "3132265881"], + [1352, 18, 10, "\'b.latitude\' is possibly \'undefined\'.", "3132265881"], + [1352, 31, 10, "\'a.latitude\' is possibly \'undefined\'.", "3625974138"], + [1353, 21, 11, "\'b.longitude\' is possibly \'undefined\'.", "3370058570"], + [1353, 35, 11, "\'a.longitude\' is possibly \'undefined\'.", "2985066057"] + ], + "src/app/widgets/widget-autopilot/widget-autopilot.component.ts:2942713930": [ + [275, 6, 10, "Type \'null\' is not assignable to type \'\\"v1\\" | \\"v2\\"\'.", "2208951047"], + [324, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [324, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [336, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [336, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [341, 47, 5, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "195031250"], + [351, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [351, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [363, 77, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], + [370, 77, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], + [401, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [401, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [413, 52, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], + [421, 52, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], + [612, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [612, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [613, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [613, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [614, 21, 22, "Object is possibly \'undefined\'.", "576852750"], + [614, 21, 32, "Object is possibly \'undefined\'.", "525756929"], + [674, 18, 22, "Object is possibly \'undefined\'.", "576852750"], + [674, 18, 32, "Object is possibly \'undefined\'.", "525756929"], + [688, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [696, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [704, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [712, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [726, 4, 22, "Object is possibly \'undefined\'.", "576852750"], + [726, 4, 28, "Object is possibly \'undefined\'.", "3500113022"], + [727, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [735, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [736, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [747, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [748, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [752, 26, 22, "Object is possibly \'undefined\'.", "576852750"], + [752, 26, 32, "Object is possibly \'undefined\'.", "525756929"], + [757, 8, 22, "Object is possibly \'undefined\'.", "576852750"], + [757, 8, 32, "Object is possibly \'undefined\'.", "525756929"], + [758, 6, 12, "Object is possibly \'null\'.", "4261782498"], + [760, 6, 12, "Object is possibly \'null\'.", "4261782498"], + [763, 4, 12, "Object is possibly \'null\'.", "4261782498"], + [801, 10, 22, "Object is possibly \'undefined\'.", "576852750"], + [801, 10, 32, "Object is possibly \'undefined\'.", "525756929"], + [834, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [834, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [1016, 10, 8, "Variable \'response\' is used before being assigned.", "2137101542"], + [1020, 104, 8, "Variable \'response\' is used before being assigned.", "2137101542"], + [1021, 15, 8, "Variable \'response\' is used before being assigned.", "2137101542"], + [1059, 8, 22, "Object is possibly \'undefined\'.", "576852750"], + [1059, 8, 32, "Object is possibly \'undefined\'.", "525756929"], + [1072, 8, 22, "Object is possibly \'undefined\'.", "576852750"], + [1072, 8, 32, "Object is possibly \'undefined\'.", "525756929"], + [1081, 8, 22, "Object is possibly \'undefined\'.", "576852750"], + [1081, 8, 32, "Object is possibly \'undefined\'.", "525756929"], + [1133, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], + [1143, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], + [1144, 17, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], + [1153, 19, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], + [1161, 21, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], + [1179, 17, 30, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1882595422"], + [1204, 23, 22, "Object is possibly \'undefined\'.", "576852750"], + [1204, 23, 32, "Object is possibly \'undefined\'.", "525756929"], + [1244, 17, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], + [1245, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], + [1246, 17, 30, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1882595422"], + [1247, 17, 25, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "398879383"] + ], + "src/app/widgets/widget-boolean-switch/widget-boolean-switch.component.ts:46644229": [ + [76, 38, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [99, 10, 12, "Object is possibly \'null\'.", "4261782498"] + ], + "src/app/widgets/widget-data-chart/widget-data-chart.component.ts:2822430409": [ + [80, 4, 18, "Type \'null\' is not assignable to type \'number | undefined\'.", "1866818091"], + [81, 4, 18, "Type \'null\' is not assignable to type \'number | undefined\'.", "1866818357"], + [83, 4, 9, "Type \'null\' is not assignable to type \'number | undefined\'.", "212862030"], + [84, 4, 9, "Type \'null\' is not assignable to type \'number | undefined\'.", "212862288"], + [107, 9, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 12, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 31, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 50, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 71, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 86, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [110, 98, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [123, 33, 22, "Argument of type \'IWidgetSvcConfig | undefined\' is not assignable to parameter of type \'IWidgetSvcConfig\'.\\n Type \'undefined\' is not assignable to type \'IWidgetSvcConfig\'.", "576852750"], + [137, 10, 22, "Type \'boolean | undefined\' is not assignable to type \'boolean | null\'.\\n Type \'undefined\' is not assignable to type \'boolean | null\'.", "609774233"], + [171, 71, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3461732844"], + [179, 6, 6, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1893769376"], + [187, 27, 22, "Object is possibly \'undefined\'.", "1240567228"], + [197, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [198, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [199, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [202, 6, 21, "Object is possibly \'undefined\'.", "406043295"], + [211, 26, 18, "Object is possibly \'null\'.", "3639984181"], + [211, 55, 18, "Object is possibly \'null\'.", "3639984181"], + [216, 18, 18, "Object is possibly \'null\'.", "3639984181"], + [237, 19, 12, "Object is possibly \'null\'.", "765689629"], + [244, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], + [245, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], + [246, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], + [247, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], + [266, 19, 12, "Object is possibly \'null\'.", "765689629"], + [271, 6, 21, "Object is possibly \'undefined\'.", "406043295"], + [277, 26, 18, "Object is possibly \'null\'.", "3639984181"], + [277, 55, 18, "Object is possibly \'null\'.", "3639984181"], + [282, 18, 18, "Object is possibly \'null\'.", "3639984181"], + [303, 19, 12, "Object is possibly \'null\'.", "765689629"], + [309, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], + [310, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], + [311, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], + [312, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], + [331, 19, 12, "Object is possibly \'null\'.", "765689629"], + [336, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [365, 8, 11, "Type \'{ minimumLine: { type: \\"line\\"; scaleID: string; display: boolean | undefined; value: null; drawTime: \\"afterDatasetsDraw\\"; label: { display: true; position: \\"start\\"; yAdjust: number; padding: number; color: string; backgroundColor: string; }; }; maximumLine: { ...; }; averageLine: { ...; }; }\' is not assignable to type \'_DeepPartialObject>> | _DeepPartialArray> | undefined\'.\\n Types of property \'minimumLine\' are incompatible.\\n Type \'{ type: \\"line\\"; scaleID: string; display: boolean | undefined; value: null; drawTime: \\"afterDatasetsDraw\\"; label: { display: true; position: \\"start\\"; yAdjust: number; padding: number; color: string; backgroundColor: string; }; }\' is not assignable to type \'_DeepPartialObject<{ type: \\"label\\"; } & LabelAnnotationOptions> | _DeepPartialObject<{ type: \\"ellipse\\"; } & EllipseAnnotationOptions> | ... 4 more ... | undefined\'.\\n Types of property \'value\' are incompatible.\\n Type \'null\' is not assignable to type \'string | number | ((ctx: PartialEventContext, options: AnnotationOptions) => string | number) | undefined\'.", "2422304113"], + [418, 18, 19, "Object is possibly \'null\'.", "1734867054"], + [418, 54, 19, "Object is possibly \'null\'.", "1734867054"], + [419, 15, 19, "Object is possibly \'null\'.", "1734867054"], + [420, 19, 18, "Object is possibly \'null\'.", "3639984181"], + [420, 70, 18, "Object is possibly \'null\'.", "3639984181"], + [420, 122, 18, "Object is possibly \'null\'.", "3639984181"], + [558, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], + [559, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], + [560, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], + [561, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], + [562, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], + [563, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], + [564, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], + [570, 29, 12, "Object is possibly \'null\'.", "765689629"], + [571, 29, 12, "Object is possibly \'null\'.", "765689629"], + [572, 31, 12, "Object is possibly \'null\'.", "765689629"], + [573, 31, 12, "Object is possibly \'null\'.", "765689629"], + [576, 29, 12, "Object is possibly \'null\'.", "765689629"], + [577, 29, 12, "Object is possibly \'null\'.", "765689629"], + [578, 31, 12, "Object is possibly \'null\'.", "765689629"], + [579, 31, 12, "Object is possibly \'null\'.", "765689629"], + [580, 30, 12, "Object is possibly \'null\'.", "765689629"], + [582, 34, 12, "Object is possibly \'null\'.", "765689629"], + [583, 28, 12, "Object is possibly \'null\'.", "765689629"], + [588, 29, 12, "Object is possibly \'null\'.", "765689629"], + [589, 29, 12, "Object is possibly \'null\'.", "765689629"], + [590, 31, 12, "Object is possibly \'null\'.", "765689629"], + [591, 31, 12, "Object is possibly \'null\'.", "765689629"], + [594, 29, 12, "Object is possibly \'null\'.", "765689629"], + [595, 29, 12, "Object is possibly \'null\'.", "765689629"], + [596, 31, 12, "Object is possibly \'null\'.", "765689629"], + [597, 31, 12, "Object is possibly \'null\'.", "765689629"], + [600, 34, 12, "Object is possibly \'null\'.", "765689629"], + [601, 28, 12, "Object is possibly \'null\'.", "765689629"], + [606, 29, 12, "Object is possibly \'null\'.", "765689629"], + [607, 29, 12, "Object is possibly \'null\'.", "765689629"], + [608, 31, 12, "Object is possibly \'null\'.", "765689629"], + [609, 31, 12, "Object is possibly \'null\'.", "765689629"], + [612, 29, 12, "Object is possibly \'null\'.", "765689629"], + [613, 29, 12, "Object is possibly \'null\'.", "765689629"], + [614, 31, 12, "Object is possibly \'null\'.", "765689629"], + [615, 31, 12, "Object is possibly \'null\'.", "765689629"], + [618, 34, 12, "Object is possibly \'null\'.", "765689629"], + [619, 28, 12, "Object is possibly \'null\'.", "765689629"], + [624, 29, 12, "Object is possibly \'null\'.", "765689629"], + [625, 29, 12, "Object is possibly \'null\'.", "765689629"], + [626, 31, 12, "Object is possibly \'null\'.", "765689629"], + [627, 31, 12, "Object is possibly \'null\'.", "765689629"], + [630, 29, 12, "Object is possibly \'null\'.", "765689629"], + [631, 29, 12, "Object is possibly \'null\'.", "765689629"], + [632, 31, 12, "Object is possibly \'null\'.", "765689629"], + [633, 31, 12, "Object is possibly \'null\'.", "765689629"], + [636, 34, 12, "Object is possibly \'null\'.", "765689629"], + [637, 28, 12, "Object is possibly \'null\'.", "765689629"], + [642, 29, 12, "Object is possibly \'null\'.", "765689629"], + [643, 29, 12, "Object is possibly \'null\'.", "765689629"], + [644, 31, 12, "Object is possibly \'null\'.", "765689629"], + [645, 31, 12, "Object is possibly \'null\'.", "765689629"], + [648, 29, 12, "Object is possibly \'null\'.", "765689629"], + [649, 29, 12, "Object is possibly \'null\'.", "765689629"], + [650, 31, 12, "Object is possibly \'null\'.", "765689629"], + [651, 31, 12, "Object is possibly \'null\'.", "765689629"], + [654, 34, 12, "Object is possibly \'null\'.", "765689629"], + [655, 28, 12, "Object is possibly \'null\'.", "765689629"], + [660, 29, 12, "Object is possibly \'null\'.", "765689629"], + [661, 29, 12, "Object is possibly \'null\'.", "765689629"], + [662, 31, 12, "Object is possibly \'null\'.", "765689629"], + [663, 31, 12, "Object is possibly \'null\'.", "765689629"], + [666, 29, 12, "Object is possibly \'null\'.", "765689629"], + [667, 29, 12, "Object is possibly \'null\'.", "765689629"], + [668, 31, 12, "Object is possibly \'null\'.", "765689629"], + [669, 31, 12, "Object is possibly \'null\'.", "765689629"], + [672, 34, 12, "Object is possibly \'null\'.", "765689629"], + [673, 28, 12, "Object is possibly \'null\'.", "765689629"], + [678, 29, 12, "Object is possibly \'null\'.", "765689629"], + [679, 29, 12, "Object is possibly \'null\'.", "765689629"], + [680, 31, 12, "Object is possibly \'null\'.", "765689629"], + [681, 31, 12, "Object is possibly \'null\'.", "765689629"], + [684, 29, 12, "Object is possibly \'null\'.", "765689629"], + [685, 29, 12, "Object is possibly \'null\'.", "765689629"], + [686, 31, 12, "Object is possibly \'null\'.", "765689629"], + [687, 31, 12, "Object is possibly \'null\'.", "765689629"], + [690, 34, 12, "Object is possibly \'null\'.", "765689629"], + [691, 28, 12, "Object is possibly \'null\'.", "765689629"], + [696, 29, 12, "Object is possibly \'null\'.", "765689629"], + [697, 29, 12, "Object is possibly \'null\'.", "765689629"], + [698, 31, 12, "Object is possibly \'null\'.", "765689629"], + [699, 31, 12, "Object is possibly \'null\'.", "765689629"], + [702, 29, 12, "Object is possibly \'null\'.", "765689629"], + [703, 29, 12, "Object is possibly \'null\'.", "765689629"], + [704, 31, 12, "Object is possibly \'null\'.", "765689629"], + [705, 31, 12, "Object is possibly \'null\'.", "765689629"], + [708, 34, 12, "Object is possibly \'null\'.", "765689629"], + [709, 28, 12, "Object is possibly \'null\'.", "765689629"], + [716, 8, 5, "Type \'null\' is not assignable to type \'string\'.", "173467459"], + [742, 8, 5, "Type \'string | null | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "173467459"], + [761, 66, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3461732844"], + [762, 18, 4, "\'info\' is possibly \'null\'.", "2087826411"], + [763, 21, 4, "\'info\' is possibly \'null\'.", "2087826411"], + [764, 23, 4, "\'info\' is possibly \'null\'.", "2087826411"], + [804, 59, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], + [806, 6, 26, "Object is possibly \'undefined\'.", "3315132651"], + [806, 6, 32, "Object is possibly \'undefined\'.", "1289477637"], + [806, 49, 14, "\'convertedTrack\' is possibly \'null\'.", "2974017500"], + [809, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], + [810, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], + [811, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], + [818, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "869002562"], + [819, 6, 21, "\'ann.averageLine.label\' is possibly \'undefined\'.", "833584207"], + [819, 41, 11, "\'lastAverage\' is possibly \'null\'.", "3212571500"], + [823, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3047629975"], + [824, 6, 21, "\'ann.minimumLine.label\' is possibly \'undefined\'.", "3059351706"], + [824, 41, 11, "\'lastMinimum\' is possibly \'null\'.", "622014457"], + [828, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1387076873"], + [829, 6, 21, "\'ann.maximumLine.label\' is possibly \'undefined\'.", "1375355140"], + [829, 41, 11, "\'lastMaximum\' is possibly \'null\'.", "348562919"], + [835, 67, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [835, 67, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], + [836, 26, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [837, 19, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [839, 4, 6, "Type \'({ x: number | null; y: number; } | { x: number; y: number | null; })[]\' is not assignable to type \'IDataSetRow[]\'.\\n Type \'{ x: number | null; y: number; } | { x: number; y: number | null; }\' is not assignable to type \'IDataSetRow\'.\\n Type \'{ x: number | null; y: number; }\' is not assignable to type \'IDataSetRow\'.\\n Types of property \'x\' are incompatible.\\n Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2123913871"], + [850, 37, 6, "Type \'undefined\' cannot be used as an index type.", "1320645186"], + [862, 55, 6, "Type \'undefined\' cannot be used as an index type.", "1320645186"] + ], + "src/app/widgets/widget-datetime/widget-datetime.component.ts:3033260432": [ + [60, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [61, 10, 10, "Type \'undefined\' is not assignable to type \'string\'.", "4110290259"], + [78, 46, 19, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "3336110771"], + [101, 4, 14, "Type \'CanvasRenderingContext2D | null\' is not assignable to type \'CanvasRenderingContext2D\'.\\n Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], + [141, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [142, 32, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [164, 56, 22, "Object is possibly \'undefined\'.", "576852750"], + [168, 8, 18, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1687642929"], + [174, 54, 22, "Object is possibly \'undefined\'.", "576852750"], + [194, 36, 17, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1983154939"] + ], + "src/app/widgets/widget-freeboardsk/widget-freeboardsk.component.ts:3637980361": [ + [44, 9, 9, "Type \'null\' is not assignable to type \'string\'.", "2966443234"], + [148, 40, 27, "Object is possibly \'undefined\'.", "3506675417"] + ], + "src/app/widgets/widget-gauge-ng-compass/widget-gauge-ng-compass.component.ts:1976286112": [ + [235, 30, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/app/widgets/widget-gauge-ng-linear/widget-gauge-ng-linear.component.ts:1738357127": [ + [93, 43, 7, "Argument of type \'() => IScale | { min: number | undefined; max: number | undefined; majorTicks: never[]; }\' is not assignable to parameter of type \'() => IScale\'.\\n Type \'IScale | { min: number | undefined; max: number | undefined; majorTicks: never[]; }\' is not assignable to type \'IScale\'.\\n Type \'{ min: number | undefined; max: number | undefined; majorTicks: never[]; }\' is not assignable to type \'IScale\'.\\n Types of property \'min\' are incompatible.\\n Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1860344028"], + [97, 44, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [97, 44, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [97, 68, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [99, 18, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [99, 47, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [126, 25, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [129, 49, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [129, 49, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [129, 74, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [129, 74, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4194286055"], + [157, 78, 22, "Object is possibly \'undefined\'.", "576852750"], + [170, 31, 22, "Argument of type \'IWidgetSvcConfig | undefined\' is not assignable to parameter of type \'IWidgetSvcConfig\'.\\n Type \'undefined\' is not assignable to type \'IWidgetSvcConfig\'.", "576852750"], + [201, 12, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [204, 29, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [205, 34, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [205, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [209, 32, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [210, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [212, 37, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [213, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [218, 32, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [219, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [221, 37, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [222, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [227, 32, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [228, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [230, 37, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [231, 35, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [277, 30, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/app/widgets/widget-gauge-ng-radial/widget-gauge-ng-radial.component.ts:1445903677": [ + [176, 78, 22, "Object is possibly \'undefined\'.", "576852750"], + [189, 31, 22, "Argument of type \'IWidgetSvcConfig | undefined\' is not assignable to parameter of type \'IWidgetSvcConfig\'.\\n Type \'undefined\' is not assignable to type \'IWidgetSvcConfig\'.", "576852750"], + [219, 12, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [224, 39, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [225, 38, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [226, 36, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [229, 39, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [230, 38, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [231, 36, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [234, 39, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [235, 38, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [236, 36, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [239, 39, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [240, 38, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [240, 85, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [240, 85, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [240, 121, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [240, 121, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [241, 46, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [241, 46, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [308, 81, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [309, 30, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [309, 84, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [313, 101, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [323, 54, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [331, 17, 9, "\'cfg.gauge\' is possibly \'undefined\'.", "4124321432"], + [333, 10, 9, "\'cfg.gauge\' is possibly \'undefined\'.", "4124321432"] + ], + "src/app/widgets/widget-iframe/widget-iframe.component.ts:2602616758": [ + [30, 4, 9, "Type \'null\' is not assignable to type \'string | undefined\'.", "2966443234"] + ], + "src/app/widgets/widget-label/widget-label.component.ts:3172675525": [ + [53, 39, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/app/widgets/widget-multi-state-switch/widget-multi-state-switch.component.ts:3679063603": [ + [50, 4, 5, "Type \'{ controlPath: { description: string; path: null; source: null; pathType: \\"multiple\\"; supportsPut: true; isPathConfigurable: true; pathRequired: true; showPathSkUnitsFilter: false; pathSkUnitsFilter: null; showConvertUnitTo: false; convertUnitTo: null; sampleTime: number; }; }\' is not assignable to type \'IPathArray | IWidgetPath[] | undefined\'.\\n The types of \'controlPath.convertUnitTo\' are incompatible between these types.\\n Type \'null\' is not assignable to type \'string | undefined\'.", "187670523"], + [160, 22, 10, "Object is possibly \'undefined\'.", "3996575854"] + ], + "src/app/widgets/widget-numeric/widget-numeric.component.ts:1149234437": [ + [62, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [70, 10, 9, "Type \'null\' is not assignable to type \'number\'.", "834508926"], + [71, 10, 8, "Type \'null\' is not assignable to type \'number\'.", "955872026"], + [72, 10, 8, "Type \'null\' is not assignable to type \'number\'.", "3919488324"], + [73, 10, 10, "Type \'undefined\' is not assignable to type \'string\'.", "4110290259"], + [74, 10, 15, "Type \'undefined\' is not assignable to type \'string\'.", "117091620"], + [107, 9, 23, "Object is possibly \'undefined\'.", "924630673"], + [112, 35, 12, "Object is possibly \'null\'.", "765689629"], + [115, 35, 12, "Object is possibly \'null\'.", "765689629"], + [118, 35, 12, "Object is possibly \'null\'.", "765689629"], + [130, 27, 22, "Object is possibly \'undefined\'.", "576852750"], + [130, 27, 36, "Argument of type \'boolean | undefined\' is not assignable to parameter of type \'boolean\'.\\n Type \'undefined\' is not assignable to type \'boolean\'.", "302325484"], + [154, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "2207342764"], + [155, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "1610969202"], + [156, 8, 14, "Type \'null\' is not assignable to type \'number\'.", "261643286"], + [189, 6, 16, "Object is possibly \'undefined\'.", "1024658627"], + [196, 4, 14, "Type \'CanvasRenderingContext2D | null\' is not assignable to type \'CanvasRenderingContext2D\'.\\n Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], + [228, 29, 22, "Object is possibly \'undefined\'.", "576852750"], + [233, 21, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [233, 21, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [234, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [235, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [236, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [236, 4, 22, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2885375952"], + [236, 29, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [237, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [238, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [238, 4, 27, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "301849712"], + [238, 34, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [239, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [239, 4, 26, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1509249862"], + [239, 33, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [240, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [240, 4, 26, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1509249624"], + [240, 33, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [241, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [241, 4, 29, "Type \'boolean | undefined\' is not assignable to type \'boolean\'.\\n Type \'undefined\' is not assignable to type \'boolean\'.", "763700999"], + [241, 36, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [242, 4, 16, "Object is possibly \'undefined\'.", "1024658627"], + [242, 4, 30, "Type \'boolean | undefined\' is not assignable to type \'null\'.\\n Type \'undefined\' is not assignable to type \'null\'.", "1638858803"], + [242, 37, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [248, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [249, 55, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [258, 17, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [274, 12, 15, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4102914388"], + [327, 18, 22, "Object is possibly \'undefined\'.", "576852750"], + [327, 18, 28, "Object is possibly \'undefined\'.", "3500113022"], + [331, 56, 22, "Object is possibly \'undefined\'.", "576852750"], + [336, 9, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [336, 25, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [338, 8, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [339, 95, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [341, 8, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [342, 96, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [362, 12, 22, "Object is possibly \'undefined\'.", "576852750"], + [362, 12, 28, "Object is possibly \'undefined\'.", "3500113022"] + ], + "src/app/widgets/widget-position/widget-position.component.ts:1704530161": [ + [91, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/app/widgets/widget-race-timer/widget-race-timer.component.ts:602896548": [ + [66, 25, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/app/widgets/widget-racer-line/widget-racer-line.component.ts:1502528665": [ + [61, 4, 5, "Type \'{ dtsPath: { description: string; path: string; source: string; pathType: \\"number\\"; pathRequired: false; isPathConfigurable: false; convertUnitTo: string; showPathSkUnitsFilter: true; pathSkUnitsFilter: \\"m\\"; sampleTime: number; }; ... 5 more ...; ttbPath: { ...; }; }\' is not assignable to type \'IPathArray | IWidgetPath[] | undefined\'.\\n The types of \'startLineNamePath.convertUnitTo\' are incompatible between these types.\\n Type \'null\' is not assignable to type \'string | undefined\'.", "187670523"], + [174, 10, 13, "Type \'null\' is not assignable to type \'string\'.", "363509612"], + [187, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [338, 4, 6, "Type \'string | null\' is not assignable to type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2123913871"], + [342, 4, 6, "Type \'string | null\' is not assignable to type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2123913871"], + [486, 25, 13, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2241094794"], + [490, 25, 13, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2926687684"] + ], + "src/app/widgets/widget-racer-timer/widget-racer-timer.component.ts:3969781521": [ + [96, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [117, 12, 17, "\'cfg.nextDashboard\' is possibly \'undefined\'.", "3067379786"], + [268, 25, 13, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "3328439093"] + ], + "src/app/widgets/widget-racesteer/widget-racesteer.component.ts:595381304": [ + [175, 55, 27, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1820305006"], + [215, 29, 1, "Argument of type \'number | null\' is not assignable to parameter of type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "177619"], + [336, 100, 27, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1820305006"] + ], + "src/app/widgets/widget-simple-linear/widget-simple-linear.component.ts:1973487785": [ + [60, 48, 4, "Argument of type \'null\' is not assignable to parameter of type \'number\'.", "2087897566"], + [74, 17, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [75, 16, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [76, 16, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [77, 64, 3, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "193413807"], + [94, 31, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [94, 31, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [97, 51, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [97, 51, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [97, 76, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [97, 76, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4194286055"], + [111, 53, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [118, 21, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [119, 28, 9, "\'cfg.gauge\' is possibly \'undefined\'.", "4124321432"], + [130, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [155, 22, 13, "Object is possibly \'null\'.", "1985302592"] + ], + "src/app/widgets/widget-slider/widget-slider.component.ts:413411294": [ + [28, 4, 5, "Type \'{ gaugePath: { description: string; path: null; source: null; pathType: \\"number\\"; isPathConfigurable: true; showPathSkUnitsFilter: false; pathSkUnitsFilter: null; showConvertUnitTo: false; convertUnitTo: null; supportsPut: true; sampleTime: number; }; }\' is not assignable to type \'IPathArray | IWidgetPath[] | undefined\'.\\n The types of \'\'gaugePath\'.convertUnitTo\' are incompatible between these types.\\n Type \'null\' is not assignable to type \'string | undefined\'.", "187670523"], + [59, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [60, 38, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [88, 23, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [102, 83, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [102, 83, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [115, 21, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [124, 41, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [124, 41, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4184355206"], + [125, 19, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [126, 48, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [126, 48, 22, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "4194286055"], + [127, 19, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [142, 23, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [142, 23, 22, "\'cfg.displayScale.upper\' is possibly \'undefined\'.", "4194286055"], + [142, 48, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [142, 48, 22, "\'cfg.displayScale.lower\' is possibly \'undefined\'.", "4184355206"], + [144, 21, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [144, 21, 22, "\'cfg.displayScale.lower\' is possibly \'undefined\'.", "4184355206"], + [150, 23, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [150, 23, 22, "\'cfg.displayScale.upper\' is possibly \'undefined\'.", "4194286055"], + [150, 48, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [150, 48, 22, "\'cfg.displayScale.lower\' is possibly \'undefined\'.", "4184355206"], + [152, 69, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [152, 69, 22, "\'cfg.displayScale.lower\' is possibly \'undefined\'.", "4184355206"], + [156, 20, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [156, 20, 22, "\'cfg.displayScale.upper\' is possibly \'undefined\'.", "4194286055"], + [156, 45, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [156, 45, 22, "\'cfg.displayScale.lower\' is possibly \'undefined\'.", "4184355206"], + [218, 10, 14, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "425845355"], + [218, 27, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [220, 10, 14, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "425845355"], + [220, 27, 16, "\'cfg.displayScale\' is possibly \'undefined\'.", "2211049739"], + [251, 24, 6, "\'colors\' is possibly \'undefined\'.", "1544348075"], + [252, 22, 6, "\'colors\' is possibly \'undefined\'.", "1544348075"] + ], + "src/app/widgets/widget-text/widget-text.component.ts:2471009854": [ + [51, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], + [52, 10, 10, "Type \'undefined\' is not assignable to type \'string\'.", "4110290259"], + [87, 4, 14, "Type \'CanvasRenderingContext2D | null\' is not assignable to type \'CanvasRenderingContext2D\'.\\n Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], + [122, 34, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [123, 32, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], + [141, 19, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [148, 8, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], + [148, 8, 15, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4102914388"], + [154, 29, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"] + ], + "src/app/widgets/widget-windsteer/widget-windsteer.component.ts:608910485": [ + [205, 34, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [206, 35, 9, "\'cfg.paths\' is possibly \'undefined\'.", "4139429943"], + [290, 17, 10, "\'cfg.paths\' is possibly \'undefined\'.", "1351545128"], + [355, 33, 22, "Object is possibly \'undefined\'.", "576852750"], + [355, 33, 46, "Object is possibly \'undefined\'.", "1732614759"] + ], + "src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts:3621756652": [ + [86, 10, 15, "Type \'null\' is not assignable to type \'Subscription\'.", "2233656204"], + [87, 10, 11, "Type \'null\' is not assignable to type \'Subscription\'.", "3429848846"], + [90, 10, 9, "Type \'null\' is not assignable to type \'TimeScaleFormat\'.", "2592770440"], + [91, 10, 14, "Type \'null\' is not assignable to type \'IChartDataSourceInfo\'.", "2985720646"], + [127, 22, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [162, 26, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [170, 24, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [204, 28, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [287, 24, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [292, 26, 5, "\'theme\' is possibly \'null\'.", "183206964"], + [309, 4, 20, "Type \'Signal\' is not assignable to type \'Signal\'.\\n Type \'Signal\' is not assignable to type \'() => BreakpointState\'.\\n Type \'BreakpointState | undefined\' is not assignable to type \'BreakpointState\'.\\n Type \'undefined\' is not assignable to type \'BreakpointState\'.", "1507091964"], + [348, 29, 22, "Object is possibly \'undefined\'.", "1240567228"], + [362, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [363, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [365, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [376, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [414, 17, 12, "Object is possibly \'null\'.", "765689629"], + [457, 30, 12, "Object is possibly \'null\'.", "765689629"], + [469, 45, 12, "Object is possibly \'null\'.", "765689629"], + [514, 30, 12, "Object is possibly \'null\'.", "765689629"], + [526, 45, 12, "Object is possibly \'null\'.", "765689629"], + [533, 4, 21, "Object is possibly \'undefined\'.", "406043295"], + [595, 8, 11, "Type \'(context: ScriptableContext<\\"line\\">) => CanvasGradient | null\' is not assignable to type \'string | ((ctx: ScriptableContext<\\"line\\">, options: AnyObject) => Color | undefined) | _DeepPartialObject | _DeepPartialObject<...> | readonly (string | ... 2 more ... | undefined)[] | undefined\'.\\n Type \'(context: ScriptableContext<\\"line\\">) => CanvasGradient | null\' is not assignable to type \'(ctx: ScriptableContext<\\"line\\">, options: AnyObject) => Color | undefined\'.\\n Type \'CanvasGradient | null\' is not assignable to type \'Color | undefined\'.\\n Type \'null\' is not assignable to type \'Color | undefined\'.", "354928724"], + [764, 8, 4, "Type \'number\' is not assignable to type \'null\'.", "2088075444"], + [771, 6, 4, "Type \'number\' is not assignable to type \'null\'.", "2088075444"], + [904, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], + [904, 6, 27, "Object is possibly \'undefined\'.", "3000548012"], + [905, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], + [905, 6, 27, "Object is possibly \'undefined\'.", "3000548012"], + [957, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], + [957, 6, 27, "Object is possibly \'undefined\'.", "3000548013"], + [958, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], + [958, 6, 27, "Object is possibly \'undefined\'.", "3000548013"], + [1012, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1013, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1014, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1015, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1035, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], + [1036, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], + [1037, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], + [1038, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], + [1039, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], + [1040, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], + [1041, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], + [1046, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1047, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1048, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1049, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1050, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1051, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1052, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1055, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1056, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1057, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1058, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1059, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1060, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1061, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1064, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1065, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1066, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1067, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1068, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1069, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1070, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1073, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1074, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1075, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1076, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1077, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1078, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1079, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1082, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1083, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1084, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1085, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1086, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1087, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1088, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1091, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1092, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1093, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1094, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1095, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1096, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1097, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1100, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1101, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1102, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1103, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1104, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1105, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1106, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1109, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1110, 27, 12, "Object is possibly \'null\'.", "765689629"], + [1111, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1112, 29, 12, "Object is possibly \'null\'.", "765689629"], + [1113, 28, 12, "Object is possibly \'null\'.", "765689629"], + [1114, 34, 12, "Object is possibly \'null\'.", "765689629"], + [1115, 28, 12, "Object is possibly \'null\'.", "765689629"] + ], + "src/app/widgets/widget-zones-state-panel/widget-zones-state-panel.component.ts:2501103102": [ + [78, 38, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] + ], + "src/default-config/config.blank.const.ts:475420683": [ + [31, 2, 12, "Type \'null\' is not assignable to type \'string\'.", "3195809883"] + ], + "src/default-config/config.blank.dashboard.ts:3810089117": [ + [4, 6, 2, "Type \'null\' is not assignable to type \'string\'.", "5861160"] + ], + "src/test.ts:2801733261": [ + [246, 54, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"] + ] + }` +}; diff --git a/.betterer.ts b/.betterer.ts new file mode 100644 index 00000000..962db0d5 --- /dev/null +++ b/.betterer.ts @@ -0,0 +1,11 @@ +import { typescript } from '@betterer/typescript'; + +export default { + 'strictNullChecks': () => + typescript('./tsconfig.strict.json', { + strictNullChecks: true, + noEmit: true, + }) + .include('./src/**/*.ts') + .exclude(/\.spec\.ts$/), +}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be3bb373..3e110a27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,27 @@ jobs: - name: Lint run: ng lint + strict-null-checks: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js 24.x + uses: actions/setup-node@v4 + with: + node-version: 24.x + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Enforce strictNullChecks ratchet + run: npm run betterer:ci + angular-vitest: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index ce9804b5..96b21a1a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ !.vscode/extensions.json # misc +/.betterer.cache /.sass-cache /connect.lock /coverage diff --git a/package-lock.json b/package-lock.json index ddb89fc2..1b3e1999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,8 @@ "@angular/platform-browser-dynamic": "21.2.7", "@angular/router": "21.2.7", "@aziham/chartjs-plugin-streaming": "^3.5.1", + "@betterer/cli": "^5.4.0", + "@betterer/typescript": "^5.4.0", "@godind/ng-canvas-gauges": "^6.2.1", "@types/canvas-gauges": "^2.1.8", "@types/d3": "^7.4.3", @@ -3137,6 +3139,318 @@ "node": ">=18" } }, + "node_modules/@betterer/betterer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@betterer/betterer/-/betterer-5.4.0.tgz", + "integrity": "sha512-t91JDLdUv5t70TPn1rzTiYA2WhtehDV23/KKkePMDo2Q/7QaHyHiTDG40+BnDh9LB7bW0Q4jOZXClf0tupOXNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@betterer/constraints": "^5.3.0", + "@betterer/errors": "^5.3.0", + "@betterer/logger": "^5.3.4", + "@betterer/reporter": "^5.4.0", + "@phenomnomnominal/debug": "^0.2.5", + "@phenomnomnominal/worker-require": "^0.0.34", + "chokidar": "^3.3.1", + "djb2a": "^1.2.0", + "fast-memoize": "^2.5.2", + "lines-and-columns": "^2.0.3", + "minimatch": "^5.0.1", + "prettier": "^2.3.2", + "simple-git": "^3.6.0", + "ts-node": "^10.2.1", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/@betterer/betterer/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@betterer/betterer/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@betterer/betterer/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@betterer/betterer/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@betterer/betterer/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@betterer/betterer/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@betterer/betterer/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@betterer/betterer/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@betterer/cli": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@betterer/cli/-/cli-5.4.0.tgz", + "integrity": "sha512-FtXbNLoxsg54w9LQWngXNuGNZj8BHG9qPv6r2Hvr5FYw1ZblAsTW9Vya9NlbOtSxIVrVf/aM9FVTIltTPhFHfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@betterer/betterer": "^5.4.0", + "@betterer/errors": "^5.3.0", + "@betterer/render": "^5.3.4", + "@betterer/tasks": "^5.3.6", + "@phenomnomnominal/tsquery": "^4.1.1", + "@phenomnomnominal/tstemplate": "^0.1.0", + "@phenomnomnominal/worker-require": "^0.0.34", + "chalk": "^4.1.2", + "commander": "^8.3.0", + "find-up": "^5.0.0", + "jest-diff": "^27.1.0", + "prettier": "^2.3.2", + "tslib": "^2.3.1" + }, + "bin": { + "betterer": "bin/betterer" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/cli/node_modules/@phenomnomnominal/tsquery": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-4.2.0.tgz", + "integrity": "sha512-hR2U3uVcrrdkuG30ItQ+uFDs4ncZAybxWG0OjTE8ptPzVoU7GVeXpy+vMU8zX9EbmjGeITPw/su5HjYQyAH8bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esquery": "^1.0.1" + }, + "peerDependencies": { + "typescript": "^3 || ^4" + } + }, + "node_modules/@betterer/cli/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@betterer/cli/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@betterer/constraints": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@betterer/constraints/-/constraints-5.3.0.tgz", + "integrity": "sha512-YmaX30/S4PoYDqGspRmUBeBE1KkLbnSQCndqPar7ZH6sWOIo29d67B7as6ODEmwpJf+GGYfmrGtTJd5c69hTng==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/errors": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@betterer/errors/-/errors-5.3.0.tgz", + "integrity": "sha512-Z4uCvA2oSuyEHNgIgMUo+WqIP/xwy40mGmkM/cJXLWWQOTOfzELa5CNgyz4t4XNebojko2bja8n2zaXlTUzViw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/logger": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@betterer/logger/-/logger-5.3.4.tgz", + "integrity": "sha512-Ns4KsZ5G0F5BsBF8K0VkmAUfEGKhgog9kv8QCKuZQQNcPAX5cESIYSeEs5H4bcMDqsk0dW/sQRt/7rZWGlrFSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.3", + "jest-diff": "^27.0.6", + "lines-and-columns": "^2.0.3", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/logger/node_modules/lines-and-columns": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@betterer/render": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@betterer/render/-/render-5.3.4.tgz", + "integrity": "sha512-zc91zlLxWafGxG25/kUsCDmxLvQhUb4PNIq4MRU/VpnncyhdelMY5OxaIAxu9IXhJFoC6FRJhL3I7eGdND64zQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/reporter": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@betterer/reporter/-/reporter-5.4.0.tgz", + "integrity": "sha512-+NB/zDkPv4q4oObWhvVpKghskf0K520NIm8rjfoBPAm75Y/PHWh2JSBeTSo6li25HRdvF5wZ+8tIJr1UAennmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@betterer/betterer": "^5.4.0", + "@betterer/errors": "^5.3.0", + "@betterer/logger": "^5.3.4", + "@betterer/render": "^5.3.4", + "@betterer/tasks": "^5.3.6", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/tasks": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@betterer/tasks/-/tasks-5.3.6.tgz", + "integrity": "sha512-UBx+My23z/q4deq5WPGNPDO/8OoK7epPpQftgaD8r1UXeD10aaV8BmjQ70Yjw/EQWDqVBBkDdbwmwspMOHjoMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@betterer/errors": "^5.3.0", + "@betterer/logger": "^5.3.4", + "@betterer/render": "^5.3.4", + "chalk": "^4.1.2", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@betterer/typescript": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@betterer/typescript/-/typescript-5.4.0.tgz", + "integrity": "sha512-wwfGRTwK1BHsqacaE6Qxii5Imy84wJBwiQoohyDWR50xSaYnU/h/4Ci6Ha3ZThgOaGrmra6ABfAGDYx7PSilkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@betterer/betterer": "^5.4.0", + "@betterer/errors": "^5.3.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/@braintree/sanitize-url": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", @@ -5137,6 +5451,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -6543,6 +6874,45 @@ "node": ">=20.0.0" } }, + "node_modules/@phenomnomnominal/debug": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/debug/-/debug-0.2.5.tgz", + "integrity": "sha512-5yAKsIqcvKUiTYfsEXTt6wiNW2jWVEpa6ej8RSPSRV681av1APBK+2BpEIdOo9DAmq2bwq45Ry3zi/wxBjndaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsite": "^1.0.0", + "esprima": "^4.0.1", + "esquery": "^1.3.1", + "tslib": "^2.0.3" + } + }, + "node_modules/@phenomnomnominal/tstemplate": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/tstemplate/-/tstemplate-0.1.0.tgz", + "integrity": "sha512-/v+GIVNFHAz4+nQtgy9e5ZAXK3xj6TbP5s9JTpnFuqkcLB+gB2lJ6x/nsDhkKhzR6o4REuzhsYoWYnXqKC/UnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@phenomnomnominal/worker-require": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@phenomnomnominal/worker-require/-/worker-require-0.0.34.tgz", + "integrity": "sha512-2GoVa1PRjrCjOpbMdzsZMQzkx/yFKqWizZc+ZNYSWE+Ym8BG+qAGDcWehUmUmGqy22wS13qitdVt6sR/DAWkIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsite": "^1.0.0", + "comlink": "^4.3.0", + "tslib": "^1.10.0" + } + }, + "node_modules/@phenomnomnominal/worker-require/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -7551,6 +7921,23 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@simple-git/args-pathspec": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz", + "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@simple-git/argv-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz", + "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-git/args-pathspec": "^1.0.3" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -9853,6 +10240,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -10473,6 +10869,13 @@ "dev": true, "license": "MIT" }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -11742,6 +12145,26 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/djb2a": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/djb2a/-/djb2a-1.2.0.tgz", + "integrity": "sha512-rXrJYOPCGvHPYw8R2u/zczFTG3xXNqklrtT17vTPm8n/4a16PKEhrfgMwJ9Zyuz96PJwZzHuK2ChQO/SSChuKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -12721,6 +13144,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -14120,6 +14550,32 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -17027,6 +17483,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", @@ -17042,6 +17514,44 @@ "node": ">=0.10.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -17299,6 +17809,13 @@ "node": ">= 0.10" } }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -18323,6 +18840,24 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/simple-git": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz", + "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "@simple-git/args-pathspec": "^1.0.3", + "@simple-git/argv-parser": "^1.1.0", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, "node_modules/sk-ais-status-plugin": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/sk-ais-status-plugin/-/sk-ais-status-plugin-1.0.0.tgz", diff --git a/package.json b/package.json index ba8e0be3..c8acd4f8 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,9 @@ "test:mcp-schema": "vitest run --config tools/gen-mcp-schema/vitest.config.ts", "gen:mcp-schema": "UPDATE_SCHEMA=1 npm run test:mcp-schema", "lint": "ng lint", + "snc": "tsc -p tsconfig.strict.json", + "betterer": "betterer --tsconfig tsconfig.betterer.json", + "betterer:ci": "betterer ci --tsconfig tsconfig.betterer.json", "dev": "ng serve --configuration=dev --serve-path=/@halos-org/skip/", "build:dev": "ng build --configuration=dev", "build:prod": "ng build --configuration=production", @@ -85,6 +88,8 @@ "@angular/platform-browser-dynamic": "21.2.7", "@angular/router": "21.2.7", "@aziham/chartjs-plugin-streaming": "^3.5.1", + "@betterer/cli": "^5.4.0", + "@betterer/typescript": "^5.4.0", "@godind/ng-canvas-gauges": "^6.2.1", "@types/canvas-gauges": "^2.1.8", "@types/d3": "^7.4.3", diff --git a/tsconfig.betterer.json b/tsconfig.betterer.json new file mode 100644 index 00000000..79d6ac7b --- /dev/null +++ b/tsconfig.betterer.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "target": "ES2022", + "skipLibCheck": true, + "types": ["node"] + } +} diff --git a/tsconfig.strict.json b/tsconfig.strict.json new file mode 100644 index 00000000..b986da88 --- /dev/null +++ b/tsconfig.strict.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "strictNullChecks": true, + "noEmit": true + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/test.ts"] +} From cbdb66513a52bd952601a89540bec3df48d3bc20 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 12:46:14 +0300 Subject: [PATCH 2/7] fix(strict): resolve strictNullChecks in the core service layer Fix all 116 strictNullChecks errors across the core service layer and the two shared Signal K interfaces, ratcheting the betterer baseline 959 -> 843. Types are made honest, not silenced -- no non-null assertions, casts, or suppressions. Where a value is genuinely nullable (SK paths emit null on timeout/sensor loss; config may be absent) the type is widened and the null handled at use; lazy initialisers get real defaults instead. - SK pipeline: widen ISkPathData.type/pathTimestamp and make ISkMetadata.units optional to match real delta/meta ordering; endpoint/version/request fields widened to their genuine pre-connect null states, with explicit throws where a missing HTTP body would otherwise fail obscurely. - Config/persistence: configuration-upgrade now skips-and-logs configs missing their app/theme section instead of throwing TypeError on real device data; lying non-null return types on transformApp/transformTheme corrected. - Dashboards/app: screensPayload keeps its undefined/null "clear" sentinel honestly; toSignal reads guard the undefined-before-first-emit case; nav fallbacks preserve the prior numeric-coercion behaviour. Two out-of-scope call sites (data-inspector, path-control-config) picked up the widened SK types and are handled in kind; path-control-config's guard fixes a latent compare(null) crash the honest typing surfaced. Verified: npm run lint, npm run build:dev (AOT/strictTemplates) and betterer:ci all pass; no file regressed vs the baseline. Refs #6 Co-Authored-By: Claude Opus 4.8 (1M context) --- .betterer.results | 144 +----------------- .../data-inspector.component.ts | 2 +- src/app/core/interfaces/app-interfaces.ts | 4 +- src/app/core/interfaces/signalk-interfaces.ts | 2 +- .../core/services/app-initNetwork.service.ts | 20 ++- src/app/core/services/app-service.ts | 15 +- src/app/core/services/canvas.service.ts | 3 +- .../services/configuration-upgrade.service.ts | 50 +++--- src/app/core/services/dashboard.service.ts | 26 ++-- src/app/core/services/data.service.ts | 10 +- .../core/services/notifications.service.ts | 16 +- .../services/remote-dashboards.service.ts | 10 +- src/app/core/services/settings.service.ts | 36 +++-- .../services/signalk-connection.service.ts | 29 ++-- .../core/services/signalk-delta.service.ts | 11 +- .../core/services/signalk-requests.service.ts | 38 ++--- src/app/core/services/storage.service.ts | 20 +-- .../path-control-config.component.ts | 2 +- 18 files changed, 173 insertions(+), 265 deletions(-) diff --git a/.betterer.results b/.betterer.results index f504f6c1..e5469086 100644 --- a/.betterer.results +++ b/.betterer.results @@ -61,7 +61,7 @@ exports[`strictNullChecks`] = { [34, 4, 10, "Type \'IConversionPathList\' is not assignable to type \'null\'.", "4013178488"], [35, 24, 10, "Object is possibly \'null\'.", "4013178488"] ], - "src/app/core/components/data-inspector/data-inspector.component.ts:3426964144": [ + "src/app/core/components/data-inspector/data-inspector.component.ts:2942185648": [ [71, 4, 20, "Type \'Signal\' is not assignable to type \'Signal\'.\\n Type \'Signal\' is not assignable to type \'() => BreakpointState\'.\\n Type \'BreakpointState | undefined\' is not assignable to type \'BreakpointState\'.\\n Type \'undefined\' is not assignable to type \'BreakpointState\'.", "1507091964"] ], "src/app/core/components/group-widget/group-widget.component.ts:2421937404": [ @@ -123,148 +123,6 @@ exports[`strictNullChecks`] = { [54, 4, 14, "Type \'null\' is not assignable to type \'CanvasRenderingContext2D\'.", "912271434"], [55, 4, 18, "Type \'null\' is not assignable to type \'HTMLCanvasElement\'.", "1193423419"] ], - "src/app/core/services/app-initNetwork.service.ts:3963658519": [ - [40, 10, 10, "Type \'null\' is not assignable to type \'boolean\'.", "4095117658"], - [41, 10, 20, "Type \'null\' is not assignable to type \'Subscription\'.", "1129182691"], - [340, 29, 43, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2187486198"] - ], - "src/app/core/services/app-service.ts:2341917173": [ - [67, 10, 19, "Type \'null\' is not assignable to type \'ITheme\'.", "1256751570"], - [100, 19, 23, "Object is possibly \'undefined\'.", "1034891909"], - [127, 4, 24, "Type \'ITheme | null\' is not assignable to type \'ITheme\'.\\n Type \'null\' is not assignable to type \'ITheme\'.", "3614169146"], - [223, 4, 24, "Type \'ITheme | null\' is not assignable to type \'ITheme\'.\\n Type \'null\' is not assignable to type \'ITheme\'.", "3614169146"] - ], - "src/app/core/services/canvas.service.ts:2103067572": [ - [76, 8, 13, "Cannot invoke an object which is possibly \'undefined\'.", "3459502336"] - ], - "src/app/core/services/configuration-upgrade.service.ts:1608134649": [ - [181, 47, 3, "Type \'null\' is not assignable to type \'v10IAppConfig\'.", "193410244"], - [181, 58, 6, "Type \'null\' is not assignable to type \'v10IWidgetConfig\'.", "2016758345"], - [181, 72, 6, "Type \'null\' is not assignable to type \'v10ILayoutConfig\'.", "1429788639"], - [181, 86, 5, "Type \'null\' is not assignable to type \'v10IThemeConfig\'.", "183206964"], - [218, 12, 20, "\'oldConfiguration.app\' is possibly \'null\'.", "103008637"], - [239, 55, 10, "Type \'null\' is not assignable to type \'Dashboard[]\'.", "1337325010"], - [242, 6, 22, "\'localStorageConfig.app\' is possibly \'null\'.", "1972111892"], - [243, 6, 22, "\'localStorageConfig.app\' is possibly \'null\'.", "1972111892"], - [244, 6, 24, "\'localStorageConfig.theme\' is possibly \'null\'.", "122221156"], - [304, 14, 6, "Type \'null\' is not assignable to type \'IAppConfig\'.", "2123913871"], - [313, 16, 6, "Type \'null\' is not assignable to type \'IThemeConfig\'.", "2123913871"], - [320, 10, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [321, 59, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [324, 38, 10, "Argument of type \'IAppConfig | null\' is not assignable to parameter of type \'IAppConfig\'.\\n Type \'null\' is not assignable to type \'IAppConfig\'.", "3800660544"], - [377, 6, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [384, 58, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [396, 10, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [397, 59, 10, "\'config.app\' is possibly \'null\'.", "3800660544"], - [417, 6, 10, "\'config.app\' is possibly \'null\'.", "3800660544"] - ], - "src/app/core/services/dashboard.service.ts:89333634": [ - [32, 63, 4, "Argument of type \'null\' is not assignable to parameter of type \'widgetOperation\'.", "2087897566"], - [286, 9, 22, "Object is possibly \'null\'.", "2213989156"], - [289, 31, 22, "Object is possibly \'null\'.", "2213989156"], - [299, 9, 22, "Object is possibly \'null\'.", "2213989156"], - [302, 31, 22, "Object is possibly \'null\'.", "2213989156"], - [333, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "1592152070"], - [334, 9, 22, "Object is possibly \'null\'.", "2213989156"], - [337, 22, 22, "Object is possibly \'null\'.", "2213989156"], - [348, 8, 13, "Type \'null\' is not assignable to type \'number\'.", "1592152070"], - [349, 9, 22, "Object is possibly \'null\'.", "2213989156"], - [352, 22, 22, "Object is possibly \'null\'.", "2213989156"] - ], - "src/app/core/services/data.service.ts:1120858683": [ - [241, 8, 12, "Type \'null\' is not assignable to type \'string\'.", "2101405159"], - [242, 8, 16, "Type \'null\' is not assignable to type \'string\'.", "4266228162"], - [252, 8, 10, "Type \'null\' is not assignable to type \'ISkMetadata\'.", "2623107177"], - [270, 6, 10, "Type \'ISkMetadata | null\' is not assignable to type \'ISkMetadata\'.\\n Type \'null\' is not assignable to type \'ISkMetadata\'.", "2623107177"], - [402, 8, 4, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2087944093"], - [412, 35, 8, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "1214764093"], - [438, 37, 8, "\'pathItem\' is possibly \'undefined\'.", "1214764093"], - [438, 57, 8, "\'pathItem\' is possibly \'undefined\'.", "1214764093"], - [453, 29, 8, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "1214764093"], - [477, 10, 13, "Type \'undefined\' is not assignable to type \'string\'.", "3344503334"], - [478, 10, 4, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2087944093"], - [482, 10, 4, "Type \'ISkMetadata | null\' is not assignable to type \'ISkMetadata | undefined\'.\\n Type \'null\' is not assignable to type \'ISkMetadata | undefined\'.", "2087701304"], - [484, 35, 10, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "384538813"], - [489, 33, 10, "Argument of type \'ISkPathData | undefined\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'undefined\' is not assignable to type \'ISkPathData\'.", "384538813"], - [494, 10, 15, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1816235499"], - [505, 31, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"], - [505, 59, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"], - [511, 74, 10, "\'pathObject\' is possibly \'undefined\'.", "384538813"] - ], - "src/app/core/services/notifications.service.ts:131921706": [ - [65, 10, 33, "Type \'null\' is not assignable to type \'Subscription\'.", "1613706193"], - [66, 10, 35, "Type \'null\' is not assignable to type \'Subscription\'.", "1439588120"], - [67, 10, 35, "Type \'null\' is not assignable to type \'Subscription\'.", "4279004213"], - [68, 10, 25, "Type \'null\' is not assignable to type \'Subscription\'.", "3596131985"], - [80, 10, 22, "Type \'null\' is not assignable to type \'number\'.", "1453628105"], - [83, 10, 17, "Type \'null\' is not assignable to type \'IAlarmInfo\'.", "2736621211"], - [252, 18, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"], - [262, 9, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"], - [267, 9, 13, "\'message.value\' is possibly \'undefined\'.", "2360183275"] - ], - "src/app/core/services/remote-dashboards.service.ts:2849282820": [ - [74, 43, 4, "Argument of type \'null\' is not assignable to parameter of type \'IScreensPayload\'.", "2087897566"], - [87, 12, 14, "Type \'undefined\' is not assignable to type \'IScreensPayload\'.", "2447800916"], - [90, 10, 14, "Type \'null\' is not assignable to type \'IScreensPayload\'.", "2447800916"], - [134, 12, 8, "\'changeTo\' is possibly \'undefined\'.", "2212884184"], - [135, 27, 8, "\'changeTo\' is possibly \'undefined\'.", "2212884184"] - ], - "src/app/core/services/settings.service.ts:3918426213": [ - [227, 27, 21, "Object is possibly \'null\'.", "1182065060"], - [228, 33, 21, "Object is possibly \'null\'.", "1182065060"], - [230, 8, 21, "Object is possibly \'null\'.", "1182065060"], - [233, 30, 21, "Object is possibly \'null\'.", "1182065060"], - [236, 8, 21, "Object is possibly \'null\'.", "1182065060"], - [239, 29, 21, "Object is possibly \'null\'.", "1182065060"], - [242, 8, 21, "Object is possibly \'null\'.", "1182065060"], - [245, 36, 21, "Object is possibly \'null\'.", "1182065060"], - [254, 8, 21, "Object is possibly \'null\'.", "1182065060"], - [257, 32, 21, "Object is possibly \'null\'.", "1182065060"] - ], - "src/app/core/services/signalk-connection.service.ts:1697882337": [ - [66, 4, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], - [67, 4, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], - [68, 4, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], - [74, 54, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"], - [175, 6, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], - [176, 6, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], - [177, 6, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], - [206, 73, 16, "Argument of type \'HttpResponse\' is not assignable to parameter of type \'{ body: ISignalKEndpointResponse; status: number; }\'.\\n Types of property \'body\' are incompatible.\\n Type \'ISignalKEndpointResponse | null\' is not assignable to type \'ISignalKEndpointResponse\'.\\n Type \'null\' is not assignable to type \'ISignalKEndpointResponse\'.", "920189509"], - [268, 66, 16, "Argument of type \'HttpResponse\' is not assignable to parameter of type \'{ body: ISignalKEndpointResponse; status: number; }\'.\\n Types of property \'body\' are incompatible.\\n Type \'ISignalKEndpointResponse | null\' is not assignable to type \'ISignalKEndpointResponse\'.\\n Type \'null\' is not assignable to type \'ISignalKEndpointResponse\'.", "920189509"], - [279, 8, 17, "Type \'null\' is not assignable to type \'string\'.", "174358918"], - [280, 8, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], - [281, 8, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], - [316, 6, 14, "Type \'null\' is not assignable to type \'string\'.", "4266297387"], - [317, 6, 12, "Type \'null\' is not assignable to type \'string\'.", "3181573879"], - [322, 79, 7, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string | URL\'.\\n Type \'undefined\' is not assignable to type \'string | URL\'.", "1210482486"], - [323, 131, 5, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string | URL\'.\\n Type \'undefined\' is not assignable to type \'string | URL\'.", "190446474"], - [331, 6, 37, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4248587053"], - [334, 6, 35, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "299693937"], - [334, 58, 5, "\'wsUrl\' is possibly \'undefined\'.", "190446474"] - ], - "src/app/core/services/signalk-delta.service.ts:3304767793": [ - [61, 10, 8, "Type \'undefined\' is not assignable to type \'string\'.", "2634452559"], - [75, 10, 10, "Type \'null\' is not assignable to type \'string\'.", "1874862562"], - [334, 41, 15, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "876287967"], - [351, 32, 12, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "3845597927"] - ], - "src/app/core/services/signalk-requests.service.ts:2835987348": [ - [92, 6, 5, "Type \'null\' is not assignable to type \'string\'.", "195031250"], - [93, 6, 10, "Type \'null\' is not assignable to type \'number\'.", "3382497788"], - [109, 6, 26, "Type \'string | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "2950021774"], - [110, 6, 31, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3535017376"], - [113, 49, 16, "Type \'undefined\' cannot be used as an index type.", "1603728970"], - [124, 26, 28, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4291747378"], - [139, 26, 6, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1121335604"] - ], - "src/app/core/services/storage.service.ts:1590506663": [ - [54, 10, 14, "Type \'null\' is not assignable to type \'string\'.", "2483034339"], - [56, 10, 17, "Type \'null\' is not assignable to type \'number\'.", "1298990515"], - [58, 10, 10, "Type \'null\' is not assignable to type \'IConfig\'.", "710858837"], - [62, 10, 14, "Type \'undefined\' is not assignable to type \'IEndpointStatus\'.", "2899844880"], - [282, 6, 6, "Type \'null\' is not assignable to type \'Config[]\'.", "2123913871"], - [347, 6, 6, "Type \'null\' is not assignable to type \'IConfig\'.", "2123913871"] - ], "src/app/core/utils/dataScales.util.ts:2208923770": [ [54, 6, 12, "Type \'null\' is not assignable to type \'number\'.", "2083421902"] ], diff --git a/src/app/core/components/data-inspector/data-inspector.component.ts b/src/app/core/components/data-inspector/data-inspector.component.ts index 6137002b..9690ea10 100644 --- a/src/app/core/components/data-inspector/data-inspector.component.ts +++ b/src/app/core/components/data-inspector/data-inspector.component.ts @@ -146,7 +146,7 @@ export class DataInspectorComponent implements AfterViewInit, OnDestroy { private toViewRow(path: ISkPathData): DataInspectorViewRow { return { path: path.path, - type: path.type, + type: path.type ?? '', supportsPut: path.meta?.supportsPut === true, sourceRows: Object.entries(path.sources ?? {}).map(([key, source]) => ({ key, diff --git a/src/app/core/interfaces/app-interfaces.ts b/src/app/core/interfaces/app-interfaces.ts index 4159d06c..48951b6f 100644 --- a/src/app/core/interfaces/app-interfaces.ts +++ b/src/app/core/interfaces/app-interfaces.ts @@ -56,8 +56,8 @@ export interface IPathUpdateEvent { path: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any pathValue: any; - pathTimestamp: string; - type: string; + pathTimestamp: string | undefined; + type: string | undefined; state: TState; defaultSource?: string; // default source sources: Record(null); - private _cssThemeColorRoles: ITheme = null; + private _cssThemeColorRoles: ITheme; private readonly _settings = inject(SettingsService); private readonly _data = inject(DataService); private readonly _iconRegistry = inject(MatIconRegistry); @@ -98,9 +98,11 @@ export class AppService { }); effect(() => { - const mode = this._environmentMode().data.value; + const environmentMode = this._environmentMode(); untracked(() => { + if (!environmentMode) return; + const mode = environmentMode.data.value; if (this.previousEnvironmentMode === mode) return; // No change in mode this.previousEnvironmentMode = mode; @@ -124,8 +126,7 @@ export class AppService { }); }); - this.readThemeCssRoleVariables(); - this._cssThemeColorRoles = this.cssThemeColorRoles$.getValue(); + this._cssThemeColorRoles = this.readThemeCssRoleVariables(); this.browserVersion.set(this.getBrowserVersion()); this.osVersion.set(this.getOSVersion()); @@ -137,7 +138,7 @@ export class AppService { console.log("***********************************************"); } - private readThemeCssRoleVariables(): void { + private readThemeCssRoleVariables(): ITheme { const root = document.body; const computedStyle = getComputedStyle(root); const cssThemeRolesColor: ITheme = { @@ -176,6 +177,7 @@ export class AppService { zoneEmergency: computedStyle.getPropertyValue('--kip-zone-emergency-color').trim(), }; this.cssThemeColorRoles$.next(cssThemeRolesColor); + return cssThemeRolesColor; } public get cssThemeColors() : ITheme { @@ -220,8 +222,7 @@ export class AppService { } this.setBrightness(1, false); } - this.readThemeCssRoleVariables(); - this._cssThemeColorRoles = this.cssThemeColorRoles$.getValue(); + this._cssThemeColorRoles = this.readThemeCssRoleVariables(); } /** diff --git a/src/app/core/services/canvas.service.ts b/src/app/core/services/canvas.service.ts index a0877043..20c3de7c 100644 --- a/src/app/core/services/canvas.service.ts +++ b/src/app/core/services/canvas.service.ts @@ -72,9 +72,10 @@ export class CanvasService { // Store the callback for this canvas (if provided), wrapping to ignore zero-size if (opts.onResize) { + const onResize = opts.onResize; const filteredResize = (w: number, h: number) => { if (w === 0 || h === 0) return; - opts.onResize(w, h); + onResize(w, h); }; this.resizeCallbacks.set(canvas, filteredResize); } diff --git a/src/app/core/services/configuration-upgrade.service.ts b/src/app/core/services/configuration-upgrade.service.ts index 430a10dd..acdac5cc 100644 --- a/src/app/core/services/configuration-upgrade.service.ts +++ b/src/app/core/services/configuration-upgrade.service.ts @@ -179,11 +179,12 @@ export class ConfigurationUpgradeService { } else { // LocalStorage upgrade path for config version 10 - const localStorageConfig: v10IConfig = { app: null, widget: null, layout: null, theme: null }; - localStorageConfig.app = this._settings.loadConfigFromLocalStorage('appConfig'); - localStorageConfig.widget = this._settings.loadConfigFromLocalStorage('widgetConfig'); - localStorageConfig.layout = this._settings.loadConfigFromLocalStorage('layoutConfig'); - localStorageConfig.theme = this._settings.loadConfigFromLocalStorage('themeConfig'); + const localStorageConfig: v10IConfig = { + app: this._settings.loadConfigFromLocalStorage('appConfig'), + widget: this._settings.loadConfigFromLocalStorage('widgetConfig'), + layout: this._settings.loadConfigFromLocalStorage('layoutConfig'), + theme: this._settings.loadConfigFromLocalStorage('themeConfig') + }; const transformedApp = this.transformApp(localStorageConfig.app as IAppConfig); const transformedTheme = this.transformTheme(localStorageConfig.theme); @@ -216,6 +217,10 @@ export class ConfigurationUpgradeService { .then(async (rootConfigs: Config[]) => { for (const rootConfig of rootConfigs) { const oldConfiguration = await this._storage.getConfig(rootConfig.scope, rootConfig.name, this.legacyFileVersion) as unknown as IConfig; + if (!oldConfiguration.app) { + this.pushError(`[Upgrade] Configuration ${rootConfig.scope}/${rootConfig.name} has no app section; skipping retire.`); + continue; + } oldConfiguration.app.configVersion = 0; // retire try { // Await the retire write for BOTH scopes so it completes before the @@ -237,9 +242,14 @@ export class ConfigurationUpgradeService { // close handled by component dialog; service only reloads on upgrade path }); } else { - const localStorageConfig: IConfig = { app: null, dashboards: null, theme: null }; + const localStorageConfig: IConfig = { app: null, dashboards: [], theme: null }; localStorageConfig.app = this._settings.loadConfigFromLocalStorage('appConfig'); localStorageConfig.theme = this._settings.loadConfigFromLocalStorage('themeConfig'); + if (!localStorageConfig.app || !localStorageConfig.theme) { + this.pushError('[Upgrade Service] Cannot start fresh: local appConfig/themeConfig failed to load.'); + this.upgrading.set(false); + return; + } localStorageConfig.app.configVersion = MIGRATION_OUTPUT_VERSION; // baseline fresh localStorageConfig.app.nightModeBrightness = 0.27; localStorageConfig.theme.themeName = ''; @@ -301,7 +311,7 @@ export class ConfigurationUpgradeService { return config; } - private transformApp(app: IAppConfig): IAppConfig { + private transformApp(app: IAppConfig | null): IAppConfig | null { if (!app) return null; const clone = cloneDeep(app); clone.configVersion = MIGRATION_OUTPUT_VERSION; @@ -310,7 +320,7 @@ export class ConfigurationUpgradeService { return clone; } - private transformTheme(theme: v10IThemeConfig): IThemeConfig { + private transformTheme(theme: v10IThemeConfig): IThemeConfig | null { if (!theme) return null; const themeConfig: IThemeConfig = { themeName: '' }; return themeConfig; @@ -318,11 +328,12 @@ export class ConfigurationUpgradeService { private upgradeConfig(config: IConfig): IConfig | null { try { - if (config.app.configVersion !== 11) { - this.pushError(`[Upgrade Service] Config version ${config.app.configVersion} upgrade is not supported. Skipping...`); + const appConfig = config.app; + if (!appConfig || appConfig.configVersion !== 11) { + this.pushError(`[Upgrade Service] Config version ${appConfig?.configVersion} upgrade is not supported. Skipping...`); return null; } - this.removeSplitShellConfigKeys(config.app); + this.removeSplitShellConfigKeys(appConfig); this.migrateUseNeedleToEnableNeedle(config.dashboards); // Iterate dashboards and force widget selector to 'widget-host2' let updatedWidgetCount = 0; @@ -375,14 +386,14 @@ export class ConfigurationUpgradeService { this.pushMsg(`[Upgrade] Doubled widget grid metrics for ${dimensionUpdatedCount} non-zero (w/h/x/y) entries.`); } - config.app.configVersion = MIGRATION_OUTPUT_VERSION; + appConfig.configVersion = MIGRATION_OUTPUT_VERSION; return { - app: config.app, theme: config.theme, dashboards: config.dashboards + app: appConfig, theme: config.theme, dashboards: config.dashboards }; } catch (error) { - this.pushError(`[Upgrade Service] Error upgrading ${config.app.configVersion}: ${(error as Error).message}`); + this.pushError(`[Upgrade Service] Error upgrading ${config.app?.configVersion}: ${(error as Error).message}`); return null; } } @@ -394,12 +405,13 @@ export class ConfigurationUpgradeService { */ private upgradeConfigV12toV13(config: IConfig): IConfig | null { try { - if (config.app.configVersion !== 12) { - this.pushError(`[Upgrade Service] Config version ${config.app.configVersion} is not an upgradable v12 config. Skipping...`); + const appConfig = config.app; + if (!appConfig || appConfig.configVersion !== 12) { + this.pushError(`[Upgrade Service] Config version ${appConfig?.configVersion} is not an upgradable v12 config. Skipping...`); return null; } - delete (config.app as unknown as Record).dataSets; + delete (appConfig as unknown as Record).dataSets; if (Array.isArray(config.dashboards)) { for (const dash of config.dashboards) { @@ -415,8 +427,8 @@ export class ConfigurationUpgradeService { } } - config.app.configVersion = V13_MIGRATION_OUTPUT_VERSION; - return { app: config.app, theme: config.theme, dashboards: config.dashboards }; + appConfig.configVersion = V13_MIGRATION_OUTPUT_VERSION; + return { app: appConfig, theme: config.theme, dashboards: config.dashboards }; } catch (error) { this.pushError(`[Upgrade Service] Error upgrading v12->v13: ${(error as Error).message}`); return null; diff --git a/src/app/core/services/dashboard.service.ts b/src/app/core/services/dashboard.service.ts index fb108542..42dedc25 100644 --- a/src/app/core/services/dashboard.service.ts +++ b/src/app/core/services/dashboard.service.ts @@ -30,7 +30,7 @@ export class DashboardService { private readonly _destroyRef = inject(DestroyRef); public dashboards = signal([], { equal: isEqual }); public readonly activeDashboard = signal(null); - private _widgetAction = new BehaviorSubject(null); + private _widgetAction = new BehaviorSubject(null); public widgetAction$ = this._widgetAction.asObservable(); public isDashboardStatic = signal(true); public widgetClipboard = signal(null); @@ -284,10 +284,11 @@ export class DashboardService { * This only updates the internal state and does NOT trigger navigation or URL changes. */ public previousDashboard(): void { - if ((this.activeDashboard() - 1) < 0) { + const active = this.activeDashboard() ?? 0; + if ((active - 1) < 0) { this.activeDashboard.set(this.dashboards().length - 1); } else { - this.activeDashboard.set(this.activeDashboard() - 1); + this.activeDashboard.set(active - 1); } } @@ -297,10 +298,11 @@ export class DashboardService { * This only updates the internal state and does NOT trigger navigation or URL changes. */ public nextDashboard(): void { - if ((this.activeDashboard() + 1) > (this.dashboards().length) - 1) { + const active = this.activeDashboard() ?? 0; + if ((active + 1) > (this.dashboards().length) - 1) { this.activeDashboard.set(0); } else { - this.activeDashboard.set(this.activeDashboard() + 1); + this.activeDashboard.set(active + 1); } } @@ -331,11 +333,12 @@ export class DashboardService { * This updates the browser URL and triggers Angular routing. */ public navigateToNextDashboard(): void { - let nextDashboard: number = null; - if ((this.activeDashboard() + 1) >= this.dashboards().length) { + const active = this.activeDashboard() ?? 0; + let nextDashboard: number; + if ((active + 1) >= this.dashboards().length) { nextDashboard = 0; } else { - nextDashboard = this.activeDashboard() + 1; + nextDashboard = active + 1; } this._router.navigate(['/page', nextDashboard]); } @@ -346,11 +349,12 @@ export class DashboardService { * This updates the browser URL and triggers Angular routing. */ public navigateToPreviousDashboard(): void { - let nextDashboard: number = null; - if ((this.activeDashboard() - 1) < 0) { + const active = this.activeDashboard() ?? 0; + let nextDashboard: number; + if ((active - 1) < 0) { nextDashboard = this.dashboards().length - 1; } else { - nextDashboard = this.activeDashboard() - 1; + nextDashboard = active - 1; } this._router.navigate(['/page', nextDashboard]); } diff --git a/src/app/core/services/data.service.ts b/src/app/core/services/data.service.ts index 2af029dc..a8ad3b2a 100644 --- a/src/app/core/services/data.service.ts +++ b/src/app/core/services/data.service.ts @@ -42,7 +42,7 @@ const isRfc3339StringDate = (date: Date | string): boolean => { }; // Translate units from sk metadata to appropriate type category -const typeFromUnits = (units: string): string | undefined => { +const typeFromUnits = (units: string | undefined): string | undefined => { if (!units) { return undefined; } @@ -239,8 +239,8 @@ export class DataService implements OnDestroy { return matchingPaths.pathDataUpdate$; } - let currentValue: string = null; - let currentTimestamp: string = null; + let currentValue: string | null = null; + let currentTimestamp: string | null | undefined = null; let currentDateTimestamp: Date | null = null; let state: TState = States.Normal; let pathUpdate: IPathUpdate = { @@ -250,7 +250,7 @@ export class DataService implements OnDestroy { }, state: state }; - let metaUpdate: ISkMetadata = null; + let metaUpdate: ISkMetadata | null = null; const dataPath = this._skData.get(path); @@ -480,7 +480,7 @@ export class DataService implements OnDestroy { state: pendingState ?? States.Normal, defaultSource: undefined, sources: {}, - meta: meta.meta ? cloneDeep(meta.meta) : null, + meta: meta.meta ? cloneDeep(meta.meta) : undefined, }; this._skData.set(metaPath, pathObject); if (pendingState !== undefined) { diff --git a/src/app/core/services/notifications.service.ts b/src/app/core/services/notifications.service.ts index 53221bd0..5932e2e3 100644 --- a/src/app/core/services/notifications.service.ts +++ b/src/app/core/services/notifications.service.ts @@ -63,10 +63,10 @@ export class NotificationsService implements OnDestroy { emergency: { sound: 4, visual: 2 }, }; - private _notificationSettingsSubscription: Subscription = null; - private _notificationDataStreamSubscription: Subscription = null; - private _notificationMetaStreamSubscription: Subscription = null; - private _resetServiceSubscription: Subscription = null; + private _notificationSettingsSubscription: Subscription; + private _notificationDataStreamSubscription: Subscription | null = null; + private _notificationMetaStreamSubscription: Subscription | null = null; + private _resetServiceSubscription: Subscription; private _notificationConfig: INotificationConfig; private _notificationConfig$ = new BehaviorSubject(DefaultNotificationConfig); @@ -78,10 +78,10 @@ export class NotificationsService implements OnDestroy { // --- HTMLAudioElement (audio) state ---------------------------------------- // Cache one player per track id and reuse across switches. private _players = new Map(); - private _activeAlarmSoundtrack: number = null; + private _activeAlarmSoundtrack: number | null = null; private _isMuted = false; - private _lastEmittedValue: IAlarmInfo = null; + private _lastEmittedValue: IAlarmInfo | null = null; constructor() { this._notificationSettingsSubscription = this.settings.getNotificationServiceConfigAsO() @@ -250,6 +250,10 @@ export class NotificationsService implements OnDestroy { } private getNotificationSeverity(message: INotification): { aSev: number; vSev: number } { + if (!message.value) { + return { aSev: 0, vSev: 0 }; + } + const state = message.value['state']; const severity = NotificationsService.ALARM_SEVERITIES[state]; if (!severity) { diff --git a/src/app/core/services/remote-dashboards.service.ts b/src/app/core/services/remote-dashboards.service.ts index a071821c..687f2a3f 100644 --- a/src/app/core/services/remote-dashboards.service.ts +++ b/src/app/core/services/remote-dashboards.service.ts @@ -85,7 +85,7 @@ export class RemoteDashboardsService { untracked(() => { if (!isRemoteControl && !this.previousIsRemoteControl) return; this.previousIsRemoteControl = isRemoteControl; - let screensPayload: IScreensPayload = undefined; + let screensPayload: IScreensPayload | null | undefined = undefined; if (!isRemoteControl) { // Clear displayName and screens on the server screensPayload = null; @@ -132,7 +132,7 @@ export class RemoteDashboardsService { untracked(() => { if (!this.isRemoteControl()) return; - if (changeTo.data.value == null) return; + if (changeTo === undefined || changeTo.data.value == null) return; const idx = Number(changeTo.data.value); if (!isNaN(idx) && idx >= 0 && idx < this.dashboard.dashboards().length) { if (this.dashboard.activeDashboard() !== idx) { @@ -156,7 +156,7 @@ export class RemoteDashboardsService { return { displayName: displayName, screens: dashboardListItems }; } - private shareScreens(screens: IScreensPayload): void { + private shareScreens(screens: IScreensPayload | null | undefined): void { this.setScreensOnRemote(this.KIP_UUID, screens) console.log('[Remote Dashboards] Sending dashboard configurations to server.'); } @@ -196,10 +196,10 @@ export class RemoteDashboardsService { * }); * ``` */ - public setScreensOnRemote(kipId: string, screensPayload: IScreensPayload): void { + public setScreensOnRemote(kipId: string, screensPayload: IScreensPayload | null | undefined): void { const payload: IRemoteDisplayCommand = { displayId: kipId, - display: screensPayload === null ? null : { ...screensPayload } + display: screensPayload == null ? null : { ...screensPayload } }; const requestId = this.requests.putRequest(this.COMMAND_SET_DISPLAY_PATH, payload, this.KIP_UUID); if (!requestId) { diff --git a/src/app/core/services/settings.service.ts b/src/app/core/services/settings.service.ts index 26fdcb17..0ff4fd20 100644 --- a/src/app/core/services/settings.service.ts +++ b/src/app/core/services/settings.service.ts @@ -107,14 +107,15 @@ export class SettingsService { private async startup(): Promise { // A missing server config comes back as {} (not a 404), so guard on the presence of app config — // an empty/appless object must not fall through to pushSettings() and dereference activeConfig.app. - if (!this.storage.isRemoteContextBootstrapped() || !this.storage.initConfig?.app) { + const initConfig = this.storage.initConfig; + if (!this.storage.isRemoteContextBootstrapped() || !initConfig?.app) { console.warn('[AppSettings Service] Shared configuration enabled but remote bootstrap handoff is missing or empty. Waiting for explicit recovery action.'); return; } - this.configVersion = this.storage.initConfig?.app?.configVersion; - this.checkConfigUpgradeRequired(false, this.storage.initConfig?.app?.configVersion); - this.activeConfig = this.storage.initConfig; + this.configVersion = initConfig.app.configVersion; + this.checkConfigUpgradeRequired(false, initConfig.app.configVersion); + this.activeConfig = initConfig; this.pushSettings(); } @@ -222,28 +223,35 @@ export class SettingsService { } private pushSettings(): void { + const app = this.activeConfig.app; + // Guaranteed non-null by startup()'s pre-check before pushSettings() is invoked; guarded again + // here defensively rather than asserted, since activeConfig is a mutable field. + if (!app) { + return; + } + if (this.activeConfig.theme) { this._themeName.set(this.activeConfig.theme.themeName); } - this.applyUnitDefaults(this.activeConfig.app.unitDefaults); - this.applyNotificationConfig(this.activeConfig.app.notificationConfig); + this.applyUnitDefaults(app.unitDefaults); + this.applyNotificationConfig(app.notificationConfig); - if (this.activeConfig.app.autoNightMode === undefined) { + if (app.autoNightMode === undefined) { this.setAutoNightMode(false); } else { - this._autoNightMode.set(this.activeConfig.app.autoNightMode); + this._autoNightMode.set(app.autoNightMode); } - if (this.activeConfig.app.redNightMode === undefined) { + if (app.redNightMode === undefined) { this.setRedNightMode(false); } else { - this._redNightMode.set(this.activeConfig.app.redNightMode); + this._redNightMode.set(app.redNightMode); } - if (this.activeConfig.app.nightModeBrightness === undefined) { + if (app.nightModeBrightness === undefined) { this.setNightModeBrightness(0.2); } else { - this._nightModeBrightness.set(this.activeConfig.app.nightModeBrightness); + this._nightModeBrightness.set(app.nightModeBrightness); } if (this.activeConfig.dashboards === undefined) { @@ -252,10 +260,10 @@ export class SettingsService { this._dashboards = this.activeConfig.dashboards; } - if (this.activeConfig.app.browserTabTitle === undefined) { + if (app.browserTabTitle === undefined) { this._browserTabTitle.set('SKip'); } else { - this._browserTabTitle.set(this.activeConfig.app.browserTabTitle); + this._browserTabTitle.set(app.browserTabTitle); } } diff --git a/src/app/core/services/signalk-connection.service.ts b/src/app/core/services/signalk-connection.service.ts index 293cd650..f7a59b22 100644 --- a/src/app/core/services/signalk-connection.service.ts +++ b/src/app/core/services/signalk-connection.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { BehaviorSubject, catchError, lastValueFrom, throwError, timeout } from 'rxjs'; -import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ISignalKUrl } from '../interfaces/app-settings.interfaces'; import { ConnectionStateMachine } from './connection-state-machine.service'; @@ -38,10 +38,10 @@ interface ISignalKEndpointResponse { export interface IEndpointStatus { operation: number; message: string; - serverDescription: string; - httpServiceUrl: string; // v1 API endpoint + serverDescription: string | null; + httpServiceUrl: string | null; // v1 API endpoint httpServiceUrlV2?: string; // v2 API endpoint (if available) - WsServiceUrl: string; + WsServiceUrl: string | null; WsServiceUrlV2?: string; // v2 WebSocket endpoint (if available) subscribeAll?: boolean; } @@ -71,8 +71,8 @@ export class SignalKConnectionService { // Server information public signalKURL: ISignalKUrl; - private serverName: string; - public serverVersion$ = new BehaviorSubject(null); + private serverName: string | undefined; + public serverVersion$ = new BehaviorSubject(null); private serverRoles: string[] = []; private http = inject(HttpClient); @@ -298,10 +298,13 @@ export class SignalKConnectionService { * @returns Configured endpoint status object */ private processEndpointResponse( - endpointResponse: { body: ISignalKEndpointResponse; status: number }, + endpointResponse: HttpResponse, proxyEnabled?: boolean, subscribeAll?: boolean ): IEndpointStatus { + if (!endpointResponse.body) { + throw new Error("Signal K server response did not include a body"); + } console.debug("[Connection Service] Signal K HTTP Endpoints retrieved"); this.serverVersion$.next(endpointResponse.body.server.version); @@ -310,6 +313,10 @@ export class SignalKConnectionService { const httpUrlV2 = endpointResponse.body.endpoints.v2?.["signalk-http"]; const wsUrlV2 = endpointResponse.body.endpoints.v2?.["signalk-ws"]; + if (!httpUrl || !wsUrl) { + throw new Error("Signal K server response is missing required v1 endpoint URLs"); + } + const serverServiceEndpoints: IEndpointStatus = { operation: 2, message: endpointResponse.status?.toString() || "Connected", @@ -368,17 +375,17 @@ export class SignalKConnectionService { return this.serverServiceEndpoint$.asObservable(); } - public setServerInfo(name: string, version: string, roles: string[]): void { + public setServerInfo(name: string | undefined, version: string | undefined, roles: string[] | undefined): void { this.serverName = name; - this.serverRoles = roles; + this.serverRoles = roles ?? []; console.log(`[Connection Service] Server Name: ${name}, Version: ${version}, Roles: ${JSON.stringify(roles)}`); } - public get skServerName() : string { + public get skServerName() : string | undefined { return this.serverName; } - public get skServerVersion() : string { + public get skServerVersion() : string | null { return this.serverVersion$.getValue(); } diff --git a/src/app/core/services/signalk-delta.service.ts b/src/app/core/services/signalk-delta.service.ts index 08572e51..4851e919 100644 --- a/src/app/core/services/signalk-delta.service.ts +++ b/src/app/core/services/signalk-delta.service.ts @@ -59,7 +59,7 @@ export class SignalKDeltaService implements OnDestroy { // Self URN message stream Observer private _vesselSelfUrn$ = new Subject(); // local self URN to filter data based on root node (self or others) - private _selfUrn: string = undefined; + private _selfUrn: string | undefined = undefined; // Delta Service Endpoint status publishing public streamEndpoint: IStreamStatus = { @@ -73,7 +73,7 @@ export class SignalKDeltaService implements OnDestroy { // full delta firehose; path selection happens in DataService and per-widget rate limiting in // WidgetStreamsDirective (sampleTime). We intentionally do not emit per-path SK subscribe/unsubscribe deltas or use // SK subscription policies (policy/format/minPeriod), so no such controls exist in widget config. - private endpointWS: string = null; + private endpointWS: string | null = null; private SubscriptionType = "self"; private readonly WS_CONNECTION_SUBSCRIBE = "?subscribe="; private readonly WS_CONNECTION_META = "&sendMeta=all"; // default but we could use none + specific paths in the future @@ -280,6 +280,9 @@ export class SignalKDeltaService implements OnDestroy { * authenticates the WS upgrade, so no auth token is ever appended. */ private buildWebSocketUrl(): string { + if (!this.endpointWS) { + throw new Error('[Delta Service] No WebSocket endpoint available to build URL from'); + } const args = this.WS_CONNECTION_SUBSCRIBE + this.SubscriptionType + this.WS_CONNECTION_META; return this.endpointWS + args; } @@ -332,6 +335,10 @@ export class SignalKDeltaService implements OnDestroy { private processWebsocketMessage(message: ISignalKDeltaMessage) { if (message.updates) { + if (typeof message.context !== 'string') { + console.warn("[Delta Service] Dropping updates without a string context:", message); + return; + } this.parseUpdates(message.updates, message.context); return; } diff --git a/src/app/core/services/signalk-requests.service.ts b/src/app/core/services/signalk-requests.service.ts index 5d04d9e2..ae33362d 100644 --- a/src/app/core/services/signalk-requests.service.ts +++ b/src/app/core/services/signalk-requests.service.ts @@ -24,8 +24,8 @@ const handledStatusCodes = new Set([200, 202, 400, 401, 403, 405, 500, 502, 504] const serverErrorStatusCodes = new Set([500, 502, 504]); export interface skRequest { requestId: string; - state: string; - statusCode: number; + state: string | null; + statusCode: number | null; statusCodeDescription?: string; widgetUUID?: string; message?: string; @@ -107,43 +107,43 @@ export class SignalkRequestsService { private updateRequest(delta: ISignalKDeltaMessage) { const index = this.requests.findIndex(r => r.requestId == delta.requestId); if (index > -1) { // exists in local array - this.requests[index].state = delta.state; - this.requests[index].statusCode = delta.statusCode; + const statusCode = delta.statusCode; + this.requests[index].state = delta.state ?? null; + this.requests[index].statusCode = statusCode ?? null; this.requests[index].message = delta.message; - const currentStatusCode = deltaStatusCodes[delta.statusCode]; + const currentStatusCode = statusCode !== undefined ? deltaStatusCodes[statusCode] : undefined; - if ((typeof currentStatusCode != 'undefined') && handledStatusCodes.has(this.requests[index].statusCode)) { + if (statusCode !== undefined && (typeof currentStatusCode != 'undefined') && handledStatusCodes.has(statusCode)) { this.requests[index].statusCodeDescription = currentStatusCode; - if (this.requests[index].statusCode == 202) { + if (statusCode == 202) { console.log("[Request Service] Async 202 response received"); return; } - if (this.requests[index].statusCode == 400) { - this.toast.show(this.requests[index].message, 0, false, 'error'); + if (statusCode == 400) { + this.toast.show(this.requests[index].message ?? '', 0, false, 'error'); console.log("[Request Service] " + this.requests[index].message ); } - if (this.requests[index].statusCode == 403) { - console.warn("[Request Service] Status Code: " + this.requests[index].statusCode + " - " + this.requests[index].statusCodeDescription); + if (statusCode == 403) { + console.warn("[Request Service] Status Code: " + statusCode + " - " + currentStatusCode); } - if (this.requests[index].statusCode == 405) { - console.error("[Request Service] Status Code: " + this.requests[index].statusCode + " - " + this.requests[index].message); + if (statusCode == 405) { + console.error("[Request Service] Status Code: " + statusCode + " - " + this.requests[index].message); } - if (serverErrorStatusCodes.has(this.requests[index].statusCode)) { - const description = this.requests[index].statusCodeDescription; - const detail = this.requests[index].message ? description + " - " + this.requests[index].message : description; + if (serverErrorStatusCodes.has(statusCode)) { + const detail = this.requests[index].message ? currentStatusCode + " - " + this.requests[index].message : currentStatusCode; this.toast.show(detail, 0, false, 'error'); - console.error("[Request Service] Status Code: " + this.requests[index].statusCode + " - " + description); + console.error("[Request Service] Status Code: " + statusCode + " - " + currentStatusCode); } } else { - this.toast.show("Unknown Request Status Code received: " + this.requests[index].statusCode + " - " + deltaStatusCodes[this.requests[index].statusCode] + " - " + this.requests[index].message, 0, false, 'error'); - console.error("[Request Service] Unknown Request Status Code received: " + this.requests[index].statusCode + " - " + deltaStatusCodes[this.requests[index].statusCode] + " - " + this.requests[index].message); + this.toast.show("Unknown Request Status Code received: " + statusCode + " - " + currentStatusCode + " - " + this.requests[index].message, 0, false, 'error'); + console.error("[Request Service] Unknown Request Status Code received: " + statusCode + " - " + currentStatusCode + " - " + this.requests[index].message); } try { this.requestStatus$.next(this.requests[index]); // dispatched results diff --git a/src/app/core/services/storage.service.ts b/src/app/core/services/storage.service.ts index 6865c8b5..5fc27bac 100644 --- a/src/app/core/services/storage.service.ts +++ b/src/app/core/services/storage.service.ts @@ -52,15 +52,15 @@ export class StorageService { private http = inject(HttpClient); private readonly _auth = inject(AuthenticationService); - private serverEndpoint: string = null; + private serverEndpoint: string | null = null; public isAppDataSupported = false; - private configFileVersion: number = null; + private configFileVersion: number | null = null; public sharedConfigName: string; - private InitConfig: IConfig = null; + private InitConfig: IConfig | null = null; private _isRemoteContextBootstrapped = false; public storageServiceReady$ = new BehaviorSubject(false); private _isLoggedIn = false; - private _networkStatus: IEndpointStatus = undefined; + private _networkStatus: IEndpointStatus | undefined = undefined; // Instrumentation toggle private _logIO = false; // set to false to silence logging @@ -279,8 +279,11 @@ export class StorageService { this.ensureReady(); const serverConfigs: Config[] = []; if (!this.serverEndpoint) { + // Unreachable in practice: ensureReady() only passes once isStorageServiceReady() has + // observed a truthy serverEndpoint, and nothing ever clears it afterward. Kept as a + // defensive fallback that degrades to "no configs" rather than a crash. console.warn("[Storage Service] No server endpoint set. Cannot retrieve config list"); - return null; + return serverConfigs; } const base = this.serverEndpoint; @@ -344,8 +347,7 @@ export class StorageService { if (isInitLoad) this.InitConfig = remoteConfig; return cloneDeep(remoteConfig); } catch (error) { - this.handleError(error as HttpErrorResponse); // throws - return null; // unreachable + this.handleError(error as HttpErrorResponse); // never returns } } @@ -590,13 +592,13 @@ export class StorageService { } } - private handleError(error: HttpErrorResponse) { + private handleError(error: HttpErrorResponse): never { this.logError(error); // Rethrow so awaiting callers (get/set/list) observe the failure. throw error; } - public get initConfig(): IConfig { + public get initConfig(): IConfig | null { return this.InitConfig; } } diff --git a/src/app/widget-config/path-control-config/path-control-config.component.ts b/src/app/widget-config/path-control-config/path-control-config.component.ts index e1cc96fd..1e5e726d 100644 --- a/src/app/widget-config/path-control-config/path-control-config.component.ts +++ b/src/app/widget-config/path-control-config/path-control-config.component.ts @@ -174,7 +174,7 @@ export class PathControlConfigComponent implements OnInit, OnChanges { if (isMultiCTRLTypeLight) { supportsPUT = false; } else { - supportsPUT = compare(this._connection.skServerVersion, '2.12.0', ">=") ? pathFormGroup.value.supportsPut : false; + supportsPUT = this._connection.skServerVersion != null && compare(this._connection.skServerVersion, '2.12.0', ">=") ? pathFormGroup.value.supportsPut : false; } } From 2a27734ead5b4f9d87fc4ec486e972dd9c3a3be8 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 13:12:26 +0300 Subject: [PATCH 3/7] build(strict): refresh betterer baseline after merging main main's #207 edited dataset-chart-options.component.ts; betterer keys files by content hash, so the merge shifted that file's key while its single tracked issue is unchanged. Refresh the committed baseline to match the merged tree so the PR-merge CI check (branch + main) matches. Refs #6 --- .betterer.results | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.betterer.results b/.betterer.results index e5469086..6c35a498 100644 --- a/.betterer.results +++ b/.betterer.results @@ -143,7 +143,7 @@ exports[`strictNullChecks`] = { [37, 10, 26, "Type \'null\' is not assignable to type \'Subscription\'.", "273497119"], [79, 8, 13, "Type \'null\' is not assignable to type \'string | undefined\'.", "2466344431"] ], - "src/app/widget-config/dataset-chart-options/dataset-chart-options.component.ts:4252167719": [ + "src/app/widget-config/dataset-chart-options/dataset-chart-options.component.ts:2604195000": [ [82, 26, 10, "Argument of type \'ISkPathData | null\' is not assignable to parameter of type \'ISkPathData\'.\\n Type \'null\' is not assignable to type \'ISkPathData\'.", "384538813"] ], "src/app/widget-config/display-chart-options/display-chart-options.component.ts:333710162": [ From 26aabfbd60b6e85f0ce0c77be5b612a66a8be63a Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 13:21:22 +0300 Subject: [PATCH 4/7] fix(delta): pass through undefined context instead of dropping updates The strictNullChecks slice guarded `processWebsocketMessage` by dropping any updates message whose `context` isn't a string. That changed behaviour: a characterization test (`updates win`) sends a context-less updates message and expects it processed, and real prior behaviour passed `undefined` context straight through to `setPathContext`. Restore the exact behaviour and keep the types honest: `context` is optional (`IMeta`/`IPathValueData` already document "empty context assumes Self"), so widen the context type end-to-end (`parseUpdates`, `parseSkMeta`, `IPathValueData.context`, `IMeta.context`, `setPathContext`) and pass the value through unchanged. No new strictNullChecks issues; the widening is contained to the core data path. Refs #6 --- src/app/core/interfaces/app-interfaces.ts | 4 ++-- src/app/core/services/data.service.ts | 2 +- src/app/core/services/signalk-delta.service.ts | 8 ++------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/app/core/interfaces/app-interfaces.ts b/src/app/core/interfaces/app-interfaces.ts index 48951b6f..79315129 100644 --- a/src/app/core/interfaces/app-interfaces.ts +++ b/src/app/core/interfaces/app-interfaces.ts @@ -17,7 +17,7 @@ import { ISkMetadata, TState } from "./signalk-interfaces"; * @memberof app-interfaces */ export interface IPathValueData { - context: string; + context: string | undefined; path: string; source: string; timestamp: string; @@ -91,7 +91,7 @@ export interface IMeta { * Empty context should assume the message is from Self. Other contexts * can be from AIS, DCS and other types of remote emitting sources * configured */ - context: string, + context: string | undefined, path: string; meta: ISkMetadata; } diff --git a/src/app/core/services/data.service.ts b/src/app/core/services/data.service.ts index a8ad3b2a..274c8622 100644 --- a/src/app/core/services/data.service.ts +++ b/src/app/core/services/data.service.ts @@ -611,7 +611,7 @@ export class DataService implements OnDestroy { } } - private setPathContext(context: string, path: string): string { + private setPathContext(context: string | undefined, path: string): string { const finalPath = context !== this._selfUrn ? `${context}.${path}` : `${SELFROOTDEF}.${path}`; return finalPath; } diff --git a/src/app/core/services/signalk-delta.service.ts b/src/app/core/services/signalk-delta.service.ts index 4851e919..091efc05 100644 --- a/src/app/core/services/signalk-delta.service.ts +++ b/src/app/core/services/signalk-delta.service.ts @@ -335,10 +335,6 @@ export class SignalKDeltaService implements OnDestroy { private processWebsocketMessage(message: ISignalKDeltaMessage) { if (message.updates) { - if (typeof message.context !== 'string') { - console.warn("[Delta Service] Dropping updates without a string context:", message); - return; - } this.parseUpdates(message.updates, message.context); return; } @@ -363,7 +359,7 @@ export class SignalKDeltaService implements OnDestroy { console.warn("[Delta Service] Unknown message type. Message content:" + message); } - private parseUpdates(updates: ISignalKUpdateMessage[], context: string): void { + private parseUpdates(updates: ISignalKUpdateMessage[], context: string | undefined): void { // if (context != this._selfUrn) { // remove non self root nodes // return; // } @@ -487,7 +483,7 @@ export class SignalKDeltaService implements OnDestroy { return results; } - private parseSkMeta(metadata: ISignalKMeta, context: string) { + private parseSkMeta(metadata: ISignalKMeta, context: string | undefined) { if (metadata?.value == null) { console.warn("[Delta Service] Dropping metadata update without a value:", metadata); return; From 1bda8784b00e6022c2fa68c92e4082f6e44705a6 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 18:46:35 +0300 Subject: [PATCH 5/7] build(strict): scope betterer to match snc and document the ratchet Review follow-ups (#208): - betterer's glob tracked src/test.ts, which tsconfig.strict.json (and npm run snc) exclude; align the exclude so both tools check the same set. Baseline 843 -> 842 (the one test-setup issue leaves scope). - tsconfig.strict.json is now the single source of the strict compiler options (dropped the redundant duplication in .betterer.ts). - Document the ratchet + the content-hash baseline-regeneration footgun in CLAUDE.md (regenerate after fixing a file or merging main). Refs #6 --- .betterer.results | 3 --- .betterer.ts | 10 +++++----- CLAUDE.md | 10 ++++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.betterer.results b/.betterer.results index 6c35a498..cc1be080 100644 --- a/.betterer.results +++ b/.betterer.results @@ -966,9 +966,6 @@ exports[`strictNullChecks`] = { ], "src/default-config/config.blank.dashboard.ts:3810089117": [ [4, 6, 2, "Type \'null\' is not assignable to type \'string\'.", "5861160"] - ], - "src/test.ts:2801733261": [ - [246, 54, 4, "Argument of type \'null\' is not assignable to parameter of type \'string\'.", "2087897566"] ] }` }; diff --git a/.betterer.ts b/.betterer.ts index 962db0d5..de002393 100644 --- a/.betterer.ts +++ b/.betterer.ts @@ -1,11 +1,11 @@ import { typescript } from '@betterer/typescript'; +// tsconfig.strict.json is the single source of the strict compiler options. +// Betterer ignores that config's own include/exclude and uses the globs below, +// so they must mirror its file scope (all app sources, no specs, no test setup). export default { 'strictNullChecks': () => - typescript('./tsconfig.strict.json', { - strictNullChecks: true, - noEmit: true, - }) + typescript('./tsconfig.strict.json') .include('./src/**/*.ts') - .exclude(/\.spec\.ts$/), + .exclude(/\.spec\.ts$/, /(^|\/)test\.ts$/), }; diff --git a/CLAUDE.md b/CLAUDE.md index f6bdb272..aa1a2290 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,6 +18,16 @@ Skip — an Angular 21 (zoneless, signals, new control flow) Signal K marine mul Node: the app builds on Node 20+. CI runs the matrix 20/22/24. +## strictNullChecks ratchet (betterer) + +`strictNullChecks` is not yet enabled in the app build — the codebase still has hundreds of latent null-safety issues, migrated file-by-file (issue #6). A [betterer](https://phenomnomnominal.github.io/betterer/) ratchet holds the line: the count can only go down. + +- `npm run snc` — `tsc -p tsconfig.strict.json`, lists your remaining `strictNullChecks` errors. `tsconfig.strict.json` is the single source of the strict compiler options and the checked file scope (all `src/**/*.ts`, no specs, no `test.ts`). +- `npm run betterer` — **regenerates** `.betterer.results` (the committed baseline). Run and commit this whenever you fix a file **or merge/rebase main into your branch**. +- `npm run betterer:ci` — the check the `strict-null-checks` CI job runs; fails on any new issue. + +The baseline is keyed by file **content hash**, so any content change to a tracked file — a real fix, or just merging main — invalidates its key and makes `betterer:ci` fail with "unexpected changes" until you regenerate. If CI reports the same count but "unexpected changes", that's the tell: run `npm run betterer` and commit the result. The check runs on Node 24; the baseline is Node-portable (Node 20/22/24 produce identical results). `tsconfig.betterer.json` is only the ts-node loader config for `.betterer.ts`, not a check config. + ## Performance and freeze measurement (perf-harness/) Self-contained freeze/jank harness (own `package.json`, never touches app deps): builds the **real production bundle**, drives it in headless Chromium at **10× CPU throttle** (Pi-class HaLOS target) against a **mock Signal K server** on one origin. Use it for perf-sensitive changes to widgets, rendering, or the data pipeline, and for before/after numbers when replicating upstream perf work. Full operator docs: `perf-harness/README.md`. From 69b6b5495a30a791cce85abc68b82649c9f3d4a4 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 18:46:35 +0300 Subject: [PATCH 6/7] fix(upgrade): reset the overlay and guard app-less config on the legacy path Review follow-up (#208): the version-less legacy upgrade path could wedge the app behind the upgrade overlay. Its outer catch pushed an error but never reset upgrading() (unlike the v11/v12 paths), and transformConfig dereferenced config.app unguarded, throwing a TypeError on a partial/corrupt legacy slot. Reset the flag in the catch and skip app-less slots. Refs #6 --- src/app/core/services/configuration-upgrade.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/core/services/configuration-upgrade.service.ts b/src/app/core/services/configuration-upgrade.service.ts index acdac5cc..17540921 100644 --- a/src/app/core/services/configuration-upgrade.service.ts +++ b/src/app/core/services/configuration-upgrade.service.ts @@ -109,6 +109,8 @@ export class ConfigurationUpgradeService { setTimeout(() => this._settings.reloadApp(), 1500); } catch (error) { this.pushError('Error fetching configuration data: ' + (error as Error).message); + // Clear the blocking overlay so the error is visible, matching the v11/v12 paths. + this.upgrading.set(false); } } else if (version === 11) { @@ -264,7 +266,7 @@ export class ConfigurationUpgradeService { // eslint-disable-next-line @typescript-eslint/no-explicit-any private async transformConfig(rootConfig: Config): Promise { const config = await this._storage.getConfig(rootConfig.scope, rootConfig.name, this.legacyFileVersion) as unknown as v10IConfig; - if (config.app.configVersion !== this.legacyConfigVersion) { + if (!config.app || config.app.configVersion !== this.legacyConfigVersion) { this.pushError(`[Upgrade Service] ${rootConfig.scope}/${rootConfig.name} is not an upgradable version ${this.legacyConfigVersion} config. Skipping.`); return null; } From 3740807efd19dcb922ee201ecb5bc15222a15790 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 5 Jul 2026 18:46:35 +0300 Subject: [PATCH 7/7] test: cover the strictNullChecks behavior edges Review follow-up (#208): the ratchet guards types, not behavior (a delta regression slipped past type-checking and was caught only by vitest). Add characterization tests for the behavior this slice made explicit: - signalk-connection: processEndpointResponse returns operation-2 for a well-formed response and throws (fail-loud) on a missing body or absent v1 WebSocket URL (new spec). - configuration-upgrade: legacy path clears the overlay on a listing failure, skips an app-less slot without crashing, and startFresh skips an app-less slot while still retiring the rest. Also clear resetSettings between tests. - signalk-delta: the no-context 'updates win' message emits context: undefined. Refs #6 --- .../configuration-upgrade.service.spec.ts | 42 +++++++++++++++ .../signalk-connection.service.spec.ts | 54 +++++++++++++++++++ .../services/signalk-delta.service.spec.ts | 3 ++ 3 files changed, 99 insertions(+) create mode 100644 src/app/core/services/signalk-connection.service.spec.ts diff --git a/src/app/core/services/configuration-upgrade.service.spec.ts b/src/app/core/services/configuration-upgrade.service.spec.ts index c526b93e..a41b370a 100644 --- a/src/app/core/services/configuration-upgrade.service.spec.ts +++ b/src/app/core/services/configuration-upgrade.service.spec.ts @@ -27,7 +27,9 @@ describe('ConfigurationUpgradeService', () => { mockStorage.listConfigs.mockClear(); mockStorage.getConfig.mockClear(); mockStorage.setConfig.mockClear(); + mockStorage.initConfig = null; mockAppSettings.reloadApp.mockClear(); + mockAppSettings.resetSettings.mockClear(); TestBed.configureTestingModule({ providers: [ @@ -204,4 +206,44 @@ describe('ConfigurationUpgradeService', () => { expect(mockStorage.setConfig).toHaveBeenCalledWith( 'user', 'uconf', expect.objectContaining({ app: expect.objectContaining({ configVersion: 0 }) }), 9); }); + + it('legacy upgrade clears the blocking overlay when slot listing fails, without wedging', async () => { + mockStorage.listConfigs.mockRejectedValueOnce(new Error('offline')); + + await service.runUpgrade(); // legacy (version-less) path + + // The catch must reset upgrading(), matching the v11/v12 paths; leaving it set + // wedges the app behind the upgrade overlay with no dismiss. + expect(service.upgrading()).toBe(false); + }); + + it('legacy upgrade skips a slot whose config has no app section, without crashing', async () => { + mockStorage.listConfigs.mockResolvedValueOnce([{ scope: 'user', name: 'default' }]); + mockStorage.getConfig.mockResolvedValue({ theme: { themeName: '' } }); // no app section + + await service.runUpgrade(); + + // transformConfig returns null for the app-less slot, so nothing is persisted and + // the prior config.app.configVersion TypeError no longer fires. + expect(mockStorage.setConfig).not.toHaveBeenCalled(); + }); + + it('startFresh skips a legacy slot missing its app section and still retires the rest', async () => { + mockStorage.initConfig = null; // remote (Signal K) path + mockStorage.listConfigs.mockResolvedValueOnce([ + { scope: 'global', name: 'gconf' }, + { scope: 'user', name: 'uconf' } + ]); + mockStorage.getConfig.mockImplementation((scope: string, name: string) => + Promise.resolve(name === 'uconf' ? { app: { configVersion: 10 } } : { theme: {} })); + + service.startFresh(); + await vi.waitFor(() => expect(mockAppSettings.resetSettings).toHaveBeenCalled()); + + // The valid user slot is retired; the app-less global slot is skipped, not retired. + expect(mockStorage.setConfig).toHaveBeenCalledWith( + 'user', 'uconf', expect.objectContaining({ app: expect.objectContaining({ configVersion: 0 }) }), 9); + expect(mockStorage.setConfig).not.toHaveBeenCalledWith( + 'global', 'gconf', expect.anything(), 9); + }); }); diff --git a/src/app/core/services/signalk-connection.service.spec.ts b/src/app/core/services/signalk-connection.service.spec.ts new file mode 100644 index 00000000..6ef43171 --- /dev/null +++ b/src/app/core/services/signalk-connection.service.spec.ts @@ -0,0 +1,54 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { SignalKConnectionService } from './signalk-connection.service'; +import { ConnectionStateMachine } from './connection-state-machine.service'; + +describe('SignalKConnectionService', () => { + let service: SignalKConnectionService; + + const mockStateMachine = { setHTTPRetryCallback: vi.fn() }; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + SignalKConnectionService, + { provide: ConnectionStateMachine, useValue: mockStateMachine }, + { provide: HttpClient, useValue: {} } + ] + }); + service = TestBed.inject(SignalKConnectionService); + }); + + // processEndpointResponse is private; exercise it directly to pin the endpoint-parsing + // contract, including the fail-loud behavior for a malformed discovery response. + const parseEndpoint = (body: unknown, proxyEnabled = false) => + (service as unknown as { + processEndpointResponse: (r: unknown, p?: boolean, s?: boolean) => { operation: number; httpServiceUrl: string | null }; + }).processEndpointResponse(new HttpResponse({ body, status: 200 }), proxyEnabled); + + const wellFormedBody = { + server: { id: 'signalk-server', version: '2.0.0' }, + endpoints: { v1: { 'signalk-http': 'http://host:3000/signalk/v1/api/', 'signalk-ws': 'ws://host:3000/signalk/v1/stream' } } + }; + + describe('processEndpointResponse', () => { + it('returns a connected (operation 2) endpoint for a well-formed v1 response', () => { + const status = parseEndpoint(wellFormedBody); + expect(status.operation).toBe(2); + expect(status.httpServiceUrl).toBe('http://host:3000/signalk/v1/api/'); + }); + + it('throws when the response has no body', () => { + expect(() => parseEndpoint(null)).toThrow(); + }); + + it('throws when the v1 WebSocket URL is absent (fail-loud, no partial connect)', () => { + const noWs = { + server: { id: 'signalk-server', version: '2.0.0' }, + endpoints: { v1: { 'signalk-http': 'http://host:3000/signalk/v1/api/' } } + }; + expect(() => parseEndpoint(noWs)).toThrow(); + }); + }); +}); diff --git a/src/app/core/services/signalk-delta.service.spec.ts b/src/app/core/services/signalk-delta.service.spec.ts index 443362d3..e75d7265 100644 --- a/src/app/core/services/signalk-delta.service.spec.ts +++ b/src/app/core/services/signalk-delta.service.spec.ts @@ -338,6 +338,9 @@ describe('SignalKDeltaService', () => { service.subscribeRequestUpdates().subscribe(v => reqs.push(v)); parse(service, { requestId: 'req-1', updates: [update([{ path: 'navigation.speedOverGround', value: 1 }])] }); expect(out).toHaveLength(1); + // A message with no context field emits context: undefined (DataService folds it to self). + // Pin it so a refactor coercing undefined -> '' or a literal URN is caught. + expect(out[0].context).toBeUndefined(); expect(reqs).toEqual([]); });