Skip to content

WIP refactorings#686

Draft
sea-grass wants to merge 36 commits into
masterfrom
cg-opapi-updates
Draft

WIP refactorings#686
sea-grass wants to merge 36 commits into
masterfrom
cg-opapi-updates

Conversation

@sea-grass
Copy link
Copy Markdown

WIP

@sea-grass sea-grass requested a review from michaelmass as a code owner May 22, 2026 18:59
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 22, 2026

Greptile Summary

This WIP PR refactors the opapi package by migrating from Prettier to Biome for formatting, swapping several lodash utilities for Ramda equivalents, upgrading openapi3-ts (v2→v4.5) and @anatine/zod-openapi (v1→v2.2), and adding a new memfs-backed test for exportHandler. It also makes minor type-safety improvements in promex.

  • Tooling migration: Replaces Prettier with Biome (biome.json), bumps pnpm to 8.15.9, adds ramda/memfs as dependencies, and removes the custom uniqueBy/removeFromArray utilities in favour of Ramda's uniqBy and prop.
  • opapi/src refactors: exploreJsonSchema changes from a curried to a direct two-argument signature, openapi.ts migrates to openapi3-ts/oas31 subpath imports, and state.ts adds StateSection as a named type.
  • Two blockers remain: opapi/foo.js is an accidentally committed WIP file with TypeScript syntax in a .js extension and broken relative imports; and opapi/src/opapi.ts has leading-space indentation on two import lines that will fail Biome's format check.

Confidence Score: 3/5

Not safe to merge as-is: foo.js is a broken TypeScript-in-JS scratch file with missing imports that should not ship, and two import lines in opapi.ts have spurious leading spaces that will fail the new Biome format gate.

The formatter migration and Ramda refactors are mechanically sound, but foo.js is an invalid artifact (TypeScript syntax, broken relative imports ./errors and ./typings) that should be deleted before merge. The two leading-space import lines in opapi.ts will immediately break the newly added biome format CI check. The large block of dead code (createOpapiFromState, NewOpapi) is clearly WIP and adds noise to the public module surface.

opapi/foo.js (must be removed) and opapi/src/opapi.ts (formatting errors on import lines, plus unexported dead code).

Important Files Changed

Filename Overview
opapi/foo.js Accidental WIP artifact: TypeScript syntax in a .js file with broken relative imports — must be removed
opapi/src/opapi.ts Adds dead code (createOpapiFromState, empty NewOpapi class) and has leading-space formatting errors on two import lines that will fail biome format check
opapi/src/state.ts Migrates error deduplication and parameter mapping from custom utilities to Ramda equivalents; behavior is preserved
opapi/src/jsonschema.ts Refactors exploreJsonSchema from curried to direct two-argument form; adds proper isReferenceObject guards; swaps lodash cloneDeep for Ramda clone
opapi/src/openapi.ts Upgrades to openapi3-ts/oas31 subpath, adds createOpenapiFromSpec helper, refactors parameter loop from forEach to for...of
opapi/src/handler-generator/index.ts Introduces Ramda R.map for schema mapping; replaces lodash mapKeys with plain Object.fromEntries
promex/src/prometheus.ts Replaces spread of defaultNormalizers with explicit property assignments; calls normalizePath() on every request (minor inefficiency)
promex/src/normalize-path.ts Removes unused Response import and tightens the return type annotation of normalizePath
opapi/src/handler-generator/export-handler.test.ts New test that uses memfs to verify exportHandler writes the expected file content
opapi/biome.json Adds Biome config to replace Prettier; mirrors prior Prettier settings (single quotes, no semis, 120 cols)
opapi/mocks/fs/promises.ts New Vitest manual mock for fs/promises using memfs to support filesystem tests without real I/O

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[opapi package] --> B[Biome replaces Prettier]
    A --> C[Ramda replaces lodash utils]
    A --> D[openapi3-ts v2 → v4.5]
    A --> E[anatine/zod-openapi v1 → v2.2]

    C --> C1[state.ts: uniqBy, prop, map]
    C --> C2[jsonschema.ts: R.map, R.clone]
    C --> C3[handler-generator: R.map]

    D --> D1[openapi.ts: openapi3-ts/oas31 subpath]
    D --> D2[state.ts: SchemaObject from oas31]
    D --> D3[isReferenceObject guard added]

    A --> F[New features]
    F --> F1[export-handler.test.ts with memfs]
    F --> F2[__mocks__/fs/promises.ts]
    F --> F3[createOpenapiFromSpec helper]

    A --> G[WIP artifacts ⚠️]
    G --> G1["foo.js — TS syntax in .js, broken imports"]
    G --> G2["NewOpapi — empty exported class"]
    G --> G3["createOpapiFromState — unexported dead code"]

    style G1 fill:#ff6b6b,color:#fff
    style G2 fill:#ffa94d,color:#fff
    style G3 fill:#ffa94d,color:#fff
Loading

Reviews (1): Last reviewed commit: "tests pass" | Re-trigger Greptile

Comment thread opapi/foo.js
Comment on lines +1 to +10
import qs from 'qs'
import { isAxiosError } from 'axios'
import { isApiError } from './errors'
import * as types from './typings'

type RoutePart =
| {
type: 'argument'
name: string
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Accidental WIP file with invalid syntax

foo.js uses TypeScript syntax (type annotations, generics, class access modifiers like private, public, readonly) inside a plain .js file — this is not valid JavaScript and will fail at runtime if executed without a transpiler. Additionally, it imports from ./errors and ./typings, relative paths that don't exist in the opapi package root. The content appears to be a scratch copy of the generated export-handler.ts handler code. This file should be removed before merging.

Comment thread opapi/src/opapi.ts
Comment on lines +11 to +12
import { exportStateAsTypescript, ExportStateAsTypescriptOptions } from './generators/ts-state'
import { generateHandler } from './handler-generator'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Leading whitespace on import lines breaks formatter check

Both import lines now have a leading space. With the switch from Prettier to Biome for formatting, running pnpm check:format (biome format .) will report these lines as misformatted and fail CI. The lines should be aligned flush with the rest of the imports.

Comment thread opapi/src/opapi.ts
Comment on lines 115 to 231
| {
generator: 'opapi'
}
) &
ExportStateOptions

