今日已更新 412 条资讯 | 累计 19972 条内容
关于我们

标签:#NeXT

找到 96 篇相关文章

AI 资讯

Sanity vs Directus for Next.js in 2026: An Honest Comparison

Sanity vs Directus is a comparison that comes up more than you'd expect on technical forums in 2026, usually from teams who already have a Postgres database running and are wondering why they'd pay for a separate content lake when Directus can wrap what they have. It's a fair question. These two tools solve adjacent problems but from genuinely different starting points, and the right choice depends heavily on whether your content is primarily relational data or editorial content. What each tool actually is Sanity is a hosted content platform. Your content lives in Sanity's managed "content lake" — a document store with real-time collaboration, a CDN-backed asset pipeline, and GROQ as the query language. You define schemas in code, deploy a customisable Studio, and talk to Sanity's API from your Next.js app. You do not manage infrastructure. Directus is an open-source data platform that wraps any existing SQL database — Postgres, MySQL, SQLite, MS SQL — and exposes it through a REST API, a GraphQL endpoint, and a web-based admin UI. Schema changes happen in the admin UI (or via migrations), and your data stays in your own database. You can self-host entirely or use Directus Cloud. That distinction — hosted content lake vs database-wrapper — drives nearly every practical difference between them. Data ownership and where your content lives With Sanity, your content lives in Sanity's infrastructure. You can export it via the export API, but you are operationally dependent on Sanity's uptime and their CDN. For most product teams that's fine — Sanity has been reliable and their SLA on Growth/Enterprise tiers is solid. But if you're in a regulated industry, have strict data residency requirements, or your client contract requires them to own the database, it's a real constraint. With Directus, the database is yours from day one. You point Directus at a Postgres instance on your own infrastructure (or a managed one like Supabase, Neon, or Railway), and Directus adds the API

2026-07-15 原文 →
AI 资讯

I Built AICostPass Because I Was Tired of Guessing My AI API Costs

While building with OpenAI, Anthropic, and other AI providers, I realized something surprising. I monitored my servers, databases, and application performance—but I had almost no visibility into my AI API spending until I checked the provider dashboard or received the monthly invoice. That led me to build AICostPass . It helps developers, indie hackers, startups, and agencies: ⚡ Track AI API costs in near real time 📊 Monitor spending by project or client 🚨 Get budget threshold email alerts 📧 Receive weekly spending summaries 💰 Export billable CSVs for client invoicing The goal is simple: help developers understand and control AI costs before the invoice arrives. 👉 https://aicostpass.com I'd love to hear how you're currently tracking AI API costs. Are you using provider dashboards, spreadsheets, or another tool?

2026-07-15 原文 →
AI 资讯

Migrating a Vite i18n App to Next.js Without Breaking Everything

The Architecture Shift: SPA vs. Framework Internationalization (i18n) is one of those features that feels straightforward in a Single Page Application (SPA). You install react-i18next , wrap your app in a provider, and you're good to go. However, when you decide to migrate that Vite-based React app to Next.js for better SEO and performance, the strategy for i18n changes fundamentally. In a Vite SPA, i18n is typically client-side. In Next.js, i18n happens at the routing and server level. If you don't plan the migration carefully, you'll end up with hydration mismatches, flashing text, or broken search engine indexing. Here is how to navigate the transition. 1. Defining the Routing Strategy In Vite, your translations often live in the same bundle, and you swap them out using a state hook. Next.js, particularly with the App Router, prefers sub-path routing (e.g., /en/about or /es/about ). This is crucial for SEO because it allows search engines to crawl localized versions of your pages individually. Instead of relying on localStorage to remember a user's language, you should now rely on the URL. Most teams moving from Vite use a middleware approach to detect the user's preferred locale and redirect them to the correct sub-path. 2. Choosing the Right Library If you were using react-i18next in your Vite project, you have two main paths in Next.js: next-i18next (Pages Router): The traditional choice for the Pages Router. next-intl or i18next + i18next-resources-to-backend (App Router): These are modern solutions that leverage Server Components. When handling complex migrations involving many components, using a specialized tool like ViteToNext.AI can help automate the transformation of your Vite project structure into a Next.js-ready architecture, saving you hours of manual refactoring. 3. Handling Server Components vs. Client Components one of the biggest hurdles is that useTranslation() hooks from standard i18n libraries are "Client hooks." In the App Router, you'll wan

