@getshuttlebee/handoff-types
v1.0.1
Published
Shared TypeScript types + encoder/decoder helpers for the /how-pricing-works → Family Connect intake handoff. Source of truth for the cross-app contract.
Maintainers
Readme
@getshuttlebee/handoff-types
Shared TypeScript types + encoder/decoder helpers for the
getshuttlebee.com/how-pricing-works → shuttlebee-family-connect intake
handoff. The single source of truth for the cross-app contract.
Why this package exists
Two repos need to agree on a payload shape that travels through a URL param.
A .d.ts copy-paste would drift. A runtime schema (Zod) adds a dependency
for one file. A published package gives us:
- Compile-time guarantees that encoder and decoder agree
- Versioning baked in — the package's semver matches
HANDOFF_VERSION - Grace period — both repos pin
^MAJOR.0.0, so contract evolutions are just annpm updateaway
Install
npm install @getshuttlebee/handoff-typesUsage — encoder side (getshuttlebee.com)
import {
buildHandoffUrl,
HANDOFF_VERSION,
type IntakeHandoff,
} from "@getshuttlebee/handoff-types";
const intake: IntakeHandoff = {
v: HANDOFF_VERSION,
source: "how-pricing-works",
ts: Date.now(),
school: { label: "Friends' Central", address: null, custom: false },
zip: "19010",
drive: { miles: 4.5, minutes: 14 },
stops: 2,
carSeats: 1,
carlineWait: 8,
slot: "amRush",
passengers: 3,
tier: "sedan",
estimate: {
perPickup: 18.17,
perRoundTripDay: 36.34,
routeCostRoundTrip: 109.02,
rangeLow: 13.4,
rangeHigh: 25.2,
},
routing: "high_confidence",
};
window.location.href = buildHandoffUrl(
"https://shuttlebee-family-connect.com/start",
intake,
);Usage — decoder side (Family Connect)
import {
parseIntake,
type IntakeHandoff,
} from "@getshuttlebee/handoff-types";
// On the landing page that receives the handoff (e.g. /start)
const url = new URL(window.location.href);
const intake = parseIntake(url.searchParams.get("h"));
if (!intake) {
// Malformed, expired, or wrong version. Log to Sentry; show the
// "let's start fresh" banner and land on a clean intake form.
return;
}
// Stash for cross-tab persistence (24h TTL recommended).
localStorage.setItem(
"pendingIntake",
JSON.stringify({ intake, expiresAt: Date.now() + 24 * 60 * 60 * 1000 }),
);Versioning policy
HANDOFF_VERSION is exported as a constant and tracks the package's
major version. The decoder rejects payloads whose v doesn't match
HANDOFF_VERSION exactly.
| Version bump | What it means | When to bump |
|---|---|---|
| MAJOR | Payload shape changed incompatibly (renamed/removed field, changed semantics) | Bump HANDOFF_VERSION, ship both repos in lock-step, leave previous major published for ~90 days |
| MINOR | Additive optional field | No HANDOFF_VERSION change; consumers can upgrade at their own pace |
| PATCH | Encoder/decoder fix, doc tweak, type-level refactor with no behavior change | Bug fix only |
Reference contract
See docs/family-connect-handoff.md
in the source repo for the strategic context (rollout phases, routing-flag
behavior, error-UX expectations) that this package can't capture in types
alone.
License
UNLICENSED — internal Shuttlebee package.
