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

标签:#node

找到 224 篇相关文章

AI 资讯

A Quality Gate for Node.js SaaS Text Summarization Chat APIs

Choose a text-summary API by the percentage of outputs that pass a source-grounded evaluation, then compare latency, regional controls, and cost only among the candidates that clear that bar. For a JavaScript subscription app serving US and EU users, the decisive constraint is rarely the cheapest advertised token rate. It is the complete production path: cleaning an article, fitting or splitting it, generating a summary, validating claims, and recovering safely when a request is interrupted. Short answer: use a narrow internal completion interface, test it with representative long documents, and keep the provider choice behind an adapter. A direct hosted endpoint is the simpler default for one approved backend. Add a self-hosted gateway only when routing, policy enforcement, or repeated provider comparisons justify another service to operate. I start this kind of decision in a notebook, but I don't stop at a few outputs that sound good. Fluent summaries can omit the one qualification that changes an article's meaning. The useful unit of comparison is an accepted summary, not a successful API response. What should a US and EU text summary API evaluation measure? Define acceptance before sending the first request. For a long article, I usually want a short abstract, the central claims, preserved numbers, and explicit uncertainty where the source is uncertain. Those fields form an output contract. The evaluator then asks whether each claim is supported by the input and whether any required idea disappeared. Build the corpus from document shapes the product expects: clean prose, copied navigation, tables flattened into text, repeated paragraphs, empty sections, contradictory statements, and inputs near the application's size limit. Keep a held-out slice for release decisions. Otherwise prompt tuning turns the evaluation set into a memory test. The regional review belongs beside quality, but it answers a different question. An API being reachable from Europe does not est

2026-08-21 原文 →
AI 资讯

AWS SNS and Dedicated SMS APIs for Critical Node.js Alert Delivery

An e-commerce alert is not complete when an API accepts a message. It is complete when the application records a terminal delivery state, suppresses an invalid recipient, or escalates through a separately governed channel. Short answer: use a dedicated SMS API for a small critical-alert worker when template ownership and direct status control matter; keep AWS SNS when SMS belongs inside an existing cloud messaging stack, and prefer a callback-capable provider when escalation must begin in under a minute. That choice creates work. A direct API keeps the send path narrow, but polling, retries, dead-letter handling, and country-specific fallback rules remain application responsibilities. For critical alerts, those responsibilities need the same idempotency and audit discipline as a ledger entry: one intent, one durable identifier, and an append-only record of every state observation. No provider turns carrier delivery into exactly-once delivery. Implement the template control plane in Node.js Start with the contract, not the vendor. The application owns an immutable alert intent containing the business event ID, recipient, template version, jurisdiction, and escalation deadline. Template ownership is the decision axis: if compliance reviewers must approve and reproduce the exact text that was sent, keep the canonical template version in the application and treat a provider template ID as deployment metadata. If a provider must own localization or regulatory registration, record that provider template ID beside the application version rather than letting it become invisible configuration. A useful state machine separates accepted from a terminal delivery result. Persist the provider message ID after the initial send, schedule periodic status reads, and append each observation with its timestamp and request ID. A retry after HTTP 429 is transport recovery, not permission to create a second alert; honor Retry-After , use exponential backoff, and preserve the same idempote

2026-08-21 原文 →
AI 资讯

Node.js Welcome Flow Explained — Custom-Domain Email API Suppression, DKIM, Polling

Short answer: for a healthtech marketplace seller alert, choose an email API with custom-domain DKIM, a pre-send suppression check, and an event list that a scheduled job can poll. Keep the notification outside the order transaction. This design fits a standard US/EU SaaS workflow when delayed delivery status is acceptable; if delivery events must drive application state within seconds, choose a webhook-capable provider instead. The decision is mostly about integration effort, but counting SDK setup hours is too narrow. Count the controls the team will still own after launch: credentials, domain gates, retry identity, callback ingress, poll cursors, retention, and vendor-specific telemetry. A short integration can leave a long operational tail. This record covers a transactional notice that tells a marketplace seller about a new order. It does not establish that clinical data belongs in the message, or that a provider satisfies a regulated workload. I'm not sure an API feature matrix can answer those questions; current contracts, residency terms, and a review of the actual message fields would. How does a US/EU SaaS welcome email API handle custom domain DKIM and suppression? The order and its notification need different state machines. Committing an order is a business event. Checking suppression, submitting email, and later observing delivery are communication work. If those concerns share one transaction, a slow provider call can hold the order path open, while a retry can blur the difference between “the order exists” and “the seller was notified.” Use four invariants to evaluate every candidate. First, a suppressed or opted-out address never reaches the send step. Second, production mail is enabled only after the custom domain is verified and DKIM is managed. Third, every retry refers to the same logical seller-order notification. Fourth, processing the same polled event twice cannot repeat an application state change. Those rules are deliberately boring. They

