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

标签:#typescript

找到 439 篇相关文章

AI 资讯

Why I’m Building an Open-Source Frontend Engineering Handbook

Every frontend developer eventually reaches the same point. You know React. You know TypeScript. You know how to build components. But then you join a real project. Suddenly the questions are no longer about writing a component — they’re about engineering. How should the project be structured? Where should authentication logic live? When is React Context enough? When should React Query own the data? How do you prevent a codebase from becoming impossible to maintain? What makes a frontend application scalable? How do AI coding tools fit into modern development? These are the questions I kept asking myself while working on frontend applications. The problem wasn’t the lack of information. The problem was that the information was scattered across hundreds of blog posts, GitHub repositories, conference talks, documentation pages, and personal notes. Tutorials Teach Frameworks Modern tutorials are excellent at teaching frameworks. You can easily learn: React Vue Angular Next.js TypeScript But very few resources explain what happens after that. How do experienced teams actually build production frontend applications? How do they organize folders? How do they write maintainable code? How do they review pull requests? How do they optimize performance? How do they scale applications from one developer to twenty? Those are engineering problems — not framework problems. Frontend Engineering Is a Different Skill Writing React code doesn’t automatically make someone a frontend engineer. Frontend engineering includes topics such as: Project architecture Feature-based organization Authentication and authorization API design State management Data fetching strategies Performance optimization Accessibility Error handling Testing CI/CD Monitoring Code quality Documentation Team conventions These subjects rarely live in one place. AI Has Changed the Way We Build Software Another reason I started this project is the rise of AI coding assistants. Learn about Medium’s values Today many de

2026-07-25 原文 →
开源项目

🔥 apify / crawlee - Crawlee—A web scraping and browser automation library for No

GitHub热门项目 | Crawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation. | Stars: 24,963 | 69 stars today | 语言: TypeScript

2026-07-24 原文 →
AI 资讯

Knip Keeps My JS/TS Dependencies Honest (I Wish Python Had It)

Every long-lived JS/TS project I've worked on accumulates the same three kinds of rot: Dependencies in package.json that nothing imports anymore. You added moment , migrated off it, and the line stayed. Exports nothing consumes. A function was public once, the last caller was deleted, the export is still there advertising an API with no users. Whole files that fell out of the import graph but never got deleted, so every new engineer reads them trying to understand code that runs nowhere. None of this breaks the build. That's exactly why it survives. The compiler is happy, the tests pass, and the dead weight compounds quietly until onboarding takes a week and your bundle ships code no user will ever execute. The tool I've settled on for this is knip (by Lars Kappert). I run it on the enterprise codebase I maintain and on basically every other TS project I touch. One command: $ npx knip Unused files (2) src/legacy/formatValue.ts src/hooks/useLegacyModal.ts Unused dependencies (3) lodash package.json moment package.json @types/uuid package.json Unused exports (5) parseLegacy src/parse.ts:42:14 toLegacyDate src/date.ts:9:14 ... Files, dependencies, and exports in one pass, cross-referenced against the actual import graph. It's the first tool I've used that treats all three as the same problem, which they are: something is declared, nothing uses it, delete it. The honest caveat Knip is not zero-config on a real codebase. Anything resolved dynamically, runtime import() , plugin systems, framework entry points it doesn't recognize (Next.js pages, a CLI bin, config files loaded by string), gets flagged as unused when it isn't. You will get false positives on day one. The fix is a knip.json that names your real entry points, and after that it's accurate. But budget an afternoon to tune it before you trust the output enough to delete on it. Anyone who tells you it's instant hasn't run it on a large app. What I actually want Here's the part that bugs me. This problem is not sp

2026-07-24 原文 →
AI 资讯

houhou — Resilience Policies for TypeScript Async Functions

