TanStack Query style caching, the Angular-native way
Angular has signals now - and as of 19.2, even a signal-based way to fetch: httpResource, built on...
找到 1693 篇相关文章
Angular has signals now - and as of 19.2, even a signal-based way to fetch: httpResource, built on...
Last night I was deep in a build session with an AI assistant. We picked it back up tonight. At some point I mentioned it had been a day and a half since we last spoke — and the model had no idea. None. As far as it knew, it was still the previous session. The gap was invisible to it. That tiny moment is one of the most underrated problems in AI systems right now. So let's talk about it. The model doesn't know what time it is An LLM gets a rough sense of "now" at the start of a conversation — a single timestamp, handed to it once. That's why it can greet you with "good morning." But that stamp is frozen. It doesn't update as the conversation runs, and it definitely doesn't travel into the next conversation. Each session starts cold. On its own, that's a curiosity. It becomes a real problem the moment the model reasons over retrieved context — search results, documents, database rows, another agent's output. Staleness is invisible Here's the dangerous part. When a model reads a retrieved document, that document usually carries no trustworthy signal about when it was true . So the model treats it as present-tense. It produces a confident answer from six-month-old data with nothing flagging that the data is old. A few places this bites: Pricing — quoting a number that changed last quarter. Availability — "in stock" from a cached page. Compliance — citing a policy that was superseded. People — stating someone's job title from two years ago. For a human reader, a slightly stale search result is fine — you see the date and judge for yourself. For an LLM, the staleness is silent. The wrong answer looks exactly like a right one. Why "just add a clock" doesn't fix it The instinct is: give the model the current time. But knowing it's 9 PM doesn't help if the document you're citing went stale in 2023 and nothing told you. The missing piece isn't the model's clock — it's the context's freshness . Two different things: What time is it now? — easy, a now() call solves it. How old
Performance optimization has always been one of the hardest parts of web development. You run...
Most of the web's foundational moments have vanished. The servers were unplugged, the code was lost, the pages 404'd into history. But the first website ever published is a striking exception: you can still read it today, more or less as it appeared when it went live on August 6, 1991. It is a plain, text-only page with a white background and blue hyperlinks, and it explains a brand-new idea called the World Wide Web. One page that described itself The author was Tim Berners-Lee, a British computer scientist working at CERN, the particle physics laboratory near Geneva. By the end of 1990 he had quietly assembled the three technologies that still define the web: HTML for writing pages, HTTP for moving them between machines, and the URL for addressing any document on any server. The first website, hosted at the address info.cern.ch , was the web explaining itself - what hypertext was, how to browse it, and how to make your own pages. It ran on a NeXT computer, the sleek black workstation designed by Steve Jobs's company during his years away from Apple. That single machine was the entire World Wide Web for a while. A handwritten label was stuck to its case: "This machine is a server. DO NOT POWER IT DOWN!!" One unplugged cable would have taken the whole web offline. Why a 1991 web page still matters to IoT It is easy to file this under nostalgia, but the first website is more than a museum piece. It is the origin point of the request-and-response model that quietly powers almost everything connected today. When an ESP32 sensor node pushes a reading to a cloud dashboard, when a smart meter checks in with a server, or when you open an app to see whether your device is online, the same basic conversation is happening: a client asks a question over HTTP, a server answers, and a URL says where to look. Berners-Lee made a deliberate choice that turned out to matter enormously. He kept the standards open and unlicensed. Anyone could implement a browser or a server without pa
What happens when thousands of people decide they're hungry at the exact same time? The Quiet Before the Storm 10:00 PM. The numbers are gentle tonight. One hundred eighty-nine requests trickle in. Someone in Lagos is ordering late-night suya. A rider in Ibadan is wrapping up his last delivery. In Bangladesh, someone is just discovering us for the first time. By 11:00 PM , things get quiet. Just 8 requests. The platform takes a breath. 2:00 AM. A mystery. 151 requests spike out of nowhere. We check the logs. Nothing unusual. Just a group of night owls ordering food, maybe shift workers, maybe students pulling an all-nighter. The beauty of a platform is we're always on, always ready. 7:00 AM. Good morning, Nigeria. Fifty-five requests. People waking up, checking their wallets, planning their day. The coffee hasn't even brewed yet, but the platform is already humming. The Morning Rush 9:00 AM. 315 requests. The workday begins. Offices buzz with conversations about lunch plans. If someone searches "foodmat site" for the third time this week, they're getting closer to finding us. A corporate client logs in to set up their employee meal program for the first time. By 10:00 AM , the traffic settles to 50 requests. A calm before the real storm. 11:00 AM. 173 requests. The hunger is building. People are making decisions about what to eat, where to order, and which vendor to choose. Our World Cup campaign notifications ping. Someone shares their referral code. The viral loop begins. The Lunch Explosion 12:00 PM. 321 requests. It's happening. The platform comes alive. 1:00 PM. 339 requests. The peak is building. Our servers are handling it smoothly. This is where the magic happens when thousands of people decide they're hungry at the exact same time. 2:00 PM. 289 requests. Still going strong. Vendor dashboards refresh. Riders accept orders. Laundry bookings come in alongside food deliveries. If someone cancels an order with a reason, we take note. Every interaction teaches us
"Prop drilling is bad, use Context" is repeated everywhere — but the actual cost stays abstract. So I put the two approaches side by side with live render counters. Click one button and the difference is impossible to miss. ▶ Live demo: https://context-vs-props-drilling.vercel.app/ Source (React 19 + TS): https://github.com/dev48v/context-vs-props-drilling Two identical 4-level trees, both React.memo 'd. One threads a value down as a prop through every level; the other provides it once via Context and reads it only at the leaf. Change the value: Prop drilling → 4 components re-render. Every component on the path receives the changed prop, so all of them re-render — and each intermediate is cluttered with a value it does nothing with except pass along. Context → 1 component re-renders. The intermediates take no value prop, so they're skipped (memoized, props unchanged). Only the consumer leaf re-renders. The summary tallies it on every click: 4 vs 1 . Why Context skips the middle This is the part that surprises people: with Context, an intermediate component can be skipped even though a descendant re-renders . < ThemeCtx . Provider value = { val } > < A /> { /* memo, no props → skipped on value change */ } </ ThemeCtx . Provider > const A = memo (() => < B />); // skipped const B = memo (() => < C />); // skipped const C = memo (() => < Leaf />); // skipped const Leaf = () => { const value = useContext ( ThemeCtx ); // ← re-renders on context change return < div > { value } </ div >; }; React re-renders context consumers directly when the provider value changes — it doesn't need to re-render the components in between. With prop drilling there's no such shortcut: the only way the value reaches the leaf is through every parent, so every parent must re-render. The catch — Context isn't a free lunch Context isn't a "no re-renders" button. Every consumer re-renders whenever the provider value changes — there's no built-in selective subscription. One big, chatty context ca
I created Agentis Lux for the purposes of entering H0 Hackathon (Vercel + AWS Databases). #H0Hackathon See Agentis Lux's Devpost.com entry . It started with a comment at a hackathon. A you.com employee said the thing out loud: the web has a second audience now. When you ask ChatGPT or Perplexity a question, a retrieval agent fetches a page and reads its HTML to answer you. Not the laid-out site with the buttons and the hero image. The markup underneath. These agents arrive by the million, and many of them rely on the raw or minimally rendered HTML rather than running your JavaScript, so they often see far less of your page than a person does. That comment sent me to build. My first answer to it was Hermes Clew , for the GitLab Duo Agent Platform Challenge. Hermes lived inside GitLab Duo Chat, no frontend, no database: a Python engine that scanned the HTML, JSX, and TSX files in a repo, scored them across six categories, and let an LLM reason over the findings. It proved the core idea. It also told developers how to fix things, lived inside one vendor's chat, and only worked on files in a repo. Agentis Lux is what happened when I took that idea to the open web and rebuilt it with a different stance. Any live URL, not just repo files. Its own product on a real cloud architecture, not a chat window. And no fix suggestions, on purpose, where Hermes used to hand them out. Same six-category bones, a new body, a sharper philosophy. It scans your site and shows you what that second audience experiences when it tries to read it. What it does You paste a URL to Agentis Lux . You get a report. The report is written from the agent's point of view. Not "this is broken." More like: "an agent landing on this page can't tell which element starts checkout, because it's a styled div and not a button." It reports findings. It does not suggest fixes, and that is on purpose. I know what the agent sees, not what you should change. That is the whole value: visibility, and you decide what
You submitted your extension, waited days for review, and got back a rejection with a violation called "Purple Potassium." Your extension looks fine to you, so what does it even mean? Here is what it is, why it happens, and how to catch it before you ever hit submit. What "Purple Potassium" actually means "Purple Potassium" is Google's internal tag for excessive or unused permissions . Your manifest requests access to something your code does not actually use, and the reviewer flags it. It is one of the most common reasons a Chrome extension gets rejected, and it is frustrating precisely because the extension works fine in testing. Review is checking something testing never does: whether every permission you ask for is justified by your code. The usual causes 1. API permissions you declared but never call. You added tabs , bookmarks , or cookies to your manifest at some point, but there is no chrome.bookmarks.* call anywhere in your code. 2. Host access that is too broad. You requested <all_urls> when your extension only touches one site: // Flagged "host_permissions" : [ "<all_urls>" ] // Better "host_permissions" : [ "https://*.example.com/*" ] Leftover permissions after removing a feature. You shipped a feature that needed downloads, later removed the feature, and forgot to remove the permission. The tabs misunderstanding. The tabs permission does not grant access to the tabs API. Basic methods like chrome.tabs.create() work without it. It only grants four sensitive Tab properties: url, pendingUrl, title, and favIconUrl. If you declare tabs but never read those, it counts as unused. How to fix it by hand List everything in permissions, optional_permissions, and host_permissions. For each one, search your code for the matching chrome. call. Remove any permission with no usage. Narrow and other broad patterns to the specific hosts you need. In your reviewer notes, write one plain sentence per sensitive permission explaining why you need it. Reviewers often lack con
Prediction markets just hit $3.6B in volume. I wanted to know what the biggest traders were betting on — in real time. So I built WhaleTrack. Here's how it works under the hood. The Problem Polymarket has a public leaderboard. But it only shows P&L totals — not what whales are currently betting on, not their recent activity, not their win rate. If you want to follow smart money, you're flying blind. I wanted something that answered: what are the top traders doing right now? The Stack Vanilla JS frontend (no framework, keeps it fast) Vercel serverless function as a backend proxy (avoids CORS issues) Polymarket's public data API — no auth required Step 1: Finding the Whales Polymarket exposes a leaderboard endpoint: https://data-api.polymarket.com/v1/leaderboard?limit=20 This returns traders ranked by P&L. I pull the top 10, grab their wallet addresses, and that's my whale list. Step 2: Fetching Live Activity For each whale wallet, I hit: https://data-api.polymarket.com/activity?user={address}&limit=20 This returns their recent trades — market name, size in USDC, timestamp. Refreshes every 60 seconds. Step 3: Calculating Win Rate (the tricky part) The key is the redeemable flag — redeemable: true means they won, currentValue: 0 + redeemable: false means they lost. Took a few wrong attempts with cashPnl (always negative, not useful). Step 4: The Whale Alert Banner Every 60 seconds I check for trades over $5,000 placed in the last 10 minutes. When it fires, a green banner slides down with the whale name, market, and amount. Auto-dismisses after 12 seconds. First time I saw it fire live with a $28K bet — genuinely exciting. Results 129+ users in the first few days Zero ad spend Traffic from Twitter, Reddit, Quora What's Next More whale wallets (suggestions welcome) Click-through to open the same market on Polymarket directly Email/push alerts for big trades Check it out: whaletrack.app All feedback welcome — especially if you spot a whale I'm missing.
Eight months ago, a CS exam forced me to write pseudocode when I already knew how to code. Instead of studying, I rage-built an app. Today examintelligence.app is live. Here’s exactly how I got here—from vibe-coded POCs to a production hybrid AI pipeline—without the curated startup gloss. The Philosophy Behind the Build I’ve always believed studying for marks ≠ actually learning. When I was first introduced to organic chemistry, I hated it. Then I ran into GNNs in Machine Learning with PyTorch and Scikit-Learn , paired with the MoleculeNet dataset. Suddenly, everything clicked. I wanted to learn everything about it. That’s the core problem: exams optimize for pattern recognition, not curiosity. You’re forced down one prescribed path, and it rm -rf s the fun of learning in most cases. So one week before my first prelims, I decided to build exam intelligence. The plan was simple: introduce brutal efficiency using AI for what it’s actually built for: pattern recognition Parse every past paper, mark scheme, and examiner report. Distill it down to precisely what matters. Free up time for coding and creative work. Vibe-Coding the POC (and Why It Collapsed) I’m generally against vibe-coding. It’s unreliable, hard to maintain, and a security nightmare. But with prelims staring me in the face, I had no choice. I opened Claude and vibe-coded it module by module. The only code review I had time for was checking for suspicious os.system or subprocess calls. That was it. I shipped anyway. Initial stack: Gemini API (no agent frameworks, no LangGraph) Streamlit frontend PostgreSQL It validated my idea but functionally, it barely held together. After prelims, I finally looked at what the AI had actually built: Dashboard showing random stats Asked Gemini for a JSON response with 5 keys, saved only 2 Randomly created DB tables while trying to read subjects The kind of code you end up with when you let an AI cook unsupervised for a week. So I did the only reasonable thing: opened Neov
AgentJr — The AI Junior Developer That Manages Your Entire Freelance Business While You Sleep Most "AI developer tools" do one thing: write code. AgentJr does everything a real junior developer does. Code. Clients. Communication. Deployment. Testing. Invoices. Social media. All of it. Automatically. While you sleep. The Real Problem With AI Coding Tools Today Claude Code writes code. Devin writes code. Copilot writes code. But writing code is maybe 40% of what a freelance developer actually does. The other 60%? Talking to clients. Understanding what they actually want. Managing git properly. Branches, commits, PRs. Running tests. Catching bugs before the client sees them. Deploying to the right environment. Not accidentally pushing dev code to production. Sending updates. "Hey, your feature is live." Tracking costs. How much did this project actually cost me in API calls? Following up. Drafting invoices. Scheduling calls. Posting on LinkedIn about the work you just shipped. No AI tool today handles all of that together. They give you a coding assistant and leave the rest to you. AgentJr is different. AgentJr is not a coding assistant. It's a complete AI junior developer that manages your entire workflow — from the moment a client messages you, to the moment the project is deployed, tested, and the invoice is sent. You Are the CEO. AgentJr Is the Manager. Here's the architecture that makes AgentJr unique: You — give direction. Set priorities. Approve plans. That's it. AgentJr (Manager) — understands requirements, asks smart questions, builds plans, manages git, monitors work, handles client communication, runs tests, manages deployments, tracks costs, drafts invoices, posts social media updates. Claude Code / Codex / Gemini CLI (Worker) — writes the actual code. Spawned by AgentJr on your terminal. Per project. You choose which one. AgentJr never writes code itself. It orchestrates the worker that does. This separation is intentional — and it's what makes the whole s
When I started thinking about real-time alerts for my SaaS, my first instinct was Slack. Familiar,...
This article covers debugging and deploying a Rust backed WASM module with a Firebase hosted web app...
Let's be honest — standard GitHub profiles are a bit... static. As a Full Stack Developer & AI/ML Specialist, I wanted a way to showcase my contributions that actually felt alive. I didn't just want a grid of green squares; I wanted a universe. So, I built GitPulse. What is GitPulse? GitPulse is a cinematic, interactive web application that transforms standard GitHub profiles and repository logs into glowing, animated contribution universes. Instead of just seeing numbers, you experience your code history visually. The Tech Stack I wanted this to feel premium, fast, and visually stunning without relying on heavy frontend frameworks if I didn't need to. Canvas API & GSAP: For buttery-smooth micro-animations and physics. Glassmorphism UI: Using CSS backdrop-filters to create a modern, deep aesthetic. Vanilla JavaScript: Keeping the core logic incredibly fast and lightweight. The "Stellar Duel" Feature One of the coolest features I added was the Stellar Duel mode. I thought: What if you could compare your GitHub activity with a friend, but instead of a boring chart, it looked like a sci-fi dashboard? Stellar Duel Using the GitHub API, GitPulse fetches the data and renders a live, side-by-side visual duel of your commit history, stars, and PRs. It’s highly interactive and honestly, just really fun to look at. The Biggest Challenge: Performance Rendering thousands of data points (commits) visually on a canvas can crash a browser if you aren't careful. To solve this, I had to heavily optimize the animation loop. Instead of manipulating the DOM for every star/commit node, I used HTML5 Canvas to batch render the visual elements. I also implemented requestAnimationFrame properly to ensure the animations pause when the user switches tabs, saving CPU cycles. See it in Action I've integrated GitPulse directly into my main portfolio. You can try it live here: GitPulse Live Demo And if you want to see more of the projects I build (especially in the AI/ML and Computer Vision space
Living in the UAE means constantly Googling numbers: what is the visa fee now, how much zakat do I owe, what is today's fuel price, how is my gratuity calculated. The answers online are usually outdated. So I built Adad , a free set of 15 calculators that pull from official UAE government sources and refresh every 24 hours. No sign-up, works in 8 languages. A few that people use most: Visa fees: real GDRFA and ICA rates Zakat: gold, silver, savings DEWA bills: estimate before the bill lands Gratuity: UAE labour-law end-of-service It is at adad.ae if it is useful to you. Happy to answer how the data pipeline stays current.
Open your browser DevTools and run this: const user = { name : " Bob " } console . log ( user ) user . name = " Alice " You would expect the log to show { name: "Bob" } , the value at the time of the console.log call. The collapsed line is what you expect: ▶ Object { name: "Bob" } But expand it, and you will see: name: "Alice" Oops. So what's going on? console.log() is the most-used debugging tool in JavaScript, but it can be subtly unreliable. Not because it is broken, but because it optimizes for speed and interactivity rather than for accuracy . It was built for fast exploration in a live, interactive environment, and those priorities come with tradeoffs that can genuinely mislead you during debugging. Over the next sections, we'll look at a few ways the console can mislead you - and, more importantly, why each one exists. Objects Aren't Snapshots When you pass an object to console.log() in browser DevTools, the browser does not immediately serialize it into a string. Instead, it stores a live reference to that object and defers the actual rendering until you expand the entry. This is called lazy evaluation, and it is what caused the surprise. The collapsed ▶ Object you see is essentially a placeholder: the properties shown inside it are evaluated at the moment you click the arrow, not at the moment you called console.log() . By then, your code has already continued running. That means what you're seeing is not a frozen record of the object at the time of logging, but a live view into whatever the object happens to look like when DevTools renders it. In the example: You log { name: "Bob" } DevTools stores a reference to the user object The code continues executing user.name is mutated to "Alice" You expand the logged object later and see the current state This behavior can feel unintuitive at first, because most developers mentally model console.log() as "print this value right now", but in browser DevTools, it is closer to "show me this object as it exists when
Last month, I spent three days wrestling with 500 PDF invoices. Each one had the same data—vendor name, invoice number, total amount—but the layouts were all over the place. Different fonts, missing headers, tables that somehow broke across pages. I tried regex. I tried OCR with layout analysis. I even tried building a rule-based parser that looked for keywords like "Total:" . Nothing worked reliably. Every time I fixed one pattern, another invoice broke. I was one commit away from throwing my laptop out the window. Then I took a step back. I realized I didn't need to understand every layout variation. I just needed to understand the data . And that's where AI came in. What didn’t work Let me be clear: I tried the usual suspects first. Regex. Classic. I wrote patterns like r"Total\s*:\s*\$?(\d+\.\d{2})" . Worked on 60% of invoices. The rest had "Total Due" or "Amount Total" or the dollar sign in a different place. Regex is great when you control the input. I didn't. OCR with layout parsing. I used Tesseract with --psm 6 and tried to extract lines by bounding boxes. It helped a bit, but tables with merged cells or rotated text threw it off. Plus, I had to write code to guess which box was a field name and which was a value. Rule-based parser. I built a dictionary of known vendors and their layouts. That worked … until I got an invoice from a new vendor. Maintenance became a nightmare. I was solving the wrong problem. Instead of fighting formatting, I needed to focus on meaning . The AI approach that saved me I remembered that large language models are surprisingly good at understanding context. If I could give the model the raw text from a PDF and a description of what I wanted, maybe it could extract the fields directly. Here’s the core idea: treat extraction as a structured generation task. Provide a prompt with a few examples (few-shot) or just describe the schema, and let the model output JSON. I found an API that did exactly this with a simple HTTP call. (Full d
— โคลงสี่สุภาพ ว่าด้วยสามภาษาแห่งการสร้างเว็บ — HTML — โครงสร้าง <html> เปิดทางฟ้า ประกาศ <head> ซ่อนนัยน์นาถ นามนี้ <body> ร่างกายปราศ ซึ่งชีวิต ทุกแท็กเปิดปิดที่ หล่อหล่อมความจริง CSS — ความงาม สีสันลอยลิบฟ้า แต่งแต้ม ตัวอักษรเรียงแถม ถ้วนถี่ ขอบเขตเว้นระยะแย้ม เผยโฉม ทุกพิกเซลที่ปรี่ ปรุงแต่งให้งาม JavaScript — ชีวิต เมื่อคลิกนิ้วหนึ่งครั้ง โลดแล่น ฟังก์ชันทำงานแย้ม ยามใช้ if else ตรรกะแจ่ม จักรกล ทุกบรรทัดที่ให้ ชีวิตแก่หน้าเว็บ สามภาษา — หนึ่งเดียว html คือร่างให้ โครงครัน css แต่งแต้มฝัน สวยหรู javascript พลิกผัน ให้เคลื่อนไหว สามภาษาคู่ฟู ฟื้นฟูโลกา — Nokka | มิถุนายน 2569 เชิงอรรถ: โคลงสี่สุภาพบทนี้ใช้ฉันทลักษณ์มาตรฐาน — บทละ 4 บาท บาทละ 2 วรรค วรรคหน้า 5 พยางค์ วรรคหลัง 2 พยางค์ สัมผัสบังคับระหว่างวรรคท้ายของบาทที่ 1, 2, 3 กับวรรคแรกของบาทถัดไป เนื้อหากล่าวถึงสามเทคโนโลยีหลักของการพัฒนาเว็บไซต์ในฐานะ "กาย — ใจ — วิญญาณ" ของทุกหน้าเว็บ
Building an HSK Speaking Test AI: Real-time Tone Grading with Gemini I built a free Mandarin speaking assessment tool that grades tone + grammar in real time. Here's the engineering behind it. The Problem HSK (Chinese proficiency test) has a speaking component (HSKK), but most learners can't self-assess their level. Online tutors are expensive. Generic AI conversation tools don't grade tones. So I built ToneTutor: a 3-minute spoken-HSK test that estimates your speaking level and identifies weak points. The Tech Stack Frontend: Web Audio API (record user voice → PCM → LINEAR16) React + TypeScript (real-time transcript display) Backend: FastAPI (Python) on Google Cloud Run Gemini 2.5 Flash (real-time conversation + transcript grading) Firestore (user sessions + results) The Challenge: Web Audio API records as WebM. Gemini expects LINEAR16 (WAV). iOS Safari doesn't support WebM. So: Transcode WebM → PCM in browser (Web Audio context) Send raw PCM bytes to backend Backend wraps PCM in WAV header → sends to Gemini Speech-to-Text Gemini analyzes transcript + provides HSK level estimate The Grading Loop python async def grade_session(transcript: str): prompt = """ Rate this Mandarin response on HSK 1-6 scale. Assess: tone accuracy, grammar, vocabulary range. Provide: level estimate + weak points. """ response = await gemini.generate_content(prompt, stream=True) return parse_hsk_level(response) Results - 3-min test - Real-time feedback - Shareable HSK score card - Free (limited sessions) Open source coming soon. Built because I'm a native speaker + voice actor frustrated with generic tools. Try it: tonetutor.tefusiang.com (free for 3 sessions) Curious about the speech-to-text pipeline or tone grading logic? Ask below.
Why your Cloudflare Turnstile token works in the browser but 403s from requests You solved the Turnstile widget. You can see the token in the page. You copy it into your script, POST the form from requests, and the server hands you back a 403 — or a JSON body with "success": false. The token clearly worked a second ago in the browser, so what changed? Short answer: a Turnstile token is not a password you can carry around. It's a one-time, short-lived proof bound to a very specific context, and replaying it from a different context is exactly what it's designed to reject. Below is what that context is, how to tell which constraint you're hitting, and the fix for each. The real scenario You're automating a flow on a Cloudflare-protected site. There's a cf-turnstile widget on the form. You get a token one of two ways: you render the page in a real browser (Playwright/Selenium) and read cf-turnstile-response, or you hand the sitekey + page URL to a solving service and get a token back. Either way, you then submit the form with a plain HTTP client requests, httpx, axios) and it fails. The frustrating part: it's intermittent-looking. The reason it feels random is that there are four separate constraints, and you're usually tripping a different one each time. The four things a Turnstile token is bound to 1. It's single-use Once Cloudflare validates a token server-side (the siteverify call your target makes), that token is spent. Submit twice, retry, or test it once by hand, and the second use returns false. You get a fresh one per submission. 2. It has a short TTL Turnstile tokens expire fast — a few minutes. Solve early, do other work, submit later, and the token can be dead on arrival. The widget auto-refreshes in the browser precisely because tokens go stale; a script that grabs the token and sits on it loses that refresh. 3. It's bound to the sitekey and the page URL Multiple widgets. Some pages embed more than one Turnstile (login + newsletter). Solving the wrong site