2026-08-21 原文 →
AI 资讯

Your agent isn't reckless. It just can't see the blast radius.

I've been running Claude Code as a daily driver for about three months now. It writes Ansible I'd have taken a week to write. It reads a codebase faster than I do. It is, genuinely, very good. It also once wanted to force-push to main , and it wanted to for an extremely good reason. Sit with that for a second, because it's the whole post. The rebase was stuck. Force-pushing would have unstuck it. Every link in that chain of reasoning is sound. The agent wasn't being careless, wasn't hallucinating, wasn't "drifting" or whatever we're calling it this month. It made a locally correct decision with a non-local consequence, which is the exact category of mistake that human code review is worst at catching — because the diff looks fine . It could see the command. It could not see the crater. The thing I stopped doing For a while my answer was to read everything. Every diff, every command, eyes on the screen, hand hovering over Ctrl-C like a man watching a toddler near a staircase. This does not scale, and the reason it doesn't is embarrassing when you say it out loud: reviewing output scales with how much the agent writes. That number is going exactly one direction, and it isn't down. So I flipped it. Instead of reviewing what it produces, I started writing down what it must never do. And here's the good news that took me way too long to notice: that list is short . Not "short for a security policy" short. Short like you can fit it on a napkin. Here's mine: A credential it read an hour ago gets inlined into a source file. A rebase gets stuck, and the fastest route to a green terminal is git push --force origin main . rm -rf "$BUILD_DIR/" runs on the one machine where BUILD_DIR never got set. A version bump gets typed straight into package-lock.json , because that's the file the version number is visibly in. A failing test quietly grows a .skip and CI goes green. Someone runs cat .env "just to see which variables exist." That last one is my favourite, and I'll come back to

2026-08-21 原文 →
开发者

Making a screenshot PDF searchable — no OCR, because we rendered the page

We archive whole web pages as PDFs. Under the hood each page is a full-height screenshot dropped onto a PDF page — which looks perfect and is completely useless the moment you want to use the text. Ctrl+F finds nothing. You can't copy a sentence. A screen reader opens the document and sees… an empty page with one big image. The fix is the same trick a "searchable scan" uses: draw the real text invisibly , on top of the image, at the exact coordinates where each word appears. The difference is that a scanner needs OCR to guess the text — we rendered the page ourselves , so we already have the ground truth. No OCR, no guessing. Here's how we built it with pdf-lib and @pdf-lib/fontkit , and the one part that turned out to be genuinely hard. The shape of it While the page is still open in the headless browser, ask the DOM where every word is. Assemble the PDF: embed the screenshot as the page background. For each word, drawText it at its coordinates with opacity: 0 . Steps 1 and 3 are easy. The trap is in which words you're allowed to draw. Step 1 — ask the browser where the words are Running inside the page (Puppeteer's page.evaluate ), we walk every text node and measure each word with a Range : const walker = document . createTreeWalker ( document . body , NodeFilter . SHOW_TEXT ); // ...for each word in each text node: const range = document . createRange (); range . setStart ( node , start ); range . setEnd ( node , end ); const rects = range . getClientRects (); if ( ! rects . length ) continue ; // display:none or empty line box const b = rects [ 0 ]; // first rect = where the word starts out . push ({ t : word , x : b . left + window . scrollX , // document coordinates, not viewport y : b . top + window . scrollY , w : b . width , h : b . height , fs : parseFloat ( getComputedStyle ( el ). fontSize ) || 12 , }); getClientRects() gives viewport coordinates, so we add scrollX/scrollY to get document coordinates — the ones that line up with a full-page screenshot.

2026-08-20 原文 →
AI 资讯

How to Stop Your Discord Bot From Sleeping on Render's Free Tier

