Skip to content

[R-392] fix: Fix duplicated identifiers in functions.ts and index.ts (#392) Co-authored-by: Claude Code - #407

Merged
luisfpg merged 1 commit into
cyclosproject:masterfrom
hfspace:fix/392-duplicate-operation-exports
Sep 23, 2026
Merged

luisfpg merged 1 commit into
cyclosproject:masterfrom
hfspace:fix/392-duplicate-operation-exports

Conversation

@hfspace

@hfspace hfspace commented Jul 29, 2026 •

Copy link
Copy Markdown

Fix duplicated identifiers in functions.ts and index.ts (#392)

  • Generate and export a multi-tagged operation only once, under its first tag
  • Turn free-text tags into valid identifiers before using them as a name suffix
  • Fail generation when method names clash within a tag or across a shared tag, instead of emitting broken code

in detail:

Operation function export names

Rationale for how the generated functions are written and exported by functions.ts and
index.ts (see issues #379, #389 and #392).

The problem

Both index files re-export every generated function and its params type. Two situations used to
produce duplicated export names, which don't compile (TS2308: Module has already exported a member named '...'):

  1. An operation declaring multiple tags (Duplicate function exports #379). The operation belongs to one service per tag,
    but all of those services reference the very same operation - and thus the very same function.
    Collecting the functions per service exported it once per tag:

    "delete": {
      "operationId": "deleteAppointment",
      "tags": ["Appointment", "Called by frontend"]
    }
    // index.ts - broken: the same name is exported twice
    export { deleteAppointment as deleteAppointment } from './fn/appointment/delete-appointment';
    export { deleteAppointment as deleteAppointment } from './fn/appointment/delete-appointment';
  2. Distinct operations sharing a method name (Duplicated X-Operation-Name Attribute Is Ignored And Not Generated #389). operationIds are made unique when the
    spec is read, but x-operation-name is used as-is, so the same name can appear under
    different tags:

    "/api/car/consumption":   { "get": { "x-operation-name": "getConsumption", "tags": ["Car report"] } },
    "/api/plane/consumption": { "get": { "x-operation-name": "getConsumption", "tags": ["Plane"] } }

The fix

  • Functions are de-duplicated by identity before writing and exporting them, so a multi-tagged
    operation yields a single function. It is written under its first tag only
    (fn/appointment/delete-appointment.ts), is exported once without any suffix, and every
    service of the operation imports that same file.
  • Distinct operations that still share a method name are disambiguated by appending their tag to
    the exported name. The tag is free text, so it is converted to a valid identifier first
    (Car report becomes CarReport). Should the method name and the tag collide, the tag
    cannot disambiguate anymore - the operations would silently overwrite each other's file - so
    generation fails with an error asking to make the names unique within the tag.

The generated exports for the two examples above:

// index.ts / functions.ts
export { deleteAppointment as deleteAppointment } from './fn/appointment/delete-appointment';
export { getConsumption as getConsumptionCarReport } from './fn/car-report/get-consumption';
export { getConsumption as getConsumptionPlane } from './fn/plane/get-consumption';

The params types follow the same naming: GetConsumptionCarReport$Params and
GetConsumptionPlane$Params.

The same reasoning applies within a service: operations grouped into one service by a shared
(super-category) tag must have unique method names, as the service class would otherwise declare
the same method twice - the export/file checks don't catch this, since names and paths are
derived from the first tag only. When services are generated, this also fails with an error.

Note that only the export name carries the tag - the function file itself is always
fn/<first tag>/<method name>.ts, and there is no per-tag export for a multi-tagged operation.
Tag-scoped access is what the generated services are for.

@luisfpg
luisfpg merged commit a1d44b5 into cyclosproject:master Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants