Same core, Angular bindings
The library is a plain async function with no framework dependency. A binding owns three things: the responder list the component renders (resolved from a policy), the one call, and its state. This page's injectable service is all of it; the React example is the same as a hook.
The whole binding
@Injectable({ providedIn: "root" })
export class CheckinService {
readonly responders = signal<Responder[]>([]);
readonly status = signal<Status>("idle");
readonly response = signal<SmartCheckinResponse | undefined>(undefined);
// who may answer in this browser — availability and the default come back in the list
async configure(policy) {
this.policy = policy;
this.responders.set(await resolveResponders(policy));
}
async request(input, responder = this.responders().find((r) => r.isDefault)) {
this.status.set("waiting");
try {
this.response.set(await requestCheckin(input, {
getCredential: credentialGetterFor(responder, { origin: this.policy.origin }),
}));
this.status.set("done");
} catch (e) {
this.status.set(e instanceof CheckinFlowError && e.outcome.status === "declined"
? "declined" : "error");
}
}
}
// …and the component renders the list: one button per responder, the default leading
@for (r of checkin.responders(); track r.id) {
<button [class]="r.isDefault ? 'smart-btn primary' : 'smart-btn'"
[disabled]="!r.available" [title]="r.reason" (click)="start(r)">
{{ r.name }}
</button>
}
Compiled in the browser (JIT) so this page needs no Angular build step — which is why the bundle is large. A real app compiles ahead of time and ships only its own code plus this library.