A step-by-step tutorial to stop a discord bot from sleeping on Render's free tier — the real cause, the fix, and a working code example. How to Stop Your Discord Bot From Sleeping on Render's Free Tier You've deployed your Discord bot to Render's free tier, it worked for a bit, and now it's going offline — sometimes after a few minutes, sometimes randomly. This is one of the most common issues developers hit deploying a bot for the first time, and it has a specific, well-understood cause and a fix you can ship in under ten minutes. Table of Contents Why This Happens on Render Specifically Confirming This Is Your Actual Problem Step 1: Install StayPresent Step 2: Wrap Your Bot's Entry Point Step 3: Read Render's Assigned Port Step 4: Set Your Render Start Command Step 5 (Optional): Prevent Inactivity Sleep Specifically Verifying It Worked FAQs Conclusion Why This Happens on Render Specifically Render's free-tier web services are checked for health over HTTP, and free services also spin down after a period without incoming traffic. A discord.py bot connects outward to Discord's gateway — it never opens an HTTP port of its own, which is completely normal bot behavior. Render's health checker, seeing nothing respond on the expected port, has no way to know the bot is actually working fine internally. It just sees silence, and reacts accordingly. Confirming This Is Your Actual Problem If your bot's entry point goes straight into bot.run(TOKEN) with nothing else, and Render's dashboard shows the deployment as unhealthy or repeatedly restarting with no matching error in your bot's own logs, this is almost certainly it. Step 1: Install StayPresent pip install staypresent[prod] Add it to your requirements.txt as well: staypresent[prod] discord.py Step 2: Wrap Your Bot's Entry Point Keep your existing bot code in bot.py completely unchanged. Create a new main.py : import os import staypresent staypresent . web . json ({ " status " : " running " }) staypresent . run ( " bot.py

2026-08-20 原文 →
AI 资讯

Part 1 — What Actually Happens When Code Runs

When we write: const result = add ( 10 , 20 ); it feels like the computer simply "runs the code." But the CPU doesn't understand JavaScript. There are several layers between the code we write and the hardware actually executing instructions. That's what I wanted to understand first. From JavaScript to the CPU In Node.js, JavaScript is handled by V8 , the JavaScript engine. A simplified view looks like this: JavaScript ↓ V8 ↓ Bytecode ↓ JIT compilation ↓ Machine instructions ↓ CPU V8 doesn't simply "interpret JavaScript" or "compile JavaScript" once and forget about it. It can start with bytecode and progressively compile frequently executed ("hot") code into more optimized machine code. Eventually, the CPU is executing instructions that operate at a much lower level than the JavaScript we originally wrote. What does the CPU actually do? At its core, a CPU repeatedly executes instructions. A simplified mental model is: Fetch → Decode → Execute → Repeat The CPU has several important pieces involved in this process. Registers are tiny, extremely fast storage locations inside the CPU. They're used to hold values the CPU is actively working with. The ALU (Arithmetic Logic Unit) performs many arithmetic and logical operations. The Program Counter (PC) keeps track of where the next instruction comes from. And the CPU runs according to a clock, measured in GHz. A 3 GHz CPU has roughly 3 billion clock cycles per second, but that does not mean it executes 3 billion instructions per second. Different instructions and architectures have different costs. Modern CPUs are far more sophisticated than this simplified model, using pipelining, multiple execution units, branch prediction, out-of-order execution, and more. But the basic model is enough to start reasoning about performance. The CPU doesn't get everything from RAM One of the most important things I learned here is that where data lives matters . A simplified hierarchy looks like: Registers ↓ L1 Cache ↓ L2 Cache ↓ L3 Cache

2026-08-20 原文 →
AI 资讯

An AI-Powered Platform for Smarter Investments: Stock Trading Platform

📈 Building the Future of Trading: An AI-Powered Platform for Smarter Investments The Introduction: Empowering Every Investor Hello, Builders and tech enthusiasts! I'm thrilled to share my journey as part of the "Meet The Builders" campaign, where innovators are leveraging Google AI to tackle real-world challenges. My project is an ambitious endeavor to democratize effective stock trading through an intuitive, AI-enabled platform. Inspired by industry leaders like Zerodha, I set out to create a comprehensive website that not only facilitates trading but also acts as a smart, AI-powered guide, helping users navigate the often-complex world of stock markets more effectively. This project is my story, a testament to how technology, especially AI, can empower individuals to make more informed investment decisions. The Deep Dive: Why Investors Need a Guiding Hand The stock market can be a daunting place. For many retail investors, it's a whirlwind of data, conflicting advice, and emotional decision-making that can lead to missed opportunities or significant losses. From understanding market trends and analyzing complex financial reports to knowing when to buy or sell, the sheer volume of information can be overwhelming. Many feel like they're trading blind, lacking the expertise and analytical tools available to professional institutions. I believe there's a significant gap here – a need for a personal, intelligent assistant that can cut through the noise, provide actionable insights, and guide users towards more strategic trading choices. This conviction fueled the inception of my project. The Solution: Stock Trading Platform – Intelligent Trading, Engineered for Success My project, Stock Trading Platform, is a robust web-based platform designed to simplify stock trading with the power of artificial intelligence. While currently in its final polishing stages on my local machine and version-controlled with Git and hosted on GitHub, the core functionality revolves around a

2026-08-19 原文 →
AI 资讯

Marketplace Call Summarization API: Multiple Documents, Async Jobs, Verified CRM Exports

TL;DR For marketplace sales calls, use an async job when several documents must become one reviewed set of CRM actions; use an inline request only when one short document can finish inside the caller's latency budget. Preserve one result per input, expose partial progress, and export only records that carry their source ID, outcome, and schema version. Start with this decision table: Pick Use it when Quality and latency consequence Operational burden Inline request One short transcript produces one independent summary Fast feedback, but the request deadline limits retries and review stages Low until traffic spikes or callers retry Bounded parallel calls A small set of independent transcripts can finish separately Lower wall time, with variable completion order The caller owns concurrency, backoff, and reconciliation Durable async job Multiple documents feed one CRM export or need validation More queue latency, but enough room for retries and quality checks Requires job state, idempotency, metrics, and retention rules The important boundary is not "batch or no batch." It is ownership. If the API accepts a collection, the service should own that collection through terminal results and a verifiable export. Don't make a client reconstruct truth from whichever promises happened to resolve. What should a Node.js batch summarization API do with multiple documents? It should turn an admission request into a stable job record, process every document under a declared concurrency limit, and publish an item-level outcome before it declares the job complete. The result model needs at least four identities: job, input document, processing attempt, and export. Without them, a duplicate submission can look like new work, a retry can overwrite useful evidence, and an export can silently omit a failed call. For the marketplace example, imagine that a seller has three calls about the same account: discovery, pricing, and legal review. The desired CRM update is not merely three paragra

2026-08-19 原文 →
AI 资讯

I Wrote 238 Tests Against My Own Auth Package and Found 4 Real Bugs

I'd already done a lot right by the time I started writing tests for Beaver-Auth . Every module had gone through multiple rounds of deliberate review. Enumeration protection, hashed tokens, refresh rotation, TOTP replay defense — the design was solid, and I knew it was solid, because I'd thought hard about every piece of it. Then I wrote 238 tests against the actual code, and found 8 real bugs. Some of them were the kind that would have silently broken production on day one. This post isn't about the bugs specifically — it's about the gap between "I reviewed this carefully" and "this is shippable," and why that gap is bigger than most of us assume, even when the reviewing was genuinely careful. "Passing tests" and "shippable" are different claims Here's the trap I nearly walked into: I'd built a solid test suite covering the core auth flows — registration, login, verification — and every test passed. It felt done. But passing tests only tell you the code does what the tests expect. If the tests were written from the same mental model as the code, they'll happily confirm a bug is correct behavior, because both the code and the test agree on the same wrong assumption. The fix wasn't "write more tests." It was testing against the real, integrated system — not a hand-built mock of my own logic, and not testing modules in isolation from what actually calls them. A few of the bugs below only surfaced because a test exercised the real dependency chain instead of assuming it worked. Bug 1: TypeScript let an argument-shift bug compile clean This is the one that scared me most. Beaver-Auth dispatches background work (like sending a verification email) through a TaskDispatcher interface: interface TaskDispatcher { dispatch ( taskName : string , payload : unknown , handler : () => Promise < void > , onFailure ?: ( error : unknown ) => Promise < void > | void , ): Promise < void > } The default implementation had drifted to a different signature — missing the payload parameter e

2026-08-19 原文 →
AI 资讯

How We Built a Safe GitHub Bounty Lifecycle for MyZubster

How We Built a Safe GitHub Bounty Lifecycle for MyZubster MyZubster is evolving into a distributed ecosystem of repositories, services, automation, hardware projects, AI components, and contributor workflows. As the number of repositories and contributors increased, one problem became increasingly important: How do we automate bounty workflows without accidentally treating a GitHub event as proof of payment, verification, or settlement? We recently completed an important part of that architecture: a real-time GitHub bounty lifecycle system . And we tested it end-to-end. The lifecycle We use an explicit bounty lifecycle instead of assuming that an issue, pull request, or merge means a bounty has been completed. The lifecycle is roughly: text PROPOSED ↓ VALIDATED ↓ APPROVED ↓ FUNDED ↓ ACTIVE ↓ SUBMITTED ↓ UNDER_REVIEW ↓ VERIFIED ↓ REWARD_RECORDED ↓ SETTLEMENT_PENDING ↓ SETTLED The important part is that GitHub automation only controls a limited part of this flow. Today, GitHub can automatically move a bounty through: APPROVED ↓ assignment ACTIVE ↓ linked PR SUBMITTED ↓ review UNDER_REVIEW And then automation stops. GitHub Webhooks Across the Ecosystem We configured repository webhooks across 17 first-party MyZubster repositories. The subscribed events are: issues pull_request pull_request_review The central endpoint is: POST /api/github-bounties/webhook The backend is Node.js / Express and validates GitHub webhook signatures using: X-Hub-Signature-256 with an HMAC-SHA256 secret. Unsigned requests are rejected. For example: POST /api/github-bounties/webhook → HTTP 401 while valid GitHub webhook deliveries receive a normal application response. A Useful Production Bug: PM2 Had a Stale Secret One of the most interesting parts of the deployment was a real production debugging problem. GitHub was delivering webhook events correctly, but every delivery returned: 401 Unauthorized Cloudflare was healthy. The public API was healthy. The webhook route was healthy. GitHub delive

2026-08-19 原文 →
AI 资讯

Transactions in NestJS and TypeORM without passing the EntityManager around

Transactions promise a simple guarantee: either everything commits, or nothing does. And yet, in a NestJS application with a repository layer, it is perfectly possible to run a rollback with no errors and then find a row still sitting in the database that should have disappeared with it. This is not a TypeORM or PostgreSQL bug. One of the repositories involved was never inside the transaction, because the EntityManager stopped being passed down three layers up. There was no exception, no warning, and the tests passed because that repository was mocked. This article describes how to make that class of failure impossible: the transaction opens at a single point — the controller handling the request — and repositories enlist themselves in the transaction in progress, without receiving anything as a parameter. It comes to about sixty lines built on AsyncLocalStorage . The second part is the one rarely told: three consequences of the transaction boundary, each with its fix. A network call inside the transaction holds a pooled connection and its locks for the entire wait. A failure record written in the catch is rolled back along with the very failure it was meant to document. And nesting two execute calls does not open a nested transaction but two independent ones, with the self-deadlock that allows. The problem: passing the EntityManager by hand TypeORM offers a transaction like this: await dataSource . transaction ( async ( manager ) => { await manager . getRepository ( UserModel ). save ( user ); await manager . getRepository ( UserSettingModel ). save ( settings ); }); For a small project this is the correct answer and nothing more is needed. The problem shows up once a repository layer exists. The manager is the transaction: if a repository does not use that manager, its queries run on a different connection and end up outside the transaction. Silently, with no error and no warning. The rollback simply does not revert them. So the manager has to reach the repository

2026-08-19 原文 →
AI 资讯

Your backup is not a backup until you have restored it

This is an English write-up of a post from my Japanese dev diary. Original: https://saas-diary.com/tech-log/backup-restore-drill-automation/ For over a year, my backup job has reported success every single night. Green check, every day, no exceptions. Then I asked myself one question and went cold: "How many times have I actually restored from it?" Zero. Not once. "It was backed up" and "it can be restored" are different states My setup has two paths. One mirrors all source to a private repo. The other packs the things I can never recreate — notes, config, and Android signing keys — into an encrypted bundle and ships it to a private channel every night. Both were green every day. But green only proved the upload finished . It never proved the contents were right, or that the archive could even be opened. Within one month, I had two failures that stayed green the whole time. Failure 1. The collector for signing keys used three hardcoded paths. I kept shipping new apps, so the number of keys kept growing — but the collector didn't. By the time I noticed, 7 of 10 keys were missing from the backup . Five of those apps were live on the store. If my machine had died, I could never have shipped an update for them again. The backup reported success every night through all of it. Failure 2. The mirror push failed 7 days in a row (a large binary hit the host's file-size limit). But the script printed "✅ done" and returned exit code 0 even when one half failed. A failure that isn't visible isn't a failure — it's a time bomb. So I automated a restore drill Once a month, a job now does this: Rebuild the encrypted bundle (without shipping it) Actually decrypt it with the stored passphrase Extract it and count what's inside Check the mirror is not stalled (latest commit timestamp via API) Delete the scratch folder and the generated bundle The encryption is openssl-compatible AES-256-CBC with PBKDF2 (SHA-256, 100k iterations). I deliberately avoided depending on the openssl binary,

2026-08-18 原文 →
AI 资讯

Authentication done right: JWT, sessions, and OAuth explained — Like a Marvel superhero assembling the team

The Quest Begins (The "Why") I still remember the first time I tried to add login to a side‑project. I’d read a tutorial that said “just store a token in localStorage and you’re good,” slapped together a few fetch calls, and called it a day. A week later I got an email from a user: “Hey, I can’t log out, and someone else seems to be using my account.” My heart sank. I realized I’d bolted a flashy lock onto a screen door — it looked secure, but anyone with a screwdriver could walk right in. That moment kicked off a deep dive. I wanted to understand the trade‑offs between sessions , JSON Web Tokens (JWT) , and OAuth so I could pick the right tool for each job, not just the shiniest one. What followed felt like assembling a superhero squad: each member has a unique power, and knowing when to call on them makes the difference between saving the day and causing collateral damage. The Revelation (The Insight) Sessions – The Trusty Sidekick Sessions are the classic, server‑side approach. When a user logs in, the server creates a random identifier (the session ID), stores it in a database or cache (Redis, Memcached, etc.), and sends it back to the browser as an HttpOnly cookie. On every request, the browser automatically includes that cookie, the server looks up the ID, and pulls the associated user data. Why I love it: The secret never leaves the server, so stealing a cookie only gives an attacker a session ID that’s useless without the server’s store. Revoking a session is trivial — just delete the row from the store. Works great for traditional web apps where you control both front‑ and back‑end. Where it stumbles: Horizontal scaling requires a shared session store; otherwise each instance forgets who the user is. Every request does a database/lookup, which can add latency if the store isn’t fast enough. JWT – The Lone Wolf with a Signed Badge A JWT is a compact, URL‑safe string that contains claims (like sub , exp , roles ) and is cryptographically signed (HMAC or RSA).

2026-08-17 原文 →
AI 资讯

Why I Built Unlockt: A Local-First Instagram Saved Archiver, Canvas Collage Studio & 9:16 Video Vault

Like many developers, designers, and digital marketers, my Instagram "Saved" collection had turned into a digital graveyard with over 5,000 bookmarked posts, reels, and carousels. The native Instagram web app offers virtually zero productivity tools: ❌ No full-text search across captions or hashtags ❌ No way to extract individual slides from carousel photo dumps ❌ No offline preservation (if a creator archives a post, it disappears forever) ❌ Existing web downloaders ask for account passwords, inject trackers, or bombard you with ads. So I spent the last few months developing Unlockt — a 100% free, MIT open-source, local-first Chromium extension and Node.js Express dashboard. --- ## 🏗️ Architecture & Engineering Highlights Here is how Unlockt is designed under the hood: ┌─────────────────────────────────┐ │ Chromium Extension (MV3) │ ──► Reads Instagram GraphQL via active session └────────────────┬────────────────┘ │ Local REST Sync ▼ ┌─────────────────────────────────┐ │ Express Backend (Port 3000) │ ──► SSRF-Hardened Proxy & HTTP 206 Video Streamer └────────────────┬────────────────┘ │ ┌────────┴────────┐ ▼ ▼ ┌──────────────┐ ┌───────────────────────────┐ │ data/saved. │ │ /thumbnails /videos │ │ json (DB) │ │ (Local High-DPI Storage) │ └──────────────┘ └───────────────────────────┘ 1. Zero-Password Session Scraping Rather than asking users for their credentials or running headless browser instances that trigger Meta account checkpoints, Unlockt operates as a Manifest V3 Chromium extension. It uses the cookies and CSRF tokens already present in your authenticated browser tab with randomized jitter delays (800ms - 2200ms) to respect rate limits. 2. 1-Click HTML5 Canvas Collage Studio One of my favorite features is the Carousel Studio . When you open a 10-slide photo dump, Unlockt extracts every slide and can render them onto an off-screen HTML5 <canvas> element to produce high-resolution moodboards ( 2x1 , 2x2 , 3x2 , 3x3 , and 5x2 ) with crisp 4px white margin div

2026-08-17 原文 →
AI 资讯

Null Is Not Zero: Building a JavaScript SEO Audit That Admits Its Limits

We moved a server-side SEO engine into a Chrome extension. Measuring the page was the easy half. Saying what we could not measure was the hard half. We had been running an on-page analysis engine on our own servers for years. You give it a URL, it fetches the page, it reports. Ordinary. Then we moved that engine into the browser, because a server cannot reach localhost , a staging box, an intranet, or anything behind a login. The browser can. Porting the analysis was mechanical work. What took the real time was a category of problem that barely exists on the server: in a live tab, half the things you want to measure are sometimes unavailable, and the honest answer is not a number. This post is about the decisions that came out of that, with the code that implements them. The One Rule: Null Is Not Zero Every derivation in the engine returns number | null , and the two mean different things. 0 means we measured it and it is zero. A page with no layout shift really does score zero. null means we could not measure it. No interaction happened yet, the browser does not support that entry type, or the document came from another origin and the size fields were zeroed out. A zero printed where a null belongs is a made-up number. It is worse than an empty cell, because the reader has no way to tell it apart from a real measurement. So the two never collapse: the derivation keeps them separate and the UI renders them differently. That sounds obvious written down. It is surprisingly easy to violate, and the next section is the most common way. PerformanceObserver Fails Silently, So Ask It First Here is the trap. Calling observe() with an entry type the browser does not support does not throw . It does not warn. It quietly does nothing, and your handler is simply never called. Which means an unsupported metric produces exactly the same result as a measured zero. The one thing the rule above forbids. The fix is to ask before you observe, and to record the refusal: js const SUPPOR

2026-08-16 原文 →
AI 资讯

Build a POS receipt printer in Node.js

Disclosure: I build Receiptful, the printing API used in this tutorial. The Node and Express parts apply whatever you print with. You have orders coming into your point of sale, and you want each one to print on the thermal printer at the counter. This is a complete walkthrough of a small Node service that does exactly that. By the end you will have an endpoint you can POST an order to and watch paper come out. There is nothing to install next to the printer for this tutorial to work, and no ESC/POS to write by hand. You send HTML, Receiptful prints it. Before you start You need two things from the console : A paired printer, which gives you a printer ID . If you have not done this yet, the getting started guide walks through it in a couple of minutes. An API key (the rf_live_… value), created under API keys and shown only once. On the code side you need Node 18 or newer, so that fetch is available globally with no extra dependency. We will use TypeScript, but the same code works in plain JavaScript if you drop the types. Put your credentials in the environment rather than in the source: export RECEIPTFUL_API_KEY = "rf_live_3f9c…" export RECEIPTFUL_PRINTER_ID = "42" Step 1: model the order Start with the shape of an order. Yours will have more fields, but this is enough to print a useful receipt: interface LineItem { name : string ; quantity : number ; unitPrice : number ; // in cents, to avoid float rounding } interface Order { id : number ; items : LineItem []; placedAt : Date ; } Keeping money in cents and formatting only at the edges saves you from the classic floating point rounding bugs that show up as a receipt total that is one cent off. Step 2: render the order as HTML This is the part that decides how the receipt looks. Receiptful converts the HTML you send into ESC/POS for your specific printer, so you get to lay a receipt out with tags you already know instead of byte codes. function money ( cents : number ): string { return " $ " + ( cents / 100 ). toFi

2026-08-16 原文 →
AI 资讯

Tenant-Aware Speech-to-Text Explained — MP3/WAV File Uploads Across US/EU in 2026

Short answer: for a small fintech product that turns reviewer voice notes into structured code findings, start with one synchronous speech-to-text file-upload adapter for MP3 and WAV, but write every upload to a tenant ledger before making the transcription request. That is usually the fastest integration because it keeps the first release small while preserving per-tenant cost visibility and a clean path to regional routing. Choice Shipping effort Tenant attribution Best fit Main constraint Direct file upload Lowest Clear with an internal ledger Short reviewer notes Bound by the selected API's request and duration limits Object storage plus async worker Medium Clear with job records Long or bursty recordings More states to operate Self-hosted transcription Highest Fully internal Strict control requirements or sustained workloads Model serving becomes your job My recommendation is the first row for the initial release. Keep the adapter replaceable, measure billed units rather than guessing from file size, and promote work to a queue only after real upload patterns justify it. The point isn't to find a universally fastest model. It is to ship weekly without losing the tenant-level evidence needed to understand margin. How should a simple speech-to-text API handle MP3 and WAV file uploads? Treat the upload as a business event, not as an anonymous call to an AI endpoint. Before sending any audio, create an internal record with tenantId , changeId , uploadId , media type, byte count, selected processing region, and a start timestamp. After transcription, add the external request identifier when one exists, the terminal status, and the billable unit reported by the selected service. A byte count is useful for capacity planning; it is not a substitute for actual billing data. That distinction matters in a multi-tenant SaaS. One tenant may submit many short WAV notes, while another submits compressed MP3 files with longer conversations. Charging, margin analysis, and abuse

2026-08-16 原文 →
AI 资讯

JWT Authentication in Express That You Can Actually Revoke

Access tokens, refresh token rotation, and theft detection: the parts most Node.js tutorials leave out. A friend messaged me about his side project a few months ago: "Someone else is logged into my account. I changed my password. They're still in." He had followed the tutorials to the letter. Sign a JWT on login, send it to the frontend, keep it in localStorage , attach it to every request. Done. What none of those tutorials mentioned is that this setup has no way to un -log anyone in. A JWT is a signed piece of paper. Once you hand it over, it stays valid until it expires, and his expired in 30 days. Changing the password accomplished nothing, because the token had already been signed and nothing about it depended on the password. There was no list of active sessions to delete from. There was nothing to revoke. His only remaining move was rotating the signing secret, which logged out every user on the platform at once. That was his entire kill switch: burn it all down. This is the walkthrough I wish someone had handed me the first time I built auth. Token design, storage, refresh rotation, theft detection, the Express code, the Axios interceptor on the frontend, and the specific mistakes that turn a working login into an incident. It's long. Auth is one of those areas where the missing ten percent is the part that gets you. What the standard tutorial leaves out Nearly every "JWT authentication in Node.js" post ends in the same place: sign a token, put it in localStorage , send a Bearer header. That gets you a demo. Four things stand between that and production. localStorage is readable by any JavaScript on the page. That includes the analytics snippet you added last week, the npm package that got compromised upstream, and any XSS hole in your own code. One call to localStorage.getItem('token') and an attacker holds a working credential they can replay from their own machine. You can't detect it and you can't stop it. There is no revocation. The appeal of JWTs is st

2026-08-16 原文 →
AI 资讯

Your `if` statements are a database nobody can query

Somewhere in your codebase there is a line that looks like this: if ( user . plan === ' enterprise ' || user . tenantId === ' acme-corp ' ) { // ... } Nobody remembers who wrote the second half of that condition. It has been there for two years. It is almost certainly still load-bearing. Here is the thing I want to convince you of: that line isn't code. It's data, and it's stored in the worst possible place. Every conditional that encodes a business decision is really a row. It has a condition, an outcome, and a bunch of implicit context about when it applies. You have hundreds of these rows. They're spread across a dozen services, written in four different styles, and there is no way to list them. You have a database. You just can't query it. Five things a database gives you that your code doesn't Once you look at it this way, the problems stop feeling like sloppiness and start feeling structural. There's no schema. One service decides a customer is premium by checking plan === 'premium' . Another checks subscription.tier > 2 . A third checks a flag that was set during a migration in 2023. All three are "the same rule" until the day they aren't, and there's nothing in the system that would notice the drift. There's no way to query it. Try to answer a simple question: what rules are live in production right now? You can't. Someone has to read the source. And grep won't save you, because the interesting conditions are compound, spread across guard clauses, and half of them are expressed as an early return rather than an if . There are no migrations. Changing a rate limit from 100 to 200 requires a pull request, a review, a CI run, and a deploy window. You're pushing a code change through the full pipeline to change a number. It's a schema migration with none of the tooling that makes schema migrations tolerable. There's no audit log. Git tells you who edited the line. It doesn't tell you who decided the rule, when it was supposed to expire, or whether the customer it

2026-08-15 原文 →