From d26f6172d256738daf1ff8f27c9c6762c602b5e5 Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Tue, 28 Jul 2026 22:46:14 -0400 Subject: [PATCH] feat(types): take the response envelopes from types 0.4.0 Five response bodies were declared by hand because the package shipped schemas for them without exported type aliases. Types 0.4.0 exports an alias for every schema, and guards it with a test upstream, so OAuthProvidersResult, CredentialUpdateResult, and the three organization envelope results become aliases like the rest of the wire contract. The shapes are identical, so nothing changes for adopters, and the dependency stays types-only with no runtime import. Refs fells-code/seamless-auth-types#10 --- .changeset/tidy-moons-tap.md | 5 +++++ AGENTS.md | 8 ++++---- package-lock.json | 8 ++++---- package.json | 2 +- src/client/createSeamlessAuthClient.ts | 28 +++++++++----------------- tests/wireTypes.test.ts | 27 +++++++++++++++++++++++++ 6 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 .changeset/tidy-moons-tap.md diff --git a/.changeset/tidy-moons-tap.md b/.changeset/tidy-moons-tap.md new file mode 100644 index 0000000..9ff7f98 --- /dev/null +++ b/.changeset/tidy-moons-tap.md @@ -0,0 +1,5 @@ +--- +'@seamless-auth/react': patch +--- + +Take the last five response envelopes from `@seamless-auth/types` instead of declaring them here. `OAuthProvidersResult`, `CredentialUpdateResult`, `OrganizationResult`, `OrganizationMembersResult`, and `OrganizationMembershipResult` were hand-written because the package had no exported alias for their schemas; types 0.4.0 exports one for every schema, so they are aliases now like the rest. The shapes are identical, so this is a no-op for adopters, and the dependency stays types-only. diff --git a/AGENTS.md b/AGENTS.md index 0f92428..abed167 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,10 +117,10 @@ Rules for this dependency: - session material stays unexposed. `LoginStartResult` and `OrganizationSwitchResult` `Omit` the token, subject, and session id the API returns, because sessions are carried by cookies here. -- a few shapes have upstream schemas but no exported type alias - (`OAuthProvidersResponse`, `CredentialUpdateResponse`, and the organization - envelope responses). Those stay declared locally until the package exports - them. +- do not redeclare a shape the package already exports. Every exported schema has + a `z.infer` alias as of types 0.4.0, and a test upstream keeps it that way, so + a local interface mirroring a wire shape is a bug. If an alias is genuinely + missing, file it on `seamless-auth-types` rather than working around it. The PRF helper types and `SeamlessAuthResult` stay local: they are SDK concerns, not wire contracts. diff --git a/package-lock.json b/package-lock.json index 920a58e..1738812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.6.0", "license": "AGPL-3.0-only", "dependencies": { - "@seamless-auth/types": "^0.2.0", + "@seamless-auth/types": "^0.4.0", "@simplewebauthn/browser": "^13.1.0", "eslint-plugin-license-header": "^0.9.0", "libphonenumber-js": "^1.12.7", @@ -3066,9 +3066,9 @@ "license": "MIT" }, "node_modules/@seamless-auth/types": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@seamless-auth/types/-/types-0.2.0.tgz", - "integrity": "sha512-4QHdkZLKFNvU8g//nDU983rmIXNsnwIPj/eXsCWmj6+FAgCcEaHhmE/U7u3ogGk7+ZuwRWagQ8aBjHqoWggovg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@seamless-auth/types/-/types-0.4.0.tgz", + "integrity": "sha512-1WhLjgyCN9UdX6TEzKbbASROeLMxZmo2LiAYOlYE2sGIPojnbAE/yFbp5Ydt13GibiZ6MwYlHMLcsBVQMeelWA==", "license": "AGPL-3.0-only", "dependencies": { "zod": "^4.3.6" diff --git a/package.json b/package.json index 6f2dbaf..24fa8fe 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "typescript-eslint": "^8.46.1" }, "dependencies": { - "@seamless-auth/types": "^0.2.0", + "@seamless-auth/types": "^0.4.0", "@simplewebauthn/browser": "^13.1.0", "eslint-plugin-license-header": "^0.9.0", "libphonenumber-js": "^1.12.7", diff --git a/src/client/createSeamlessAuthClient.ts b/src/client/createSeamlessAuthClient.ts index b932f23..8cfed63 100644 --- a/src/client/createSeamlessAuthClient.ts +++ b/src/client/createSeamlessAuthClient.ts @@ -17,12 +17,17 @@ import { import type { AddOrganizationMemberRequest, CreateOrganizationRequest, + CredentialUpdateResponse, LoginMethod as LoginMethodShape, LoginSuccessResponse, LogoutScope as LogoutScopeShape, MeResponse, MessageResponse, + OAuthProvidersResponse, + OrganizationEnvelopeResponse, OrganizationListResponse, + OrganizationMembersResponse, + OrganizationMembershipEnvelopeResponse, OrganizationSwitchResponse, PublicOAuthProvider, RegistrationRequest, @@ -36,7 +41,6 @@ import type { } from '@seamless-auth/types'; import { createFetchWithAuth } from '../fetchWithAuth'; -import { Credential, Organization, OrganizationMembership } from '../types'; import { getWebAuthnErrorDetail } from './errors'; import { NETWORK_ERROR_STATUS, @@ -97,19 +101,12 @@ export type OrganizationMemberUpdateInput = UpdateOrganizationMemberRequest; export type OrganizationsResult = OrganizationListResponse; -export interface OrganizationResult { - organization: Organization; -} +export type OrganizationResult = OrganizationEnvelopeResponse; -export interface OrganizationMembersResult { - members: OrganizationMembership[]; - total: number; -} +export type OrganizationMembersResult = OrganizationMembersResponse; /** Response body for a single membership mutation. */ -export interface OrganizationMembershipResult { - membership: OrganizationMembership; -} +export type OrganizationMembershipResult = OrganizationMembershipEnvelopeResponse; /** * Response body when the active organization changes, minus its session @@ -122,9 +119,7 @@ export type OrganizationSwitchResult = Omit< export type OAuthProvider = PublicOAuthProvider; -export interface OAuthProvidersResult { - providers: OAuthProvider[]; -} +export type OAuthProvidersResult = OAuthProvidersResponse; export interface StartOAuthLoginInput { providerId: string; @@ -155,10 +150,7 @@ export interface PasskeyRegistrationData { } /** Response body returned when credential metadata is updated. */ -export interface CredentialUpdateResult { - message: string; - credential: Credential; -} +export type CredentialUpdateResult = CredentialUpdateResponse; export interface RegisterPasskeyOptions { metadata: PasskeyMetadata; diff --git a/tests/wireTypes.test.ts b/tests/wireTypes.test.ts index f82b510..3f03be9 100644 --- a/tests/wireTypes.test.ts +++ b/tests/wireTypes.test.ts @@ -6,8 +6,13 @@ import type { Credential, Organization, User } from '@/types'; import type { + CredentialUpdateResult, LoginStartResult, MessageResult, + OAuthProvidersResult, + OrganizationMembersResult, + OrganizationMembershipResult, + OrganizationResult, OrganizationSwitchResult, StepUpStatus, } from '@/client/createSeamlessAuthClient'; @@ -58,6 +63,28 @@ describe('wire types match what the API sends', () => { expect(organizationSwitch.sessionId).toBeUndefined(); }); + // These five were declared by hand until the package exported aliases for them + // (seamless-auth-types#10). Pinning the envelopes keeps that from creeping back. + it('takes the response envelopes from the package', () => { + const providers = {} as OAuthProvidersResult; + const organization = {} as OrganizationResult; + const members = {} as OrganizationMembersResult; + const membership = {} as OrganizationMembershipResult; + const credentialUpdate = {} as CredentialUpdateResult; + + const providerId: string | undefined = providers.providers?.[0]?.id; + const total: number = members.total; + const lastUsedAt: string | null | undefined = credentialUpdate.credential?.lastUsedAt; + + expect([ + providerId, + organization.organization, + membership.membership, + total, + lastUsedAt, + ]).toHaveLength(5); + }); + it('keeps the acknowledgement and step-up shapes intact', () => { const message: string = '' as MessageResult['message']; const method: 'webauthn' | 'totp' | null = null as StepUpStatus['method'];