Files
zeekayandGitHub 3df66c3990 i18n: /m* through /r* and the product kits draw their words through t (#70)
Pages from /machines to /risk, the model pages, and the kits they are made of
(page-kit, ProductLanding, LayerPage, Stack, the models kit, band's Head) mark
authored copy where it is written and draw it in the reader's language. A server
page marks a literal phrase('…') or wraps it in <T>; a client page calls t. The
kits translate a string prop through useSay (components/marketing/say), so a
page passes phrase('…') and markup passes as written. An arrow that points
turns over right to left (data-i18n-mirror).

Rebased on main: the fold-based heroes (Fold, PageHero, MachinesHero, the
product and layer pages) keep main's structure and copy, with their label,
title and lede marked phrase. /platform is main's CloudLanding; the deleted
/platform/layers and the old referral components stay deleted.

Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
2026-10-30 00:18:44 -07:00

103 lines
5.3 KiB
TypeScript

'use client'
import type { ReactNode } from 'react'
import { ArrowRight, BarChart3, Clock, Layers, Radio, Server, Shield, Zap } from 'lucide-react'
import { OSSRevenueBanner } from '@/components/oss/OSSRevenueBanner'
import { ProductLanding } from '@/components/product/ProductLanding'
import { ProductFooter } from '@/components/products/ProductFooter'
import { phrase } from '@hanzo/i18n'
/**
* PubSub, through the ONE product kit.
*
* One of NINE pages — console, insights, kms, kv, mq, pubsub, sql, storage,
* stream — that shared a class set 100%: the same hero, stat row, six-card
* grid, terminal and call to action, written out nine times in ~310 utility
* classes each. They now pass one typed props object to `ProductLanding` and
* carry zero layout classes. See LLM.md, "Tailwind is NOT gone", for the rule.
*
* EVERY WORD IS THE ONE THAT WAS HERE, the four stat tiles included: 28 of the
* group's 36 carry a port, a version, a licence or a coding the prose does not
* (measured), so unlike /ledger's four they are not restatements and they stay.
*
* One fix the move makes by construction: ProductFooter sat INSIDE the final
* card's `max-w-4xl` wrapper on all nine, so the OSS attribution and deploy
* block rendered narrower here than on every other product page.
*/
export default function PubSubPage({ source }: { source: ReactNode }) {
return (
<>
<ProductLanding
badge={"NATS JetStream"}
badgeIcon={Radio}
title={"Hanzo PubSub"}
tagline={phrase("The bus everything else rides")}
lede={phrase("PubSub moves messages between services on port 4222. Publish to a subject and everyone listening on it — or on a pattern that matches it — has the message. Add a stream and those same messages are written down too, so a consumer that was offline can start from the beginning, from a sequence number, or from a timestamp. Streams, key-value buckets, an object store and request-reply are one server, not four.")}
ctas={[
{ label: "GitHub", href: "https://github.com/hanzoai/pubsub" },
]}
what={{
title: phrase("More than pub/sub"),
sub: phrase("Durable streams, key-value, an object store and request-reply — one server, one connection, one set of credentials."),
pillars: [
{
icon: Layers,
title: phrase("Streams write it down"),
body: phrase("A stream captures the subjects you name and keeps them by age, by count or by bytes. A consumer starts at the beginning, at the newest message, at a sequence number or at a wall-clock time, and reads at the original pace or as fast as it can take them."),
},
{
icon: Zap,
title: phrase("Acked, so nothing goes quiet"),
body: phrase("A message is redelivered until a consumer acknowledges it. Publishers that stamp a message id get duplicates collapsed inside a window — two minutes unless you say otherwise. Ack every message, ack a sequence to cover everything below it, or ask for no acks at all when fire-and-forget is what you meant."),
},
{
icon: Server,
title: phrase("Key-value buckets"),
body: phrase("A bucket is a stream that keeps the last value per key, so you get watches, per-key history and expiry from the same server and the same connection. It is where Hanzo Stream keeps its consumer offsets."),
},
{
icon: Clock,
title: phrase("Request-Reply"),
body: phrase("Send to a subject and wait for one answer. Put several instances of a service in a queue group on that subject and each request goes to exactly one of them — load balancing with nothing in front of them doing it."),
},
{
icon: BarChart3,
title: phrase("Object Store"),
body: phrase("Payloads too big to be a message are chunked into a stream and put back together on the way out, with a watch for when one changes. Same connection, same credentials, no second system to stand up."),
},
{
icon: Shield,
title: phrase("Accounts, not passwords"),
body: phrase("An account walls one tenant's subjects off from another's. Credentials are nkeys or signed JWTs rather than a shared secret, permissions are granted per subject, and TLS covers the wire."),
},
],
}}
stats={{
items: [
{ value: "4222", label: phrase("Client port") },
{ value: phrase("Replay"), label: phrase("From any point") },
{ value: phrase("Durable"), label: phrase("Written to disk") },
{ value: phrase("Replicas"), label: phrase("Set per stream") },
],
}}
code={{
head: { title: phrase("Publish and Subscribe") },
name: "events.go",
source,
}}
finalCta={{
title: phrase("Put it between your services"),
sub: phrase("Free tier includes 1M messages/month."),
buttons: [
{ label: phrase("View on GitHub"), href: "https://github.com/hanzoai/pubsub" },
],
}}
>
<OSSRevenueBanner />
</ProductLanding>
<ProductFooter slug={"pubsub"} name={"PubSub"} />
</>
)
}