AI 资讯
One Prompt Can Make a Game Demo. That Is Not the Same as Making a Game.
A playable first-person shooter generated from one prompt would have sounded absurd not long ago. Now, videos of AI-built browser games that resemble Call of Duty and Counter-Strike are spreading across social media. On August 10, Axios reported on the rise of “one-shot” AI game prompting : give a model one detailed instruction, let it produce the code, and receive something you can play. This is a real milestone. It is also easy to misunderstand. A one-prompt game can prove that a model knows how to assemble controls, graphics, physics, enemies, and a recognizable game loop. It cannot prove that the result will stay interesting after the first few minutes. The first prompt creates the demo. The decisions after that create the game. Why These Demos Feel So Important Game ideas used to face a large gap between imagination and interaction. You could describe a mechanic, draw a map, or write a design document. But discovering whether the idea actually felt good required code, assets, an engine, and enough technical work to reach a playable build. Prompt-to-game tools are shrinking that gap. This change is not limited to experimental AI demos. Roblox recently announced mobile-first creation tools that turn text prompts into basic games , giving creators a starting point they can playtest, change, share, and publish. That starting point matters. A playable failure teaches you more than a beautiful design document. You can immediately discover that the movement is slow, the arena is empty, the objective is confusing, or the central mechanic is less interesting than it sounded. The value of one-shot generation is not that the first result is finished. It is that the first result arrives early enough to challenge your assumptions. A Recognizable Game Is Not Necessarily a Good Game A model can generate the visible parts of a familiar genre surprisingly well. Ask for a browser FPS and it may produce: First-person movement Weapons and ammunition Enemies that chase or shoot Hea
AI 资讯
Building epilot Apps from your terminal, with a little help from AI agents
A few months ago we shipped the epilot CLI , and it quietly became one of my favorite tools. One command, npx epilot , gives you every single epilot API operation in your terminal: entities, journeys, workflows, pricing, files, permissions, 50+ APIs. Interactive pickers if you're exploring, --json and --no-interactive if you're scripting. It also turned out to be a perfect match for AI agents like Claude. Agents are great at driving CLIs: they discover operations, read the help, make calls, parse the JSON. No custom integration or MCP server needed, the CLI is the integration. And because handing an agent live CRM access is a scary idea, the CLI ships with two safety nets, both enforced server-side: # A session that physically cannot write. The restriction is baked # into the token, so the bearer can't turn it off. epilot auth login --readonly # A token that additionally gets all PII anonymized in every response epilot access-token createAccessToken -d '{ "name": "AI agent token", "read_only": true, "anonymize": true }' Read-only plus anonymized means an agent can explore, analyze and report on your real org all day, and the worst it can do is read data it can't even de-anonymize. Now we've made the CLI even better. On top of the raw API commands, we added app facades : a set of high-level epilot app commands that take you from an empty folder to a working app installed in your org. And that's what this post is really about, because apps are where the fun is. What are epilot Apps? epilot is very configurable out of the box: journeys, workflows, automations, pricing. But at some point every team hits a wall, something the UI simply doesn't offer. A custom tab on the contact or opportunity page showing data from your own systems A whole custom page in the epilot navigation Your own block in the journey builder A widget in the end-customer portal A flow action that calls your API when a workflow step runs An external product catalog or an API proxy to your backend That
AI 资讯
Your rate limiter is broken behind a tunnel — the X-Forwarded-For problem
You put your app behind a tunnel (or any reverse proxy) to test webhooks. Everything works. Then you notice something odd in your logs: every single request comes from the same IP address. Congratulations, you've met the X-Forwarded-For problem. What actually happens When a request flows through a tunnel, the TCP connection to your app comes from the relay, not the real client. So request.remote_addr — the value your framework uses for rate limiting, IP logging, geo-blocking, brute-force detection — is the relay's address. For every request. From every user. The consequences are quiet and nasty: Your rate limiter now rate-limits the relay, not the client. One aggressive user trips the limit and everyone gets blocked. Or worse, the limit is per-IP and effectively unlimited, because each relay node looks like one "user." * Your access logs are fiction. Security review of an incident? Every entry says the same address. * IP allowlists silently break. "Only allow my office IP" now allows nothing, or everything, depending on how it's wired. The fix (and its trap) The proxy already tells you the real client IP — in the X-Forwarded-For header. Every framework has a setting to trust it. Flask: ProxyFix . Express: app.set('trust proxy', ...) . Rails, Django, Laravel: equivalents exist. Here's the trap: trust that header blindly and anyone can spoof it. A client can send X-Forwarded-For: 1.2.3.4 directly, and if your app believes headers from anyone, your rate limiter is bypassed with a curl flag. The correct setup has two halves: 1. Trust `X-Forwarded-For` only when the immediate connection comes from a proxy you control (your tunnel relay, your load balancer). 2. Strip or ignore the header on direct connections. Most frameworks express this as "trusted proxies" — a list of proxy IPs whose forwarded headers you believe. Set it. It's five minutes of config that determines whether your security features are real or decorative. Why this matters more in the tunnel era Tunnels us
开发者
The Pot - a borscht in pure CSS
This is a submission for Frontend Challenge - Comfort Food Edition, CSS Art. ...
AI 资讯
I Built HackForPinas to Make Philippine Hackathons Easier to Discover
In my previous article, I talked about Train Track, the transit app I built around Metro Manila's railway systems. This project started with a completely different problem. I kept thinking about how difficult it can be to discover hackathons and coding competitions. Not because they don't exist. They do. The problem is that they're scattered everywhere. A university might announce one. A government agency might host another. A private company might run one. A developer community might post another. And suddenly you're checking multiple websites just to figure out: What can I actually join? So I built HackForPinas. What is HackForPinas? HackForPinas is a free, public, and open-source directory for Philippine: Hackathons Coding challenges Technology competitions The idea is pretty straightforward: Make opportunities easier to discover. Events can be filtered by: Region Format Organizer type Status Organizers are categorized as: Government University Private Instead of browsing through unrelated websites, users can explore opportunities in one place. But the more I worked on it, the more I realized that the directory itself wasn't the hardest part. The data was. The Data Problem Imagine trying to collect hackathons from different websites. One might have an RSS feed. Another might use WordPress. Another might expose an API. Another might have an ordinary HTML page. And another might not have anything structured at all. So HackForPinas uses multiple scraping strategies: WordPress REST API RSS GDG Community Eventbrite HTML + Cheerio The scraper runs through a background endpoint and collects events from different Philippine technology sources. The interesting part wasn't: "Can I scrape a website?" It was: Can I turn information from completely different sources into one consistent dataset? That became a much more interesting engineering problem. I Didn't Want Anyone to Publish Directly There's another problem with a public directory. If anyone can submit an event, what s
AI 资讯
# I Built My Developer Portfolio as Peter Parker's Lab 🕷️
I could have built another developer portfolio. You know the one. Dark background. Glowing buttons. "Full Stack Developer | AI | Cloud | DevOps" Six project cards. GitHub link. Done. But honestly, that doesn't feel like me. Before I was interested in AI, software engineering, cloud, automation and all the other things I keep breaking and rebuilding, I was just a kid who loved Spider-Man. And the older I got, the more I realized that I didn't actually relate to Spider-Man because he was a superhero. I related to Peter Parker . The curious kid. The awkward kid. The kid who builds things. The kid who experiments. The kid who fails and somehow keeps going. That felt familiar. So when I started building my portfolio, I wanted it to represent that. I called it: 🧪 Peter Parker's Lab The idea is that my portfolio is basically my digital lab. A place where I can show what I'm building, what I'm learning and what I'm experimenting with. 🕷️ Peter Parker → curiosity 🕸️ Spider-Man → persistence 💻 Developer → everything I'm building today And honestly, "lab" describes my development journey pretty well. I build something. It breaks. I investigate why. I fix it. Then I get another idea and break something else. 😂 That's the fun part. I'm currently interested in building things around: AI AI agents automation full-stack applications developer tools cloud infrastructure DevOps local-first software I'm not trying to pretend I've mastered all of it. I'm trying to keep learning by building real things . That's what I want this portfolio to show. Not just a list of technologies. Not just a list of GitHub repositories. But the problems I'm curious about and the things I'm actually trying to create. 🌐 Peter Parker's Lab https://peterparker-lab.vercel.app/ This is version one. I'll keep changing it as I change. New projects. New experiments. New ideas. Probably new bugs too. Because maybe the best portfolio isn't one that says: "Look how much I know." Maybe it's one that says: "Look what I
AI 资讯
CSS Just Got a Parent Selector. Your Forms Will Never Look the Same
For as long as I've been writing CSS, there's been one direction it refused to look: up. You could style a child based on its parent all day long, but the second you wanted a parent to react to something happening inside it — a checked checkbox, an invalid field, a filled-in input — you were reaching for JavaScript. Every time. It didn't matter how small the interaction was. :has() breaks that rule on purpose, and it's been safe to use in production for a while now — it's supported across Chrome, Edge, Firefox, Safari, and Opera, no polyfill required. I didn't fully appreciate what that meant until I rebuilt a form I'd been maintaining for two years and deleted most of the JavaScript in it. Not all of it — I'll get to where it still earns its place — but most. The rule CSS used to have /* This has always worked: style a child based on the parent */ .card.featured .title { color : gold ; } /* This has never worked, until :has(): style the parent based on a child */ .card :has ( .badge--sold-out ) { opacity : 0.6 ; } :has() reads as "select this element, if it contains a match for whatever's inside the parentheses." Once that clicks, a huge category of things people were writing classList.toggle() calls for turns into a single selector. Styling a label when its input is focused This used to mean a focus and blur listener on the input, toggling a class on the label. Now: .field :has ( input :focus ) { border-color : var ( --accent-color ); box-shadow : 0 0 0 3px color-mix ( in srgb , var ( --accent-color ) 25% , transparent ); } Wrap the label and input in a .field container, and the whole field lights up the moment the input inside it gets focus — no listener, no class toggle, and it can never drift out of sync with the actual focus state, because it is the actual focus state. Required-field indicators that can't go stale I've fixed this bug more times than I want to admit: a form gets a field added, and someone forgets to also add the little red asterisk that's suppo
AI 资讯
Astro 7: Rust Compiler, Rust Markdown Pipeline and Vite 8 for Builds Up to 61% Faster
Astro 7 focuses on build performance, utilizing native tooling and a rewritten compiler in Rust. The new version includes faster Markdown processing and stricter HTML rules. Recent updates introduced advanced routing and incremental builds, while issues around legacy file compatibility and dependency counts were raised in feedback. Astro targets content-driven sites with minimal JavaScript. By Daniel Curtis
AI 资讯
Stop Comparing AI Coding Tools by Autocomplete Quality
The biggest mistake in choosing an AI coding tool is comparing autocomplete latency. Cursor and Windsurf are editors with agent abilities. Claude Code works mostly through a terminal on your local repository. GitHub Copilot spans IDEs, GitHub, code review, and a cloud agent. Replit Agent connects generation to a hosted environment where the app actually runs. CodeGeeX provides affordable IDE help for Chinese-language development. They execute in different places. That means a single "best AI coding tool" ranking is a category error — the right question is where the AI should run your work. The four execution models IDE assistants and agentic editors (Cursor, Windsurf, Copilot IDE features, CodeGeeX) stay close to your current edits. Feedback is immediate, and you stay in control of scope. The cost is that complex work still consumes your attention, and two overlapping AI editor subscriptions rarely make sense — run a two-week crossover pilot and keep one. Local terminal agents (Claude Code) read repositories, edit files, and run commands on your machine. This fits debugging, dependency migrations, and test loops. The security docs describe a read-only default with permission requests, and you should keep that default: start read-only, smallest directory, no broad allowlists for network, deletion, or deployment commands. Cloud coding agents (GitHub Copilot cloud agent) work in an ephemeral Actions-powered environment and come back with commits or a pull request. Good for bounded issues, tests, and docs. Budget is not just the seat — AI credits and Actions minutes are separate. Hosted application environments (Replit Agent) go from natural language to a running prototype in the browser. Great for education and proof-of-concept. Test git import/export, database migration, and code export before you depend on it. Quick decision table Primary workflow Evaluate first Main risk Frequent coding inside one AI editor Cursor Editor migration; broad changes still need review Cr
AI 资讯
The Case of the Lying Clock: 5 Vue Mysteries Solved
Every detective has their cold cases. These are mine — five Vue concepts that confused me until I investigated them properly. Grab your magnifying glass. Case #1: The Lying Clock Imagine you set an alarm to go off every 60 seconds. You press start at 10:00:00. First alarm: 10:01:00 — perfect Second alarm: 10:02:00 — still good But your phone is also doing other things: checking email, refreshing weather, running background tasks. Sometimes it fires the alarm a tiny bit late. Third alarm: 10:03:01 (1 second late) Fourth alarm: 10:04:02 (2 seconds off now) Five hours later: your alarm fires at 15:05:12 when it should fire at 15:05:00 That gap growing bigger over time — that's drift . The timer slowly slides away from where it should be. Why Does This Happen? JavaScript runs on a single thread — it can only do one thing at a time. When a timer is supposed to fire, the browser puts it in a queue. But if the thread is busy doing something else, the timer waits. The MDN documentation for setTimeout lists several reasons timers fire late: Nested timeouts are throttled to a minimum of 4ms after 5 levels of nesting (per the HTML5 spec ) Background tabs are throttled to a maximum of once per second ( MDN : "timeouts are throttled to firing no more often than once per second (1000 ms) in inactive tabs") Chrome 88+ introduced intensive throttling for hidden pages: timers that have been hidden for more than 5 minutes are checked only once per minute Tracking scripts in Firefox get even more aggressive throttling: 10 second minimum in background tabs Does This Happen on New Devices Too? Yes, but less. Modern devices are faster, so the delay per tick is smaller — maybe 1-2 milliseconds instead of 10-20. But over hours, even 1ms per tick adds up. And background tab throttling happens on every device, no matter how fast — it's a browser policy, not a hardware limitation. Is This Common Knowledge? It's the kind of thing you learn when your boss says "why does the clock on our dashboa
产品设计
Font Preview Is a Classification Problem, Not a Beauty Contest
An Arabic type preview becomes more useful when it helps a designer classify intent. “Which one looks best?” is too vague. The better question is whether the phrase needs readability, geometry, ceremony, ornament, handwriting energy, or poetic flow. Six familiar script directions make that distinction visible: Naskh, Kufic, Thuluth, Diwani, Ruq'ah, and Nastaliq. They should not be treated as interchangeable decorations. Each changes the apparent density, rhythm, and purpose of the same phrase. Define the intent before rendering A preview UI can ask for a short design intent alongside the text: type PreviewIntent = | ' readable ' | ' structural ' | ' ceremonial ' | ' ornamental ' | ' informal ' | ' poetic ' The mapping is not a legal or historical verdict, but it is a practical starting point. Naskh commonly supports readable, balanced text. Kufic emphasizes geometry and strong outlines. Thuluth benefits from display scale and ceremonial space. Diwani creates dense, flowing ornament. Ruq'ah feels direct and handwritten. Nastaliq's descending rhythm is especially relevant to Persian and Urdu presentation. Preview the real phrase Specimen text hides problems. A designer should render the actual name, quotation, or headline because letter combinations alter width, joins, baseline rhythm, and negative space. The same style can feel excellent for a short name and crowded for a longer sentence. The comparison surface should keep content constant while changing one variable at a time. That makes differences attributable to the font direction rather than color, size, or wording. Respect right-to-left layout Font selection and layout cannot be separated. A preview needs explicit RTL direction, sensible alignment, and enough room for vertical and descending forms. Nastaliq in particular may need more line height than a compact Latin-oriented component assumes. .preview { direction : rtl ; text-align : right ; overflow : visible ; } This looks elementary, but many design tools
AI 资讯
Warning Lines Are an Interface: Reading Bullet-Hell Hazards as Data
In a dense survival game, danger is not communicated only by the projectile itself. The warning that appears before impact is part of the interface. Its direction, duration, width, and overlap with other warnings determine whether a player can make a meaningful decision. No Humanity provides a useful compact example. The reviewed classic build places a tiny ship inside a vertically framed arena and measures survival time while lasers, projectiles, sweeping shapes, doodled faces, and radial bursts occupy the screen. The ship does not visibly attack in the reviewed footage; survival depends on reading hazards early and preserving room to move. Treat every warning as an event A guide or analysis tool can represent a warning with a small event record: type HazardEvent = { source : ' laser ' | ' radial ' | ' sweep ' | ' projectile ' telegraphRegion : Rect impactRegion : Rect leadTimeMs : number escapeSides : Array < ' left ' | ' right ' | ' up ' | ' down ' > } This is more useful than describing a screenshot as “chaotic.” It separates what the player can know before impact from what becomes visible afterward. A fair hazard may be difficult, but it gives the player a readable interval and at least one plausible escape route. Open space has option value Beginners often move toward the largest empty area. That is not always safe. A large pocket can be a trap if a sweep closes its only exit. Smaller central space can be more valuable because it preserves several escape directions. The strategy is therefore not “find empty pixels.” It is “preserve optional movement.” A rough evaluator might score a position by reachable space after the next known impact, not by current distance from a projectile. position score = future reachable area + escape directions - overlapping impact risk This framing explains why early movement matters. Waiting until the projectile is fully drawn converts a route-planning problem into a reaction-time test. Overlap changes the meaning of each signal T
AI 资讯
Designing Puzzle Hints Around Blockers, Not Tap Sequences
A weak puzzle walkthrough records every input. A stronger one explains why the board refuses to move. That distinction matters in traffic-sorting puzzles, where a correct tap can still be useless if a garage exit, crossing lane, or temporary holding space remains blocked. I used Car Sort level 13 as a small case study for a better hint model. The useful unit is not “tap car number seven.” It is a dependency: this vehicle cannot leave until that lane opens; that lane cannot open until a matching garage accepts its front car. Model the board as dependencies The visible board can be represented as a directed graph. Cars and blockers are nodes. An edge from A to B means A must move before B becomes actionable. The graph does not need to reproduce the game engine. It only needs to describe the decisions a player can verify on screen. type MoveNode = { id : string color : string blockedBy : string [] releases : string [] checkpoint : string } This structure makes a hint resilient. If a player has already cleared one harmless car, the guide can still say, “restore the center exit, then release the stack behind it.” A memorized tap list often becomes useless as soon as the board differs by one move. Separate release moves from cleanup moves Puzzle solvers tend to treat every successful departure as equal. They are not equal. A release move changes the dependency graph by opening a lane or exposing a buried color. A cleanup move removes a car that was already free. Good guidance labels those roles explicitly. The player should know whether the current move creates new options or merely reduces clutter. That is especially useful on compact boards, where an attractive matching car may tempt the player even though it does not improve the central bottleneck. The reserved route for this analysis is documented as Car Sort puzzle help . The value of that page is its focus on visible blockers and release points rather than an unexplained command stream. Add visual checkpoints After
AI 资讯
A Lower Price Tag Is Not a Migration Plan: Quarantining New Models Before They Touch Your Agent
Last month a model I'd been watching dropped its token price by half, and three people sent me the announcement within an hour. The implied question was always the same: when are you switching? My answer, these days, is: after it survives quarantine. Because the last time I swapped a model based on announcement-day excitement, everything looked fine for nine days. Then a scheduled job started emitting subtly malformed JSON — valid enough to parse, wrong enough to corrupt downstream state — and I spent a weekend reconstructing which records had been poisoned. The money I saved on tokens wouldn't cover one hour of that cleanup. The economics of model swaps are lopsided. The upside is small and predictable (cheaper tokens). The downside is unbounded and sneaky (behavioral regressions in edge cases your happy-path tests never exercised). So I built a pipeline that treats every new cheap model like an untrusted dependency with an attractive changelog: it gets isolated, probed, and graduated in stages. Here's the whole thing. What the pipeline needs (and what it doesn't) Three ingredients: candidate model access, somewhere disposable to run the evaluation, and checks that don't require a second LLM to grade the first one. For model access and the throwaway compute, I'm currently using MonkeyCode's free model access together with its free server option — bursty evaluation workloads are exactly the kind of thing I'd rather not attach to a production billing account. Disclosure: This article was prepared as part of MonkeyCode's product outreach. Nothing in the pipeline below is tied to that provider, though. Every endpoint is an environment variable, and I'd encourage you to wire it to whatever you're actually evaluating. I want to be explicit about two things I'm not assuming: that any particular model is on the free tier when you read this, and that any free offering stays available forever. Treat free infrastructure the way you treat a library's latest tag — convenient, n
AI 资讯
SPF, DKIM, and DMARC together — why the missing DMARC record was blocking registration emails
Background Registration confirmation emails were not reliably reaching users on Gmail and Outlook outside Japan — sometimes landing in spam, sometimes not arriving at all. Investigation pointed to a single root cause: the wpmm.jp domain had SPF and DKIM configured, but no DMARC record . What each of the three does SPF (Sender Policy Framework) declares in DNS which IP addresses are authorized to send mail for a domain. Receiving servers check the sending IP against the SPF record to confirm the source is legitimate. DKIM (DomainKeys Identified Mail) adds a cryptographic signature to the message headers and body. The receiving server looks up the public key in DNS and verifies that the message has not been tampered with and was signed by a party controlling that domain. DMARC (Domain-based Message Authentication, Reporting and Conformance) sits above both. It tells receiving servers what to do when SPF and DKIM alignment fails, and it collects aggregate reports about how mail from the domain is being treated. The key point is that SPF and DKIM are independent checks. Without DMARC, there is no single authoritative statement about how the alignment result should influence delivery decisions. Major providers including Gmail weigh the absence of DMARC when scoring incoming mail. Adding the DMARC record The following TXT record was added to the wpmm.jp DNS: _dmarc.wpmm.jp TXT "v=DMARC1; p=none; rua=mailto:info@wpmm.jp" p=none means "collect data, but do not reject or quarantine mail that fails alignment." Starting with p=reject or p=quarantine risks blocking legitimate mail if DKIM alignment turns out to be misconfigured somewhere. The safe approach is to start with p=none , monitor the reports, and tighten the policy gradually. rua=mailto:info@wpmm.jp sets the destination for aggregate reports. Google and other receivers periodically send XML summaries showing which mail passed or failed SPF/DKIM alignment. This moves visibility from passive (you notice when users compl
AI 资讯
Docker no dia a dia - comandos essenciais e primeiros containers reais
1. Retomando: de imagens a containers em execução Na primeira parte desta série vimos o que é o Docker, o problema que ele resolve e os três conceitos fundamentais — imagens, containers e registries. Agora que a base teórica está posta, o foco deste artigo é prático: os comandos que efetivamente viram hábito no uso diário — run , exec , logs , ps , build — aplicados a containers reais, não só ao hello-world . 2. docker run além do básico O artigo anterior já usou docker run para subir um Nginx. Vale conhecer as flags que aparecem o tempo todo: # Modo interativo, útil para explorar uma imagem manualmente docker run -it ubuntu bash # Variáveis de ambiente docker run -e POSTGRES_PASSWORD = segredo -d postgres # Montar um diretório do host dentro do container (volume bind mount) docker run -v $( pwd ) /dados:/dados -d minha-imagem # Remover o container automaticamente quando ele parar docker run --rm -it python:3.12 python3 # Limitar recursos docker run --memory = 512m --cpus = 1 minha-imagem -it combina -i (interativo, mantém STDIN aberto) com -t (aloca um pseudo-terminal) — é o par de flags para "entrar" em um container e usar um shell como se fosse uma máquina normal. --rm evita acumular containers parados no disco depois de testes rápidos e descartáveis — sem ela, cada docker run deixa um container parado para trás até ser removido manualmente. -e define variáveis de ambiente; imagens oficiais como a do Postgres costumam documentar quais variáveis elas esperam (usuário, senha, nome do banco inicial). 3. Inspecionando o que está rodando O comando mais usado para ter uma visão geral do que o Docker está gerenciando na máquina: docker ps # containers em execução docker ps -a # todos, incluindo parados docker ps -q # só os ids (útil em scripts) Para investigar um container específico mais a fundo: docker inspect meu-container # todos os metadados em JSON: rede, volumes, config docker top meu-container # processos rodando dentro do container docker stats # uso de CPU/mem
AI 资讯
Architecting a Real-Time Collaborative Task Board
Building rich, collaborative web interfaces today requires much more than simply rendering components to the DOM. It demands rigorous planning around rendering performance, deterministic state management, and network resilience. In this article, I will break down the architectural decisions and trade-offs behind a real-time, enterprise-grade Kanban Board. Built with React 19, Vite, TypeScript, and Tailwind CSS, this application is designed to handle high data volumes via virtualization while maintaining a bulletproof, offline-first architecture. 🚀 Live Demo System Architecture: The Smart/Dumb Paradigm To guarantee scalability and testability, the application strictly adheres to the Container/Presentational (Smart/Dumb) design pattern. This ensures absolute separation of concerns. Containers (Smart): Orchestrate state access via Zustand, handle asynchronous actions, and manage event listeners. Presentational Components (Dumb): Pure, stateless functions exclusively concerned with UI rendering and accessibility. They receive data strictly via props. Unidirectional Data Flow: State mutations propagate downward from the global store, ensuring predictable render cycles. Here is the architectural topography of the system: Quality Attributes (NFRs) and Technical Decisions To ensure this MVP could scale into a production-ready product, the system was designed around strict Non-Functional Requirements (NFRs). Performance: DOM Virtualization & React 19 Paradigms Rendering 1,000+ DOM nodes concurrently destroys the framerate of standard React applications. We implemented client-side virtualization via @tanstack/react-virtual. By recycling DOM nodes and dynamically measuring element heights, the browser only renders the exact cards visible within the viewport, maintaining a steady 60fps during complex Drag-and-Drop operations. Furthermore, this codebase natively embraces React 19. It intentionally omits manual memoization (useMemo, useCallback, React.memo), relying entirely on t
AI 资讯
JWT auth without the confusion
The mental model that fixes everything JWT is just a token format . It is not authentication, not a session, and not a database. Once you separate those ideas, most of the pain disappears. A JWT is a JSON object that is signed. That's it. The payload holds claims like sub (subject) and exp (expiration). The signature proves the token wasn't tampered with. What JWT is not Not a session store : You can't revoke a JWT before it expires. If you need revocation, you need a blocklist or short expiry. Not a database : Don't stuff heavy data in the payload. It gets sent on every request. Not a magic bullet : It's a way to pass claims between parties without a shared server-side state. The three flows that matter 1. Access token only Simplest flow: login returns a JWT, client sends it in the Authorization header, server verifies it on every request. // server middleware (Express example) const jwt = require ( ' jsonwebtoken ' ); function auth ( req , res , next ) { const header = req . headers . authorization ; if ( ! header ) return res . status ( 401 ). json ({ error : ' No token ' }); const token = header . split ( ' ' )[ 1 ]; // Bearer <token> try { req . user = jwt . verify ( token , process . env . JWT_SECRET ); next (); } catch ( err ) { res . status ( 401 ). json ({ error : ' Invalid token ' }); } } Works fine for small apps, but every request hits your auth logic and the token can't be invalidated early. 2. Access + refresh token Common pattern for SPAs. Access token lives 15 minutes, refresh token lives 7 days. The refresh token is stored securely (httpOnly cookie) and used only to get a new access token. // issue tokens on login const accessToken = jwt . sign ({ userId }, process . env . JWT_SECRET , { expiresIn : ' 15m ' }); const refreshToken = jwt . sign ({ userId }, process . env . REFRESH_SECRET , { expiresIn : ' 7d ' }); res . json ({ accessToken }); res . cookie ( ' refreshToken ' , refreshToken , { httpOnly : true , secure : true , sameSite : ' strict ' })
开源项目
ARCLUX 🐳 — a codebase intelligence tool that refuses to guess published: false
If you've ever stared at a 15,000-file monorepo wondering "what actually breaks if I touch this...
开发者
Your `fetch()` in `beforeunload` is being silently dropped. Use `navigator.sendBeacon()`.
When a user closes a tab, submits a form, or clicks an external link, you often need to send one last...