The problem Most resilience libraries in the TypeScript ecosystem are tied to HTTP clients (axios-retry, p-retry, Polly.js) or require wrapping your function in a class with a .execute() ceremony. What if you just want to wrap any async function — a database query, an internal service call, a file operation — with retry logic, a timeout, and a circuit breaker, without pulling in heavy dependencies? Enter houhou . What is houhou? Houhou is a zero-dependency TypeScript library (~500 LOC) that wraps any async function with composable resilience policies. The wrapped function keeps the exact same signature — you call it like the original. import { task } from ' houhou ' const charge = task ( chargeCard ) . retry ( 3 ) . timeout ( 10 _000 ) . fallback (() => ({ status : ' pending ' })) await charge ( account , amount ) Policies at a glance Retry Re-execute on failure with fixed or exponential backoff: task ( fetchUser ). retry ( 3 ) // shorthand task ( fetchUser ). retry ({ attempts : 5 , backoff : ' exponential ' , jitter : true , delay : 500 }) Timeout Reject if the function doesn't complete in time: task ( fetchUser ). timeout ( 5000 ) Fallback Run an alternative function on failure: task ( fetchUser ). fallback (() => loadFromCache ( id )) Circuit Breaker Prevent repeated calls to an unhealthy service: task ( queryDb ). circuitBreaker ({ failureThreshold : 5 , successThreshold : 2 , resetTimeout : 30 _000 }) Delay Wait before execution: task ( syncData ). delay ( 1000 ) Policy ordering matters Policies are nested : the last method called wraps the previous ones. Execution order is reverse of declaration order. task ( fn ). retry ( 3 ). timeout ( 1000 ) // → timeout wraps retry // → function runs → retry on failure (up to 3 times) → 1s total timeout // → if the timeout fires, there are no more retries task ( fn ). timeout ( 1000 ). retry ( 3 ) // → retry wraps timeout // → function runs → 1s timeout → if timeout fires, retry catches it // → the whole cycle repeats up

2026-07-24 原文 →
AI 资讯

Two credentials, two threat models: auth for a content API

A headless content API has two kinds of callers, and it's tempting to secure them the same way. That's the mistake. There's a human logging into an admin UI to edit content, and there's a machine — a website, a build step — pulling published content through a delivery endpoint. They authenticate with different credentials, and those credentials have opposite properties. Treat them identically and you either make the machine path painfully slow or the human path dangerously weak. I built a small headless content API ( Depot ) partly to get this boundary right. Here's the reasoning. The two credentials A session proves "this human is logged in." Short-lived, rides in an httpOnly cookie, checked on management routes. A delivery token proves "this machine may read this account's published content." Long-lived, sent as a Bearer header, checked on every public read. Two auth surfaces, kept explicit: /** * - requireUser() — admin session (httpOnly JWT cookie) for the management API. * - requireToken() — a `depot_…` bearer token for the public delivery API. */ Why they get different hashing Here's the part people get wrong. Both credentials get stored as hashes — never plaintext — but not the same kind of hash , and the reason is entropy. Passwords are low-entropy. Humans pick summer2024 . An attacker who steals your DB will brute-force guesses against the stored hashes, so you want hashing to be deliberately slow — that's exactly what bcrypt's cost factor buys you: import bcrypt from " bcryptjs " ; const ROUNDS = 10 ; // deliberately slow — the point is to resist brute force export function hashPassword ( plain : string ): Promise < string > { return bcrypt . hash ( plain , ROUNDS ); } export function verifyPassword ( plain : string , hash : string ): Promise < boolean > { return bcrypt . compare ( plain , hash ); } Tokens are high-entropy. I generate them — 32 random bytes — so there's nothing to guess. A stolen hash can't be reversed by brute force because the keyspace i

2026-07-24 原文 →
AI 资讯

Syncle: keep any two databases in sync, live and across engines

You have a row in Postgres. You want that same row in MongoDB — not tonight in a batch job, but the instant it changes. And next month you'll want it in Redis too, and maybe POSTed to some webhook. Today that's a Kafka cluster, a Debezium connector, a sink connector, a schema registry, and a weekend. For a job that is, at its heart, one sentence: a source → one or more destinations → kept in sync. Syncle is an open-source tool that does exactly that sentence, and nothing you didn't ask for. Connect your databases, draw a bridge from a source to one or more destinations, and the moment a row changes in the source it's written to every destination you linked. Any engine to any engine — PostgreSQL · MySQL/MariaDB · SQLite · MongoDB · Redis — plus HTTP endpoints when you need them. This post is a tour of what it does and, for the curious, how it's built. The core idea: a bridge A bridge reads rows from a source and writes each one to its destinations . A destination is either: another database — the headline feature. Postgres → MongoDB, MySQL → SQLite, MongoDB → Redis. One bridge can fan out to several databases at once, and bridges can chain (A → B → C). an HTTP endpoint — POST/PUT/PATCH each row to a URL with a payload you design, for feeding a service instead of a database. The interesting part isn't that it copies data — plenty of things copy data. It's the guarantees around how . No duplicates, ever Every database write is an idempotent upsert , keyed by columns you choose. So replays, retries, and at-least-once redeliveries never double-write. Under the hood each engine does it with its own native atomic operation: Engine Upsert PostgreSQL / SQLite INSERT ... ON CONFLICT MySQL INSERT ... ON DUPLICATE KEY UPDATE MongoDB updateOne(filter, ..., { upsert: true }) Inserts, updates, and deletes all propagate — a delete routes to a keyed delete on each target. Missing table? It builds it If the destination table or collection doesn't exist, Syncle creates it from the sou

