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'];