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

标签:#JavaScript

找到 1021 篇相关文章

AI 资讯

Building a 3D Product Configurator in Three.js — Lessons From 9 Client Deployments

Over the last year I shipped 9 production 3D configurators for polish manufacturers — pools, garage doors, saunas, pergolas, greenhouses, packaging, decorative lamps, terrace roofs, and light-boxes. Each one runs live on its own subdomain of my studio at grodev.pl . Some of the lessons were obvious in hindsight. Some cost me a weekend of debugging. Sharing the non-obvious ones here. 1. Draco compression is not optional for CAD-heavy models Manufacturers send you STEP or SolidWorks files exported to glTF . Raw output is 40–120 MB per variant. On 4G mobile that's a 20-second load with an empty white canvas. Draco compression brings that to 2–5 MB with no visible quality loss on product shots: import { GLTFLoader } from ' three/examples/jsm/loaders/GLTFLoader.js ' import { DRACOLoader } from ' three/examples/jsm/loaders/DRACOLoader.js ' const dracoLoader = new DRACOLoader () dracoLoader . setDecoderPath ( ' /draco/ ' ) // self-hosted, don't use CDN const loader = new GLTFLoader () loader . setDRACOLoader ( dracoLoader ) loader . load ( ' /models/pool-3.5m.glb ' , ( gltf ) => { scene . add ( gltf . scene ) }) Self-host the decoder — Google's CDN version added ~600 ms to first paint in my measurements. Copy node_modules/three/examples/jsm/libs/draco/ to your public/ folder. Tooling: gltf-pipeline -i model.glb -o model.draco.glb --draco.compressionLevel 10 2. Instancing beats individual meshes past ~200 objects A pergola with 40 louvres × 3 tilt positions × user color picker = 120 meshes updating on every frame. Naive approach tanks FPS to 12 on mid-range phones. InstancedMesh batches identical geometry into one draw call: const geo = new THREE . BoxGeometry ( 1 , 0.05 , 3 ) const mat = new THREE . MeshStandardMaterial () const louvres = new THREE . InstancedMesh ( geo , mat , 40 ) const dummy = new THREE . Object3D () for ( let i = 0 ; i < 40 ; i ++ ) { dummy . position . set ( 0 , 0 , i * 0.15 ) dummy . rotation . x = userTilt // update per frame is fine dummy . updateM

2026-08-05 原文 →
AI 资讯

I built a short-code marketplace with zero npm dependencies (Node.js 22, no framework)

I've been going back and forth on whether to share this — it's a pretty niche idea, and I wasn't sure if it's clever or just weird. But here's the technical side of it, which I figure this crowd might actually appreciate regardless. What I built: claimo.me — you claim a short code (2-4 letters, or a custom name) for a one-time fee, no subscription, permanently yours. Each code is configurable as a redirect link, a QR code, or a small profile card. There's also a "Claimo Map" — every possible code is a clickable pixel you can browse, inspired by the old Million Dollar Homepage. The part I actually want to talk about here: it's zero-dependency. No Express, no ORM, no build step — just Node.js 22+'s built-in http module and the new built-in node:sqlite. I wanted to see how far "just the standard library" actually gets you for something real — payments (Stripe), admin moderation, rate limiting, a live interactive map UI, the works. Some things that surprised me building it this way: node:sqlite's DatabaseSync is genuinely pleasant to use, but it's missing conveniences like better-sqlite3's .transaction() helper — I ended up writing a small manual BEGIN/COMMIT/ROLLBACK wrapper. Routing without a framework is maybe 40 lines of code and I stopped missing Express within a day. The real cost isn't runtime performance, it's losing the ecosystem — anything I'd normally npm install for free (input validation, rate limiting, even basic templating) I had to hand-roll. Some of that was genuinely good for me, some of it I'd reconsider on a bigger project. Business side, since half of you will ask: it's a real registered business, payments go through Stripe only (I never touch card data), no crypto, nothing weird. The paid tiers fund keeping a free short-link tier alive too. Honestly — is the zero-dependency thing a genuinely good call for a real production app, or am I just going to regret it in a year? And separately: does "own a short code" as a product idea make any sense to you

