From 465331d6721cce6f39a9fd6eac7022d9ce27b734 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:42:18 +0000 Subject: [PATCH 1/3] Initial plan From b85940c221198a252f362572803a6d93e06fb417 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:48:18 +0000 Subject: [PATCH 2/3] refactor(measure): encapsulate infrastructure exports Co-authored-by: marcuscastelo <27441558+marcuscastelo@users.noreply.github.com> Agent-Logs-Url: https://github.com/marcuscastelo/macroflows/sessions/7fde155e-0dd3-4a73-83c1-cf7b5e6b7899 --- .../infrastructure/measureRepository.ts | 13 +++++-- .../infrastructure/supabase/constants.ts | 1 - .../infrastructure/supabase/realtime.ts | 37 ++++++++++--------- .../supabase/supabaseBodyMeasureGateway.ts | 11 +++++- .../supabase/supabaseMeasureMapper.ts | 19 +++++++--- .../measureInfrastructureExports.test.ts | 30 +++++++++++++++ 6 files changed, 81 insertions(+), 30 deletions(-) delete mode 100644 src/modules/measure/infrastructure/supabase/constants.ts create mode 100644 src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts diff --git a/src/modules/measure/infrastructure/measureRepository.ts b/src/modules/measure/infrastructure/measureRepository.ts index adf5df72b..56ffcedec 100644 --- a/src/modules/measure/infrastructure/measureRepository.ts +++ b/src/modules/measure/infrastructure/measureRepository.ts @@ -9,6 +9,11 @@ import { logging } from '~/shared/utils/logging' const supabaseGateway = createSupabaseBodyMeasureGateway() +/** + * Creates the measure repository backed by the Supabase gateway. + * + * @returns Repository implementation for measure CRUD operations. + */ export function createMeasureRepository(): BodyMeasureRepository { return { fetchUserBodyMeasures, @@ -18,7 +23,7 @@ export function createMeasureRepository(): BodyMeasureRepository { } } -export async function fetchUserBodyMeasures( +async function fetchUserBodyMeasures( userId: User['uuid'], ): Promise { try { @@ -29,7 +34,7 @@ export async function fetchUserBodyMeasures( } } -export async function insertBodyMeasure( +async function insertBodyMeasure( newBodyMeasure: NewBodyMeasure, ): Promise { try { @@ -40,7 +45,7 @@ export async function insertBodyMeasure( } } -export async function updateBodyMeasure( +async function updateBodyMeasure( bodyMeasureId: BodyMeasure['id'], newBodyMeasure: NewBodyMeasure, ): Promise { @@ -55,7 +60,7 @@ export async function updateBodyMeasure( } } -export async function deleteBodyMeasure( +async function deleteBodyMeasure( bodyMeasureId: BodyMeasure['id'], ): Promise { try { diff --git a/src/modules/measure/infrastructure/supabase/constants.ts b/src/modules/measure/infrastructure/supabase/constants.ts deleted file mode 100644 index f2bd14100..000000000 --- a/src/modules/measure/infrastructure/supabase/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' diff --git a/src/modules/measure/infrastructure/supabase/realtime.ts b/src/modules/measure/infrastructure/supabase/realtime.ts index 50928b842..1f159a9fc 100644 --- a/src/modules/measure/infrastructure/supabase/realtime.ts +++ b/src/modules/measure/infrastructure/supabase/realtime.ts @@ -2,17 +2,18 @@ import { type BodyMeasure, bodyMeasureSchema, } from '~/modules/measure/domain/measure' -import { SUPABASE_TABLE_BODY_MEASURES } from '~/modules/measure/infrastructure/supabase/constants' import { registerSubapabaseRealtimeCallback } from '~/shared/supabase/supabase' import { logging } from '~/shared/utils/logging' +const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' + let initialized = false /** * Sets up granular realtime subscription for body measure changes * @param onBodyMeasureChange - Callback for granular updates with event details */ -export function setupBodyMeasureRealtimeSubscription( +function setupBodyMeasureRealtimeSubscription( onBodyMeasureChange: (event: { eventType: 'INSERT' | 'UPDATE' | 'DELETE' old?: BodyMeasure @@ -26,6 +27,12 @@ export function setupBodyMeasureRealtimeSubscription( ) } +/** + * Initializes the body measure realtime subscription once per session. + * + * @param deps - Dependencies used to react to realtime body measure changes. + * @returns Nothing. + */ export function initializeMeasureRealtime(deps: { refetchBodyMeasures: () => void }): void { @@ -34,21 +41,17 @@ export function initializeMeasureRealtime(deps: { } logging.debug(`Measure realtime initialized!`) initialized = true - registerSubapabaseRealtimeCallback( - SUPABASE_TABLE_BODY_MEASURES, - bodyMeasureSchema, - (event) => { - logging.debug(`Event:`, event) + setupBodyMeasureRealtimeSubscription((event) => { + logging.debug(`Event:`, event) - switch (event.eventType) { - case 'INSERT': - case 'UPDATE': - case 'DELETE': { - // For measures, we simply refetch since we don't have complex caching - deps.refetchBodyMeasures() - break - } + switch (event.eventType) { + case 'INSERT': + case 'UPDATE': + case 'DELETE': { + // For measures, we simply refetch since we don't have complex caching + deps.refetchBodyMeasures() + break } - }, - ) + } + }) } diff --git a/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts b/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts index 1702a626d..22493a3c7 100644 --- a/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts +++ b/src/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway.ts @@ -3,13 +3,20 @@ import { type NewBodyMeasure, } from '~/modules/measure/domain/measure' import { type BodyMeasureGateway } from '~/modules/measure/domain/measureGateway' -import { SUPABASE_TABLE_BODY_MEASURES } from '~/modules/measure/infrastructure/supabase/constants' -import { supabaseBodyMeasureMapper } from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' +import { createSupabaseBodyMeasureMapper } from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' import { type User } from '~/modules/user/domain/user' import { supabase } from '~/shared/supabase/supabase' import { wrapErrorWithStack } from '~/shared/utils/errorUtils' import { logging } from '~/shared/utils/logging' +const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' +const supabaseBodyMeasureMapper = createSupabaseBodyMeasureMapper() + +/** + * Creates the Supabase gateway used to persist body measures. + * + * @returns Gateway implementation for body measure persistence operations. + */ export function createSupabaseBodyMeasureGateway(): BodyMeasureGateway { return { fetchUserBodyMeasures, diff --git a/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts b/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts index cd29328a1..819cf6230 100644 --- a/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts +++ b/src/modules/measure/infrastructure/supabase/supabaseMeasureMapper.ts @@ -6,10 +6,9 @@ import { import { type Database } from '~/shared/supabase/database.types' import { parseWithStack } from '~/shared/utils/parseWithStack' -export type InsertBodyMeasureDTO = +type InsertBodyMeasureDTO = Database['public']['Tables']['body_measures']['Insert'] -export type BodyMeasureDTO = - Database['public']['Tables']['body_measures']['Row'] +type BodyMeasureDTO = Database['public']['Tables']['body_measures']['Row'] // Conversion functions function toInsertDTO(newBodyMeasure: NewBodyMeasure): InsertBodyMeasureDTO { @@ -35,7 +34,15 @@ function toDomain(dto: BodyMeasureDTO): BodyMeasure { }) } -export const supabaseBodyMeasureMapper = { - toDomain, - toInsertDTO, +/** + * Creates the mapper responsible for converting body measure data + * between Supabase DTOs and domain entities. + * + * @returns Mapper functions for reading and writing body measure records. + */ +export function createSupabaseBodyMeasureMapper() { + return { + toDomain, + toInsertDTO, + } } diff --git a/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts new file mode 100644 index 000000000..a80014fe5 --- /dev/null +++ b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest' + +import * as measureRepositoryModule from '~/modules/measure/infrastructure/measureRepository' +import * as realtimeModule from '~/modules/measure/infrastructure/supabase/realtime' +import * as gatewayModule from '~/modules/measure/infrastructure/supabase/supabaseBodyMeasureGateway' +import * as mapperModule from '~/modules/measure/infrastructure/supabase/supabaseMeasureMapper' + +describe('measure infrastructure module exports', () => { + it('exports only the measure repository factory', () => { + expect(Object.keys(measureRepositoryModule)).toEqual([ + 'createMeasureRepository', + ]) + }) + + it('exports only the Supabase gateway factory', () => { + expect(Object.keys(gatewayModule)).toEqual([ + 'createSupabaseBodyMeasureGateway', + ]) + }) + + it('exports only the Supabase mapper factory', () => { + expect(Object.keys(mapperModule)).toEqual([ + 'createSupabaseBodyMeasureMapper', + ]) + }) + + it('keeps realtime exports limited to the public initializer', () => { + expect(Object.keys(realtimeModule)).toEqual(['initializeMeasureRealtime']) + }) +}) From 9f5c459eb2538d0df1b2a283322afce22f5f5023 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Tue, 24 Mar 2026 18:10:26 -0300 Subject: [PATCH 3/3] chore(pr): apply PR #1469 suggestions --- .../application/usecases/measureState.ts | 11 ++- .../infrastructure/supabase/realtime.ts | 92 ++++++++++--------- .../measureInfrastructureExports.test.ts | 12 ++- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/modules/measure/application/usecases/measureState.ts b/src/modules/measure/application/usecases/measureState.ts index cacd39e82..2d2d5c23e 100644 --- a/src/modules/measure/application/usecases/measureState.ts +++ b/src/modules/measure/application/usecases/measureState.ts @@ -1,21 +1,22 @@ import { createResource, createRoot } from 'solid-js' import { createMeasureCrud } from '~/modules/measure/application/usecases/measureCrud' -import { initializeMeasureRealtime } from '~/modules/measure/infrastructure/supabase/realtime' +import { createMeasureRealtimeService } from '~/modules/measure/infrastructure/supabase/realtime' + +const measureRealtimeService = createMeasureRealtimeService() export function createMeasureState(deps: { getCurrentUserIdOrGuestId: () => string fetchUserBodyMeasures?: ReturnType< typeof createMeasureCrud >['fetchUserBodyMeasures'] - initializeMeasureRealtime?: (deps: { - refetchBodyMeasures: () => void - }) => void + initializeMeasureRealtime?: typeof measureRealtimeService.initializeMeasureRealtime }) { const localFetch = deps.fetchUserBodyMeasures ?? createMeasureCrud().fetchUserBodyMeasures const localInitializeRealtime = - deps.initializeMeasureRealtime ?? initializeMeasureRealtime + deps.initializeMeasureRealtime ?? + measureRealtimeService.initializeMeasureRealtime return createRoot(() => { const [bodyMeasures, { refetch: refetchBodyMeasures }] = createResource( diff --git a/src/modules/measure/infrastructure/supabase/realtime.ts b/src/modules/measure/infrastructure/supabase/realtime.ts index 1f159a9fc..22d77a879 100644 --- a/src/modules/measure/infrastructure/supabase/realtime.ts +++ b/src/modules/measure/infrastructure/supabase/realtime.ts @@ -7,51 +7,57 @@ import { logging } from '~/shared/utils/logging' const SUPABASE_TABLE_BODY_MEASURES = 'body_measures' -let initialized = false +export function createMeasureRealtimeService() { + let initialized = false -/** - * Sets up granular realtime subscription for body measure changes - * @param onBodyMeasureChange - Callback for granular updates with event details - */ -function setupBodyMeasureRealtimeSubscription( - onBodyMeasureChange: (event: { - eventType: 'INSERT' | 'UPDATE' | 'DELETE' - old?: BodyMeasure - new?: BodyMeasure - }) => void, -): void { - registerSubapabaseRealtimeCallback( - SUPABASE_TABLE_BODY_MEASURES, - bodyMeasureSchema, - onBodyMeasureChange, - ) -} - -/** - * Initializes the body measure realtime subscription once per session. - * - * @param deps - Dependencies used to react to realtime body measure changes. - * @returns Nothing. - */ -export function initializeMeasureRealtime(deps: { - refetchBodyMeasures: () => void -}): void { - if (initialized) { - return + /** + * Sets up granular realtime subscription for body measure changes + * @param onBodyMeasureChange - Callback for granular updates with event details + */ + function setupBodyMeasureRealtimeSubscription( + onBodyMeasureChange: (event: { + eventType: 'INSERT' | 'UPDATE' | 'DELETE' + old?: BodyMeasure + new?: BodyMeasure + }) => void, + ): void { + registerSubapabaseRealtimeCallback( + SUPABASE_TABLE_BODY_MEASURES, + bodyMeasureSchema, + onBodyMeasureChange, + ) } - logging.debug(`Measure realtime initialized!`) - initialized = true - setupBodyMeasureRealtimeSubscription((event) => { - logging.debug(`Event:`, event) - switch (event.eventType) { - case 'INSERT': - case 'UPDATE': - case 'DELETE': { - // For measures, we simply refetch since we don't have complex caching - deps.refetchBodyMeasures() - break - } + /** + * Initializes the body measure realtime subscription once per session. + * + * @param deps - Dependencies used to react to realtime body measure changes. + * @returns Nothing. + */ + function initializeMeasureRealtime(deps: { + refetchBodyMeasures: () => void + }): void { + if (initialized) { + return } - }) + logging.debug(`Measure realtime initialized!`) + initialized = true + setupBodyMeasureRealtimeSubscription((event) => { + logging.debug(`Event:`, event) + + switch (event.eventType) { + case 'INSERT': + case 'UPDATE': + case 'DELETE': { + // For measures, we simply refetch since we don't have complex caching + deps.refetchBodyMeasures() + break + } + } + }) + } + + return { + initializeMeasureRealtime, + } } diff --git a/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts index a80014fe5..4c7094a7e 100644 --- a/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts +++ b/src/modules/measure/infrastructure/tests/measureInfrastructureExports.test.ts @@ -24,7 +24,15 @@ describe('measure infrastructure module exports', () => { ]) }) - it('keeps realtime exports limited to the public initializer', () => { - expect(Object.keys(realtimeModule)).toEqual(['initializeMeasureRealtime']) + it('keeps realtime exports limited to the public factory', () => { + expect(Object.keys(realtimeModule)).toEqual([ + 'createMeasureRealtimeService', + ]) + }) + + it('creates a realtime service with the public initializer', () => { + expect(Object.keys(realtimeModule.createMeasureRealtimeService())).toEqual([ + 'initializeMeasureRealtime', + ]) }) })