From ce6773848542c4209d686f613de5a264b01d0d4b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 01:38:03 +0000 Subject: [PATCH 1/4] refactor(B5): shared geometry module factory for mun/TI (F3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract buildGeometryModuleFromTopology to collapse the duplicate topojson.feature → features → optional Map pipeline in municipality and territory geometry submodules. F2 (ensureCachedDownload) was already delivered in scripts/lib/cli.mjs via P3-H. Closes #28 --- src/lib/bahiaGeometryModuleFactory.ts | 85 ++++++++++++++++++++++++++ src/lib/bahiaMunicipalityGeometries.ts | 20 +++--- src/lib/bahiaTerritoryGeometries.ts | 13 ++-- 3 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 src/lib/bahiaGeometryModuleFactory.ts diff --git a/src/lib/bahiaGeometryModuleFactory.ts b/src/lib/bahiaGeometryModuleFactory.ts new file mode 100644 index 00000000..1a36558a --- /dev/null +++ b/src/lib/bahiaGeometryModuleFactory.ts @@ -0,0 +1,85 @@ +import { feature } from 'topojson-client' + +import type { Feature, MultiPolygon, Polygon } from 'geojson' +import type { GeometryCollection, Topology } from 'topojson-specification' + +type PolygonalFeature> = Feature< + Polygon | MultiPolygon, + Properties +> + +type GeometryModuleBase< + Properties extends Record, + TObjectName extends string, +> = { + topology: Topology<{ [key in TObjectName]: GeometryCollection }> + features: readonly PolygonalFeature[] +} + +type IndexedGeometryModule< + Properties extends Record, + TObjectName extends string, + KeyProperty extends keyof Properties & string, +> = GeometryModuleBase & { + getFeatureByKey: (key: Properties[KeyProperty]) => PolygonalFeature | undefined +} + +type BuildGeometryModuleOptions< + Properties extends Record, + TObjectName extends string, + KeyProperty extends keyof Properties & string, +> = { + topology: Topology<{ [key in TObjectName]: GeometryCollection }> + objectName: TObjectName + keyProperty?: KeyProperty +} + +export function buildGeometryModuleFromTopology< + Properties extends Record, + TObjectName extends string, + KeyProperty extends keyof Properties & string, +>( + options: BuildGeometryModuleOptions & { + keyProperty: KeyProperty + }, +): IndexedGeometryModule + +export function buildGeometryModuleFromTopology< + Properties extends Record, + TObjectName extends string, +>( + options: BuildGeometryModuleOptions, +): GeometryModuleBase + +export function buildGeometryModuleFromTopology< + Properties extends Record, + TObjectName extends string, + KeyProperty extends keyof Properties & string, +>({ + topology, + objectName, + keyProperty, +}: BuildGeometryModuleOptions): + | GeometryModuleBase + | IndexedGeometryModule { + const features = feature(topology, topology.objects[objectName]) + .features as PolygonalFeature[] + + const base: GeometryModuleBase = { + topology, + features, + } + + if (!keyProperty) { + return base + } + + const featuresByKey = new Map( + features.map((entry) => [entry.properties[keyProperty], entry]), + ) + + return { + ...base, + getFeatureByKey: (key) => featuresByKey.get(key), + } +} diff --git a/src/lib/bahiaMunicipalityGeometries.ts b/src/lib/bahiaMunicipalityGeometries.ts index da8f41e6..8c956f68 100644 --- a/src/lib/bahiaMunicipalityGeometries.ts +++ b/src/lib/bahiaMunicipalityGeometries.ts @@ -1,23 +1,19 @@ -import { feature } from 'topojson-client' - import type { BahiaMunicipalityFeature, MunicipalityGeometryModule, MunicipalityTopology, } from '@/lib/bahiaGeometriesTypes' +import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory' import municipalityTopologyJson from '@/lib/geometries/bahia-municipalities.topo.json' -const topology = municipalityTopologyJson as unknown as MunicipalityTopology - -const municipalityFeatures = feature(topology, topology.objects.municipalities) - .features as BahiaMunicipalityFeature[] - -const municipalityByCodarea = new Map( - municipalityFeatures.map((entry) => [entry.properties.codarea, entry]), -) +const { topology, features, getFeatureByKey } = buildGeometryModuleFromTopology({ + topology: municipalityTopologyJson as unknown as MunicipalityTopology, + objectName: 'municipalities', + keyProperty: 'codarea', +}) export const municipalityGeometryModule: MunicipalityGeometryModule = { topology, - features: municipalityFeatures, - getMunicipalityFeature: (codarea: string) => municipalityByCodarea.get(codarea), + features: features as readonly BahiaMunicipalityFeature[], + getMunicipalityFeature: getFeatureByKey, } diff --git a/src/lib/bahiaTerritoryGeometries.ts b/src/lib/bahiaTerritoryGeometries.ts index 548f5f73..e5c8c51b 100644 --- a/src/lib/bahiaTerritoryGeometries.ts +++ b/src/lib/bahiaTerritoryGeometries.ts @@ -1,18 +1,17 @@ -import { feature } from 'topojson-client' - import type { BahiaTerritoryFeature, TerritoryGeometryModule, TerritoryTopology, } from '@/lib/bahiaGeometriesTypes' +import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory' import territoryTopologyJson from '@/lib/geometries/bahia-identity-territories.topo.json' -const topology = territoryTopologyJson as unknown as TerritoryTopology - -const territoryFeatures = feature(topology, topology.objects.territories) - .features as BahiaTerritoryFeature[] +const { topology, features } = buildGeometryModuleFromTopology({ + topology: territoryTopologyJson as unknown as TerritoryTopology, + objectName: 'territories', +}) export const territoryGeometryModule: TerritoryGeometryModule = { topology, - features: territoryFeatures, + features: features as readonly BahiaTerritoryFeature[], } From f4b41c2b9d0f4e2b977d119960cd6348bce752f1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 01:38:07 +0000 Subject: [PATCH 2/4] =?UTF-8?q?docs(B5):=20mark=20F2=E2=80=93F3=20delivere?= =?UTF-8?q?d=20in=20escala-dry-pos-b2=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/escala-dry-pos-b2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plans/escala-dry-pos-b2.md b/docs/plans/escala-dry-pos-b2.md index 0fc73029..2b8ec5bb 100644 --- a/docs/plans/escala-dry-pos-b2.md +++ b/docs/plans/escala-dry-pos-b2.md @@ -1,7 +1,7 @@ # Escala e DRY pós-B2 (geometrias + scripts CLI) -Status: Fase 1 entregue com B3 (2026-07-19); Fases 2–3 pendentes -Atualizado em: 2026-07-27 (`capture-review-debts` pós-B8+: `scripts/lib/topology.mjs` resolve por precedente onde o helper da F2 vive — `scripts/lib/`, não `src/lib/`; 2026-07-26 pós-B8 F2: 5º call site; 2026-07-20 pós-A8: `build-bahia-demographics.mjs` reforça o gatilho da F2 como 4º call site) +Status: Fases 1–3 entregues (F1 com B3 em 2026-07-19; F2 em P3-H/`scripts/lib/cli.mjs`; F3 em #28) +Atualizado em: 2026-07-31 (F3: `bahiaGeometryModuleFactory.ts` + wrappers mun/TI) Item do roadmap: [docs/roadmap.md](../roadmap.md) (Trilha B, item B5) Responsável: — From 8188c9a386d3c3fd084f1ef515836ae4b87ccbdd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 01:41:00 +0000 Subject: [PATCH 3/4] refactor(B5): reuse BahiaFeature type and drop wrapper casts --- src/lib/bahiaGeometriesTypes.ts | 5 ++++- src/lib/bahiaGeometryModuleFactory.ts | 13 ++++--------- src/lib/bahiaMunicipalityGeometries.ts | 8 ++++++-- src/lib/bahiaTerritoryGeometries.ts | 7 +++++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/lib/bahiaGeometriesTypes.ts b/src/lib/bahiaGeometriesTypes.ts index 6e601405..379d32ec 100644 --- a/src/lib/bahiaGeometriesTypes.ts +++ b/src/lib/bahiaGeometriesTypes.ts @@ -2,7 +2,10 @@ import type { Feature, MultiPolygon, Polygon } from 'geojson' import type { GeometryCollection, Topology } from 'topojson-specification' /** Every committed mesh is polygonal; only the properties differ. */ -type BahiaFeature = Feature +export type BahiaFeature> = Feature< + Polygon | MultiPolygon, + Properties +> type BahiaMunicipalityProperties = { codarea: string diff --git a/src/lib/bahiaGeometryModuleFactory.ts b/src/lib/bahiaGeometryModuleFactory.ts index 1a36558a..8b4f7627 100644 --- a/src/lib/bahiaGeometryModuleFactory.ts +++ b/src/lib/bahiaGeometryModuleFactory.ts @@ -1,19 +1,14 @@ import { feature } from 'topojson-client' -import type { Feature, MultiPolygon, Polygon } from 'geojson' +import type { BahiaFeature } from '@/lib/bahiaGeometriesTypes' import type { GeometryCollection, Topology } from 'topojson-specification' -type PolygonalFeature> = Feature< - Polygon | MultiPolygon, - Properties -> - type GeometryModuleBase< Properties extends Record, TObjectName extends string, > = { topology: Topology<{ [key in TObjectName]: GeometryCollection }> - features: readonly PolygonalFeature[] + features: readonly BahiaFeature[] } type IndexedGeometryModule< @@ -21,7 +16,7 @@ type IndexedGeometryModule< TObjectName extends string, KeyProperty extends keyof Properties & string, > = GeometryModuleBase & { - getFeatureByKey: (key: Properties[KeyProperty]) => PolygonalFeature | undefined + getFeatureByKey: (key: Properties[KeyProperty]) => BahiaFeature | undefined } type BuildGeometryModuleOptions< @@ -63,7 +58,7 @@ export function buildGeometryModuleFromTopology< | GeometryModuleBase | IndexedGeometryModule { const features = feature(topology, topology.objects[objectName]) - .features as PolygonalFeature[] + .features as BahiaFeature[] const base: GeometryModuleBase = { topology, diff --git a/src/lib/bahiaMunicipalityGeometries.ts b/src/lib/bahiaMunicipalityGeometries.ts index 8c956f68..ef4819da 100644 --- a/src/lib/bahiaMunicipalityGeometries.ts +++ b/src/lib/bahiaMunicipalityGeometries.ts @@ -6,7 +6,11 @@ import type { import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory' import municipalityTopologyJson from '@/lib/geometries/bahia-municipalities.topo.json' -const { topology, features, getFeatureByKey } = buildGeometryModuleFromTopology({ +const { topology, features, getFeatureByKey } = buildGeometryModuleFromTopology< + BahiaMunicipalityFeature['properties'], + 'municipalities', + 'codarea' +>({ topology: municipalityTopologyJson as unknown as MunicipalityTopology, objectName: 'municipalities', keyProperty: 'codarea', @@ -14,6 +18,6 @@ const { topology, features, getFeatureByKey } = buildGeometryModuleFromTopology( export const municipalityGeometryModule: MunicipalityGeometryModule = { topology, - features: features as readonly BahiaMunicipalityFeature[], + features, getMunicipalityFeature: getFeatureByKey, } diff --git a/src/lib/bahiaTerritoryGeometries.ts b/src/lib/bahiaTerritoryGeometries.ts index e5c8c51b..68dc3ee5 100644 --- a/src/lib/bahiaTerritoryGeometries.ts +++ b/src/lib/bahiaTerritoryGeometries.ts @@ -6,12 +6,15 @@ import type { import { buildGeometryModuleFromTopology } from '@/lib/bahiaGeometryModuleFactory' import territoryTopologyJson from '@/lib/geometries/bahia-identity-territories.topo.json' -const { topology, features } = buildGeometryModuleFromTopology({ +const { topology, features } = buildGeometryModuleFromTopology< + BahiaTerritoryFeature['properties'], + 'territories' +>({ topology: territoryTopologyJson as unknown as TerritoryTopology, objectName: 'territories', }) export const territoryGeometryModule: TerritoryGeometryModule = { topology, - features: features as readonly BahiaTerritoryFeature[], + features, } From 479af4f2ef246f6d69dd5e583e9edb274b3e47a9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 01:42:01 +0000 Subject: [PATCH 4/4] style: prettier bahiaGeometryModuleFactory --- src/lib/bahiaGeometryModuleFactory.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/lib/bahiaGeometryModuleFactory.ts b/src/lib/bahiaGeometryModuleFactory.ts index 8b4f7627..08217a7e 100644 --- a/src/lib/bahiaGeometryModuleFactory.ts +++ b/src/lib/bahiaGeometryModuleFactory.ts @@ -3,10 +3,7 @@ import { feature } from 'topojson-client' import type { BahiaFeature } from '@/lib/bahiaGeometriesTypes' import type { GeometryCollection, Topology } from 'topojson-specification' -type GeometryModuleBase< - Properties extends Record, - TObjectName extends string, -> = { +type GeometryModuleBase, TObjectName extends string> = { topology: Topology<{ [key in TObjectName]: GeometryCollection }> features: readonly BahiaFeature[] } @@ -69,9 +66,7 @@ export function buildGeometryModuleFromTopology< return base } - const featuresByKey = new Map( - features.map((entry) => [entry.properties[keyProperty], entry]), - ) + const featuresByKey = new Map(features.map((entry) => [entry.properties[keyProperty], entry])) return { ...base,