Skip to content

feat(types)!: adopt @seamless-auth/types for the wire contract - #117

Merged
Bccorb merged 1 commit into
mainfrom
feat/adopt-seamless-auth-types
Jul 29, 2026
Merged

feat(types)!: adopt @seamless-auth/types for the wire contract#117
Bccorb merged 1 commit into
mainfrom
feat/adopt-seamless-auth-types

Conversation

@Bccorb

@Bccorb Bccorb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Closes #115. Closes #116.

@seamless-auth/types 0.2.0 publishes the auth API's schemas, so the SDK's hand-written request and response types can stop being a parallel copy. Credential.lastUsedAt is the proof that the copy had already drifted: it was typed Date | null while the API serializes an ISO string, so adopter code calling .getTime() typechecked and then threw. That is #116, and it resolves here.

Decisions

Types-only. Imported with import type, so the package's Zod dependency never reaches the browser bundle. Verified: dist/index.js contains zero references to zod or the package. The one place that wanted a runtime import is the OAuth code list, which is now a compile-time Record<OAuthErrorCode, true> in src/client/errors.ts. It fails to build if the upstream union gains or loses a member, which is what the runtime Set was there to guard against.

The SDK keeps its own export names. Adopters still import Credential and User from @seamless-auth/react; those names are aliases now rather than declarations.

Session material stays unexposed. LoginStartResult and OrganizationSwitchResult Omit the token, subject, and session id the API returns, preserving the existing decision that sessions are carried by cookies and adopters never handle raw tokens. Aliasing them wholesale would have advertised token handling as part of the public API.

Breaking at the type level

No runtime behavior changes, but several types are now accurate, so adopter code that relied on the old ones will not compile:

  • Credential.lastUsedAt is string | null | undefined, and createdAt is now present
  • Credential.deviceType, friendlyName, platform, browser, and deviceInfo are optional
  • User.phone is string | null, User.roles is required, and lastLogin is carried
  • Organization timestamps are string rather than string | Date

Versioned as a minor per the 0.x convention in this repo, with a changeset spelling out each change and the new Date(...) workaround.

Verification

tests/wireTypes.test.ts pins these shapes at compile time, since npm run typecheck covers tests/. The @ts-expect-error guards are the real check: if lastUsedAt reverts to Date, the directive becomes unused and the build fails. I confirmed that mechanism fires rather than passing vacuously.

Left local, deliberately

The PRF helpers and SeamlessAuthResult are SDK concerns rather than wire contracts. Separately, five shapes have upstream schemas but no exported type alias (OAuthProvidersResponse, CredentialUpdateResponse, and the three organization envelope responses), so those stay declared here. Worth a follow-up on the types repo to export them.

Validation

npm run lint, npm run typecheck, npm run format:check, npm test -- --runInBand (268 passing across 31 suites), npm run build, and npm run check-npm-build all pass.

The SDK's request and response types were hand-written and maintained in
parallel with the auth API's schemas, so they could drift from what the API
actually sends. Credential.lastUsedAt was the proof: typed Date | null while the
API serializes an ISO string, so adopter code calling a Date method typechecked
and then threw.

Alias the published contract instead. The dependency is types-only, imported
with import type, so Zod never reaches the browser bundle and the export names
adopters import are unchanged. The OAuth code list stays a compile-time
Record<OAuthErrorCode, true> for the same reason: it fails to build if the
upstream union changes, without importing the runtime list.

LoginStartResult and OrganizationSwitchResult Omit the token, subject, and
session id the API returns, keeping the existing decision that sessions are
carried by cookies and adopters never handle raw tokens.

Several types are now more accurate, which is breaking at the type level:
credential timestamps are strings, several credential fields are optional,
User.phone is nullable, and User.roles is required. tests/wireTypes.test.ts
pins these at compile time so a regression fails the build.

Closes #115
Closes #116
@Bccorb
Bccorb merged commit 41dfea7 into main Jul 29, 2026
3 checks passed
@Bccorb
Bccorb deleted the feat/adopt-seamless-auth-types branch July 29, 2026 02:10
@Bccorb

Bccorb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Filed the follow-up for the missing type aliases: fells-code/seamless-auth-types#10. Once those ship, OAuthProvidersResult, CredentialUpdateResult, and the three organization envelope results can drop their local declarations too.

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.

Credential.lastUsedAt is typed as Date but the API returns an ISO string Adopt @seamless-auth/types for the API response shapes

1 participant