React 19 Actions: I Explained 3 Hooks Without Ever Explaining What an Action Is
Three parts into this series, and if you'd asked me to define the word sitting underneath every...
找到 2656 篇相关文章
Three parts into this series, and if you'd asked me to define the word sitting underneath every...
Let me describe a moment you might recognize. You had an idea for an app. You didn't know how to...
When you're building a multi-tenant app, there's one bug category that's worse than any other: a user seeing someone else's data. Not a crash, not a broken button — a genuine privacy failure. I recently built a Next.js + Supabase + Stripe starter kit, and before I'd call the database layer "done," I wanted to actually prove the security held, not just assume it did because the code looked right. The setup Supabase's Row Level Security (RLS) lets you write policies directly in Postgres that filter rows based on who's asking — the database itself becomes the security boundary, not just your application code. That's powerful, but it also means a single wrong policy (or a missing one) silently exposes everything. Here's the policy pattern I used for an owner-scoped table: create policy "projects: owner reads" on public . projects for select to authenticated using (( select auth . uid ()) = owner_id ); Simple enough. But "simple enough" is exactly the kind of thing worth verifying rather than trusting. The actual test Rather than just trusting the policy, I ran an impersonation test directly in the SQL editor: begin ; set local role authenticated ; set local request . jwt . claims = '{"sub":"<a-real-user-id>"}' ; select id , name from public . projects ; rollback ; This temporarily pretends to be a specific user (inside a transaction that touches nothing) and asks: what can this user actually see? Run it with the real owner's ID — you get their rows. Run it with a fake or different user's ID — you should get zero rows back, not an error. That's the key detail: RLS filters rows out silently rather than throwing a permission error, so "nothing happened" is actually the success signal, not a bug. I did the same check for writes — attempting to delete another user's row from a different account's session, confirming it returns 0 rows affected rather than either succeeding or throwing. Why this matters more than it seems It's easy to write an RLS policy that looks right and s
Most teams don't notice their CI/CD pipeline is broken — they just notice that deploys "feel slow" and shrug it off as normal. It isn't. A pipeline that takes 25 minutes to ship a one-line copy change isn't a fact of life, it's a symptom. Here are the mistakes we see most often when reviewing pipelines — roughly in order of how much time they silently burn. 1. Running the full test suite on every single change If a developer fixes a typo in a README and the pipeline still runs the entire integration suite, database migrations, and end-to-end tests, you're paying full price for a change that touched nothing critical. Fix: split your pipeline into stages based on what actually changed. Path-based triggers (only run frontend tests if frontend files changed) and a fast "smoke test" tier before the full suite can cut average pipeline time dramatically without sacrificing safety. 2. No caching between builds Reinstalling every dependency from scratch on every run is one of the most common — and most fixable — sources of wasted time. Package managers, build artifacts, and Docker layers are all cacheable, and most CI platforms support this natively. Fix: cache dependency directories keyed by lockfile hash, and structure Dockerfiles so rarely-changing layers (base image, dependencies) come before frequently-changing ones (application code). 3. Sequential steps that don't need to be sequential Linting, unit tests, and security scans are often run one after another when they have no dependency on each other. That's pure wasted wall-clock time. Fix: parallelize independent jobs. Most CI systems support fan-out/fan-in patterns — run lint, test, and scan simultaneously, then gate the deploy on all three passing. 4. Environments that drift from production A pipeline that passes in staging and fails in production usually means the environments aren't actually equivalent — different env vars, different resource limits, different service versions. Teams respond by adding more manual
If you ever needed a quick list of Docker commands, here you go.. 1. Check that Docker is installed docker --version Shows the installed Docker version. 2. Run your first container docker run hello-world Pulls the official test image (if needed) and runs it. You should see a “Hello from Docker!” message. 3. See running containers docker ps Lists containers that are currently running. Use docker ps -a to also show stopped ones. 4. See downloaded images docker images Shows every image on your machine (name, tag, size, ID). 5. Stop a running container docker stop CONTAINER_ID Gracefully stops a container. Get the ID from docker ps . 6. Remove a stopped container docker rm CONTAINER_ID Deletes a container that is already stopped. 7. Force stop and remove docker rm -f CONTAINER_ID Force-stops the container (if it’s still running) and removes it in one step.
In this post I'll show how to merge PDF files entirely in the browser using PDF.js and pdf-lib — no server, no file upload, no backend. Everything runs on the user's machine, which is great for privacy and for keeping hosting costs at zero (it's just a static site). Why process PDFs on the client? Most "free" PDF websites quietly upload your documents to their server, which: Exposes private/sensitive files to third parties Imposes size limits Often slaps a watermark on the output Requires you to trust their storage If you handle PDFs with client-side JavaScript (WebAssembly / WASM + PDF.js), none of that happens. The user's file never leaves their device, and you don't need a backend at all — so it's cheap and private. Caveats pdf-lib works well with standard PDFs; heavily encrypted or unusual documents may need extra handling. Very large PDFs are memory-hungry since everything is client-side, but for typical documents it's fast and free. Some complex PDFs with unusual fonts can lose fidelity — test on your own files first. Try it I packaged this approach (plus split, compress, rotate, unlock, image-to-PDF) into a free no-upload tool: https://yourutilityhub.com/pdf/merge-pdf The whole project is open source: https://github.com/Jalal-khn/utilityhub- If you have questions about the architecture or want a deeper dive on any part, ask away. The basic idea Read the input file with FileReader Parse it with pdf-lib (a pure-JS PDF library) Copy the source pages into a new document Save the merged PDF and trigger a download Here's the core function: js import { PDFDocument } from "pdf-lib"; async function mergePdfs(files) { const merged = await PDFDocument.create(); for (const file of files) { const bytes = await file.arrayBuffer(); const src = await PDFDocument.load(bytes, { ignoreEncryption: true }); const pages = await merged.copyPages(src, src.getPageIndices()); pages.forEach((page) => merged.addPage(page)); } const out = await merged.save(); return new Blob([out], { typ
We've all heard the modern blogging advice: "Keep it short, write for humans, don't write novels." While short-form content has its place, long-form technical guides—when executed correctly—are absolute powerhouses. Writing a comprehensive, 5,000-word deep dive isn't about padding your word count with fluff; it's about building an irresistible, authoritative resource that search engines and developers simply cannot ignore. If done right, a single 5,000-word post can push your brand-new domain to a Domain Rating (DR) of 20 , pull in 1,000+ daily organic views , and land your site directly inside Google's AI Overviews (Position 0) . Here is the exact framework to pull this off. 1. Why 5,000 Words Still Works (When Done Right) Long-form content isn't dead— shallow long-form content is . When you cover a complex technical topic thoroughly, three things happen: High Information Gain: You answer questions that 500-word summaries skip over. Natural Keyword Spreading: You rank for hundreds of long-tail queries without keyword stuffing. Passive Backlink Generation: Developers, bloggers, and tech curators link to comprehensive references instead of surface-level posts. 2. Targeting the Right Topic You can't write 5,000 words on "How to install Node.js." You'll run out of meaningful things to say by page two. To sustain this length and quality, choose topics that have depth, high friction points, and multiple moving parts . Ideal Candidates: The Ultimate Architectural Guide: e.g., "Building a Multi-Tenant Microservices Architecture with Go and PostgreSQL" End-to-End Production Blueprints: e.g., "From Zero to Production: Deploying Next.js, Redis, and Prisma on AWS EKS" Comprehensive Comparative Deep Dives: e.g., "State Management in 2026: An In-Depth Benchmark of Redux, Zustand, Jotai, and Signal" 3. How to Structure for Readability & Google AI (Position 0) Google’s AI Overviews look for clean, structured answers to extract directly into Position 0. If your post is an unorganiz
How we eliminated subtle stylesheet caching glitches during deployments on DEV without a massive rewrite.
We use AI daily to scaffold code, write copy, and debug layouts, but when it comes to maintaining a strict design system, things get blurry. You can feed an LLM your component library rules, spacing scales, and color tokens, but it still loves to hallucinate random padding values or invent arbitrary classes if you aren't paying close attention. It's great for writing boilerplate, but bridging the gap between a strict visual token structure and AI-generated code often feels like managing a junior dev who ignores the style guide. How are you integrating AI into your workflow without letting it compromise your design tokens and codebase consistency? Do you use it mainly for initial scaffolding, or have you found a reliable way to keep it strictly aligned with your system? If you're into clean design systems, frontend code, and bridging the visual-to-code gap, check out my work at Joemetry.
Hello, I'm Maneshwar, and I'm building LiveReview — a blast-radius aware AI code review built for...
If you ship anything that touches AI-generated images — a thumbnail pipeline, a user-upload feature, a design tool — you've probably noticed something: the images your model spits out are heavier than they should be, and they carry baggage you never asked for. That baggage is provenance metadata. Modern generators (GPT Image / DALL·E, Google's Nano Banana / Gemini, Midjourney, many hosted Stable Diffusion endpoints) stamp each output with tags that mark it as machine-made. Some of it is harmless. Some of it survives a Photoshop round-trip. And most developers have no idea it's even there until a downstream platform flags an image or a QA person asks "why does this PNG have a certificate chain in it?" This is a hands-on guide to seeing that metadata and removing it — from the CLI, from Node, from Python, and (when you just want it gone) from the browser. What actually gets embedded There are four layers worth knowing about, because they don't all come off the same way: EXIF fields — the classic camera-metadata block. Generators repurpose fields like Software , ImageDescription , or a custom Make / Model to identify themselves. Trivial to read, trivial to strip. XMP packets — an XML blob (Adobe's format) holding richer provenance: model name, generation timestamp, sometimes a prompt hash. Lives in its own segment of the file. C2PA manifests — the interesting one. The Coalition for Content Provenance and Authenticity standard embeds a cryptographically signed manifest (stored in a JUMBF box) that records the asset's origin. Because it's signed, it's designed to be tamper-evident — which also means naive metadata strippers often miss it. Pixel-level watermarks — e.g. SynthID-style signals baked into the pixels themselves. These are not metadata at all; no EXIF tool touches them. (More on the limits below.) The mistake I see repeatedly: someone runs a one-liner that clears EXIF, sees "no EXIF" in their viewer, and assumes the image is clean. The C2PA manifest and XMP pac
Our tool transcribes audio in the browser — Whisper running locally via transformers.js , no upload. It worked fine, until analytics showed something too clean to be a coincidence: .mov uploads on mobile failed 100% of the time. Not 90%. Every single one. Desktop had never reported a single .mov failure. This is what I found, and how it got fixed without pulling in ffmpeg.wasm or WebCodecs. The 30-second reproduction I took one AAC audio track and put it in two containers — same encoder, same bytes for the audio itself, only the wrapper differs: const buf = await file . arrayBuffer (); await new AudioContext (). decodeAudioData ( buf ); On an iPhone 17 Pro simulator (iOS 18.7 / Safari 26.5): File iOS Safari Chromium sample.mov (ftyp qt ) EncodingError: Decoding failed OK sample.mp4 (ftyp isom ) OK OK So it isn't the codec. It's the container. The obvious fix that doesn't work First instinct: it's the brand in the ftyp box. Patch qt → isom , four bytes, done. It still fails. I'm writing this down so nobody else burns an afternoon on it. The ftyp brand is not what Safari looks at. The difference lives inside moov . The actual root cause Dig down to moov → trak → mdia → minf → stbl → stsd — the sample description that tells the decoder how the audio is encoded. Both files carry an mp4a entry. They are not the same mp4a entry: QuickTime writes: MP4 expects: version = 1 <— version = 0 compressionID = -2 (fffe) <— compressionID = 0 + 16 bytes of v1 extension <— (absent) esds wrapped in a 'wave' box <— esds is a direct child extra 'chan' channel layout (absent) iOS Safari's decodeAudioData only accepts a version 0 audio sample entry. Chromium accepts both — which is exactly why desktop never saw this and mobile never survived it. That version field is a uint16 . Two bytes decide whether the file plays. The fix: rebuild the container, don't touch the codec Since the audio bitstream is already valid AAC, nothing needs to be re-encoded. The job is pure byte plumbing: extract
Creating a repeating pattern sounds simple: place the same image side by side and export it. In practice, the edges rarely match. Visible seams appear, previews become difficult to inspect, and exporting large repeated layouts can quickly become tedious. That’s why I built Seamlessify, a browser-based tool for turning ordinary images into seamless, tileable patterns. What it can do The free direct-stitching workspace lets you: Upload PNG, JPG, or WebP images Use direct stitching or blend visible edges Preview the result as a 3×3 repeating pattern Zoom in to inspect boundaries Control repeat count and output dimensions Process multiple images in batches Export PNG, JPG, or ZIP files The direct-stitching workflow runs in the browser and doesn’t require an account. Optional AI pattern generation I also added an AI workspace for creating new seamless images from: A text prompt A reference image A reference image combined with written instructions Generated images can be downloaded directly or sent into the stitching workspace for additional processing, cropping, and batch export. The goal was to keep the workflow simple: generate or upload an image, inspect the repetition, make adjustments, and export—all without jumping between several different tools. Why I built it Many existing pattern tools are either too limited for batch work or include complicated controls that make a small task feel much bigger than it should. Seamlessify is designed to stay approachable for designers, print-on-demand creators, textile projects, wallpapers, backgrounds, packaging, and game assets. You can try it here: 👉 https://seamlessify.com I’d love to hear what formats, controls, or workflows would make it more useful for your projects.
Publora is in the Zapier app directory now. I didn't do it to tick a box on some distribution list. My job is making our product easy to live with, and if a user has an agent that can wire us in deeper so they don't have to build the plumbing themselves, I'll go make that happen. Zapier is exactly that case: it connects Publora to thousands of other apps, so nobody has to hand-roll the integration. Worth it. I'd just add that "a no-code platform" and "publishing your own app on a no-code platform" turn out to be two very different Zapiers. Prove it works for users who don't exist yet Here's the requirement I reread three times, sure I'd misunderstood. To submit an app for review, every trigger, every action, and every search has to be tested inside a live Zap, turned on, with at least one successful run in the history. You can't delete those Zaps; the reviewer can ask to see them. So the logic goes like this. You want to publish an app so people can start using it. But to publish it, you first have to prove it's already being used. Run every component for real, as if you had the users you're publishing it to attract. The app isn't in the directory yet, and a history of real use already has to exist. You end up standing in for your own users who aren't there yet. You build the Zaps, run each one, make sure every one has a green run, and don't touch them afterward. A routine task you run like a rocket launch The second surprise. My tasks here are the plain ones: schedule a post, publish a post, delete a post. This isn't a satellite launch. It's what our API does a thousand times a day over one line of code. As a Zapier app, each of those ordinary tasks has to be wrapped, configured, and run live on its own. Create Post, Update Post, Delete Post, two triggers, two searches, each with its own test run under the validator's eye. Scheduling a post is something I can describe in one sentence. Here it became a component with a run history. Then the small surprises a "no-cod
Almost every codebase I've inherited sends email the same way: somewhere inside a POST handler, between the database write and the response, there's an await on the mail provider's SDK. It works, for months. Then one afternoon your signup endpoint starts timing out, and it takes an hour to work out that the cause is your email provider having a bad day three thousand kilometres away. I build Pulsenote , a transactional email API, so I've spent an unreasonable amount of time in the space between "your API call returned 200" and "the message is in the inbox". This post is what lives in that gap, why a try/catch doesn't cover it, and where the line sits between "you need a pipeline" and "you're overengineering a side project". The naive version Here's the code. You've written this. // users.controller.ts @ Post ( ' signup ' ) async signup (@ Body () dto : SignupDto ) { const user = await this . users . create ( dto ); await this . mailer . send ({ to : user . email , subject : ' Confirm your email ' , html : renderConfirmation ( user ), }); return { id : user . id }; } Nine lines, obvious intent, no infrastructure. For a lot of applications this is genuinely the right answer, and I'll come back to that at the end. But let's be precise about what it costs, because "it's fine" and "I haven't measured it" are different statements. It puts a third party in your request path. Your p99 for POST /signup is now your p99 plus the provider's p99. Not their median — their tail. A slow provider becomes a slow endpoint, then no endpoint. This is the failure mode that actually takes services down. If the provider degrades to five seconds per call, every signup request holds a connection and an event-loop continuation for five seconds. Your connection pool fills, your load balancer queues, health checks fail, the pod gets restarted, and now you're down — because of email. The blast radius of a non-critical dependency became the whole endpoint. A provider 5xx loses the mail entirely.
Hi everyone! 👋 I just published v1.0.0 of my very first web project: an installable Progressive Web App (PWA) built from scratch using HTML, CSS, and Vanilla JavaScript. I designed it to be extra accessible and versatile, especially for older adults, shopkeepers, students, and everyday users. ✨ Key Features Multiple Modes: Standard, Scientific, Sales, Interest (Simple & Compound), Unit Conversions, BMI, Age, and Adjustable Percentage. Customization: Adjustable button sizes and custom background themes (including Dark and Light modes). Offline PWA Support: Service worker caching allows full offline functionality and direct installation on mobile or desktop devices. 🔗 Try It Out & Explore Code 🚀 Live Demo: mdalif027-tech.github.io/easy-calculator 💻 GitHub Repository: github.com/mdalif027-tech/easy-calculator 💬 Looking for Feedback Since this is my first app, I would really appreciate any thoughts on: Mobile touch layout and responsiveness across screen sizes. UI/UX design or theme improvements. Recommendations for features to add in future releases. Thank you so much for checking it out!
Building a website still involves a lot of repetitive work: planning the layout, writing content, generating code, connecting forms, and finally deploying everything. I built Waspes to automate as much of this workflow as possible. A user describes the website they want, and the platform generates the structure, content, visual elements, and functional components. The interesting part is that Waspes doesn't only generate a visual mockup. Generated websites can include working contact forms and can be published directly to a waspes.com subdomain with HTTPS. Waspes can also generate detailed prompts for tools like Claude, Cursor, v0, and Lovable, making it possible to use the generated specification as a starting point for further development. The goal is simple: turn an idea into a working website with as little friction as possible.
Sometimes software development reaches a point where the tools designed to make your job easier start becoming part of the job. I ran into that with browser debugging. I wanted something that should have been simple: Set a breakpoint. Press F5. Debug my JavaScript. Instead, I found myself spending too much time thinking about development servers, browser launch configuration, debugger connections, ports, profiles, and the debugging environment itself. That led to a simple question: What if browser debugging could go back to convention over configuration? So I built CloudIDEaaS JavaScript Debugger . ⚡ The Goal: Press F5 and Debug The philosophy behind CloudIDEaaS is straightforward: Spend your time debugging your application instead of debugging your debugging environment. For a straightforward JavaScript or HTML project, I wanted the workflow to look like this: Set a breakpoint. Press F5 . Start debugging. Behind those three steps, CloudIDEaaS can start the local web server, launch Chrome, establish the debugging connection, configure your breakpoints, and then load the application. The important part is that you don't have to think about most of that. 🔴 Real Debugging Inside VS Code This isn't intended to replace Chrome DevTools or compete feature-for-feature with every large JavaScript debugging platform. It's focused on providing the debugging features I use most often directly inside Visual Studio Code: 🔴 Source and conditional breakpoints 👣 Step over, step into, and step out ▶️ Continue and pause 🔍 Local variables and object inspection 📚 Scopes and call stacks 🧮 Expression evaluation ⚠️ Exception breakpoint configuration 🌐 A built-in local web server One feature that was particularly important to me was startup breakpoints . The debugger establishes the connection and configures your breakpoints before loading the application, making it possible to catch JavaScript that executes during startup. 🧠 What's Actually Happening Under the Hood? Building the debugger a
NoRespawn — Gaming Community Forum Join No Respawn — a community built for gamers to share their best clips, swap strategies, get help, discover new tricks, and talk about the games they love across every genre. norespawn.space
Retries are one of those things that look harmless until the first time they duplicate a real business operation. A request times out, so the client retries it. Reasonable. But what if the first request actually reached the server? What if the application already created the order, reserved the stock, sent the message, or called a payment provider — and only the response was lost? From the client's point of view, the request failed. From the application's point of view, it may already be finished. Send the same request again and you can get the worst kind of bug: one that is technically understandable, difficult to reproduce, and very expensive in production. This is the problem that pushed me to build HttpIdempotencyBundle , a small Symfony bundle for explicit HTTP request idempotency. But the interesting part is not the bundle itself. The interesting part is everything that has to be true before we can safely say: "This request is a retry of the same operation, so we should not execute it again." And just as importantly, what we cannot guarantee. A timeout does not mean the operation failed Consider a simple endpoint: #[Route('/orders', methods: ['POST'])] public function createOrder (): JsonResponse { $order = $this -> orderService -> create (); return new JsonResponse ([ 'id' => $order -> getId (), ], 201 ); } Now imagine this sequence: Client -> POST /orders Server -> creates order #742 Server -> sends 201 response Network -> connection dies Client -> sees timeout Client -> retries POST /orders Nothing unusual happened. The client did exactly what clients often do after a timeout. The server did exactly what it was asked to do. And yet, unless we have another mechanism in place, we may now create order #743 as well. The key idea is simple: transport failure and business-operation failure are not the same thing. HTTP cannot always tell the client whether the operation happened. Give the operation an identity A common solution is an Idempotency-Key . The client g