fix(types): type listOrganizations result as a keyed map, not an array#667
Merged
Merged
Conversation
GET /v0/organizations returns `organizations` as a map keyed by org id (OpenAPI `additionalProperties`), but OrganizationsResult declared it as OrganizationItem[]. Consumers had to work around the mismatch (e.g. socket-vscode used Object.values()). Fix the strict type to Record<string, OrganizationItem> at its generator source (generate-strict-types-emit.mts) and regenerate types-strict.mts. The org plan field is already typed as an open-ended string in both the OpenAPI schema and the generated types; tests now lock that in alongside the map shape so a future narrowing regresses loudly. This is a breaking type change, but the runtime was always a map, so any consumer relying on the array type was already broken at runtime.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes two result-type correctness bugs on the
listOrganizations()result type, surfaced during the socket-vscode SDK migration (task #65).organizationsis a map, not an array.GET /v0/organizationsreturnsorganizationsas an object keyed by org id (OpenAPIadditionalProperties).OrganizationsResultdeclared it asOrganizationItem[]. socket-vscode had to reach forObject.values()to stay correct. The strict type is nowRecord<string, OrganizationItem>.planis an open-ended string. The task flagged the org plan field as a narrow 3-value union that needed widening. In the currentmainit is already typed asstring(both the OpenAPI schema and the generated types agree), so there was nothing to widen. New tests lock that in so a future narrowing regresses loudly.Breaking change
This is a breaking type change: consumers that treated
organizationsas an array will now get type errors. The runtime was always a map, so anything relying on the array type was already broken at runtime — this change makes the compiler catch it.Fixed at the generator source
src/types-strict.mtsis auto-generated from the OpenAPI spec. The fix was made in the generator's emit template (scripts/repo/generate-strict-types-emit.mts) and the generated artifact regenerated to match — not hand-patched downstream.Root cause, before/after, and how types are produced
How the types are produced
src/types-strict.mtscarries a banner:AUTO-GENERATED from OpenAPI definitions using AST parsing - DO NOT EDIT MANUALLY. It is produced byscripts/repo/generate-strict-types.mts, which reads the localopenapi.json, runsopenapi-typescriptover it, and emits strict wrapper types via the template functions inscripts/repo/generate-strict-types-emit.mts. TheOrganizationsResultwrapper is emitted bygenerateWrapperTypes()in that emit file, so that is the correct place to fix it.OpenAPI source of truth
The
/organizationsGET 200 schema:additionalPropertiesmeans a map keyed by org id — aRecord, not an array.planis{ "type": "string" }, i.e. already open-ended.Before / after (strict type)
export type OrganizationsResult = { cause?: undefined data: { - organizations: OrganizationItem[] + organizations: Record<string, OrganizationItem> } error?: undefined status: number success: true }The generator has a separate, pre-existing path bug (the fleet migration moved these scripts into
scripts/repo/, butgetRootPath()still only walks up one level, so it looks foropenapi.jsonunderscripts/instead of the repo root). Running the generator in-place therefore fails today. I validated the template change by running the generator against the OpenAPI at the path it expects, confirmed it emitsRecord<string, OrganizationItem>, then applied that exact change to the committed generated artifact preserving its existing format. Fixing the generator's path resolution is out of scope for this change.JSDoc example
The
listOrganizations()doc example iterated withresult.data.organizations.forEach(...), which is invalid for a map. Updated toObject.values(result.data.organizations).forEach(...).Tests
OrganizationsResultruntime test intest/repo/unit/types-strict.test.mtsto mock the real map shape (previously it mocked an array — which never occurs at runtime), asserting map-keyed access viaObject.values()/Object.keys()and key lookup.organizationsto a keyedRecordandplanto an openstring(not a narrow union). The block is validated by the type checker.Verification
All run on Node 24.18.0:
pnpm run type(tsgo --noEmit)pnpm run lint(oxlint + oxfmt)vitest run .../types-strict.test.mtspnpm run buildRecordshape)