2026-08-05 原文 →
AI 资讯

Building an Editable 3D Indoor Map in the Browser

Indoor maps are often treated as a rendering problem: take a floor plan, extrude a few walls, and display the result. That is useful for a viewer, but it breaks down when a team needs to edit a real space, place assets, or hand the result to another application. We are building KiMap around a different boundary: turn a floor plan into an editable indoor scene in the browser, then keep the resulting structure useful for an SDK consumer. Why a floor plan is not enough A production indoor workflow needs more than a textured image on a plane. At minimum, the editor has to preserve the relationships between walls, floors, rooms, openings, and the objects placed in the space. Those relationships determine whether the result can later support navigation, facility workflows, a digital twin, or a custom web experience. That is why the current KiMap workflow starts with structure. You can define the indoor geometry, inspect it in 2D and 3D, and keep editing instead of committing to a static export too early. The browser editor boundary The editor is built with React and Three.js. The goal is not to replace every DCC tool. It is to make the early spatial workflow accessible to teams that need to test an indoor experience before investing in a full custom pipeline. The parts we are concentrating on are: editable floor-plan structure and bounded spaces 2D and 3D scene inspection in the same workflow reusable 3D furniture and local asset handling saving an indoor project without dropping the referenced model data a path toward SDK-oriented rendering and integration The last point matters. A scene that looks correct in an editor is not automatically useful to an application. We want the data boundary to be explicit enough that an SDK consumer can load the geometry and assets without rebuilding the scene from scratch. What we are testing next KiMap is in free early access. The most useful feedback is not generic interest; it is a concrete blocker from someone building an indoor-nav

2026-08-05 原文 →
AI 资讯

Environment Variables the Safe Way

Environment Variables the Safe Way Environment variables are the standard way to configure applications without hardcoding secrets or environment-specific details. But they're easy to misuse. I've seen API keys committed to repos, configs that crash when a variable is missing, and defaults that silently override production settings. Here's how I handle them safely. Never Commit Secrets The most important rule: never put real secrets in your code or commit them to version control. That includes .env files. Add .env to your .gitignore immediately. If you're using a framework like Laravel or a tool like Vite, the default .env.example is your friend. Commit that, but never the real one. For local development, you can generate a .env from the example and fill in your own values. For production, set variables through your hosting provider's dashboard or a secrets manager like AWS Secrets Manager or HashiCorp Vault. Read Variables Explicitly Don't access process.env directly all over your codebase. Instead, centralize your configuration. Create a config.js (or config.ts ) that reads and validates all the variables you need. // config.js const required = [ ' DATABASE_URL ' , ' JWT_SECRET ' , ' PORT ' ]; const missing = required . filter ( key => ! process . env [ key ]); if ( missing . length ) { throw new Error ( `Missing required environment variables: ${ missing . join ( ' , ' )} ` ); } module . exports = { databaseUrl : process . env . DATABASE_URL , jwtSecret : process . env . JWT_SECRET , port : parseInt ( process . env . PORT , 10 ) || 3000 , }; Now your app imports config and uses config.port . This has several benefits: Fail fast: if a required variable is missing, the app crashes at startup, not later when you try to use it. Type safety: you can parse and validate values once. Easy to mock in tests. Use Defaults Carefully Defaults are convenient, but they can hide problems. For example, if you default PORT to 3000 in production, you might accidentally run on the w

2026-08-05 原文 →
开发者

I got tired of mocking Date, so I built a TimeProvider for TypeScript

