mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 18:35:07 +03:00
chore/function-recent-errors
13
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
476d4a5851 |
refactor(ui): drop redundant Button variant="default" props (#50161)
## What kind of change does this PR introduce? Mechanical cleanup on top of the Button default-variant change (#50160). ## What is the current behavior? Many callsites still pass `variant="default"` even though that is now the component default. ## What is the new behavior? Removes redundant static `variant="default"` from legacy `Button` and `ButtonTooltip` callsites. Keeps explicit defaults where they document the API: - `button-default.tsx` and `button-sizes.tsx` demos - `DocsButton`, which pins neutral styling at the wrapper boundary ## To test Studio: - [Auth → Rate Limits](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/auth/rate-limits): dirty the form so Cancel appears; Cancel stays neutral, Save stays green - [Project Settings → API Keys](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/settings/api-keys): `DocsButton` in the header actions stays neutral Design system: - [Design system → Button](https://design-system-git-dnywh-dc924ac1-supabase.vercel.app/design-system/docs/components/button): `button-default` / `button-sizes` still show explicit default styling; Primary (green) is restricted to the Primary section (and `asChild`) WWW: - [www → Brand assets](https://zone-www-dot-com-git-dnywh-dc924ac1-supabase.vercel.app/brand-assets): Download logo kit / Download button kit stay neutral |
||
|
|
932180541e |
fix(ui-patterns): give the shared InfoTooltip trigger an accessible name (#49345)
Closes FE-4093 ## Problem The shared `InfoTooltip` trigger's only child is an SVG and it has no accessible name, so a screen reader announces an unnamed button and the information the tooltip carries is unreachable. `button-name`, critical. `/pricing` renders 34 of them. ## Solution * Add an optional `label` prop rendered as `sr-only` text, with a generic fallback so the 22 call sites that pass nothing still get a name. The prop is optional because 26 files import this component across www, Studio, design-system and ui-patterns. * Drop the redundant `role="button"` from a native button. * Label the four www call sites. A shared fallback alone would leave `/pricing` announcing 34 identical names, which passes axe and stays unusable. The labels come from the feature title and plan already in scope, so no pricing data changes. Docs is unaffected. It has its own `InfoTooltip` at `apps/docs/features/ui/InfoTooltip.tsx` and never imports the shared one. ## Manual testing 1. Open [/pricing](https://zone-www-dot-com-git-ui-patterns-infotooltip-ac-07e2ab-supabase.vercel.app/pricing) using a Screenreader. 2. Tab through the comparison table. Each info tooltip announces its own feature, for example "About Database size". 3. Tab to a plan-specific tooltip. It announces the feature and the plan, for example "About Automatic backups on the pro plan". 4. Confirm the icons render unchanged and the tooltips still open on hover and on focus. 5. Run axe on the page. `button-name` reports zero elements. 6. Open the [design system InfoTooltip page](https://design-system-git-ui-patterns-infotooltip-acces-66ec02-supabase.vercel.app/design-system/docs/fragments/info-tooltip) and confirm the demo still renders. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Accessibility Improvements** * Added descriptive labels to pricing information tooltips. * Improved screen reader context for compute estimates, features, and plan-specific pricing. * Added a default “More information” label for unlabeled tooltips. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
10c425ad0b | feat(www): markdown copy/ask affordances (#48475) | ||
|
|
9113c2ba04 |
feat: markdown alternate tags + llms.txt cleanup (#48287)
The June/July marketing redesign (#47271, #47228) rebuilt the homepage and product pages off the Pages Router, silently dropping their `<link rel="alternate" type="text/markdown">` head tags, and llms-full.txt has been accidentally embedding every blog/customer/event page via an `MD_CONTENT` spread. I restored the tags behind a shared helper, added a CI drift test so a future redesign can't drop them silently again, and trimmed both llms files to the agreed docs-index shape. **Changed:** - **Markdown siblings advertised again**: homepage, the 5 product pages, pricing, and blog emit absolute `.md` alternate URLs via a new `mdAlternates(slug)` helper (the one documented consumer of the tag parses it from `<head>` and fetches the `.md` sibling, so tags must point at the sibling, never the page itself). - **Drift test**: a vitest file walks `content/md/**` and asserts every markdown-served slug's page wires the helper (or is covered by the Pages Router `_app.tsx` mechanism, whose alternate-link wiring the test also asserts directly so removing it fails CI too). Source-level assertions by design: page modules can't be imported under www's vitest config. Fails correctly when wiring is removed (verified by hiding a page and by altering the `_app.tsx` tag). - **Vector orphan fixed**: `content/md/vector.md` moved to `modules/vector` matching the live route (the page previously had no negotiation or tag, and `/modules/vector.md` 404'd); `/vector.md` now 308s to `/modules/vector.md` and the legacy `/llms/vector.txt` redirect no longer chains. - **llms.txt + llms-full.txt**: the `## Product Overview` sections are gone from both, each keeps a `## Pricing` section. This deletes the hand-maintained links array (a drift trap) and fixes the accidental ~470-page embed, shrinking llms-full.txt from ~9.8MB to ~4.9MB and dropping the 4.1MB generated content module from that route's serverless bundle. **Note:** this PR is scoped to apps/www only. The docs side (troubleshooting pages and the rest of the docs surface) is handled separately through a consolidated manifest-gated mechanism; an earlier troubleshooting-tag commit was reverted out of this branch to keep the scopes clean. <details> <summary>Why alternate tags matter (background)</summary> Agents ingest markdown far more efficiently than our rendered HTML: a fraction of the tokens and no extraction step. Since #47770 removed UA-based serving (UA sniffing broke a major AI app's fetcher and poisoned CDN caches), markdown is served only on explicit request: a `.md` suffix URL, an `Accept: text/markdown` header, or llms.txt. That's the right serving model, but it makes the markdown twin invisible to any agent that doesn't already know our URL convention, and the major AI fetchers send browser/wildcard Accept headers, so bare URLs hand them HTML. The `<link rel="alternate" type="text/markdown">` head tag is the standards-based advertisement of the sibling. It has a documented consumer today: an agent CLI that parses the tag from `<head>` and then fetches the `.md` sibling, which is also why the tag must point at a real sibling URL and never at the page itself. Peer docs sites ship this tag as table stakes. These www pages used to carry it until the June/July marketing redesign silently dropped it; the drift test in this PR turns that regression class into a CI failure. </details> ## To test Tested locally (www + docs dev servers): - [x] `/llms.txt` renders `## Documentation` + single-link `## Pricing`, no Product Overview - [x] `/llms-full.txt` renders `# Supabase` → `## Pricing` → `## Documentation`, no Product Overview, ~4.9MB - [x] Full www suite: 6 files / 71 tests green; drift test fails correctly when a page is removed or the `_app.tsx` wiring is altered - [x] `generateMdContent.mjs` emits `modules/vector`, bare `vector` slug gone On the Vercel preview (browser-verified with Playwright): - [x] Alternate tag present on `/`, `/auth`, `/database`, `/storage`, `/edge-functions`, `/realtime`, `/pricing`, and a blog post: exactly one tag each, href = preview origin + `.md` sibling - [x] `/vector.md` → 308 → `/modules/vector.md`, renders as markdown (`# Supabase Vector`) - [x] `/llms.txt` shows single-link `## Pricing`, no Product Overview - [x] Coverage sweep: all 482 `MD_PAGES` slugs + changelog index/entry curled on the preview; 471 pages carry exactly one tag, all `.md` siblings 200 as `text/markdown`. The 11 misses are legacy blog slugs whose HTML 308-redirects away (stale `MD_PAGES` entries predating this PR, no head to tag; follow-up tracked in Linear) Post-merge prod: - [ ] Full llms.txt link sweep (every linked URL 200s; previews can't cover the docs-hosted links) ## Linear - fixes GROWTH-1013 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Markdown alternate links across key product, pricing, blog, and troubleshooting pages. * Added Supabase Vector documentation covering features, use cases, workflows, and technical details. * Updated AI-focused documentation indexes with dedicated pricing content. * Added redirects for updated Vector documentation URLs. * **Tests** * Added coverage to verify Markdown documentation links stay aligned with available pages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
96d43099bb |
chore: refactor Button API so that it can be used a standard button (#46880)
## Problem Our `<Button>` component breaks the default `button` contract by redefining the `type` prop to set its variant (`primary`, `default`, etc) instead of the button type (`submit`, `button`, etc). This is confusing and forces to write more code when using it with shadcn components that expect/inject the standard button props. ## Solution - rename the `type` prop to `variant` - rename the `htmlType` prop to `type` - propagate the changes where necessary - format code ## How to test As this is just prop renaming, if it builds it's ok --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com> |
||
|
|
c26b64a033 |
feat(www): emit BreadcrumbList JSON-LD on marketing surfaces (#45478)
## Summary
- Adds `breadcrumbListSchema(items)` helper to `apps/www/lib/json-ld.ts`
and a hand-curated `apps/www/lib/breadcrumbs.ts` route map.
- Wires inline `<script type="application/ld+json">` BreadcrumbList
blocks into 18 marketing surfaces: blog (index + slug), customers (index
+ slug), events (index + slug), 5 product pages (database, auth,
storage, edge-functions, realtime), 3 modules (vector, cron, queues),
pricing, careers, company, features.
- Pages router callers wrap the script in `<Head>`; app router callers
place it directly in JSX. Dynamic surfaces append a leaf at render time
using the page's title (`frontmatter.title` for blog, `meta_title ??
title` for customers, `event.meta_title ?? event.title` for events).
- Modules sit at `Home > {Name}` since no `/modules` index page exists;
products sit at `Home > {Product}` (no shared products parent). Absolute
`https://supabase.com` URLs match the existing `CANONICAL_ORIGIN`
convention so anchors stay stable across Vercel previews.
Linear:
[GROWTH-822](https://linear.app/supabase/issue/GROWTH-822/add-breadcrumblist-json-ld-to-www-marketing-surfaces)
(sub-issue under
[GROWTH-724](https://linear.app/supabase/issue/GROWTH-724)).
> **Note on branch name:** the branch is
`pamela/growth-820-www-breadcrumb-jsonld`; the actual Linear issue is
GROWTH-822. The branch was named before the sub-issue was created.
Ignore the `820` in the branch.
Explicitly deferred (separate PRs / low SEO ROI): `/launch-week/*`,
`/solutions/*`, `/partners/*`, `/alternatives/*`, `/changelog`,
`/legal/dpa`, `/aws-reinvent-2025`, `/wrapped`, `/contribute/*`,
`/brand-assets`, `/ga`, `/ga-week`, `/state-of-startups*`, and the
homepage (Organization + WebSite already cover homepage entity signals;
single-item BreadcrumbList is ignored by Google).
## Test plan
- [x] On the Vercel preview, `curl -s https://<preview>/database | grep
'"BreadcrumbList"'` returns the script block with `Home > Database`.
- [x] `curl -s https://<preview>/blog/<recent-slug> | grep
'"BreadcrumbList"'` returns `Home > Blog > {post title}`.
- [x] `curl -s https://<preview>/customers/<slug> | grep
'"BreadcrumbList"'` returns `Home > Customer Stories > {customer
title}`.
- [x] `curl -s https://<preview>/events/<slug> | grep
'"BreadcrumbList"'` returns `Home > Events > {event title}`.
- [x] `curl -s https://<preview>/modules/vector | grep
'"BreadcrumbList"'` returns `Home > Vector`.
|
||
|
|
34241f1f66 |
revert(pricing): remove A/B experiment, keep control (#44293) (#45361)
Reverts the pricing page flexibility/visibility A/B experiment from #44293. We decided to keep the control (original pricing page). **Changes:** - `PricingPlansSection.tsx`: removed all experiment logic (PostHog flag evaluation, exposure tracking, flexibility section) - `PricingContent.tsx`: removed `PricingPageExperimentVariant` type export - `PricingPlans.tsx`: removed `experimentVariant` prop and all variant-conditional rendering - `telemetry-constants.ts`: removed `PricingPageExperimentExposedEvent` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated pricing page component variants and simplified the component hierarchy by removing conditional experiment-dependent UI branches. * Removed pricing page experiment tracking and associated telemetry event definitions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d409836ca7 |
feat(www,docs): serve marketing pages as .md, advertise via link rel=alternate (#45277)
## Summary Adds `/<page>.md` routes for 10 marketing/product pages (homepage, auth, database, edge-functions, realtime, storage, vector, pricing, modules/cron, modules/queues) so AI agents can fetch clean markdown instead of parsing JS-rendered HTML. Also advertises the markdown alternate via `<link rel="alternate" type="text/markdown">` on marketing and docs pages so agents can discover it. Pricing is generated dynamically via `generatePricingContent()` (single source of truth with `/llms.txt` and `/llms-full.txt`); the other nine slugs are bundled at build time from `content/md/*.md` into a `MD_CONTENT` map. Supersedes #44891 (rebased fresh off current master to avoid a 9-commit replay over rename/rename conflicts created by #44897). ## Changes - New `/api-v2/md/[...slug]` route handler returns the bundled markdown (or dynamic pricing) with `Content-Type: text/markdown`, `X-Content-Type-Options: nosniff`, and appropriate cache headers - Middleware rewrites `/<slug>.md` and `Accept: text/markdown` to the API route for the `MD_PAGES` allowlist; trailing-slash variants (`/auth/`) are normalized so they resolve the same as `/auth` - Build-time codegen `scripts/generateMdContent.mjs` scans `content/md/` and emits `app/api-v2/md/content.generated.ts` exporting both `MD_CONTENT` (Map) and `MD_PAGES` (Set, incl. dynamic `pricing`). Fails the build on slug collision between `content/md/` and `DYNAMIC_SLUGS`. Adding a new marketing `.md` is just dropping a file in `content/md/` (also update `PRODUCT_OVERVIEW_LINKS` in `/llms.txt` since that list is editorial). - 8 permanent redirects `/llms/<product>.txt` → `/<product>.md` so legacy URLs in caches and downstream `llms.txt` copies keep working - `/llms.txt` product overview now references `.md` URLs (incl. `modules/cron`, `modules/queues`); `/llms-full.txt` iterates `MD_CONTENT.values()` (homepage first, then alphabetical) and appends dynamic pricing - `/llms/[slug]` route slimmed to proxy SDK reference files (`js.txt`, `dart.txt`, etc.) since redirects handle product slugs and pricing; pricing branch retained as fallback in case redirects are bypassed - `apps/www/pages/_app.tsx` injects the alternate link conditionally based on `MD_PAGES`; `/pricing` (app router) sets it via page metadata - `apps/docs/app/page.tsx` (the `/docs` root) sets the text/markdown alternate to `/llms-full.txt`; per-guide pages override with their specific `.md` URL via `genGuideMeta` in `GuidesMdx.utils.tsx`. Other docs pages (reference, troubleshooting) inherit nothing. - `apps/www/.vercelignore`: replaces the prior `*.md`/`README.md` rules with `*.md` + `!content/md/**/*.md` so Edge Function READMEs and future scratch `.md` files aren't silently shipped to the build artifact - Drops `apps/www/data/llms/*.txt` and the related `outputFileTracingIncludes` - Test coverage for the new middleware branches: `.md` suffix rewrite (allowlisted vs. fall-through), `Accept: text/markdown` content negotiation, trailing-slash normalization ## Testing (Vercel preview) Local dev server smoke tests passing on `:3771` after each iteration. Re-verified on the preview URL after the latest hardening commit: - [x] `curl -I https://<preview>/llms/auth.txt` — expect `308 Permanent Redirect` to `/auth.md` - [x] `curl https://<preview>/auth.md | head -3` — expect `# Supabase Auth` - [x] `curl https://<preview>/pricing.md | head -3` — expect `# Supabase Pricing` with current tier values - [x] `curl https://<preview>/modules/cron.md | head -3` — expect `# Supabase Cron` - [x] `curl -H 'Accept: text/markdown' https://<preview>/ | head -3` — expect `# Supabase` (homepage.md) - [x] `curl https://<preview>/llms.txt` — Product Overview section lists `.md` URLs and includes Cron + Queues - [x] `curl https://<preview>/llms-full.txt | grep -E '^# Supabase (Cron\|Queues\|Pricing)'` — Cron and Pricing each match once; Queues matches twice (marketing module + existing docs guide) - [x] View source on `/`, `/pricing`, `/database` — expect `<link rel="alternate" type="text/markdown" href="/<slug>.md">` - [x] View source on `/docs` — expect `<link rel="alternate" type="text/markdown" href="/llms-full.txt">` - [x] View source on a docs guide page (e.g., `/docs/guides/auth`) — expect per-guide `.md` alternate; reference/troubleshooting pages should NOT emit a markdown alternate - [x] `curl -I https://<preview>/auth.md` — expect `X-Content-Type-Options: nosniff` - [x] `curl -I -L -H 'Accept: text/markdown' https://<preview>/auth/` — should resolve to markdown content (trailing-slash normalization, with Vercel's auto-redirect) ## Linear - fixes GROWTH-760 ## Follow-up (separate PR) GROWTH-760 also asks about extending `.md` to blog/customers/events. Different mechanism (path-prefix middleware, MDX read at request time via `gray-matter`) so it deserves its own review. Will open a follow-up PR after this lands. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Serve prebuilt and dynamic Markdown docs via new markdown endpoints and routing; pages now advertise markdown alternates (including pricing). * Added Cron and Queues module documentation pages. * **Documentation** * Minor formatting tweaks to Realtime and Storage docs. * **Chores** * Added build-time Markdown content generation and adjusted ignore/deploy rules for generated files. * Added redirects from legacy text-based product URLs to new markdown pages. * **Tests** * Expanded tests for markdown routing and content-negotiation behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
939b4034d9 |
/pricing and /blog invisible to LLMs — fix SSR bailout (#45292)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? push client directive to leaf interactive components, keep content in server shell ## What is the current behavior? /pricing and blog pages are rendered it client side so crawlers don't get any content on fetch. Verifiy content with ```bash curl -s http://localhost:3000/pricing | sed 's/<script[^>]*>.*<\/script>//g; s/<style[^>]*>.*<\/style>//g; s/<[^>]*>//g' | wc -w curl -s http://localhost:3000/blog | sed 's/<script[^>]*>.*<\/script>//g; s/<style[^>]*>.*<\/style>//g; s/<[^>]*>//g' | wc -w curl -s http://localhost:3000/blog/<some-slug> | sed 's/<script[^>]*>.*<\/script>//g; s/<style[^>]*>.*<\/style>//g; s/<[^>]*>//g' | wc -w ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Featured blog post now displays prominently on the blog landing page * Added smooth anchor link navigation on the pricing page * Improved table of contents tracking with scroll-to-anchor behavior on blog posts * Enhanced pricing comparison and plans sections with better data integration <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f590319f0a |
feat(pricing): implement A/B experiment for pricing page flexibility/visibility (#44293)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES/NO ## What kind of change does this PR introduce? Includes 5 new variants to test pricing to solve cost flexibility and visibility concerns ## What is the current behavior? Current pricing page ## What is the new behavior? Variants: <img width="3840" height="2160" alt="CleanShot 2026-03-30 at 19 32 02@2x" src="https://github.com/user-attachments/assets/a4f3b048-fa0d-4137-9f24-2eb6d431f3c8" /> <img width="3840" height="2160" alt="CleanShot 2026-03-30 at 19 33 34@2x" src="https://github.com/user-attachments/assets/a9045a33-b8c3-4df1-a47d-00cf58495bb3" /> <img width="3840" height="2160" alt="CleanShot 2026-03-30 at 19 32 22@2x" src="https://github.com/user-attachments/assets/10511fc8-5e88-4e3c-baa1-56798009d86f" /> <img width="3840" height="2160" alt="CleanShot 2026-03-30 at 19 33 09@2x" src="https://github.com/user-attachments/assets/aa01aaf6-0e41-4466-bfc7-77ae912b6043" /> <img width="3840" height="2160" alt="CleanShot 2026-03-30 at 19 32 43@2x" src="https://github.com/user-attachments/assets/21834f7d-43cb-4981-9c29-934f44986714" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes **New Features** - Launched A/B testing variants on the pricing page featuring customized pricing plan layouts, descriptions, and billing options based on your assigned experiment group. - Certain plan tiers now display optional flexibility information, hourly billing rates, and project scaling details to help optimize your cost estimation and plan selection. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f8815aff9b |
refactor(pricing): remove A/B experiment logic and consolidate pricing components (#44205)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Removes the control variant and replaces it with the test variant as winner. ## What is the current behavior? - ## What is the new behavior? - ## Additional context Add any other context or screenshots. |
||
|
|
af47a2d010 |
experiment:pricing feedback iteration on calculator (#42451)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature — A/B experiment for a new pricing calculator on the `/pricing` page, gated behind a PostHog feature flag. ## What is the current behavior? The pricing page shows a single compute pricing section (`PricingComputeSection`) to all visitors with no experimentation support. ## What is the new behavior? - Introduces a **pricing calculator A/B experiment** (`pricingCalculatorExperiment`) using PostHog feature flags evaluated client-side. - **Control variant**: Renders the existing `PricingComputeSection` (no change). - **Test variant**: Renders a new `NewPricingComputeSection` with: - A plan selector toggle (Pro / Team) - An interactive compute calculator with slider-based instance sizing - Ability to add/remove multiple project instances - Live monthly cost estimate breakdown (plan + compute - credits) - Expandable compute pricing table with detailed specs - Adds `getFeatureFlag` and `onFeatureFlags` methods to the shared `PostHogClient` for client-side flag evaluation on www pages (where server-side evaluation lacks full person context). ## Additional context - The experiment is scoped to the `/pricing` page only. - New components (`NewPricingComputeSection`, `NewComputePricingCalculator`) are created alongside the existing ones — the control path is completely untouched. - Feature flag evaluation happens client-side because www pages don't have full person context on the server. - Telemetry type `PricingCalculatorExperimentExposedEvent` is added to `telemetry-constants.ts`. --------- Co-authored-by: Sean Oliver <882952+seanoliver@users.noreply.github.com> Co-authored-by: Mert YEREKAPAN <mertyerekapan@gmail.com> Co-authored-by: Mert YEREKAPAN <33198490+myerekapan@users.noreply.github.com> |
||
|
|
3f828ff8f5 |
Move pricing page to app router (#42425)
Move the /pricing/ page to the app router <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a dedicated pricing page with pricing plans, interactive cost calculators, add-ons options, and plan comparison tools. * **Updates** * Enhanced pricing components with performance improvements and streamlined navigation handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |