Repository navigation
Releases: CUSTOM-DOMAIN-APP/customdomain-sdk
Release list
v0.5.1
Published to npm as customdomain-js@0.5.1 and @customdomain/react@0.5.1, with provenance.
Ported byte identical from the product monorepo's packages/sdk/src at 07a9358. packages/react/src
is unchanged.
Changed
customdomain-js: both widget iframes (the oneopen()andpurchaseDomain()create, and the
oneloadSharedFlow()creates) now carryallow="clipboard-write", so the widget's Copy buttons
(DNS records, the AI prompt) write to the clipboard directly from the cross-origin frame instead of
through a fallback. It is write only: the widget cannot read the clipboard.customdomain-js: the three doc comments that ship in the type definitions (onOpenConfig,
themeandapiBase) now use the CustomDomain™ brand.@customdomain/react: no source change. It depends oncustomdomain-js@0.5.1.
Full changelog: CHANGELOG.md · Diff: v0.5.0...v0.5.1
v0.5.0
Published to npm as customdomain-js@0.5.0 and @customdomain/react@0.5.0, with provenance.
Ported byte identical from the product monorepo's packages/sdk/src and packages/react/src.
Everything here is additive: no type was removed or narrowed.
Added
customdomain-js:OpenConfig.getToken, a function that returns a fresh widget token
(mint it on your server withPOST /v1/tokens). Widget tokens last about 15 minutes, and a user
adding DNS records by hand often takes longer. When the widget reports that its token expired
(the newcustomdomain:token-expiredmessage), the SDK callsgetTokenand hands the result
back, so the widget refreshes silently instead of stopping. WithoutgetToken, or if it throws
or returns nothing, the SDK answers at once and the widget tells the user they can close the
window; the connection still finishes on its own once the records appear.customdomain-js:OpenConfig.wwwRedirectfor root domains.trueconnects the root and
wwwand redirects the root towww;falseconnects the root alone; unset lets the end user
choose.customdomain-js:OpenConfig.onFallback, thecustomdomain:fallbackwindow event and the
FallbackDetailtype ({ reason, provider?, screen?, message? }). They fire when the widget
sends the user to copy records by hand instead of a one-click or sign-in flow, and say why:
no_end_user_rail,conflicts_exceed_tolerance,provider_unsupportedorunknown_provider.customdomain-js:CheckDomainResultnow carries the control plane's rail verdict:
rails(aRailInfoof{ available, reason?, caveat? }foroauth,domainConnect,
apiKeyandmanual),recommendedRail,endUserAutomatic,blockedReason
(unregisteredorplatform_subdomain),dashboardUrlandnameservers, each mapped from its
snake_case wire name incheckDomain().supportsAutomaticonly ever meant that an adapter
exists for the provider;railssays whether each rail can run for this domain right now.
Changed
customdomain-js: errors the widget reports are now also dispatched as the
customdomain:errorwindow event,{ code, message, title?, details? }. The event was
documented, but a widget error used to reach onlyonError.customdomain-js: the init payload sent to the widget now includeswwwRedirectand
canRefreshToken(true whengetTokenis set).@customdomain/react: no source change. It depends oncustomdomain-js@0.5.0, so
getToken,wwwRedirectandonFallbackare accepted as props and passed to the SDK. Its
README now documentsonPurchaseandpurchaseDomain(), which closes the known gap noted
under 0.4.0, along with the new props.- Both packages: descriptions and READMEs use the CustomDomain™ brand, and every README ends
with the same support and license sections.
Compatibility
getTokenandwwwRedirectare read by the hosted widget that ships with the next CustomDomain™
product release. A hosted widget without that support ignores both, so setting them now is safe.
onFallback,customdomain:fallback,customdomain:errorand thecheckDomainrail fields
work with the hosted widget and API live today.
Full changelog: CHANGELOG.md · Diff: v0.4.1...v0.5.0
v0.4.1
Published to npm as customdomain-js@0.4.1 and @customdomain/react@0.4.1, with provenance.
Published from 5665302, tag v0.4.1.
Fixed
customdomain-js:checkRecords()read a response shape the control plane never sent. It
looked for a top-levelobservedarray and anin_syncfield, so every record came back
ok: falseandinSync: falseeven on a fully propagated domain. It now reads each record's
ownobservedvalues and the booleandrift, and still accepts the older shape.
Changed
customdomain-js: thednsRecordsdocumentation now explains root domains: aCNAMEis
not allowed at a zone root, flattening providers accept your hostname there anyway, and
providers without flattening (GoDaddy, Namecheap) needArecords, a subdomain, or the reverse
proxy edge.POST /v1/domains:checkreturnsintegration_warningswith code
supplied_apex_unrealizablewhen this applies.- Both packages: the npm
homepagepoints at the product page, the keywords were extended,
and the READMEs show monthly downloads.
Full changelog: CHANGELOG.md · Diff: v0.4.0...v0.4.1
v0.4.0
Published to npm as customdomain-js@0.4.0 and @customdomain/react@0.4.0, with provenance.
Published from c3a612e, tag v0.4.0. Everything here already existed in the monorepo and had
never reached npm; this release is the hand sync catching up. Source files were ported byte
identical.
Added
customdomain-js:CheckDomainResultcarries the control plane's Public Suffix List parse
of the checked domain:subdomain,registrableDomain,publicSuffix. These are the
authoritative split, and exist so a client never has to guess one; the hand-maintained suffix
lists they replaced produced wrong Domain Connect hosts and illegal root records on multi-label
suffixes (.co.uk,.s3.amazonaws.com).subdomainis""when the checked domain is the
registrable root: a verdict, not a missing value, so test it with=== ""rather than for
falsiness.customdomain-js: the rest of the pre-flight the server already returned but the type
omitted:conflictTolerance,willFallbackToManual,apexSupported,apexMessage. All seven
new fields are mapped from their snake_case wire names incheckDomain().customdomain-js:WhiteLabel.fontUrl, a stylesheet URL loaded into the widget iframe so a
self-hosted@font-faceis available to the widget's Shadow DOM. The widget already consumed it
and the SDK forwardswhiteLabelwhole, so it always worked at runtime; it was simply
inexpressible in TypeScript without a cast.@customdomain/react: the buy-a-domain rail, which was absent from this wrapper
entirely while the SDK it wraps already dispatchedcustomdomain:purchase. The
PurchaseInitiatedpayload type is re-exported from the SDK,onPurchaseis wired to the
customdomain:purchasewindow event (previously dropped silently for every React consumer),
andpurchaseDomain()starts the flow. The last one is not a convenience:open()could not
reach the buy screen at all, because the SDK gates it on apurchaseflag that is not a member
ofOpenConfig.- Parity gates (both packages, type only):
packages/sdk/src/whitelabel-parity.assert.ts
asserts that every white-label key the widget reads is expressible on the SDK'sWhiteLabel;
packages/react/src/sdk-parity.assert.tsasserts that the React wrapper re-exports the SDK's
purchase payload type and can both receive and start the purchase flow. They compile as part of
each package's build, so a surface that drifts out of sync fails CI in the repo where publishing
actually happens. @customdomain/widget(private; reaches users as the hostedwidget.js): a failed or timed
out connection shows the reason the control plane recorded (error_code,error_message)
instead of generic timeout copy, falling back to that copy when there is no message.
Changed
@customdomain/react:open()and the newpurchaseDomain()share onesdkConfig()
helper, and the wrapper-only callbacks (stripped before the config reaches the SDK so window
events do not fire twice) live in a namedWRAPPER_ONLY_KEYSconstant. No behavior change to
open().
Known gap (closed in 0.5.0)
- The React README in this repo did not document
onPurchaseorpurchaseDomain().
Full changelog: CHANGELOG.md · Diff: v0.3.0...v0.4.0
v0.3.0
Published to npm as customdomain-js@0.3.0 and @customdomain/react@0.3.0, with provenance.
Published from fb4b049, tag v0.3.0. A packaging and documentation release: the source
files under packages/sdk/src and packages/react/src are byte identical to 0.2.0. No public API
changed.
Added
release.ymlcreates a GitHub release after a successful publish;v0.3.0was the first one.- READMEs written for an npm audience,
llms.txt, the hero images, and search keywords in both
manifests.
Full changelog: CHANGELOG.md · Diff: v0.2.0...v0.3.0
v0.2.0
Published to npm as customdomain-js@0.2.0 and @customdomain/react@0.2.0, with provenance.
Published from 1055c8f, tag v0.2.0 (the tag was added on 2026-09-26, pointing at the commit
recorded in the npm provenance attestation). This was the first version published to npm, and the
first from this repo; the source changes in it were developed in the monorepo (b3287ef), where
the version bump from 0.1.0 is visible.
Added
customdomain-js: the purchase rail: acustomdomain:purchasewindow event, the
PurchaseInitiatedpayload type (domain,sessionId,clientSecret,url), and the handoff
it represents. The widget cannot mount Stripe.js inside its own iframe, so it creates the
checkout session on the server and hands it to the host, which mounts Embedded Checkout and then
finalizes withPOST /v1/registrar/fulfill.customdomain-js:OpenConfig.endUserRef, the integrator's own identifier for the end user
a connect belongs to, sent to the control plane asend_user_refso the console can show who
connected a domain. Distinct fromuserId, which is only echoed back on step events.customdomain-js:OpenConfig.managed, which opts the Domain Connect flow into the durable
asynchronous rail (ongoing DNS authority) instead of a one-shot connect. The control plane falls
back to the synchronous redirect when the provider or deployment cannot do async, so enabling it
never breaks a connect. Defaults tofalse.customdomain-js:SuccessResult.alreadyConnected(a resume of a live domain rather than a
fresh connect), plusprocessedDomainsandpendingDomainsfor multi-domain flows.- Repository:
.github/workflows/release.yml, which publishes both packages with--provenance.
@customdomain/react'scustomdomain-js: workspace:*dependency is rewritten to the concrete
published version bypnpm publish(plainnpm publishwould not). - Repository:
.github/workflows/ci.yml, a recursive build and typecheck, plus a behavior
gate that drives the real built bundles through headless Chromium (both connect journeys,
locale, white-label theming, multi-domain, resume). - Both packages:
publishConfig: { access: "public", provenance: true }in both manifests, andrepository,
homepageandbugspointing atCUSTOM-DOMAIN-APP/customdomain-sdk.
Changed
customdomain-js:prefilledDomainis forwarded to the widget whole (string or array);
previously only the first entry survived, which silently truncated multi-domain flows.customdomain-js:loadSharedFlow()posts the init payload oncustomdomain:ready, so a
resumed shared flow renders with the tenant's branding.open()andloadSharedFlow()share one
buildInitPayload()so the two entry points cannot drift.
Removed
Breaking for TypeScript consumers who referenced them (both were type level only):
customdomain-js:DNSRecord.fallbackValue.customdomain-js:OpenConfig.applicationUrl.
Full changelog: CHANGELOG.md