2026-07-24 原文 →
AI 资讯

Why Most Web Change Monitors Fail: Solving DOM Mutations and False Positives

If you have ever tried building a website change detection system or visual testing tool, you’ve likely stumbled into the "False Positive Trap." You configure a cron job to monitor a target URL, take snapshots every 15 minutes, and compare them. But within hours, your inbox is flooded with alerts for: Tailwind CSS dynamic hash class mutations (e.g. class="bg-blue-500_a3f9" turning into class="bg-blue-500_b81c" after a deployment) Lazy-loaded images rendering at offset offsets Anti-bot verification scripts altering invisible DOM nodes Hydration mismatches in React/Vue single-page applications At PageWatch.tech , solving these exact edge cases was the primary focus of our engineering roadmap. In this article, I’ll share the 3 core algorithmic fixes we implemented to achieve reliable, noise-free website change monitoring. 🛑 Problem 1: Structural Hash Instability in Modern Frameworks Modern frontend frameworks like Next.js, Nuxt, and Remix insert dynamic build IDs, hydration keys, and inline CSS chunk hashes into the HTML structure. For example, a innocent paragraph tag might look like this today: <p class= "text-gray-700 css-1a2b3c" data-reactroot= "" > Product Price: $99 </p> And like this tomorrow after a routine production deployment: <p class= "text-gray-700 css-9x8y7z" data-reactroot= "" > Product Price: $99 </p> A standard raw string comparison flags this as a critical change even though zero user-facing content changed . The Solution: Attribute Normalization & CSS Class Sanitization Before computing DOM structural hashes, we run a normalize pass that strips generated hashes and framework-specific attributes: import * as htmlparser2 from " htmlparser2 " ; /** * Normalizes dynamic framework attributes and hashed CSS classes * before running DOM diff calculations. */ export function normalizeDOMNode ( node : any ): void { if ( node . attribs ) { // 1. Remove hydration and framework metadata const volatileAttrs = [ " data-reactroot " , " data-reactid " , " data-hydr

2026-07-23 原文 →
AI 资讯

Deep Learning & Computer Vision in Web Diffing: Solving Layout Shifts with Neural Embeddings and SSIM

When engineers talk about visual regression or website change monitoring, pixel-level diffing algorithms (like pixelmatch or Euclidean RGB distance) are usually the default solution. However, in real-world web environments, pixel-by-pixel comparisons fundamentally fail under normal user interactions and dynamic rendering conditions: Elastic Layout Shifts: A single 20px dynamic banner inserted at the top of a page pushes every subsequent DOM element down, causing 100% of the downstream pixels to fail a pixelmatch test, even if the content itself hasn't changed. Sub-Pixel Anti-Aliasing Jitter: Operating systems (macOS vs. Linux vs. Windows) render font glyphs with subtle sub-pixel anti-aliasing variations, creating thousands of false-positive pixel deltas. Semantic vs. Cosmetic Changes: Changing a single word in a paragraph should trigger a localized alert, but a minor color gradient shift in a hero image shouldn't trigger an emergency notification. At PageWatch.tech , we solved this by combining classical Structural Similarity (SSIM) , ORB Feature Alignment , and Siamese Neural Networks (SNN) for latent-space semantic comparison. In this article, I will dive into the mathematics, neural network architectures, and TypeScript implementation of our computer vision diff pipeline. 🧮 1. Beyond Pixel Comparison: Structural Similarity Index (SSIM) Unlike raw Mean Squared Error (MSE), SSIM measures visual change based on human perception across three dimensions: Luminance , Contrast , and Structure . Mathematically, the SSIM between two image windows $x$ and $y$ is defined as: $$\text{SSIM}(x, y) = \frac{(2\mu_x\mu_y + C_1)(2\sigma_{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)}$$ Where: $\mu_x, \mu_y$ are the local pixel mean intensities. $\sigma_x^2, \sigma_y^2$ are the local variances. $\sigma_{xy}$ is the covariance between $x$ and $y$. $C_1, C_2$ are stabilization constants. TypeScript Implementation of SSIM Window Sliding Below is a snippet of how

2026-07-23 原文 →
AI 资讯

Understanding Monads in TypeScript: A Practical Guide

You have read five blog posts about monads. Each one started with category theory, mentioned burritos, and left you more confused than before. Let's skip all of that. Here is the short version: a monad is a container that supports three operations. You already use two monads every day in TypeScript — Promise and Array . Once you see the pattern, every monad in @oofp/core will feel familiar. The Three Operations Every monad has three things: of(a) — Put a value into the container. map(f) — Transform the value inside, keep the container shape. chain(f) — Transform the value into a new container, then flatten. That's it. If a type supports these three operations and follows a few simple laws, it's a monad. Let's see this with types you already know. Promise — a monad you use daily // of — wrap a value const p = Promise . resolve ( 42 ); // map — transform the inside (then with a plain return) const doubled = p . then (( x ) => x * 2 ); // Promise<number> // chain — transform into a new Promise (then with an async return) const fetched = p . then (( id ) => fetch ( `/api/users/ ${ id } ` )); // Promise<Response> Promise.resolve is of . .then acts as both map and chain — JavaScript conflates them. When your callback returns a plain value, it's map . When it returns a Promise , it's chain . The runtime flattens the nested Promise<Promise<T>> into Promise<T> automatically. Array — another monad hiding in plain sight // of — wrap a value const arr = [ 42 ]; // map — transform each element const doubled = arr . map (( x ) => x * 2 ); // [84] // chain — transform each element into an array, then flatten const expanded = arr . flatMap (( x ) => [ x , x * 10 ]); // [42, 420] Array.of (or just [value] ) is of . .map is map . .flatMap is chain . Same pattern, different container. The insight is: chain is map followed by flatten . That's why it's also called flatMap or bind . You apply a function that returns a new container, then flatten the nested result. Maybe: Eliminating null

2026-07-23 原文 →
AI 资讯

Meta Ports React Compiler to Rust for Faster Builds and Tighter Toolchain Integration

Meta's React library has integrated a Rust version of the React Compiler into its main repository, aimed at enhancing build speed and compatibility with the Rust-based JavaScript toolchain. This port, which memoizes components automatically, demonstrates significant performance improvements, boasting up to 50% faster compilation. The public API remains unchanged to facilitate easy upgrades. By Daniel Curtis

2026-07-23 原文 →
AI 资讯

I built a serverless URL shortener for $4.68/year (total)

I wanted two things: short links under my own brand (every link I share points traffic back to my site ), and a real excuse to run a complete system on the edge — DNS, distributed compute, storage, auth and a dashboard — in production, at zero infrastructure cost. The result is flino.link : a shortener that responds in under 10 ms from 300+ locations, runs entirely on Cloudflare's free tier, and whose only expense is the domain: $4.68 a year. This post covers the design decisions, which is where the interesting parts are. The architecture in 30 seconds A single Cloudflare Worker serves the whole domain: GET /<slug> — the hot path. One read from Workers KV (globally replicated) and a 302 redirect. Nothing else touches that path. /api/links — a REST API with Bearer auth to create, list and delete links. /admin — a single-page dashboard served as inline HTML from the Worker itself. No framework, no build step. A Durable Object with embedded SQLite keeps per-slug click counts. No servers, no containers, no database to manage. The whole Worker is three TypeScript files and zero runtime dependencies. Why a dedicated domain? My first idea was to hang the shortener off a route on flino.dev . Bad idea: shorteners attract abuse — spam, phishing — and their domains sooner or later end up on blocklists. If that happens, I don't want it dragging my main domain down with it. A separate domain isolates that reputation risk completely, and a short .link costs less than a coffee per year. KV for links, a Durable Object for counters This is the central design decision. Workers KV is perfect for a shortener's access pattern — read-heavy, write-light, reads served from the edge — but its writes are eventually consistent : two concurrent increments in different datacenters would clobber each other. For counting clicks, it's useless. A Durable Object solves exactly that: it's a single global instance with transactional SQLite storage. Every increment, no matter which datacenter it comes

2026-07-23 原文 →
AI 资讯

I Built urldn-link-check — A GitHub Action to Catch Broken Links Before They Reach Production

Documentation is often the last thing developers think about—until a broken link frustrates users or a README sends someone to a 404 page. I wanted a simple way to automatically verify links in Markdown documentation during CI, so I built urldn-link-check. It's an open-source GitHub Action and CLI that scans your documentation and reports issues before they're merged. Features ✅ Detect broken links (404/500) ↪️ Detect redirect chains 🔒 Detect insecure HTTP links 📏 Find overly long URLs 📄 Scan Markdown & MDX files ⚡ Fast concurrent scanning 💬 GitHub PR summaries 📊 JSON & Markdown reports Installation npm install -D urldn-link-check or npx urldn-link-check . GitHub Action uses: urldn/link-check@v1 That's it. Every push or pull request can automatically verify your documentation. Why I Built It While maintaining documentation, I noticed that broken links often go unnoticed until someone reports them. Instead of checking them manually, I wanted a lightweight tool that integrates directly into GitHub Actions and fits naturally into a CI workflow. Open Source The project is MIT licensed and contributions are welcome. ⭐ GitHub: https://github.com/urldn/link-check 📦 npm: https://www.npmjs.com/package/urldn-link-check https://www.npmjs.com/package/urldn-link-check This is the first developer tool in the URLDN ecosystem, with more open-source projects planned in the future. If you have ideas or feedback, I'd love to hear them.

2026-07-23 原文 →
AI 资讯

What Changed in Zod 4, and How I Migrated Production Schemas

Headline: Zod 4 is a rewrite of the TypeScript-first schema validation library, released as the stable major in 2025. Four changes hit my code directly: string formats moved to top-level functions ( z.email() instead of z.string().email() ), the four error options collapsed into one error parameter, error formatting moved to standalone helpers ( z.flattenError , z.treeifyError , z.prettifyError ), and .strict() / .passthrough() became z.strictObject() / z.looseObject() . The deprecated Zod 3 APIs still work with warnings, so I migrated incrementally. Key takeaways Zod 4 is the stable major of the TypeScript-first schema validator, released in 2025; it requires TypeScript 5.5 or newer. String formats are now top-level tree-shakeable functions — z.email() , z.uuid() , z.url() — and z.string().email() is deprecated but still works. A single error parameter replaces Zod 3's message , invalid_type_error , required_error , and errorMap . Error formatting moved to z.flattenError (form fields), z.treeifyError (nested), and z.prettifyError (human-readable string). The zod/mini build exposes the same validators through a functional, tree-shakeable API; z.infer , .parse() , and .safeParse() did not change. I reach for Zod on almost every project to validate untrusted input at the boundary — request bodies, form data, environment variables, API responses. Zod 4 changed enough of the surface that a mechanical upgrade tripped a handful of files, so I mapped exactly what moved. What actually changed in Zod 4? Zod 4 is a ground-up rewrite of the TypeScript-first schema validation library, released as the stable major in 2025. The headline is performance: the Zod team's release notes report large reductions in TypeScript compiler instantiations and faster runtime parsing, which matters most in large codebases where schema types dominate type-check time. Four API changes touched my code directly — string formats moved to top-level functions, the four error options collapsed into one,

2026-07-22 原文 →
AI 资讯

The Hard Part of a Global Birth-Chart Calculator Was Time

A birth-chart form looks simple: ask for a date, time, and place, then calculate. The interface may be simple. The input is not. 1992-11-01 01:30 does not identify one universal instant. It is a wall-clock reading that only becomes meaningful after you resolve the place, the historical time-zone rule, and any daylight-saving transition. If the time is unknown, inventing a convenient default can create chart features that were never supported by the user’s data. I ran into these problems while building AstroZen , a Next.js application that calculates a BaZi Four Pillars chart and a Western natal chart before generating an optional interpretation. The most important architectural decision was this: Calculation is an evidence pipeline. Interpretation is a separate layer. This post explains the calculation pipeline, the failure modes I had to remove, and why “unknown” must remain unknown. 1. A city name is not a coordinate Early prototypes often use a short city list or a default coordinate. That works for a layout demo, but it is not acceptable once location affects the result. Names are ambiguous: Paris can mean France or Texas. Springfield needs a state or region. Country abbreviations come in several forms. A valid city must resolve to both coordinates and a time-zone identifier. AstroZen sends the submitted city to Open-Meteo’s geocoding service, then scores the returned candidates against the requested country and optional region hint. It keeps the following structured result: type ResolvedPlace = { name : string ; region : string | null ; country : string ; countryCode : string ; latitude : number ; longitude : number ; timeZone : string ; // IANA, for example "Europe/Madrid" }; Candidate selection considers exact city-name matches, country matches, an optional region hint, and population as a small tie-breaker. Population never replaces the country check. The more important rule is what happens when resolution fails: if ( ! selected || ! countryMatches ( selecte

2026-07-22 原文 →