2026-07-14 原文 →
AI 资讯

Building an AI-Powered Lead Qualification API with Next.js 15 and Gemini 3.5 Flash

Every business wants more leads. But the real challenge isn't generating them—it's identifying which leads deserve your team's attention first. Instead of manually reviewing every inquiry, we can build a simple AI-powered API that analyzes incoming leads and assigns a priority score automatically. In this article, I'll show a lightweight production-ready approach using Next.js 15 and Gemini 3.5 Flash. Project Structure app/ ├── api/ │ └── qualify/ │ └── route.ts ├── lib/ │ └── gemini.ts └── page.tsx API Route import { NextResponse } from "next/server"; export async function POST(req: Request) { const { company, message } = await req.json(); const prompt = ` Company: ${company} Message: ${message} Give: - Score (1-100) - Priority - Reason `; // Call Gemini API here return NextResponse.json({ success: true, score: 92, priority: "High" }); } Example Response { " score " : 92 , " priority " : " High " , " reason " : " Large company with a clear automation requirement. " } Now your CRM, chatbot, or automation workflow can instantly decide which leads should be contacted first. Why This Matters A simple AI scoring layer can help teams: Reduce manual lead review Respond faster to high-value prospects Prioritize enterprise customers Improve sales efficiency Save hours every week The best part is that this API can be connected to forms, chatbots, CRMs, or n8n workflows without changing your existing process. Production Tips Before deploying this to production, make sure you: Validate incoming requests Store API keys securely Add rate limiting Log AI responses for monitoring Cache repeated requests where appropriate Small improvements like these make a huge difference once traffic starts growing. Final Thoughts AI shouldn't replace your sales team—it should remove repetitive work so they can focus on conversations that actually matter. A lightweight lead qualification API is one of the fastest AI features you can add to an existing product, and it scales well as your business

2026-07-14 原文 →
AI 资讯

React useOptimistic: Optimistic UI Patterns That Actually Work (2026)

The problem with most web UIs is the gap between user action and visible feedback. A user clicks "like" and waits 200-400ms for the server to respond before the button changes. That delay reads as slowness even when the server is fast. The network round-trip is the ceiling. Optimistic UI inverts this: assume the operation will succeed, update the UI immediately, then reconcile with the server response when it arrives. If the server fails, roll back. React 19's useOptimistic hook gives you this pattern with minimal boilerplate and automatic rollback built in. The API const [ optimisticState , addOptimistic ] = useOptimistic ( state , // the current "real" state — synced from server updateFn , // (currentState, optimisticValue) => newOptimisticState ) optimisticState — during a pending transition, reflects the optimistic update. Once the transition completes, it reverts to state addOptimistic(value) — triggers an optimistic update, must be called inside startTransition Pattern 1: Like Button ' use client ' import { useOptimistic , useTransition } from ' react ' import { toggleLike } from ' @/actions/likes ' type LikeState = { liked : boolean ; count : number } export function LikeButton ({ postId , initialLiked , initialCount }: { postId : string initialLiked : boolean initialCount : number }) { const [ isPending , startTransition ] = useTransition () const [ optimisticState , addOptimistic ] = useOptimistic < LikeState > ( { liked : initialLiked , count : initialCount }, ( current ) => ({ liked : ! current . liked , count : current . liked ? current . count - 1 : current . count + 1 , }) ) function handleToggle () { startTransition ( async () => { addOptimistic ( ' toggle ' ) // updates UI immediately await toggleLike ({ postId }) // syncs with server }) } return ( < button onClick = { handleToggle } disabled = { isPending } > < Heart className = { cn ( ' h-4 w-4 ' , optimisticState . liked && ' fill-red-500 text-red-500 ' ) } /> < span > { optimisticState . count }

2026-07-13 原文 →
AI 资讯

Casting your friend group as a K-Pop group without making a database the product

Try the demo: K-Saju Crew For fun only. K-Saju is an entertainment project. The K-Pop roles below are a playful interpretation of saju-inspired signals, not personality assessment or advice. A two-person compatibility page can stay stateless with almost no effort. Put both birth dates in a URL, render the result on the server, and the link is the record. No account, no database, no cleanup job. That was already a product rule in K-Saju. We do not retain personal inputs. A result is reproducible from its GET parameters. Then we built /crew : “What if your friend group debuted as a K-Pop group?” A creator makes a link, sends it to a group chat, and each friend enters their own birth date. At three to seven members, the app assigns distinct positions, shows pairwise chemistry, and creates a shareable poster. The fun part is the casting. The engineering problem is that the social flow needs a temporary shared state. A link cannot accumulate submissions by itself. This post is about the decisions behind that feature: where we allowed state, how we made the result durable without retaining a lobby forever, and how we kept the casting explainable instead of treating it as a black-box score. The conflict: a self-service group flow needs somewhere to collect data There were two clean but incomplete options. The first was to keep everything stateless. The creator would enter all members' dates at once, then receive a result URL. It matched our existing architecture, but it defeated the point of sharing a link. The person who starts the group often does not know everyone else's date, and asking them to collect it in a chat creates friction before the feature has started. The second was a conventional persistent group object. It would make joining easy, but it would turn a deliberately stateless service into one that keeps user-provided dates indefinitely unless we built retention and deletion policies around it. We chose a hybrid instead: The lobby is temporary state. It store

2026-07-13 原文 →
AI 资讯

I built two Next.js 15 + Tailwind v4 templates with zero extra dependencies — here's what I learned

Earlier this month I shipped two premium templates — a SaaS landing page and a developer portfolio. Not a startup, not a SaaS, just templates. This post is about the two constraints I built them under, why they made the code better, and a few things I learned launching as a solo dev with zero audience. Constraint 1: zero dependencies beyond next, react, and tailwind Open the package.json of most templates and you'll find 20+ packages: icon libraries, animation libraries, carousel plugins, UI kits, utility libraries. Every one of them is a version conflict waiting to happen for the buyer, and most are replaceable with a few lines of code in 2026. What I used instead: Icons → inline SVG components. An icon component is ~10 lines. You need maybe 15 icons for a landing page. Animations → plain CSS. Scroll-blur navbars, gradient glows, an animated "typing" terminal — all doable with keyframes and transitions. No framer-motion. The dashboard mockup in the hero → pure CSS. Divs, borders, gradients. It looks like a product screenshot but it's ~80 lines of JSX and weighs nothing. Result: both templates land at ~100KB first-load JS, npm install takes seconds, and there is nothing to break when Next.js 16 arrives. Constraint 2: every piece of content in ONE typed config file The thing I hated most about templates I've used: content is smeared across 30 components. Changing a headline means hunting through JSX. So both templates keep all content in a single file — lib/content.ts for the landing page, site.config.ts for the portfolio. Headlines, nav, pricing tiers, testimonials, project lists, even the lines that animate in the fake terminal. Components are pure renderers of that config's TypeScript type. Two things surprised me here: TypeScript becomes your content linter. Forget an alt text, malform a link, give a pricing tier three features when the type expects a non-empty array — the build fails. Content mistakes surface at compile time. It forces better component design. W

2026-07-12 原文 →
AI 资讯

What actually crosses the React Server Component boundary

Everyone can type "use client" . Almost nobody can say what survives the trip across it — and then something breaks: next build dies at prerender, the error names no file and no import chain, and the prop that killed it was an arrow one level down inside an object called options . Here's the uncomfortable secret: the boundary is one serializer . React walks every prop you hand a client component, encodes each value it has a branch for, and throws on the first one it doesn't. This post reads those branches out of React 19's Flight source — one file, no framework — and shows the two traps that pass code review and fail the build anyway. What crosses A prop is legal if the serializer has a branch for it. Everything else falls into one prototype check and throws. The whole contract fits on a screen: // app/page.tsx — a Server Component. Every comment is the serializer's verdict. export default function Page () { return ( < Chart title = "Q3" data = { { rows : [ 1 , 2 , 3 ] } } when = { new Date () } seen = { new Set ([ 1 ]) } index = { new Map () } rows = { fetchRows () } // an un-awaited Promise; the client calls use(rows) bytes = { new Uint8Array ( 8 ) } // ArrayBuffer, DataView, every typed array upload = { new File ([], ' a.csv ' ) } // there is no File branch — a File is a Blob form = { new FormData () } stream = { new ReadableStream () } kind = { Symbol . for ( ' chart ' ) } // global symbols cross; Symbol('chart') throws Slot = { Legend } // a client component: a function, and a client reference save = { saveRow } // a "use server" function: a server reference err = { new Error ( ' boom ' ) } // crosses — and arrives empty in production // no branch — every one of these throws at render match = { /q3/ } href = { new URL ( ' https://x.dev ' ) } cache = { new WeakMap () } user = { new User ( ' ada ' ) } bare = { Object . create ( null ) } onPick = { ( id ) => select ( id ) } /> ); } Four of those lines are the ones people get wrong: new Error() crosses, and product

2026-07-12 原文 →
AI 资讯

React Compiler in 2026: What It Actually Memoizes (And What It Doesn't)

Headline: React Compiler — formerly React Forget — shipped stable with React 19 and automatically memoizes components, hooks, and callbacks by analyzing data flow at build time. No dependency arrays to write; the compiler infers them. Here is what it handles, when it opts out, and whether you should delete your useMemo calls. Key takeaways React Compiler inserts useMemo , useCallback , and React.memo automatically at build time — no dependency arrays to maintain. Enable it in Next.js 15/16 with experimental.reactCompiler: true in next.config.ts . The compiler is conservative: if it cannot prove memoization is safe, it emits the component unchanged. "use no memo" is the escape hatch for functions the compiler should not touch. Run npx react-compiler-healthcheck@latest before enabling to see coverage and violations. What does React Compiler actually do? React Compiler transforms component and hook code at build time to insert memoization automatically. Instead of useMemo(() => expensiveCalc(a, b), [a, b]) , the compiler analyzes data flow, determines which values are stable across renders, and emits equivalent memoized code. The compiled output uses React's memo infrastructure at runtime. The compiler is babel-plugin-react-compiler — it works with any Babel-based build pipeline. How do I enable it in Next.js? // next.config.ts const nextConfig = { experimental : { reactCompiler : true , }, }; export default nextConfig ; Before enabling, run the healthcheck: npx react-compiler-healthcheck@latest The healthcheck reports optimizable component count, files with violations, and blocking patterns. Fix violations first for more coverage on day one. What does the compiler memoize? Components — equivalent to React.memo ; re-renders only when props change. Values — equivalent to useMemo ; computed results, derived arrays, objects. Callbacks — equivalent to useCallback : event handlers, functions passed as props. Dependencies are inferred from escape analysis — n

2026-07-12 原文 →
AI 资讯

Partial Prerendering in Next.js: The Static Shell + Dynamic Stream Model

Headline: Partial Prerendering (PPR) in Next.js serves a static HTML shell from the CDN edge instantly, then streams Suspense-wrapped dynamic children from the origin in the same HTTP response. No full-page ISR staleness, no full-page origin latency. I shipped it on two production routes — here is the model. Key takeaways PPR serves a static HTML shell from the CDN edge , then streams dynamic Suspense children from the origin in the same response. The static shell is built at build time — outside <Suspense> renders statically; inside renders dynamically per request. PPR replaces the ISR vs. dynamic tradeoff for pages that are mostly static with isolated personalized sections. No changes to Server Components or Suspense — just experimental.ppr: 'incremental' in config and export const experimental_ppr = true per route. PPR and use cache are complementary : CDN delivery for the shell, origin memoization for dynamic islands. What does PPR actually do? PPR splits a page into two rendering phases within the same HTTP response. At build time, Next.js freezes everything that does not read dynamic request data into a static HTML shell on the CDN edge. At request time, the CDN delivers the shell at edge latency while the origin streams each <Suspense> boundary's content into the same response. On a product page: navigation, title, and description arrive at CDN speed. The in-stock badge and personalized recommendations stream from the origin a fraction of a second later. The user sees a nearly-complete page immediately. How is PPR different from ISR and streaming Suspense? Strategy First byte Dynamic freshness Staleness ISR (revalidate: N) CDN edge Whole page up to N seconds stale Full page Dynamic rendering Origin 100% fresh; waits for slowest query None Streaming Suspense (no PPR) Origin Fresh; TTFB includes origin latency None PPR CDN edge Dynamic islands 100% fresh Static shell only How do I enable PPR? // next.config.ts export default { experimental : { ppr : ' inc

2026-07-12 原文 →
AI 资讯

Server Components vs Client Components: The Mental Model Shift Every Vite Developer Needs

Introduction If you have been building applications using Vite, you are likely used to a specific workflow: write React components, bundle them with esbuild/Rollup, and serve a single HTML file that fetches a large JavaScript bundle. In this world, everything is a "Client Component." However, as the React ecosystem shifts toward the App Router and React Server Components (RSC), the architecture is fundamentally changing. For developers moving from a Vite-centric mindset to a Next.js framework, the biggest hurdle isn't the syntax—it's the mental model. In this guide, we will break down the core differences between Server and Client components and how to adapt your Vite-based habits to this new reality. The Vite World: Single-Page Application (SPA) Default In a standard Vite + React project, your entire application lifecycle happens in the browser. The browser requests the page. The server sends a nearly empty index.html . The browser downloads the JS bundle. React hydrates the app, fetches data from an API via useEffect , and renders the UI. While this is excellent for developer experience (DX) and highly interactive dashboards, it often leads to "Layout Shift" and slower "Time to Interactive" for content-heavy pages because the client has to do all the heavy lifting. The Shift: Thinking in "Environment Splits" With React Server Components, the paradigm shifts from "Everything happens on the client" to "Compute where it makes sense." 1. What are Server Components? By default, in the Next.js App Router, every component is a Server Component. These components execute only on the server . They never send their code to the client-side bundle. This allows you to: Access backend resources directly: You can query your database or file system inside the component. Keep secrets safe: API keys and sensitive logic stay on the server. Reduce bundle size: Large dependencies (like a markdown parser or date library) stay on the server and only the resulting HTML is sent to the user

2026-07-10 原文 →
AI 资讯

The Complete Redbelly EligibilitySDK Integration Guide: Widget to Backend to On-Chain

The Redbelly Network EligibilitySDK is the compliance backbone for any dApp that needs to verify user eligibility (KYC, KYB, investor accreditation) before letting a wallet in. The official documentation covers each piece well on its own reference page, but there is no single walkthrough connecting the frontend widget to the backend verifier to the on-chain permission check to a production deployment. This guide is that walkthrough. Everything here was verified against the live documentation at https://docs.redbelly.network/ in July 2026: contract addresses, route names, config fields, issuer DIDs and every error string in the reference section. Every code example was then compiled against the published SDK package (v0.0.31) on React 19 with Vite and on Next.js 16 with the App Router, and the backend verifier was booted and exercised for real. Where the docs and reality diverge (a quickstart repo that is not publicly visible, a credential faucet still under development, three undocumented behaviours the builds surfaced), the guide says so and gives you the workaround. What you will build, in order: A mental model of the two verification mechanisms (and why conflating them costs you a day) A working backend verifier with the three routes the widget demands A plain React integration with full loading and error states A production-grade Next.js App Router setup: secure proxy, SIWE sessions, request gating, and both static and dynamic rendering approaches An end-to-end test run on Redbelly Testnet The decision logic for choosing between the three SDK flows, and the pattern for combining them A complete error reference: every documented error, its cause, and its fix A developer following this guide should have the widget running inside an existing dApp within about four hours. 1. Overview and Architecture What the EligibilitySDK actually is The Redbelly "Onboarding and Eligibility Kit" ( @redbellynetwork/eligibility-sdk ) is a set of React components and hooks for provin

2026-07-09 原文 →
AI 资讯

How I Structure Large Next.js Projects — Folder Architecture Guide

Bad nextjs folder structure does not show up on day one. It shows up at month six when three developers search for the checkout form hook and find four copies. I reorganised a client dashboard after exactly that — this guide is the tree I use now on large App Router projects, why each folder exists, mistakes from my first Next.js apps, and the 10-second findability rule . Real folder tree — production-shaped layout my-app/ ├── app/ # routes only — thin pages │ ├── (marketing)/ # route group — shared layout, no URL segment │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── pricing/page.tsx │ ├── (dashboard)/ │ │ ├── layout.tsx │ │ └── orders/page.tsx │ ├── api/ # route handlers │ │ └── webhooks/stripe/route.ts │ ├── layout.tsx # root layout │ └── globals.css ├── components/ # shared UI — buttons, cards, shell │ ├── ui/ │ └── layout/ ├── features/ # business domains — colocated logic │ ├── auth/ │ │ ├── components/ │ │ ├── hooks/ │ │ └── actions.ts │ └── orders/ │ ├── components/ │ ├── api.ts │ └── types.ts ├── lib/ # server + shared utilities │ ├── db.ts │ └── env.ts ├── hooks/ # truly global client hooks ├── types/ # global TS types ├── data/ # static data, blog posts list └── public/ Routes live in app/ . Business logic lives in features/ . Generic design system pieces live in components/ui . That separation is the whole game. Why each folder exists Folder Purpose Do not put here app/ URLs, layouts, loading.tsx Fat business logic features/ Domain modules (orders, auth) Generic Button components/ui Reusable primitives Order-specific tables lib/ DB clients, env validation React components app/api Webhooks, REST edge cases Every form POST (prefer actions) Thin pages — route files under 40 lines // app/(dashboard)/orders/page.tsx — orchestration only import { OrderTable } from "@/features/orders/components/OrderTable"; import { getOrders } from "@/features/orders/api"; export default async function OrdersPage() { const orders = await getOrders(); return ( <section> <h1>Orders

2026-07-09 原文 →
AI 资讯

How I add semantic search to a Next.js site using Sanity Embeddings

Sanity Embeddings semantic search in Next.js is one of those features that looks complicated from the outside but is surprisingly lean to wire up once you understand the moving parts. This post covers the current native Embeddings feature built into Sanity datasets — not the older Embeddings Index API, which Sanity is sunsetting. If you found a guide that talks about a separate embeddings-index resource you have to provision via the Management API, it is stale; skip it. What Sanity Embeddings actually is Sanity's native Embeddings feature lets you mark document types for vector indexing directly inside your dataset. Sanity handles the embedding model and the vector store; you never manage a separate service. Queries use a dedicated sanity.embeddings.query GROQ function that takes a natural-language string and returns documents ranked by semantic similarity. The feature is available on Growth and Enterprise plans as of mid-2026. The workflow has three parts: Configure which document types get indexed (dataset setting or the Embeddings pane in Sanity Studio). Run a semantic query from your Next.js route handler using the Sanity client. Render the results in a search UI component. Setting up the embeddings index in your dataset Go to Manage → your project → Embeddings (or open the Embeddings pane inside Sanity Studio if your plan surfaces it there). Create an index, give it a name (e.g. site_search ), and select which document types and fields to embed. For a blog you would typically pick post with fields title , excerpt , and body (plain text extracted from Portable Text). Sanity backfills existing documents automatically. New and updated documents are re-embedded on publish via an internal webhook — you do not configure that yourself. There is no code required for the indexing step. The index name you choose here ( site_search ) is what you will pass in the GROQ query. Querying embeddings from a Next.js route handler Create a route handler that accepts a search term,

2026-07-08 原文 →
AI 资讯

I gave an LLM agent write access to my cloud drive. Three bugs taught me how to constrain it.

I wanted a media library that knew the difference between what should exist and what does. Most automation I tried picked one side. Some tools search well and never track what you already have. Others move files and assume the move worked. I wanted the gap between those two things to be the thing the software acted on. So I built Mediary Scout . You name a movie or a show. An LLM agent searches your indexers, transfers the best match into your own cloud drive, then reads the drive back to confirm what landed and what is still missing. It runs self-hosted. You bring your own drive, your own model, your own metadata key. There are desktop builds for Mac and Windows if you just want to double-click and run it, and a read-only demo if you want to watch one acquisition play out first. The drives it speaks today happen to be Chinese cloud storage (115, Quark, GuangYaPan). That detail does not matter for the rest of this post. The part that took real work was different: handing an LLM tools that move and delete files, and stopping it from doing something dumb with them. Three bugs taught me most of what I now believe about that. The shape of the thing The web app does almost nothing interesting. It writes a row to a Postgres queue and returns. A long-running worker picks up the row and starts a sandboxed agent. The agent gets a small set of tools: search resources, transfer a candidate, list a directory, move files into a season folder, mark episodes as obtained. Every tool runs through a deterministic workflow that owns the actual side effect. The agent proposes. The workflow decides whether the proposal is allowed, performs it, and reads the world back. That split is the whole design. The model is the part I cannot fully predict, so it gets the smallest possible blast radius. The deterministic code around it holds every irreversible action and every check. When I violated that split, things broke. They broke in the order below. Bug 1: the agent searched sixteen times and

2026-07-07 原文 →
AI 资讯

Why I stopped using online image compressors and built a CLI instead

Four years of optimizing React and Next.js projects taught me one thing: unoptimized images are everywhere, and nobody wants to fix them. Every project has the same pattern. Heavy PNG and JPG files are sitting inside /public , there is no consistent image pipeline, and some of those files have no business being that large in a production codebase. This is especially common in small and mid-sized projects. There is no CDN transformation layer or dedicated asset pipeline. Images get added while the product is moving quickly, and the cleanup becomes a task for “later.” Later, of course, never comes. Then, at 1am, while refactoring an extremely vibe-coded Next.js project, I found myself doing the cleanup manually again. Find an image. Upload it to an online compressor. Hit the free limit. Open another tool. Convert a few more. Download everything. Replace the original files. Hunt through the codebase for every import and src path. Hope I did not miss one. And I finally thought: I am a developer. Why am I doing this by hand? So I built pixcrush . npx pixcrush . One command to convert the images, compress them, and update their matching code references automatically. “But doesn’t Next.js already optimize images?” Yes, and if your application uses next/image consistently, you should absolutely take advantage of it. The Next.js <Image> component can resize images for different devices, lazy-load them, and serve modern formats such as WebP. Files inside /public can be referenced from the root URL, while statically imported images also give Next.js access to their intrinsic dimensions. The official Next.js image documentation explains these runtime optimizations in detail. But that solves a different layer of the problem. I wanted to clean up the source assets themselves: Replace heavy PNG and JPG files with smaller WebP files when conversion is worthwhile. Update existing imports and string-based image paths across the repository. Identify images that are no longer reference

2026-07-07 原文 →
AI 资讯

We built 126 browser tools with zero uploads. Here is what broke along the way

We are two friends building Pageonaut , a collection of 126 free browser tools (converters, calculators, PDF and image utilities, dev helpers). Early on we committed to one constraint: everything runs client-side . No file uploads, no accounts, no server-side processing. That one decision shaped the whole architecture, and it broke things in ways I did not expect. Here are the lessons, including the one where our server filled up with 419 GB of cache and took the site down twice. Why client-side only Every time I needed a quick converter, the top search results wanted me to upload my file to their server, create an account, or pay to remove a watermark. For work that a browser can trivially do locally. So the rule became: drop a file into one of our converters and it never leaves your device. You can watch the network tab while using it. This is great for privacy and trust, and it has a nice side effect: our server does almost nothing per user, so hosting stays cheap even if a tool gets popular. The cost: some tools are genuinely harder to build. PDF manipulation in the browser (we use client-side libraries instead of a server queue), image processing on the main thread without freezing the UI, and no "just call an API" escape hatch. When a tool truly needs the network (say, fetching a URL you give it), the page says so explicitly. Lesson 1: Unbounded URL params + ISR = a full disk This is the expensive one. We built shareable challenge pages: beat my score, try this color, that kind of thing. The URLs look like /tools/<slug>/challenge/<value> , where <value> is user-generated. With Next.js ISR, every unique URL that renders gets persisted to the filesystem cache. You can see where this is going. The value space is infinite. Bots found the pattern and started enumerating it. .next/server/app grew to 419 GB . The disk filled up, the site went down, and because we did not understand the root cause immediately, it happened a second time a few days later. The fix was tw

2026-07-07 原文 →
AI 资讯

Behind the Curtain: APE-QIL QUANTUM SUPREME OCTOPUS and the 3-Tier Sovereign Auth Pipeline

Most API authentication I've seen in production follows the same pattern: a single apiKey check at the top of each route handler, maybe a rate limiter slapped on as middleware, and a quota check that lives in the database layer. It works — until you have 673 routes across 3 access tiers, and you realize you can't answer the question "which routes require a paid subscription?" without grepping every file. I ran into this exact problem building the APE-QIL QUANTUM SUPREME OCTOPUS — a Bio-inspired Autonomous Intelligence Organism that operates as an AI routing platform with 14+ provider integrations. The codebase has 673 API routes divided into three access tiers: public (79 routes), free API key (337 routes), and paid subscription (255 routes). Manually maintaining auth on each route was untenable. So I built a composable request pipeline that makes the auth structure declarative and CI-enforced. This post walks through the architecture: the 3-tier sovereign auth model, the composable withRequestPipeline function, and the CI guard that fails the build if any protected route is missing its wrapper. The 3-Tier Sovereign Auth Model The access model is deliberately simple — three tiers, each with a clear boundary: // TIER 0 — Public, no auth (79 routes) // Health checks, pricing, blog, lead magnet, metrics // Example: /api/health, /api/pricing, /api/blog/* // TIER 1 — Free API key required (337 routes) // Any valid API key in the database grants access // Enforced via: withSovereignAuth('free') // Example: /api/v1/chat/completions (free tier limits) // TIER 2 — Paid subscription required (255 routes) // Requires Pro ($79/mo) or Business ($249/mo) tier // Enforced via: withSovereignAuth('professional') // Example: /api/v1/chat/completions (premium models, higher limits) The key design decision: the tier is declared at the route level, not inferred from the user's subscription at runtime. This means the route registry itself is the source of truth for "what requires what."

2026-07-06 原文 →