widget: ship generated type declarations + typed element/events

Integrators had to read the demo to learn the API: the package built via
vite with no .d.ts and no types field, so @bitlogin/widget and
@bitlogin/widget/session resolved to `any`.

Emit declarations from source (tsconfig.types.json -> dist-types,
build:types wired into build) and point the package types/exports
conditions at them, for both the root and the /session entry. Generated,
so they can't drift from the implementation.

Add the type surface integrators actually want:
- HTMLElementTagNameMap["bitlogin-auth"], so querySelector/createElement
  return the typed element.
- Typed addEventListener/removeEventListener overloads (declaration-merged
  interface) for the bitlogin-* events.
- Exported event-detail types and BitLoginEventMap (src/events.ts), matching
  element.ts's dispatchEvent shapes.

Verified with a consumer conformance typecheck (typed detail with no cast,
typed element methods, readCachedSession return type). README gains a
"TypeScript" section.

Addresses the ".d.ts / documented API surface" item of the Tessera
integration feedback (docs/TODO.md).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BXQCvJPA6sVSQ5j8uBd9bB
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-08-11 19:37:32 -04:00
1 parent 1b39e0f60c
commit 5ae41ed9db
7 files changed
+154 -6

No files matched your search

+37
View File
@@ -20,6 +20,7 @@ import { Nip46Signer } from "./signers/nip46.js";
import type { Signer, SignerCapabilities, SignerMethod } from "./signers/types.js";
import { renderQrSvg } from "./qr.js";
import { setActiveSession, clearActiveSession } from "./globalSession.js";
import type { BitLoginEventMap } from "./events.js";
import {
buildPasskeyCreateOptions,
buildPasskeyGetOptions,
@@ -2217,3 +2218,39 @@ function pickRandomIndices(max: number, count: number): number[] {
function escapeHtml(input: string): string {
return input.replace(/[&<>"']/gu, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]!);
}
// Declaration merge: give BitLoginAuthElement typed addEventListener/
// removeEventListener overloads for its own bitlogin-* events, so a host gets
// `e.detail` typed without a cast. The string overload preserves the base
// signature for every other event. Merging an interface of the same name adds
// these to the class type; the runtime implementation stays HTMLElement's.
export interface BitLoginAuthElement {
addEventListener<K extends keyof BitLoginEventMap>(
type: K,
listener: (this: BitLoginAuthElement, ev: BitLoginEventMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof BitLoginEventMap>(
type: K,
listener: (this: BitLoginAuthElement, ev: BitLoginEventMap[K]) => void,
options?: boolean | EventListenerOptions
): void;
removeEventListener(
type: string,
listener: EventListenerOrEventListenerObject,
options?: boolean | EventListenerOptions
): void;
}
declare global {
interface HTMLElementTagNameMap {
// So document.querySelector("bitlogin-auth") and
// document.createElement("bitlogin-auth") are typed as the element.
"bitlogin-auth": BitLoginAuthElement;
}
}
+44
View File
@@ -0,0 +1,44 @@
/**
* Public event contract for `<bitlogin-auth>`. These are the CustomEvents the
* element dispatches on itself; the detail shapes here match element.ts's
* `dispatchEvent` calls exactly. Integrators previously had to read the demo to
* learn them — the typed `addEventListener` overloads on BitLoginAuthElement
* (see element.ts) now surface them in editors.
*/
import type { SignerMethod, SignerCapabilities } from "./signers/types.js";
/** `bitlogin-login` — a session was established OR silently restored on page
* load (the two are intentionally indistinguishable to a listener). */
export interface BitLoginLoginEventDetail {
/** The signed-in account's public key (hex), when known. */
publicKey?: string;
/** How the session was established. */
method: SignerMethod;
/** What the active signer supports (NIP-07 extensions vary). */
capabilities: SignerCapabilities;
}
/** `bitlogin-connection-granted` — the user shared an NWC wallet connection with
* an origin (Connection Vault, reveal mode). */
export interface BitLoginConnectionGrantedEventDetail {
origin: string;
}
/** `bitlogin-signer-claimed` — the element tried to install itself as
* `window.nostr`; `windowNostrClaimed` says whether it won the slot. */
export interface BitLoginSignerClaimedEventDetail {
windowNostrClaimed: boolean;
error?: string;
}
/** Maps each `bitlogin-*` event name to its concrete event type. Events without
* a detail payload are plain `CustomEvent<undefined>`. */
export interface BitLoginEventMap {
"bitlogin-login": CustomEvent<BitLoginLoginEventDetail>;
"bitlogin-logout": CustomEvent<undefined>;
"bitlogin-connection-granted": CustomEvent<BitLoginConnectionGrantedEventDetail>;
"bitlogin-signer-claimed": CustomEvent<BitLoginSignerClaimedEventDetail>;
"bitlogin-signer-released": CustomEvent<undefined>;
"bitlogin-request-pending": CustomEvent<undefined>;
"bitlogin-offer-pending": CustomEvent<undefined>;
}
+7
View File
@@ -96,6 +96,13 @@ window.bitlogin = {
export { readCachedSession } from "./session.js";
export type { PublicSessionInfo } from "./publicSession.js";
export { BitLoginAuthElement } from "./element.js";
export type {
BitLoginEventMap,
BitLoginLoginEventDetail,
BitLoginConnectionGrantedEventDetail,
BitLoginSignerClaimedEventDetail
} from "./events.js";
export type { SignerMethod, SignerCapabilities } from "./signers/types.js";
export { WorkerClient } from "./worker/workerClient.js";
export { createNip07Provider } from "./provider.js";
export type { BitLoginConfig } from "./config.js";