From 7bdac8ebf43a9aea01a4acf2b0c6ad4908295abe Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Wed, 29 Jul 2026 23:29:28 -0400 Subject: [PATCH] refactor(core): take the remaining duplicated types from @seamless-auth/types SeamlessUser is now an alias of the types package's MeUser, and the eight messaging wire shapes are re-exported rather than declared again. Each was field for field identical to a definition that already existed upstream, which is the drift that package exists to prevent. The transport interfaces and adopter-facing configuration stay here: they carry provider implementations and adapter options, not wire shapes. No public API change and no runtime cost. Every name is still exported under the same name, the re-exports are type-only, and the built output still imports only @seamless-auth/types/role/matching. Verified by compiling the whole public type surface against real values. Closes #133 --- .changeset/dedupe-remaining-types.md | 13 ++++ packages/core/src/authMessaging.ts | 95 +++++++++------------------- packages/core/src/getSeamlessUser.ts | 18 +++--- 3 files changed, 54 insertions(+), 72 deletions(-) create mode 100644 .changeset/dedupe-remaining-types.md diff --git a/.changeset/dedupe-remaining-types.md b/.changeset/dedupe-remaining-types.md new file mode 100644 index 0000000..e895f26 --- /dev/null +++ b/.changeset/dedupe-remaining-types.md @@ -0,0 +1,13 @@ +--- +"@seamless-auth/core": patch +--- + +Take the remaining duplicated types from `@seamless-auth/types`. + +`SeamlessUser` is now an alias of the types package's `MeUser`, and the eight messaging wire shapes (`MessagingChannel`, `DeliveryResult`, `EmailMessage`, `SmsMessage`, `SendOtpEmailInput`, `SendOtpSmsInput`, `SendMagicLinkEmailInput`, `AuthDeliveryInstruction`) are re-exported rather than declared again. Each was field for field identical to a definition that already existed upstream, which is the drift `@seamless-auth/types` exists to prevent. + +What stays declared here is what genuinely belongs to this package: the transport interfaces, which carry provider implementations, and the adopter-facing configuration (`EmailTransport`, `SmsTransport`, `AuthMessageOverrideContext`, `AuthMessageOverrides`, `AuthMessagingHandlers`, `SeamlessAuthMessagingOptions`). + +No public API change and no runtime cost. Every name is still exported from `@seamless-auth/core` and both adapters, the re-exports are type-only so they are erased at compile time, and the built output still imports only `@seamless-auth/types/role/matching` at runtime, so neither `zod` nor the schema barrel enters the module graph. + +Closes #133. diff --git a/packages/core/src/authMessaging.ts b/packages/core/src/authMessaging.ts index 970cdab..c10542d 100644 --- a/packages/core/src/authMessaging.ts +++ b/packages/core/src/authMessaging.ts @@ -1,26 +1,34 @@ -export type MessagingChannel = "email" | "sms"; +/** + * The messaging contract. + * + * The wire shapes belong to `@seamless-auth/types`, so they are re-exported rather + * than declared again: the auth API sends the delivery instruction and this + * package consumes it, and two definitions of that could drift. The re-export is + * type-only, so it is erased at compile time and neither zod nor the schema + * barrel enters the runtime module graph. + * + * What stays here is what is genuinely this package's: the transport interfaces, + * which carry provider implementations, and the adopter-facing configuration. + */ +export type { + AuthDeliveryInstruction, + DeliveryResult, + EmailMessage, + MessagingChannel, + SendMagicLinkEmailInput, + SendOtpEmailInput, + SendOtpSmsInput, + SmsMessage, +} from "@seamless-auth/types"; -export interface DeliveryResult { - accepted: boolean; - provider: string; - channel: MessagingChannel; - messageId?: string; - raw?: unknown; -} - -export interface EmailMessage { - to: string; - from?: string; - subject: string; - text: string; - html?: string; -} - -export interface SmsMessage { - to: string; - from?: string; - body: string; -} +import type { + DeliveryResult, + EmailMessage, + SendMagicLinkEmailInput, + SendOtpEmailInput, + SendOtpSmsInput, + SmsMessage, +} from "@seamless-auth/types"; export interface EmailTransport { readonly name: string; @@ -32,27 +40,6 @@ export interface SmsTransport { send(message: SmsMessage): Promise; } -export interface SendOtpEmailInput { - to: string; - token: string; - from?: string; - subject?: string; -} - -export interface SendOtpSmsInput { - to: string; - token: string | number; - from?: string; -} - -export interface SendMagicLinkEmailInput { - to: string; - magicLinkUrl: string; - token?: string; - from?: string; - subject?: string; -} - export interface AuthMessageOverrideContext { appName?: string; } @@ -78,9 +65,7 @@ export interface AuthMessageOverrides { export interface AuthMessagingHandlers { sendOtpEmail(input: SendOtpEmailInput): Promise; sendOtpSms(input: SendOtpSmsInput): Promise; - sendMagicLinkEmail( - input: SendMagicLinkEmailInput, - ): Promise; + sendMagicLinkEmail(input: SendMagicLinkEmailInput): Promise; } export interface SeamlessAuthMessagingOptions { @@ -94,21 +79,3 @@ export interface SeamlessAuthMessagingOptions { smsFrom?: string; }; } - -export type AuthDeliveryInstruction = - | { - kind: "otp_email"; - to: string; - token: string; - } - | { - kind: "otp_sms"; - to: string; - token: string | number; - } - | { - kind: "magic_link_email"; - to: string; - token?: string; - magicLinkUrl: string; - }; diff --git a/packages/core/src/getSeamlessUser.ts b/packages/core/src/getSeamlessUser.ts index 9cd2277..2a24b56 100644 --- a/packages/core/src/getSeamlessUser.ts +++ b/packages/core/src/getSeamlessUser.ts @@ -1,3 +1,5 @@ +import type { MeUser } from "@seamless-auth/types"; + import { verifyCookieJwt } from "./verifyCookieJwt.js"; import { authFetch } from "./authFetch.js"; import { assertSecretStrength } from "./validateSecrets.js"; @@ -8,14 +10,14 @@ import { assertSecretStrength } from "./validateSecrets.js"; * `lastLogin` is an ISO 8601 timestamp, null until the user's first login. * `activeOrganizationId` is null when the access token carries no org context. */ -export interface SeamlessUser { - id: string; - email: string; - phone: string | null; - roles: string[]; - lastLogin?: string | null; - activeOrganizationId?: string | null; -} +/** + * The caller's own user record, hydrated from the auth API. Distinct from + * `SeamlessAuthUser`, which is only what the access cookie carries. + * + * Aliased to the types package's `MeUser` rather than declared again. The name + * stays `SeamlessUser` here because that is what adapters and adopters import. + */ +export type SeamlessUser = MeUser; export interface GetSeamlessUserOptions { authServerUrl: string;