diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ba1309..f0bb669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v0.12.1 — 2026-09-25 07:57 + +- Hotfix for the v0.12.0 bootstrap crash (`UnknownDependenciesException`): `MeshModule` now exports `MeshService`, and `WeatherService`'s `import type { MeshService }` became a value import — with `emitDecoratorMetadata` the type-only import is erased, so the constructor's DI token was `undefined` at runtime even with the export (the unit suite instantiates services directly and never booted the module graph, which is why 58 green tests still shipped a broken app). +- `docker-compose.yml`: `WEATHER_CRON` passthrough (`${WEATHER_CRON:-}`) mirroring the existing notify-var pattern — empty/unset falls back to the service default `'0 * * * *'`. No LAT/LON/CITY passthroughs (service-side defaults suffice). +- New DI smoke spec `src/weather/di.spec.ts`: compiles `WeatherModule` with Nest's `Test.createTestingModule().compile()` (no `app.init()`, so no TCP connection) and asserts `WeatherService` resolves its `MeshService` from `MeshModule` — fails if the export regresses. + ## v0.12.0 — 2026-09-25 07:42 - New `src/weather` module: hourly cron-triggered weather report for Lublin, broadcast on the mesh (pinned send channel). `WeatherService.fetchWeather()` GETs Open-Meteo current conditions (`temperature_2m`, `apparent_temperature`, `relative_humidity_2m`, `wind_speed_10m`, `weather_code` — free API, no key) via global fetch; `formatReport()` renders one short line (e.g. `Lublin weather: 18°C (feels 17), partly cloudy, humidity 65%, wind 12 km/h`) with a WMO weather-code → description map (~19 common codes, unknown → `code `). The `@Cron` handler fetches, formats and broadcasts via `MeshService.sendText(BROADCAST_ADDR, report)`; any error logs `[weather] report failed: ...` and is swallowed — the cron never crashes the app. diff --git a/docker-compose.yml b/docker-compose.yml index fccc94a..9882176 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: MESH_POLL_MS: '1000' MESH_NOTIFY_URL: ${MESH_NOTIFY_URL:-} MESH_NOTIFY_CHANNEL: '${MESH_NOTIFY_CHANNEL:-3}' + WEATHER_CRON: ${WEATHER_CRON:-} volumes: - mesh_data:/app/data restart: unless-stopped diff --git a/package.json b/package.json index 8271564..5cc1dc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mesh-bot", - "version": "0.12.0", + "version": "0.12.1", "description": "", "author": "", "private": true, diff --git a/src/mesh/mesh.module.ts b/src/mesh/mesh.module.ts index aa9cbbf..bc945c3 100644 --- a/src/mesh/mesh.module.ts +++ b/src/mesh/mesh.module.ts @@ -23,5 +23,6 @@ function openMessageDb(): DatabaseSync { useFactory: () => new MeshService(openMessageDb()), }, ], + exports: [MeshService], }) export class MeshModule {} diff --git a/src/weather/di.spec.ts b/src/weather/di.spec.ts new file mode 100644 index 0000000..2a142b7 --- /dev/null +++ b/src/weather/di.spec.ts @@ -0,0 +1,17 @@ +import { Test } from '@nestjs/testing'; +import { describe, expect, it } from 'vitest'; +import { MeshService } from '../mesh/mesh.service.js'; +import { WeatherModule } from './weather.module.js'; +import { WeatherService } from './weather.service.js'; + +describe('DI wiring (module graph)', () => { + // compile() instantiates providers but does not run lifecycle hooks, so no TCP connection is opened. + it('resolves WeatherService with MeshService provided by MeshModule', async () => { + process.env.MESH_DB_PATH = ':memory:'; // keep openMessageDb from touching the repo's ./data + const moduleRef = await Test.createTestingModule({ + imports: [WeatherModule], + }).compile(); + expect(moduleRef.get(WeatherService)).toBeInstanceOf(WeatherService); + expect(moduleRef.get(MeshService)).toBeInstanceOf(MeshService); + }); +}); diff --git a/src/weather/weather.service.ts b/src/weather/weather.service.ts index dea85dc..6caa40e 100644 --- a/src/weather/weather.service.ts +++ b/src/weather/weather.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; import { BROADCAST_ADDR } from '../mesh/codec.js'; -import type { MeshService } from '../mesh/mesh.service.js'; +import { MeshService } from '../mesh/mesh.service.js'; /** Open-Meteo `current` block fields we use (free API, no key). */ export interface CurrentWeather {