26 KiB
Legacy src/services/ dependency graph
Generated from the actual imports in server/src on 2026-07-29 (after the
mapsService migration — step 4's head in the dependency-honest order below: the
1429-line geo core folded into the DI-native MapsService, its pure helpers
into nest/maps/maps.helpers.ts, the 3 geo MCP tools onto the decorator
registry (maps.mcp.ts — the weather/airport tools stay in the legacy
mapsWeather.ts registrar), BookingImportService injects it, and a 3-export
maps.bridge.ts serves placeEnrichment + the places registrar; earlier the
same day, the tripService migration + its quirk-fix pass completed step 2: the
1121-line hub folded into the DI-native TripsService, its MCP surface moved
to the decorator-driven trips.mcp.ts/share.mcp.ts, the plugin RPC host
injects it, and the bridge cascade landed — todo.bridge, share.bridge,
collab.bridge and vacay.bridge deleted with their last consumers, the
days/budget bridges pruned to their surviving exports. Earlier context: the budgetService
migration (step 1) and the
Phase 0 quick wins: getAppUrl/getMcpSafeUrl → src/app-config deleted the
fake → notifications edges for the identity/MCP stack and freed mapsService;
the addon-enablement reads moved from adminService into nest/addons/
(addons.bridge.ts for out-of-container consumers), collapsing the admin
god-file's ~27-consumer fan-in to the admin routes + one admin-2 residual
and freeing oauthService).
This regeneration parses static from '...', require('...') and dynamic
import('...') specifiers, so the lazy edges the earlier grep-based analysis
missed (scheduler jobs, the db-boot airport backfill, the fire-and-forget
notification sends) are now included. Regenerate any time by re-running that
three-pattern import scan over server/src (a throwaway script suffices; the
patterns are the whole trick).
How to read it:
- imports (services/) — what the legacy file pulls from other legacy files. A service is
migration-ready when this column contains only helpers (see classification below) and/or
tripAccess(Wave-2 "don't migrate, delete": absorb intoDatabaseService.canAccessTrip). Edges tagged (lazy) areimport()/require()at call time — they never block a migration, because a migrated Nest service can keep the same lazy import until the target domain migrates (collab/packing/trips/reservations/vacay all do exactly that fornotificationService). - imported by (services/) — legacy files that would need a bridge or repoint when this one migrates (a legacy module can't inject).
- nest consumers — in-container consumers: repoint to the injected service
(
exports: [XService]+ module import), never a bridge. - out-of-container consumers —
mcp/,scheduler.ts,websocket.ts,db/,middleware/,systemNotices/,index.ts: these are the bridge pressure (todo.bridge.ts precedent).
Node classification
- Already DI-native (legacy file deleted): tags, categories, todo, packing, day-notes,
trip-invite, assignments, share, settings, files, collab, vacay, reservations, day,
permissions (module-scoped cache retained on purpose — the bridge and DI instances share
one invalidation), audit (the
writeAuditinjectable;client-ip.tsand the deliberately side-effectfulaudit-log.logger.tsstay plain modules insidenest/audit/), exchange-rates (the exchangeRateService fold intonest/budget/as the dep-freeExchangeRatesService— module-scoped rate cache retained on purpose, permissions-style, so out-of-container instances and the DI singleton share one cached upstream feed; itsexchange-rates.bridgewas deleted with the budgetService migration), budget (the 755-line money core folded intoBudgetService;budget.bridge.tsis down to the two exports still consumed outside the container — userCleanupService and the legacy transports registrar), trip (the 1121-line hub folded intoTripsService; its six bridge imports became injected services, and the fold deletedtodo.bridge,share.bridge,collab.bridgeandvacay.bridgewith their last consumers; a 3-exporttrips.bridge.tsserves the legacy prompts registrar's getTripSummary andbudget.mcp.ts's getTripOwner/listMembers seam — injecting there would need a forwardRef'd TripsModule↔BudgetModule cycle;days.bridge.tsshrank to getDay/listDays for the transit/transports registrars), maps (the 1429-line geo core folded intoMapsService; pure helpers —buildUserAgent, the opening-hours parsers, POI categories, Overpass endpoint resolution — are plain exports innest/maps/maps.helpers.ts(transitService imports its UA from there — helper import, non-blocking); the module-scoped POI cache / photo-fetch semaphore / frozen Overpass mirrors stay module-scoped on purpose so the bridge instance and the DI singleton share them; a 3-exportmaps.bridge.ts(getMapsKey/searchPlaces/getPlacePhoto) serves the legacyplaceEnrichmenthelper andmcp/tools/places.tsuntil the place domain migrates). - Domain migration targets (the wave material): adminService, airportService, atlasService, authService, backupService, collectionsService, journeyService, journeyShareService, notificationService, oauthService, oidcService, passkeyService, placeService, transitService, transitItineraryService, weatherService, wikiService.
- Cross-cutting Wave-2 targets: permissions and auditLog are done (2026-07) — see the DI-native list above; only tripAccess remains (delete, don't migrate).
- Helpers that stay as plain modules (pure/infra, not wave material): avatarUrl,
queryHelpers, conflictResult, cookie, demo, distanceService, ephemeralTokens, apiKeyCrypto,
mfaCrypto, passwordPolicy, webauthnConfig, timezoneService, llmConfig, kmlImport, placeImage,
placePhotoCache, placeEnrichment, unsplashService, userCleanupService,
inAppNotifications, inAppNotificationActions, notificationPreferencesService, notifications
(+
notifications/registry),memories/cluster,airtrail/cluster. Several of these are themselves candidates to fold into a domain service when its domain migrates (e.g. placeEnrichment → places — the exchangeRateService → budget fold landed 2026-07 on exactly this pattern).
Domain-level graph (edges = "must migrate first, or bridge"; dotted = lazy, non-blocking)
flowchart TD
classDef ready fill:#2e7d32,color:#fff
classDef blocked fill:#b71c1c,color:#fff
classDef hub fill:#e65100,color:#fff
classDef infra fill:#455a64,color:#fff
subgraph frontier["READY FRONTIER (no unmigrated domain deps)"]
atlas[atlasService]:::ready
weather[weatherService]:::ready
airport["airportService (boot special case)"]:::ready
wiki[wikiService]:::ready
collections[collectionsService]:::ready
place["placeService (maps edge → maps.bridge since the fold)"]:::ready
transit["transitService (UA → maps.helpers since the fold)"]:::ready
oauth[oauthService]:::ready
end
notifSvc[notificationService]:::blocked
transitItin[transitItineraryService]:::blocked
admin[adminService]:::blocked
auth[authService]:::blocked
oidc[oidcService]:::blocked
passkey[passkeyService]:::blocked
journey[journeyService]:::blocked
journeyShare[journeyShareService]:::blocked
backup["backupService (last by design)"]:::infra
notifCluster["notifications cluster<br/>(notifications + prefs + inApp + channelRegistry)"]:::infra
memories["memories/ cluster"]:::infra
cleanup[userCleanupService]:::infra
collections -.-> notifSvc
transitItin --> transit
notifSvc --> notifCluster
admin --> auth & notifSvc & cleanup
auth --> atlas
oidc --> auth
passkey --> auth
journey --> memories
journeyShare --> journey
memories --> notifSvc & admin
(The former placeService → mapsService edge (via the placeEnrichment helper) is a
maps.bridge repoint since the 2026-07 maps fold, and transitService's UA import is
the pure maps.helpers — helpers never block — so both sit on the frontier now;
notificationService → notifications cluster is a hard import — the former
mapsService/transitService/webauthnConfig/ oauthService → notifications edges were only getAppUrl/getMcpSafeUrl and died with the
Phase 0 move to src/app-config, which had put mapsService on the frontier. memories/ ↔ admin/notificationService edges make the
journey/memories corner tangle with the admin corner. The former
auth/collections/backup → permissions and notifSvc/oauth → auditLog edges are gone since the
2026-07 Wave-2 pair: the permissions consumers repointed to nest/permissions/permissions.bridge,
the writeAudit consumers to nest/audit/audit.bridge, and the log*-only consumers to the plain
nest/audit/audit-log.logger — none of them block a migration anymore. The
tripService hub node is gone since the 2026-07 trip fold — the DI-native
TripsService injects its former bridge targets and keeps the legacy
avatarUrl/timezoneService/userCleanupService helpers as plain imports (helpers
never block). The dotted
collections -.-> notifSvc edge is a call-time import() for the collection-invite
notification: it stays as-is through a migration, exactly like the identical lazy sends in the
already-migrated collab/packing/trips/reservations/vacay services.)
Full adjacency table
| service | imports (services/) | imported by (services/) | nest consumers | out-of-container consumers |
|---|---|---|---|---|
adminService |
apiKeyCrypto, authService, avatarUrl, llmConfig, memories/helpersService, notificationService, passwordPolicy, userCleanupService (+ permissions.bridge) |
(none) | nest/admin/admin.service.ts, nest/packing/packing.mcp.ts (deletePackingTemplate, the admin-2 residual) |
scheduler.ts |
airportService |
(none) | (none) | nest/airports/airports.service.ts, nest/booking-import/kitinerary-mapper.ts | db/database.ts, mcp/tools/mapsWeather.ts, mcp/tools/transports.ts |
apiKeyCrypto |
(none) | adminService, airtrail/airtrailService, authService, llmConfig, memories/helpersService, memories/immichService, memories/photoResolverService, memories/synologyService, memories/unifiedService, notifications, oidcService, unsplashService | nest/maps/maps.service.ts, nest/plugins/plugin-oauth.service.ts, nest/plugins/plugin-runtime.service.ts, nest/plugins/plugins.service.ts, nest/settings/settings.service.ts | db/migrations.ts |
atlasService |
(none) | authService | nest/atlas/atlas.service.ts, nest/plugins/host/plugin-host-deps.factory.ts | mcp/resources.ts, mcp/tools/atlas.ts |
authService |
apiKeyCrypto, atlasService, avatarUrl, demo, distanceService, ephemeralTokens, mfaCrypto, passwordPolicy, tripMembership, userCleanupService, webauthnConfig (+ permissions.bridge) |
adminService, oidcService, passkeyService | nest/assignments/assignments.mcp.ts, nest/auth/auth.service.ts, nest/auth/passkey-enabled.guard.ts, nest/budget/budget.mcp.ts, nest/collab/collab.mcp.ts, nest/days/day-notes.mcp.ts, nest/days/days.mcp.ts, nest/oidc/oidc.service.ts, nest/packing/packing.mcp.ts, nest/reservations/reservations.mcp.ts, nest/share/share.mcp.ts, nest/tags/tags.mcp.ts, nest/todo/todo.mcp.ts, nest/trips/trips.mcp.ts, nest/vacay/vacay.mcp.ts | mcp/index.ts, mcp/tools/atlas.ts, mcp/tools/collections.ts, mcp/tools/journey.ts, mcp/tools/notifications.ts, mcp/tools/places.ts, mcp/tools/transit.ts, mcp/tools/transports.ts |
avatarUrl |
(none) | adminService, authService, inAppNotifications, journeyService | nest/budget/budget.service.ts, nest/collab/collab.service.ts, nest/files/files.service.ts, nest/packing/packing.service.ts, nest/reservations/reservations.service.ts, nest/trips/trips.service.ts | (none) |
backupService |
(none — permissions.bridge, plugin backup/paths infra only) |
(none) | nest/backup/backup.controller.ts, nest/backup/backup.service.ts | (none) |
collectionsService |
notificationService (lazy), placeImage (+ permissions.bridge) |
(none) | nest/collections/collections.service.ts, nest/plugins/host/plugin-host-deps.factory.ts | mcp/tools/collections.ts |
conflictResult |
(none) | placeService | nest/packing/packing.controller.ts, nest/packing/packing.service.ts, nest/places/places.controller.ts, nest/plugins/host/plugin-host-deps.factory.ts | (none) |
cookie |
(none) | (none) | nest/auth/auth-public.controller.ts, nest/auth/auth.service.ts, nest/auth/passkey.controller.ts, nest/oidc/oidc.controller.ts, nest/oidc/oidc.service.ts | (none) |
demo |
(none) | authService | nest/auth/auth.controller.ts, nest/collections/collections.controller.ts, nest/files/files.controller.ts, nest/places/places.controller.ts, nest/plugins/host/plugin-host-deps.factory.ts, nest/trips/trips.controller.ts | middleware/auth.ts, middleware/mfaPolicy.ts |
distanceService |
(none) | authService, transitItineraryService | (none) | (none) |
ephemeralTokens |
(none) | authService | nest/files/files.service.ts | index.ts, websocket.ts |
inAppNotificationActions |
(none) | inAppNotifications | (none) | (none) |
inAppNotifications |
avatarUrl, inAppNotificationActions, notificationPreferencesService | notificationService | nest/notifications/notifications.service.ts | mcp/resources.ts, mcp/tools/notifications.ts |
journeyService |
avatarUrl, memories/photoResolverService | journeyShareService | nest/assignments/assignments.service.ts, nest/journey/journey.service.ts, nest/places/places.service.ts, nest/plugins/host/plugin-host-deps.factory.ts, nest/plugins/journal-entry-rows.controller.ts | mcp/resources.ts, mcp/tools/journey.ts, mcp/tools/places.ts |
journeyShareService |
journeyService | (none) | nest/journey/journey.service.ts | mcp/tools/journey.ts |
kmlImport |
(none) | placeService | (none) | (none) |
llmConfig |
apiKeyCrypto | adminService | nest/llm-parse/llm-client.factory.ts, nest/llm-parse/llm-config.resolver.ts | (none) |
mfaCrypto |
(none) | authService | (none) | (none) |
notificationPreferencesService |
notifications, notifications/builtins, notifications/channelRegistry | inAppNotifications, notificationService, notifications, notifications/channelRegistry | nest/admin/admin.service.ts, nest/notifications/notifications.service.ts, nest/plugins/install/manifest.ts | (none) |
notifications |
apiKeyCrypto, notificationPreferencesService (+ audit-log.logger) |
notificationPreferencesService, notificationService, notifications/builtins | nest/auth/auth.service.ts, nest/notifications/notifications.service.ts | (none) |
notificationService |
inAppNotifications, notificationPreferencesService, notifications, notifications/channelRegistry (+ audit-log.logger) |
adminService, collectionsService, memories/synologyService, memories/unifiedService | nest/admin/admin.controller.ts, nest/collab/collab.service.ts, nest/packing/packing.service.ts, nest/plugins/host/plugin-host-deps.factory.ts, nest/reservations/reservations.service.ts, nest/trips/trips.service.ts, nest/vacay/vacay.service.ts | scheduler.ts |
oauthService |
(none — addons.bridge + audit.bridge only) |
(none) | nest/oauth/oauth-api.controller.ts, nest/oauth/oauth.service.ts | mcp/index.ts, mcp/oauthProvider.ts |
oidcService |
apiKeyCrypto, authService, tripMembership | (none) | nest/oidc/oidc.service.ts | (none) |
passkeyService |
authService, webauthnConfig | (none) | nest/admin/admin.service.ts, nest/auth/passkey.controller.ts | (none) |
passwordPolicy |
(none) | adminService, authService | (none) | (none) |
placeEnrichment |
(none — maps.bridge since the maps fold) |
placeService | (none) | (none) |
placeImage |
(none) | collectionsService, placeService | nest/collections/collections.controller.ts, nest/common/place-image-upload.ts, nest/places/places.controller.ts | (none) |
placePhotoCache |
(none) | placeService | nest/maps/maps.service.ts, nest/share/share.service.ts | scheduler.ts |
placeService |
conflictResult, kmlImport, placeEnrichment, placeImage, placePhotoCache, queryHelpers, unsplashService | (none) | nest/booking-import/booking-import.service.ts, nest/days/days.mcp.ts, nest/places/places.service.ts, nest/plugins/host/plugin-host-deps.factory.ts, nest/trips/trips.service.ts | mcp/resources.ts, mcp/tools/places.ts |
queryHelpers |
(none) | placeService | nest/assignments/assignments.service.ts, nest/days/days.service.ts, nest/share/share.service.ts | (none) |
timezoneService |
(none) | airtrail/airtrailMapper, transitItineraryService | nest/trips/trips.service.ts | (none) |
transitItineraryService |
distanceService, timezoneService, transitService (+ type-only reservations.bridge) |
(none) | (none) | mcp/tools/transit.ts |
transitService |
(none — buildUserAgent is the pure nest/maps/maps.helpers) |
transitItineraryService | nest/transit/transit.controller.ts | mcp/tools/transit.ts |
tripAccess |
(none) | (none) | nest/booking-import/booking-import.service.ts, nest/budget/budget.service.ts, nest/collab/collab.service.ts, nest/days/day-notes.service.ts, nest/integrations/airtrail-import.controller.ts, nest/packing/packing.service.ts, nest/reservations/reservations.service.ts, nest/todo/todo.service.ts | (none) |
tripMembership |
(none) | authService, oidcService | nest/plugins/host/plugin-host-deps.factory.ts, nest/trip-invite/trip-invite.service.ts | (none) |
unsplashService |
apiKeyCrypto | placeService | nest/trips/trips.controller.ts, nest/trips/trips.service.ts | (none) |
userCleanupService |
(none — budget.bridge, plugin paths infra only) |
adminService, authService | nest/trips/trips.service.ts | (none) |
weatherService |
(none) | (none) | nest/plugins/host/plugin-host-deps.factory.ts, nest/weather/weather.controller.ts, nest/weather/weather.service.ts | mcp/tools/mapsWeather.ts |
webauthnConfig |
(none) | authService, passkeyService | (none) | (none) |
wikiService |
(none) | (none) | nest/help/help.controller.ts | (none) |
Subdirectory clusters
notifications/:channelRegistry⇄notificationPreferencesService(cycle),builtins → notifications + channelRegistry, andnotificationPreferencesService → builtins(registering the built-in channels closes a second loop). Migrates as one unit withnotifications.ts/notificationService.memories/:helpersService(base) ← immich/synology/unified/photoResolver;photoResolverServiceis the seamjourneyServiceconsumes (it also pulls immich/synology/thumbnail/trekPhotoCache— the latter is swept byscheduler.ts);synology/unified → notificationServicecouples this cluster to the admin corner (thumbnailService's adminService edge was onlyisAddonEnabled— nowaddons.bridge);immichServicewrites audits vianest/audit/audit.bridge.airtrail/:airtrailClient(base) ← mapper ← service ← import/sync;import/syncconsume (since the 2026-07 reservations fold) thenest/reservations/reservations.bridgeinstead of the deletedreservationService(their adminService edge was onlyisAddonEnabled— nowaddons.bridge); since the auditLog fold,airtrailServicewrites audits vianest/audit/audit.bridgeandairtrailSynclogs via the plainnest/audit/audit-log.logger(same split inmemories/immichService→ bridge).
Decoding "what's next"
Ready frontier (all legacy deps are helpers, tripAccess, or lazy sends):
| Candidate | Why now / why not | Bridge tax (legacy dependents + out-of-container) |
|---|---|---|
| transitService ← pick | Frontier-ready since the maps fold (its only maps import is the pure maps.helpers UA); next in the step-4 chain, unblocks transitItineraryService |
transitItineraryService; mcp/tools/transit.ts |
| placeService | Frontier-ready since the maps fold (the placeEnrichment edge is a maps.bridge repoint); its legacy imports are all helpers — the fold can absorb placeEnrichment (exchange-rates precedent) and retire maps.bridge with it |
mcp/resources.ts, mcp/tools/places.ts |
| collectionsService | Frontier-ready — legacy deps are the placeImage helper + permissions.bridge, plus a call-time lazy import() of notificationService that a migrated service keeps (collab precedent) |
mcp/tools/collections.ts |
| atlasService | Zero deps, but its legacy dependent is authService (Wave-5) → bridge lives long |
authService; mcp/tools/atlas.ts, mcp/resources.ts |
| oauthService | Dependency-free since the Phase 0 addons extraction (adminService edge was only isAddonEnabled → addons.bridge) — but the coherence order keeps it after admin so the mcp/oauthProvider.ts merge (mcp-2) lands with it |
mcp/index.ts, mcp/oauthProvider.ts |
| weatherService / wikiService / airportService | Independent leaves; airport has the db/database.ts boot lazy-require special case |
little / none |
Blocked, and by what (shortest unblock path):
notificationService← notifications cluster (its auditLog edge is now the plainaudit-log.loggerimport — gone as a blocker since the 2026-07 Wave-2 pair).transitItineraryService←transitService(frontier-ready since the maps fold) — its reservation edge is now thereservations.bridgerepoint (type-only).adminService/authServicecorner:authService←atlasService(its permissions edge is now thepermissions.bridgerepoint);adminService←authService+notificationService.oauthServiceis dependency-free since the Phase 0 addons extraction (its adminService edge was onlyisAddonEnabled, nowaddons.bridge), but the coherence order stays atlas → auth → admin → oauth so themcp/oauthProvider.tsmerge (mcp-2) lands after admin (oidc/passkey ride auth; permissions done 2026-07).journeyService←memories/cluster (which itself touches admin + notificationService) →journeyShareServiceafter.collectionsService/backupService: unblocked since the permissions fold (their edges arepermissions.bridgerepoints now; collections' notificationService edge is lazy — see above); backup still stays last by design (owns the closeDb/reinitialize lifecycle and the plugin backup/paths infra).
Dependency-honest order (each step's deps are already done at that point;
vacayService, reservationService (the residue fold), dayService and the
Wave-2 permissions + auditLog pair were the first frontier picks — all done
2026-07, after collabService completed Wave 3):
exchangeRateServicefold →budgetService(both done 2026-07 —ExchangeRatesServicethen the fullBudgetServicefold innest/budget/;userCleanupServiceis free, repointed tobudget.bridge)tripService(done 2026-07 — the hub folded intoTripsService; the trips/share MCP surfaces moved to the decorator registry, the plugin host injects it, and the todo/share/collab/vacay bridges died with their last consumers)- notifications cluster →
notificationService mapsService(done 2026-07 — the geo core folded intoMapsService, geo MCP tools onto the decorator registry, BookingImportService injects it, and the 3-exportmaps.bridgecovers placeEnrichment + the places registrar) →transitService→placeService→transitItineraryServiceatlasService→authService(+ oidc/passkey) →adminService→oauthServicememories/cluster →journeyService→journeyShareService;collectionsService(frontier-ready since the permissions fold — can also go any time)- Independent any time:
weatherService,wikiService,airportService(move the boot backfill into Nest bootstrap when you do it);backupServicelast
Corrections to migrate.md this graph surfaced:
reservationServicedid not importbudgetService/dayService— the claimed Wave-4 ordering constraint didn't exist at the service layer; its legacy remainder was frontier-ready. Borne out by the 2026-07 fold: the budget/day coupling lives in the Nest wrapper's budget-sync seam and the MCP surface, which keep their legacy imports until those domains migrate.- Every remaining Wave-3/4 domain had legacy
tripServiceas a dependent — each migration before tripService paid a small bridge/repoint tax the files migration didn't have. Resolved by the 2026-07 trip fold: that recurring tax is gone, and the fold cashed in the accumulated bridges (todo/share/collab/vacay deleted, days/budget pruned). - (2026-07-28 regeneration) The earlier grep-based analysis only saw static imports, so it
missed every lazy edge: the scheduler's
require()jobs (admin, notificationService, placePhotoCache, airtrailSync, trekPhotoCache), thedb/database.tsboot backfill's airportService require,index.ts→ ephemeralTokens,systemNotices/conditions.ts→ adminService, andcollectionsService's call-timeimport()of notificationService. It also under-reportedtripService's bridge repoints (six, not three: budget, collab, days, packing, reservations, vacay). None of this changes the order — lazy edges don't block — but the bridge-pressure column was undercounting scheduler/system-notice consumers. - (2026-07-29 regeneration, post trip fold) The predicted bridge tax was right in shape but
the fold went further than a bridge: the whole trips MCP surface (10 tools + 3 resources
- the first
@Prompt) and the 3 share-link tools moved onto the decorator registry, so the only bridge left is the 3-exporttrips.bridge.tsfor the legacy prompts registrar andbudget.mcp.ts's owner/member seam.verifyTripAccess'stripAccessre-export died with the legacy file, leavingservices/tripAccesswith zero legacy importers — it is now purely Nest-consumed (Wave-2 "delete, don't migrate" is down to those repoints).
- the first
- (2026-07-29 regeneration, post maps fold) The predicted bridge tax over-counted:
transitServiceneeded no bridge at all — its only import was the purebuildUserAgent, which now lives inmaps.helpers.tsas a plain export (helpers never block), and after the geo tools moved onto the decorator registrymcp/tools/mapsWeather.tsno longer consumes the maps domain (the registrar file survives for its weather + airport tools; itssearchPlacesimport turned out to be dead code and died with the prune). The bridge is 3 exports forplaceEnrichment+mcp/tools/places.ts, both of which the placeService migration will absorb. Step 4's remaining chain — transitService → placeService — is now entirely frontier-ready.