mirror of
https://github.com/hanzoai/cal.git
synced 2026-10-16 20:43:01 +00:00
A host page gets a booker in two lines: a [data-hanzo-cal] element carrying a
pk_ key and one ~2 kB classic script. Add [data-hanzo-cal-popup] and the same
key opens it over the page instead.
The key is a public selector, not a credential: pk_ + base64url(host/eventSlug),
the same pair the /{host}/{eventSlug} URL already names, so it resolves with no
lookup and no registry and the public config still comes from the event-type
endpoint <Booker> was always calling. Base reserves the hyphenated pk- for
read-only API credentials; this is a different kind of value and is never sent
as one.
Each booker is a frame at the new /embed route, which is what keeps a host's CSS
out — reusing the same <Booker> and the same /v1/calendar calls as the full-page
flow, which is untouched. What a frame cannot do is size itself, so /embed posts
its height and the loader applies it; the same channel carries close, because
Escape pressed inside a frame never reaches the page around it.
Every surface that shows a booker now reports reaching a visitor and creating a
booking, distinguished by one `surface` property: page, hero, inline, popup.
@hanzo/event is the fleet's client tracker, and its taxonomy is closed on
purpose — a dimension is a property, never a new event name — so the funnel
rides the conversion names qualified by kind:'booking', which also keeps free
intro calls out of the Sale goal (order_completed{kind:'plan'}). A key that
names no calendar is not a funnel step but a host misconfiguration, so it goes
to the error plane where someone is watching.
Only the two public identifiers ride along. Never the booking uid: that is the
capability that reveals the meeting location and cancels the booking. DNT and
GPC are honoured because the SDK reads neither, and a booker sitting in someone
else's page should be the polite one. Interior steps stay unreported —
<Booker>'s only lifecycle prop is onBooked, and slot_selected and
details_entered live in its private state machine; reaching them means one prop
upstream in hanzoai/gui, not a fork of the UI here.
hanzoai/spa refuses framing by default, so the image now sets ALLOW_FRAMING.
tsconfig repeats the Vite alias for hanzogui, without which the typecheck this
adds cannot resolve @hanzo/booker's peer.
20 lines
565 B
TypeScript
20 lines
565 B
TypeScript
import { defineConfig } from "vite";
|
|
|
|
// The host-page loader ships as one classic script at a stable URL, /embed.js,
|
|
// so it builds on its own: no React, no hashed chunk, nothing a third party has
|
|
// to know the name of. It lands in the SPA's dist, so it runs after the SPA
|
|
// build and must not empty the directory.
|
|
export default defineConfig({
|
|
build: {
|
|
outDir: "dist",
|
|
emptyOutDir: false,
|
|
target: "es2018",
|
|
lib: {
|
|
entry: "src/embed.ts",
|
|
formats: ["iife"],
|
|
name: "HanzoCal",
|
|
fileName: () => "embed.js",
|
|
},
|
|
},
|
|
});
|