export type SchemaOf<O extends OpenApi<any, any, any>> =
O extends OpenApi<infer Skema, infer _Param, infer _Sexion> ? Skema : never

export type ParameterOf<O extends OpenApi<any, any, any>> =
O extends OpenApi<infer _Skema, infer Param, infer _Sexion> ? Param : never

export type SectionOf<O extends OpenApi<any, any, any>> =
O extends OpenApi<infer _Skema, infer _Param, infer Sexion> ? Sexion : never
) & ExportStateOptions

function createExportClient(state: State<string, string, string>) {
function _exportClient(
dir: string,
openapiGeneratorEndpoint: string,
postProcessors?: OpenApiPostProcessors,
): Promise<void>
function _exportClient(dir: string, props: GenerateClientOptions): Promise<void>
function _exportClient(dir = '.', props: GenerateClientOptions | string, postProcessors?: OpenApiPostProcessors) {
let options: GenerateClientOptions
if (typeof props === 'string') {
options = { generator: 'openapi-generator', endpoint: props, postProcessors }
} else {
options = props
}

if (options.generator === 'openapi-generator') {
return generateClientWithOpenapiGenerator(state, dir, options.endpoint, options.postProcessors)
}
if (options.generator === 'opapi') {
return generateClientWithOpapi(state, dir)
}
throw new Error('Unknown generator')
}
return _exportClient
}

const createOpapiFromState = <
SchemaName extends string,
DefaultParameterName extends string,
SectionName extends string,
>(
state: State<SchemaName, DefaultParameterName, SectionName>,
) => {
type InputItem = {
type: Parameter<'zod-schema'>['type']
description: Parameter<'zod-schema'>['description']
}
type Inputs = {
parameters: {
query: Record<string, InputItem>
}
response: Operation<DefaultParameterName, SectionName, string, 'zod-schema'>['response']
}
const complexifyParameters = (parameters: Inputs['parameters']): ParametersMap<string, 'zod-schema'> => {
const map: ParametersMap<string, 'zod-schema'> = {}
for (const [key, value] of Object.entries(parameters.query)) {
map[key] = {
type: value.type,
description: value.description,
in: 'query',
} as any
}
return map
}
const simp = {
ops: {
get: (path: string, name: string, inputs: Inputs) => {
addOperation(state, {
name,
description: name,
method: 'get',
path,
parameters: complexifyParameters(inputs.parameters),
response: inputs.response,
})
},
},
dt: {
boolean: (description: string): InputItem => ({
type: 'boolean',
description,
}),
integer: (description: string): InputItem => ({
type: 'integer',
description,
}),
number: (description: string): InputItem => ({
type: 'number',
description,
}),
},
} as const

return {
getModelRef: (name: SchemaName): OpenApiZodAny => getRef(state, ComponentType.SCHEMAS, name),
addOperation: <Path extends string>(
operationProps: Operation<DefaultParameterName, SectionName, Path, 'zod-schema'>,
) => addOperation(state, operationProps),
exportClient: createExportClient(state),
exportTypesBySection: (dir = '.') => generateTypesBySection(state, dir),
exportServer: (dir = '.', useExpressTypes: boolean) => generateServer(state, dir, useExpressTypes),
exportOpenapi: (dir = '.') => generateOpenapi(state, dir),
exportState: (dir = '.', opts?: ExportStateAsTypescriptOptions) => exportStateAsTypescript(state, dir, opts),
exportErrors: (dir = '.') => generateErrorsFile(state.errors ?? [], dir),
exportHandler: (dir = '.') => generateHandler(state, dir),
simp,
}
}

export type SchemaOf<O extends OpenApi<any, any, any>> = O extends OpenApi<infer Skema, infer _Param, infer _Sexion>
? Skema
: never

export type ParameterOf<O extends OpenApi<any, any, any>> = O extends OpenApi<infer _Skema, infer Param, infer _Sexion>
? Param
: never

export type SectionOf<O extends OpenApi<any, any, any>> = O extends OpenApi<infer _Skema, infer _Param, infer Sexion>
? Sexion
: never

export { exportJsonSchemas, exportZodSchemas } from './handler-generator/export-schemas'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 createOpapiFromState and NewOpapi are unreachable dead code

createOpapiFromState is defined but never exported or called anywhere in the codebase. Similarly, NewOpapi is exported but contains only an empty state: {} with no methods. Both appear to be early-stage WIP sketches. Dead code in a non-WIP commit adds maintenance burden and confuses callers about the intended API surface. These should either be completed, hidden behind a feature flag, or removed before merging.

Comment thread promex/src/prometheus.ts
Comment on lines +36 to 37
normalizePath: (path, { req }) => normalizePath()(path, { req }),
metricBuckets: {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 normalizePath() factory is invoked on every request

The previous code called normalizePath() once at middleware setup time and passed the resulting function as the normalizePath option. The new code wraps it in a lambda that calls normalizePath() on every single request, allocating a new closure each time. Because normalizePath() itself is stateless (it only closes over the module-level appRoutes map), the behavior is equivalent, but the allocation is unnecessary. Consider calling normalizePath() once:

normalizePath: normalizePath(),

@sea-grass sea-grass marked this pull request as draft May 22, 2026 19:28
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.

1 participant