Every (or at least a lot of) project seems to have code like this somewhere: if (user.subscriptionEndsAt < new Date()) { // ... } There's nothing wrong with it... until you have to test it. Then you end up freezing time, mocking Date, enabling fake timers, remembering to restore them afterwards, and hoping another test didn't leave the clock in a weird state. While Jest's and Vitest's fake timers are great tools, they always felt like they were solving the problem from the outside by patching global APIs. I wanted to try something different. Time is a dependency When you think about it, the current time isn't much different from a database or an HTTP client. Your business logic depends on it, but it doesn't have to know where it comes from. Instead of writing this: const now = new Date(); what if we wrote this? const now = timeProvider.now(); Suddenly, testing becomes boring—in the best possible way. You don't need global fake timers anymore. You just pass a different implementation. .NET had the same idea While looking into this, I discovered that .NET 8 introduced a TimeProvider abstraction. Seeing that was reassuring. It suggested I wasn't the only one who felt that "current time" deserved to be treated as a real dependency. I didn't want to copy the .NET API, but I did like the underlying idea. So I started building a version that felt natural in the TypeScript ecosystem. It grew beyond a clock At first I only wanted to replace new Date(). Then I realized the same issue exists with setTimeout, setInterval, performance measurements, and a few other APIs. They all depend on the environment's notion of time. So the library slowly became an abstraction around all of those instead of just "what time is it?". Is this actually useful? That's the part I'm still curious about. In the projects I've worked on, I prefer injecting time over patching globals during tests. Maybe other teams have reached the same conclusion. Maybe everyone is perfectly happy with fake timers an

2026-08-05 原文 →
AI 资讯

Four things that surprised me running Python in the browser

I built a debugging-practice site where student code runs entirely in the browser . Python via Pyodide , JavaScript in a worker. No server executes anything. No execution bill, no queue, no sandbox to maintain. But four things bit me hard. 1. Your arguments aren't Python objects Pass a JS object into Python and you get this: TypeError: 'pyodide.ffi.JsProxy' object is not subscriptable It's not a dict . It's a live view of the JS object, and it supports neither obj[key] nor .get() . Convert explicitly: const pyArgs = input . map (( arg ) => pyodide . toPy ( arg )); const result = fn (... pyArgs ); 2. null is not None This one passed my entire test suite while being broken in production. pyodide . toPy ( null ) check result type(v) JsNull bool(v) False ✅ falsy, as expected v is None False ❌ the surprise It's falsy, so truthiness checks work fine. But is None fails — which was exactly what my code was checking. Why my tests missed it: the harness used json.loads . The app used toPy . Different conversion paths, different answers. If you need a real None , create it in Python. Don't pass one across. 3. sys.settrace is a free step debugger Want to show users their code running line by line? Python basically hands it to you: def _tracer ( frame , event , arg ): if frame . f_code . co_name != target : return None # skip library frames if event == " line " : steps . append ({ " line " : frame . f_lineno , " locals " : dict ( frame . f_locals ), }) return _tracer Two things this naive version gets wrong: Add a step cap. A tight loop generates steps faster than it burns a 5-second timeout. You need both guards. Handle exception . During unwinding, the return event still fires with arg=None . Miss it and your trace says "returned None" for code that crashed. 4. Your snapshots are lying A user screenshot exposed this one. Every step in the trace showed the final state of a list. Step 1 included mutations that hadn't happened yet. tracing: nums = []; nums.append(1); nums.append(

2026-08-05 原文 →
AI 资讯

Browser vs Node — Where the Event Loop Actually Diverges (Part 2/3)

In part 1, we built the shared mental model: call stack, microtask queue, macrotask queue, and the rule that microtasks fully drain before the next macrotask runs. That model is spec-level JavaScript behavior — but it's not the whole story once you actually run code. The event loop isn't part of the JS language spec. It's part of the host environment — the browser or Node — and each one implements it differently around that shared core. This is the post most "event loop" explainers skip, because it means going past the diagram and into how each runtime is actually built. The browser: event loop meets rendering In a browser, the event loop isn't just juggling callbacks — it's also responsible for keeping the page visually responsive. That means rendering has to get a turn too, and the browser has to decide when . Here's the roughly accurate sequence per loop iteration: Execute one macrotask (a click handler, a setTimeout callback, a network event, whatever's next in the queue) Drain the entire microtask queue Maybe render a frame — the browser doesn't render after every single task; it tries to hit ~60fps and will batch work between paints Go back to step 1 The "maybe render" part is where two APIs come in that don't exist in Node at all: requestAnimationFrame(callback) — schedules a callback to run right before the next repaint. It's not a macrotask or microtask in the queue sense — it's tied directly to the rendering pipeline. Use it for anything visual (animations, DOM measurements) instead of setTimeout , because it's synced to when the browser is actually about to paint, not an arbitrary delay. requestIdleCallback(callback) — schedules a callback to run when the browser is idle, after layout and paint, with a deadline. Meant for low-priority work you don't want competing with rendering — analytics, prefetching, non-urgent DOM updates. Here's the key interaction that's easy to miss: microtasks can starve rendering. If a promise chain keeps queueing more microtask

2026-08-05 原文 →
AI 资讯

I built an invoice generator with no backend — the whole app is one HTML file

Every invoicing tool I tried wanted an account, a subscription, and a copy of my client list on its servers — then charged me monthly to put my own logo on my own invoice. So I built the opposite. Billfold is a complete invoice generator that runs entirely in your browser. No account, no backend, no build step. The whole app is a single index.html file you could email to yourself. It's MIT-licensed and the source is right here: github.com/quantum-hacker0/billfold . Here are the three parts that were actually fun to build. 1. "No server" isn't a privacy policy — it's the architecture The usual pitch is "we take your privacy seriously." That's a promise you have to trust. I wanted it to be a fact you can verify : Open DevTools → Network, create an invoice, and count the requests. It's zero. There's nothing to upload because there's nowhere to upload it. Data lives in localStorage . The app is HTML/CSS/JS inlined into one file — no framework, no bundler, no node_modules . Download it once and it works offline forever. 2. Sharing an invoice without a database — put it in the URL hash This was the interesting constraint. How do you send someone a view-only invoice when you have no server to store it on? The trick: encode the whole document into the URL hash fragment . The fragment (everything after # ) is the one part of a URL that browsers never send to the server — it stays client-side. function shareLink ( state ) { const json = JSON . stringify ( state ); const encoded = btoa ( unescape ( encodeURIComponent ( json ))) . replace ( / \+ /g , ' - ' ). replace ( / \/ /g , ' _ ' ). replace ( /=+$/ , '' ); // base64url return location . origin + location . pathname + ' #v= ' + encoded ; } The recipient's browser reads the fragment, decodes it, and renders the invoice locally. The data rides inside the link and never touches a host — not even mine. PDF export, by the way, is just window.print() with a print stylesheet. 3. Invoices as URLs — with an npm package Because the a

2026-08-04 原文 →
开发者

JavaScript Interview Questions Every Dev Should Know — Part 2: Functions, Scope & Closures

Welcome to Part 2 of the JS interview series! This time we're tackling functions, scope, and the topic that trips up even experienced developers in interviews: closures . Missed Part 1? Check out Fundamentals & Data Types first. Q1. What is a closure? A closure is what happens when an inner function "remembers" and continues to have access to the variables from its enclosing (outer) function's scope, even after that outer function has already finished running and would normally have had its local variables cleaned up. This works because JavaScript functions don't just capture the values of outer variables — they capture live references to them, keeping the entire surrounding scope alive in memory for as long as the inner function itself is reachable. Closures are one of the most powerful and commonly used patterns in JavaScript. They're the mechanism behind data privacy (since variables inside a closure can't be accessed from outside except through the functions that were given access), factory functions that generate customized functions, memoization caches, and event handler callbacks that need to remember state from when they were created. In the classic counter example below, each call to counter() creates a fresh, independent count variable that only the returned function can see or modify — there's no way to reach into it from outside. function counter () { let count = 0 ; return () => ++ count ; } const inc = counter (); inc (); // 1 inc (); // 2 Q2. What is lexical scoping? Lexical scoping (also called static scoping) means that a variable's accessibility is determined entirely by where it's physically written in your source code — not by which function called which, or the order in which functions happen to execute at runtime. When JavaScript compiles your code, it can already determine, just by looking at the nesting of functions and blocks, exactly which variables any given piece of code will be able to see. This is what allows an inner function to "reach

2026-08-04 原文 →
AI 资讯

TypeScript `asserts` and Type Predicates in 2026: Writing Guards That Actually Narrow Correctly

TypeScript asserts and Type Predicates in 2026: Writing Guards That Actually Narrow Correctly This article was written with the assistance of AI, under human supervision and review. Most TypeScript runtime validation breaks down because engineers write guards that compile but don't actually narrow types where it matters. The pattern that teams overlook is the distinction between type predicates that return boolean values and assertion functions that throw on failure—and choosing the wrong one creates silent bugs that surface in production. The problem starts when developers write a function like isUser(value: unknown): boolean and expect TypeScript to understand what that boolean means. The compiler sees the function return true but has no idea that value is now safe to treat as a User type. Code that looks validated crashes at runtime because the type system never learned what the validation actually proved. The fix is adding the type predicate syntax value is User to the return signature. This tells TypeScript that when the function returns true , the narrowed type holds in the calling scope. For throwing guards that never return on failure, the asserts keyword encodes that guarantee into the signature itself. That distinction is critical. Type predicates return booleans and enable conditional narrowing. Assertion functions throw errors and narrow the remainder of the scope unconditionally. Mixing them up or using neither creates validation theater—code that runs checks but provides zero type safety. Key Takeaways Type predicates ( value is Type ) narrow types conditionally when the guard returns true , while assertion functions ( asserts value is Type ) narrow unconditionally by throwing on failure. Most guard functions fail to narrow because they return boolean instead of using predicate syntax—the compiler cannot infer type information from a plain boolean. Assertion functions are superior for null checks and invariants that should never fail, while type predic

2026-08-04 原文 →
开发者

Stop hls.js from flapping between quality levels on cellular (with abrSwitchInterval)

TL;DR ABR "flapping" is when your player hops between quality levels every few seconds on a jittery network, and each hop is a visible lurch. We'll detect it from LEVEL_SWITCHED events, then fix it in layers: widen the bandwidth-estimator memory, make upswitches earn their place, and cap the switch rate with abrSwitchInterval (new in hls.js 1.7). Config + a detection snippet you can paste in today. 📦 Code: github.com/USER/hlsjs-abr-tuning, replace before publishing The bug nobody reports correctly Users don't file "my ABR is flapping." They say the video "kept changing" or "couldn't decide." What's happening: on cellular, throughput is spiky, and the player's bandwidth estimator treats every spike as the new truth. One fast segment and it jumps to 1080p, one slow segment and it drops to 240p, over and over. Low rebuffer ratio, good startup time, and still a miserable watch. Counterintuitively, feeding the player fresher bandwidth data makes this worse, because fresher data is noisier. The fix is a player with a longer memory and slower reflexes. Let's build that. 1. First, detect the flap 📊 Don't tune by vibes. Count level switches per minute of playback. Every switch fires Hls.Events.LEVEL_SWITCHED . // abr-monitor.js, hls.js 1.7.x, node 20+ tooling / any modern browser import Hls from " hls.js " ; export function attachFlapMonitor ( hls ) { const switches = []; hls . on ( Hls . Events . LEVEL_SWITCHED , ( _evt , data ) => { const now = performance . now (); switches . push ({ t : now , level : data . level }); // keep a 60s sliding window while ( switches . length && now - switches [ 0 ]. t > 60 _000 ) switches . shift (); const perMin = switches . length ; const reversals = countReversals ( switches ); if ( perMin >= 6 ) { console . warn ( `[abr] flapping: ${ perMin } switches/min, ${ reversals } reversals` ); } }); } // a "reversal" = up then down (or down then up), the signature of flapping function countReversals ( s ) { let r = 0 ; for ( let i = 2 ; i < s . l

2026-08-04 原文 →
AI 资讯

I Built a Chrome Extension to Download Telegram Media More Easily

Introduction Telegram has become one of the most popular platforms for sharing files, videos, images, and other media. However, when using Telegram Web, I found that saving media files was not always convenient. For example: downloading videos from channels saving multiple images managing large files The process usually requires several manual steps. So I decided to build a Chrome Extension to make Telegram media downloads easier. The project is called TGVideoDown. Website: https://tgvideodown.com Why build a Chrome Extension? At first, I considered building a standalone desktop application. But I realized that many Telegram users already use Telegram Web inside their browsers. A browser extension provides a simpler workflow: Open Telegram Web ↓ Find the media file ↓ Click download ↓ Save directly Users don't need: additional software complicated setup third-party upload services Technical implementation TGVideoDown is built with Chrome Extension APIs. Main technologies include: Content Script Used to interact with Telegram Web pages. Because Telegram Web is a dynamic application, the extension needs to handle: dynamic DOM updates asynchronous loading user interactions Chrome Downloads API Used to manage browser downloads. Example: chrome.downloads.download({ url: fileUrl, filename: fileName }) Storage API Used for storing user preferences and extension settings. Features Currently TGVideoDown supports: Telegram video downloads Telegram image downloads Telegram audio downloads Telegram GIF downloads Telegram file downloads Large file downloads Batch media downloading Challenges during development Handling dynamic pages Telegram Web uses a highly dynamic frontend. Traditional HTML parsing is not enough. The extension needs to monitor page changes and react when new media elements appear. Download experience Large media files require a smoother download process. The goal was to make downloading as simple as possible: Click → Download → Save Current sta

2026-08-04 原文 →
AI 资讯

Understanding Race Conditions in Backend Systems and How to Solve Them with Express.js

Modern backend applications handle thousands or even millions of requests every second. Users perform actions simultaneously: buying products, transferring money, updating profiles, sending messages, and more. But what happens when two requests try to modify the same data at the same time? This is where race conditions appear — one of the most subtle and dangerous problems in backend development. A race condition can cause incorrect data, security issues, financial losses, and unpredictable application behavior. Understanding how race conditions happen and how to prevent them is an essential skill for backend developers. What Is a Race Condition? A race condition occurs when multiple processes or requests access and modify shared data at the same time, and the final result depends on the order in which those operations execute. The problem is that the developer expects operations to happen in a specific sequence, but the computer executes them based on timing, network delays, database speed, and system load. Simple Example: Bank Account Withdrawal Imagine a user has: Account Balance: $100 Two withdrawal requests arrive at the same time: Request A: Withdraw $80 Request B: Withdraw $50 The backend checks the balance: Request A: Balance >= 80? Yes Request B: Balance >= 50? Yes Both requests continue because they saw the original balance of $100. The system processes: $100 - $80 = $20 $100 - $50 = $50 The final balance might become: $50 instead of: -$30 (which should have been rejected) The application has allowed money to be withdrawn that does not exist. This is a race condition. How Race Conditions Happen in Express.js Express.js applications are often built around asynchronous operations: Database queries API calls File operations Background jobs Message queues Consider this simple inventory system: app . post ( " /purchase " , async ( req , res ) => { const product = await Product . findById ( req . body . productId ); if ( product . stock > 0 ) { product . stock -

2026-08-04 原文 →
AI 资讯

Node Date 的 epoch 毫秒坑 + 用 MCP 把转换塞进 AI 流

Node Date 的 epoch 毫秒坑 + 用 MCP 把转换塞进 AI 流 作者是 Node.js / JS 时间 方向的开发者。这篇不是广告,是踩坑记录 + 顺手做的工具。 背景 做 Node.js / JS 时间 时,时间戳转换是最常被低估的雷区。16 个时间戳工具(Unix 转换/时区/ISO8601/Cron/Duration…) 已覆盖日常;但每个语言/框架的坑都不一样,所以又补了 30 个语言/框架时间戳页(python/javascript/java/sql/…),每页含 6 个真实坑。 我踩过的坑(举几个) 秒 vs 毫秒:前端 Date.now() 是毫秒,后端常存秒,混用差 1000 倍。 时区不是字符串:存 UTC、展示本地,别把本地时间当 UTC 落库。 2038 问题:32 位系统 time_t 在 2038-01-19 溢出,老系统要提前查。 夏令时:一年有两次重复/缺失的本地时间,跨区调度尤其坑。 我顺手做的东西 转换速查页: https://gotimestamp.com/timestamp/nodejs 相关语言页: https://gotimestamp.com/timestamp/javascript 开源 MCP: https://github.com/caresotin/tsforge-mcp —— 把时间戳转换/校验直接接进 LLM 工作流,不用手算。 小结 时间戳没那么简单,但工具到位就省心。上面都是免费、开源、可直接用的,希望对同样踩坑的人有帮助。

2026-08-04 原文 →
AI 资讯

How I Built a Serverless Blog on Cloudflare Workers with KV and R2

Canonical URL: https://blog.1001020.xyz/ Suggested cover image: use a recent image from https://blog.1001020.xyz/gallery I have been building a small publishing system called 1001020 , a serverless blog and AI gallery running on Cloudflare Workers. The live site is here: 1001020 — AI Gallery & Cloudflare Experiments The goal was not to build another static blog generator. I wanted something that could publish articles, serve an image gallery, manage uploaded assets, expose structured sitemaps, and stay operational without a traditional server. The basic architecture The whole public site runs on Cloudflare Workers. Articles, settings, comments, gallery metadata, and telemetry live in Cloudflare KV. Managed images are stored in R2 and served through a dedicated image domain. The main pieces are: Cloudflare Workers for request routing and rendering Cloudflare KV for article and site metadata Cloudflare R2 for managed image uploads A theme system for different frontend layouts XML sitemap and image sitemap generation A small local AI drafting tool for preparing and publishing content The gallery is a first-class part of the site, not just a media folder. You can browse it here: AI Gallery on 1001020 Why Workers instead of a conventional backend? For this project, Workers are a good fit because the workload is mostly request routing, HTML generation, metadata reads, and small API writes. A conventional server would work, but it would add deployment and maintenance overhead that I did not need. Cloudflare Workers also make it easy to keep the app close to the edge while still handling dynamic behavior. The blog can render pages server-side, expose APIs, and support admin operations without a separate Node or container deployment. KV as the content store The project stores persistent content in KV using explicit keys for articles, gallery records, settings, telemetry, comments, newsletter subscribers, and other small datasets. This shape works well for a personal publishi

2026-08-04 原文 →
开发者

Cómo solucionar el error “Enable JavaScript and cookies to continue”

Cómo solucionar el error “Enable JavaScript and cookies to continue” Este error aparece cuando Cloudflare (u otro proxy inverso de seguridad) detecta que el navegador del usuario no cumple con los requisitos mínimos para acceder al sitio: JavaScript está deshabilitado o las cookies no están permitidas . Pero en entornos reales, el problema suele ser más sutil: el navegador sí tiene JS y cookies habilitados, pero la configuración del entorno de ejecución (como un headless browser, test automation, o un scraper) no emula correctamente el comportamiento del cliente . 🔍 Causa raíz técnica Cloudflare emite un desafío (CAPTCHA o JS challenge) para verificar que el cliente es un navegador real. Si la respuesta no cumple con el desafío (por ejemplo, porque: El navegador no ejecuta el JS del desafío (headless sin soporte), Las cookies no se persisten entre solicitudes, El User-Agent o Accept-Language no coinciden con navegadores reales, Falta el Referer o Origin en headers, Se bloquean cookies de terceros (como las de Cloudflare), … entonces el servidor devuelve este mensaje estático en lugar de redirigir a la página solicitada. ⚠️ Nota crítica : Si estás usando herramientas como curl , requests de Python, o navegadores headless sin configuración especial, no pasarás el desafío de Cloudflare . Es intencional: Cloudflare bloquea tráfico no humano por diseño. ✅ Solución definitiva (por escenario) 🛠️ Caso 1: Navegador real (usuario final) Verifica que JavaScript esté habilitado : Chrome: Configuración → Privacidad y seguridad → Configuración de sitios → JavaScript → Permitido . Firefox: Preferencias → Privacidad y seguridad → Cookies y datos de sitios → Deshabilitar “Bloquear cookies y datos de sitios” . Limpia cookies y caché (especialmente para *.cloudflare.com ). Reinicia el navegador y vuelve a cargar la página. 🛠️ Caso 2: Automatización / Scraping (Python + Playwright/Selenium) No uses requests o urllib : no ejecutan JS. Usa un navegador real con soporte para Cloudflare. ✅

2026-08-03 原文 →