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

标签:#JavaScript

找到 1018 篇相关文章

开发者

I built browser-to-browser remote file access with WebRTC – no app required

I’ve been building a browser-first project called RelicBeam, and one feature I wanted was simple in theory: Open a folder on one device and temporarily browse it from another device without installing anything. That became Remote Files, part of RelicBeam’s Device Portal. The host selects a folder, another device joins with a QR/code, the host approves the connection, and the second device can browse, preview and download files. The folder itself is never uploaded to RelicBeam. File data travels over a WebRTC DataChannel. If a direct connection isn’t possible, my own TURN server relays the encrypted traffic. Device Portal traffic is end-to-end encrypted between the connected browsers. The interesting problems The file browser itself was actually the easy part. Android file pickers kept killing sessions When I added optional uploads, I noticed something odd during testing. The first upload worked, but after opening the Android file picker a few times, the Remote Files session could suddenly disconnect. It turned out Android can background or suspend the browser while the native file picker is open. That could temporarily drop the Socket.IO signaling connection, and my server was treating any disconnect as the viewer leaving permanently. The fix was a short reconnect grace period. Temporary disconnects now get time to recover, while explicit Leave and End session actions still terminate access immediately. Firefox and Safari can browse, but not host uploads Remote Files works read-only across browsers, but writable folder access is more limited. Chrome and Edge expose writable directory handles through the File System Access API, so a host can optionally allow remote uploads into the selected folder. Firefox and Safari don’t currently expose the same writable directory picker. So today: Chrome / Edge host Browse ✅ Preview ✅ Download ✅ Optional uploads ✅ Firefox / Safari host Browse ✅ Preview ✅ Download ✅ Host uploads ❌ Firefox and Safari can still be the remote device

2026-08-30 原文 →
AI 资讯

Launching vizcrush: Three Beliefs My Benchmarks Killed

It's the week before vizcrush goes public, and I have two files open side by side. On the left, the launch copy: the JS core beats the most popular npm downsampling package by 32×, "and WASM adds another 5-10x on top." On the right, the repo's own benchmark control run: wasm/js ≈ 1.00× . One million points, same algorithm, same machine. Parity. I go looking for the measurements behind the claim. Half of it holds up: the 32× JS comparison has a result file (1.72ms against 55.52ms, real). The claimed additional 5-10× from WASM has nothing behind it, and the repo's own control run contradicts it. That afternoon set the shape of the whole launch: before anything shipped, every performance claim would either get a measurement behind it or get deleted. Three beliefs didn't survive. Each one got a public retraction, written up as an ADR in the repo. vizcrush is a set of data primitives for browser visualization (downsampling, binning, spatial indexing, streaming sketches), written in Rust, compiled to WebAssembly, with a pure-JS core behind the same API as a fallback and explicitly selectable backend. It went open source this week: the repo and the book are public, and all 11 packages are live on npm. npm install @vizcrush/core @vizcrush/downsample This is a launch story about turning benchmark results into product policy: claims, documentation, and WebGPU policy follow the measurements, while WASM dispatch stays availability-based pending further investigation. One scope note before the data. Every result here is workload-specific: LTTB (Largest-Triangle-Three-Buckets, the downsampling algorithm that picks, per bucket, the point that best preserves the visual shape of the line) is downsampling, the stats kernel is a reduction, and bin2d is histogramming. Which backend wins is algorithm- and engine-dependent, so none of what follows is a library-wide WASM-versus-JS verdict. It is three specific workloads measured on specific engines, with the claims and documentation follo

2026-08-30 原文 →
AI 资讯

🔄 Loops in JavaScript

Imagine a teacher wants to greet 5 students: Hello Arun Hello Kumar Hello Ravi Hello Priya Hello Divya Without a loop, we need to write the same code multiple times. console . log ( " Hello Arun " ); console . log ( " Hello Kumar " ); console . log ( " Hello Ravi " ); console . log ( " Hello Priya " ); console . log ( " Hello Divya " ); Instead of writing the same type of code again and again, JavaScript provides loops . 🔄 What is a Loop? A loop is used to execute a block of code repeatedly. It helps us avoid writing the same code again and again. A loop continues running based on a condition or a collection of values . In simple words: A loop means repeating a task multiple times using code. For example: For every student: Print the student's name This is the basic idea of a loop. 🤔 Why Do We Use Loops? Loops are useful when the same task needs to be performed multiple times. For example, without a loop: console . log ( " Hello " ); console . log ( " Hello " ); console . log ( " Hello " ); console . log ( " Hello " ); console . log ( " Hello " ); Using a loop: for ( let i = 1 ; i <= 5 ; i ++ ) { console . log ( " Hello " ); } Output: Hello Hello Hello Hello Hello If the task needs to be performed 100 or 1000 times, using a loop is much easier than writing the same code repeatedly. 📍 Where Are Loops Used? Loops can be used in many situations, such as: Displaying a list of products Processing a list of students Reading values from an array Printing numbers Calculating marks Processing multiple records Repeating a task until a condition becomes false For example: For every product: Display the product ⏰ When Should We Use a Loop? A loop can be used when: The same task needs to be performed multiple times. For example: For every student: Display the student's name or: While the password is incorrect: Ask for the password again Different situations require different types of loops. 🔢 Types of Loops in JavaScript JavaScript provides different types of loops: for loop whi

2026-08-30 原文 →
产品设计

Show DEV: I built A2Z Edit — free, private, browser-based image, PDF & OCR tools (100/100 Lighthouse)

Hey DEV community, I built A2Z Edit — a free, private, and browser-based toolkit for images, PDFs, OCR, QR codes, and file management. 🔧 What it does Image Tools: Remove background, resize, crop, compress, convert between formats (JPG, PNG, WebP, AVIF, HEIC), add watermarks, blur/pixelate sensitive regions, create collages, and view/strip EXIF metadata. PDF Tools: Merge, split, arrange, compress, watermark, sign, crop, edit text, redact, and convert PDFs to/from JPG, PNG, Word, and CSV/Excel. OCR Tools: Extract text from images and PDFs with support for English, Arabic, and bilingual English+Arabic recognition. QR Tools: Generate customizable QR codes and scan them from images or your camera. File Tools: ZIP creator/extractor, Base64 encoding, and color converters (RGB, HEX, HSL, CMYK). 🔒 What makes it different Your files never leave your browser . Everything runs client-side. No uploads, no servers, no signup, no limits. I built this to be fast, private, and reliable. No ads. No freemium. Just tools that work. 🚀 Check it out Try it here: https://www.a2zedit.com Would love to hear your feedback or suggestions for new tools. Let me know what you think in the comments! Note: This was built with Next.js, runs entirely in the browser, and scores 100/100 on Lighthouse (Performance, Accessibility, Best Practices, SEO).

2026-08-29 原文 →
AI 资讯

JavaScript "Variables"

Hi all, I learned about variables in JavaScript recently. Variables are containers which used to store data. It can be declared in 4 ways. Using let e.g., let x = 2 ; let y = 3 ; let z = x + y ; Using Const e.g., const x = 3 ; const y = 4 ; const z = x * y ; Using Var e.g., var a = 1 ; var b = 1 ; var c = a - b ; Automatically a=10; b=5; c=a-b;

2026-08-29 原文 →
AI 资讯

Nine puzzle solvers, one browser tab, zero servers: a tour of classic search algorithms

I recently finished building a small suite of puzzle and game solvers that all run entirely in the browser — no backend, no API calls, no machine-learning models. You paste in a Sudoku, a chess position, or a crossword pattern, and the answer comes back instantly, computed on your own device. The fun part wasn't the UI. It was that each puzzle turned out to be a textbook excuse to reach for a different classic algorithm. Nine solvers, and I got to use constraint propagation, adversarial search, heuristic search, brute-force scanning, and plain old pattern matching — the stuff that shows up in an algorithms course and then, in most day jobs, never again. This is a tour of which algorithm fits which puzzle, and a few of the potholes I hit along the way. Everything here is vanilla JavaScript running in a Web Worker. The one design constraint: no server Before the algorithms, the rule that shaped all of them: it has to run client-side. That's a privacy choice (your puzzle never leaves the tab) and a cost choice (no compute bill), but it's also a fun forcing function. You can't lean on a beefy backend or a hosted model — you get one browser thread (well, a Worker thread) and whatever you can compute in a few hundred milliseconds. That budget is exactly why classic algorithms shine here. They're fast, deterministic, and small enough to ship as a script. Let's group the solvers by the technique each one leans on. Family 1: Constraint propagation Sudoku Sudoku is the poster child for constraint propagation. A cell that can only be one value forces that value; that in turn shrinks its neighbours' options, which forces more cells, and so on. Most "easy" and "medium" boards fall over from propagation alone (naked singles + hidden singles), and only the hard ones need a backtracking search on top. The nice property: the same engine that solves the board also powers the hint feature (find the next forced cell and explain why it's forced) and a uniqueness check — count solutions,

2026-08-29 原文 →
AI 资讯

Building a Client-Side Byte to String Decoder with Unicode Support

Hey DEV community! 👋 When debugging network streams, parsing custom file formats, or inspecting database buffers, we often extract data as raw arrays of numbers rather than human-readable text. This data typically presents itself as raw byte sequences formatted in either decimal or hexadecimal notation. While there are online decoders available, pasting raw byte sequences into third-party sites that process data on their backend databases introduces an unnecessary data privacy risk. To solve this, I designed a lightweight, entirely browser-based Byte to String Converter that decodes raw byte sequences locally using standard JavaScript APIs. In this post, we will look at how bytes map to character encodings and implement a client-side JavaScript utility to decode them safely. The Structure of a Byte In modern computing, a byte is the basic unit of digital information, consisting of an 8-bit sequence: 1 byte = 8 bits Because each bit represents a binary state (0 or 1), a single byte can represent: 2 8 = 256 states This translates to numeric values spanning from: Decimal (Base 10): Range of [ 0 , 255 ] Hexadecimal (Base 16): Range of [ 00 , FF ] When we render characters on a screen, we rely on character encoding tables (such as ASCII or UTF-8) to map these numerical byte values back to their original symbolic representations. Navigating Encodings: ASCII vs. UTF-8 The reconstruction process depends entirely on the encoding format used: ASCII: A basic 7-bit standard where each character maps to exactly one byte. It covers basic English letters, numbers, and core control characters. For example, the decimal value 72 maps to the uppercase letter 'H' . UTF-8: A variable-length encoding format that utilizes between 1 and 4 bytes per character. This structure allows UTF-8 to represent emojis, mathematical notations, and diverse language scripts. Our browser utility parses byte sequences using UTF-8 to maintain compatibility with modern web standards. JavaScript Implementatio

2026-08-29 原文 →
AI 资讯

I Built an API Because My Government’s Website Got the Date Wrong (and Just… Deleted It)

There’s a funny (and slightly sad) story behind why I built mabims.dev . It started with a date. More specifically, a Hijri date . Once Upon a Time, the Government Website Had the Date For a long time, Indonesia’s Ministry of Religious Affairs (Kemenag) website displayed the current Hijri date. It was convenient. You opened the website, looked at the corner of the page, and there it was: Today: 30 Sha'ban Simple enough. A lot of people, including me, got used to relying on it. Then one day, something weird happened. A post went viral. Someone noticed that the official calendar published by Kemenag said one date , while the date displayed on Kemenag’s own website said the next day . They were off by one day. People started asking: How can the official website and the official calendar disagree with each other? The post spread. People discussed it. And then… The Solution? Just Delete It. I didn't know what exactly happened behind the scenes. Maybe it was a bug. Maybe it was a calculation issue. Maybe the website was using a different data source. I don't know. But I do remember what happened eventually. The Hijri date disappeared from the website. Problem solved. Technically. If you can't display the wrong date, you can't display a wrong date. Elegant. 😂 At the time, I just thought it was funny. A few years later, I became a developer. And suddenly, the story made a lot more sense. Years Later, I Became a Junior Developer Once I started working as a developer, I learned how easy it is to add a Hijri date to a website. You don't need to calculate the lunar calendar yourself. You just install a library. Or call an API. There are plenty of them. The problem is that most of the libraries and APIs you'll find use Umm al-Qura by default. And that's perfectly reasonable. Umm al-Qura is the official calendar of Saudi Arabia. It's well documented, widely supported, and easy to integrate. For a developer who just wants: Gregorian date → Hijri date it works great. But there's a

2026-08-29 原文 →
AI 资讯

How a WhatsApp Web Extension Interacts With the Chat Interface

When people see a browser extension add translation controls, a side panel, or a sending workflow to WhatsApp Web, a common question is: how does the extension actually interact with the page? The short answer is that a modern Chrome extension is split across several execution environments. No single script should be responsible for the interface, persistent state, task scheduling, and access to the page at the same time. This article explains the architecture at a practical level without depending on private implementation details that may change whenever WhatsApp Web changes. A browser extension does not run as one program The simplest mental model is to divide the extension into four parts: The extension interface A background service worker A content script attached to WhatsApp Web A small bridge running in the page's own JavaScript context Each part has a different job and a different level of access. The extension interface is what the user sees: forms, task history, translation settings, saved scripts, and media selection. It should focus on interaction rather than long-running work. The background service worker coordinates tasks and stores state. It can receive a request from the interface, keep track of progress, and send commands to the correct WhatsApp Web tab. The content script lives alongside the webpage. It can inspect the rendered document, inject controls, and communicate with the extension runtime. Chrome isolates it from the page's own JavaScript environment for security. The page bridge exists because isolation is sometimes a limitation. A content script can see the DOM, but it does not automatically share the same JavaScript objects as WhatsApp Web. When deeper page integration is required, a carefully scoped bridge can exchange explicit messages between the isolated extension world and the page world. Why not put everything in the content script? It is tempting to keep the entire feature in one file because the content script is already attach

2026-08-29 原文 →
AI 资讯

OWASP Mobile Top 10 — M5: Insecure Communication

Welcome to the fifth article in our OWASP Mobile Top 10 2024 series! In previous articles we covered M1: Improper Credential Usage, M2: Inadequate Supply Chain Security, M3: Insecure Authentication/Authorization, and M4: Insufficient Input/Output Validation. Today we discuss why "we already use HTTPS" isn't a sufficient answer. Introduction M5 is the most misleading item on the list, because most teams read it and move on: "We use HTTPS, this doesn't apply to us." OWASP's definition is far broader. This risk covers all aspects of getting data from point A to point B, but doing it insecurely. It encompasses mobile-to-mobile communications, app-to-server communications, or mobile-to-something-else communications. It includes all communications technologies that a mobile device might use: TCP/IP, WiFi, Bluetooth/Bluetooth-LE, NFC, audio, infrared, GSM, 3G, SMS, etc. So M5 isn't just "do you use HTTPS." It's all of this: Whether you set up TLS correctly (certificate checking, cipher selection) Whether your traffic is consistent (some endpoints HTTPS, others not) What your third-party SDKs are doing What your WebView is loading What you send over alternate channels like push notifications and SMS 💡 Key point: Just because an app uses transport security protocols doesn't mean it's implemented correctly. HTTPS is not a checkbox; it's a system that must be configured properly. A specific situation for React Native developers In React Native the network layer lives in three separate places, and most developers only think about the first: The JavaScript side — fetch , axios , XMLHttpRequest Platform configuration — ATS on iOS, Network Security Config on Android Native modules and SDKs — analytics, ads, crash reporting, payment SDKs Whatever you do on the JavaScript side, if platform configuration is loose or a third-party SDK uses plaintext HTTP, your app is exposed. OWASP Assessment Metric Value Meaning Exploitability EASY A proxy and the same network is enough Prevalence CO

2026-08-29 原文 →
开发者

Audio Fingerprinting Discovered on Alibaba Websites While Debugging BLE Multipoint Disconnects

A recent discovery revealed that AliExpress employs silent audio streams for device fingerprinting, leveraging the Web Audio API. This technique involves analyzing hardware-specific audio processing to distinguish user devices. Privacy-focused browsers have developed countermeasures, highlighting a security gap in current web standards regarding audio context initialization and user privacy. By Olimpiu Pop

2026-08-28 原文 →
AI 资讯

A test said the server started. I deleted the server. It still passed.

Here is a test from a real, well run Node project: test ( ' server starts ' , async ( t ) => { const app = build () await app . listen ({ port : 0 }) t . assert . ok ( true , ' server started ' ) }) It reads fine in review. It runs green. Now delete the body of build() so the server never comes up. The test is still green, because the only thing it asserts is true . In the same file two more of these caught the error in a catch and asserted true there too, so even the failure path was green. That is not a made up example. I found it in fastify at a pinned commit and opened a PR to fix it. More on that at the end. A whole class of tests cannot fail Once you start looking, the pattern turns up in a few shapes: A literal: assert.ok(true) , expect(1).toBe(1) , a snapshot of a constant. An assertion parked in a catch the happy path never reaches, so nothing is checked when the code works and nothing is checked when it breaks. A status list that accepts both outcomes: assert.ok([200, 500].includes(res.status)) . Each one runs, counts toward coverage and guards nothing. Coverage is the trap. The line executed, so the tool that counts executed lines is happy. Whether the line would go red on a regression is a different question. It is the one that matters. Why review misses it A reviewer reading the diff sees a test called server starts , an await listen and a green tick. The name states intent. The assertion is what actually runs, yet ok(true) does not look like a problem until you stop and ask what would ever turn this test red. A missing check does not show up in a diff the way a wrong line does. Finding them I wrote a small scanner for this. No account, no config file, no network call: npx margyn-scan /path/to/repo One of its checks is cannot-fail : tests whose assertions hold whatever the code does. It also flags tests that assert nothing at all, files the build reads that git never committed, gates declared in package.json that no workflow invokes and linter exclusion

2026-08-28 原文 →
AI 资讯

I Built 143 Free Browser Tools — Then Added 144 Step-by-Step Guides for Every Single One

Last month I shared how I built 143 free online tools that run 100% in your browser — no signup, no uploads, no watermarks. That post got a great response (and a lot of "how is this free?" comments — answer: it stays free because files never touch a server, so there are no processing costs). Today's update: every single tool now has a full guide series. What's new 144 how-to articles — one per tool — live at toolfyra.vercel.app/blog : Step-by-step guides — every input explained, common pitfalls, pro tips Real competitor comparison tables (we scraped and analyzed who ranks for what, and where their tools annoy users with account walls) FAQ sections with schema markup so answers surface directly in search and AI assistants Unique generated illustrations per article Smart related-tools clusters — finish one task, the next tool is one click away Why guides for calculator tools? Because "how to use a calculator" is what people actually search for. Tools win clicks; guides win trust and rankings . Each article is built from real search-engine data: live SERP results, keyword expansions, and competitor FAQ analysis — zero guesswork. The engineering side (for the dev readers) Every tool is a single HTML page with vanilla JS — calculators run client-side, file tools use Canvas/FileReader APIs The blog is generated (Python build script): schema.org BlogPosting + FAQPage + BreadcrumbList, per-post OG images as optimized SVGs, canonical URLs, sitemap + IndexNow pings on every deploy New site-wide: instant search (type "pdf" → live results dropdown, keyboard-first: / to focus, ↑↓ to navigate), a Tools dropdown with 11 categories, and a mobile hamburger panel — all vanilla JS, no dependencies Privacy by architecture: there is literally no upload endpoint to breach What's next More waves of content (FAQ, mistakes-to-avoid, and comparison articles for every tool) A batch of new tools from our demand-research pipeline (we score thousands of real search phrases before writing a line

2026-08-28 原文 →
AI 资讯

Astro Introduces Sätteri: A Rust-powered Markdown And Mdx Processor With Up To 60% Faster Builds

Sätteri is a high-performance Markdown and MDX processor developed by the Astro team. Built in Rust, it enhances build speeds by up to 61% for Astro 7.0. Sätteri supports flexible JavaScript plugins and integrates various Markdown features natively. It maintains compatibility with the unified ecosystem while offering faster parsing and reduced dependencies. By Daniel Curtis

2026-08-27 原文 →
AI 资讯

Essential developer utility tools

1. Crypto & Security Tools Crucial for authentication setup, payload verification, and security testing. JWT Parser / Decoder: Decodes JSON Web Tokens ( Header , Payload , and Signature ) without transmitting secret keys over the internet. Token & Password Generator: Generates cryptographically secure random passwords and API tokens with customizable character sets, lengths, and complexity rules. Hash Text Generator: Computes cryptographic hashes (MD5, SHA-1, SHA-256, SHA-512) for strings to verify integrity or check signature matching. Bcrypt Hash / Verifier: Hashes plain-text passwords or checks plain text against an existing hash using the bcrypt algorithm. UUID / ULID Generator: Creates universally unique identifiers (v4 UUIDs) or time-sortable lexicographically sortable unique identifiers (ULIDs). BIP39 Mnemonic Generator: Generates seed phrases and cryptographic keys used in wallet initialization and HD key generation. RSA Key Pair Generator: Generates public and private RSA key pairs directly in the browser for local testing of asymmetric encryption systems. Basic Auth Generator: Quickly constructs Authorization: Basic <base64> HTTP header credentials from a username and password. 2. Formatters & Prettifiers (Development) Saves hours when dealing with messy logs, API responses, or raw system configurations. JSON Prettify & Minify: Formats unformatted API JSON strings with customizable indentation or compresses them into a single line to reduce payload sizes. JSON Diff: Highlights additions, deletions, and structural changes between two JSON payloads. SQL Prettify: Formats raw SQL queries into clean, readable multi-line statements with capitalized keywords. YAML / XML Formatter: Cleans up indentation, validates structure, and formats raw XML and YAML files. Docker Run to Docker Compose: Translates single CLI flags ( docker run -d -p 80:80 ... ) into a structured docker-compose.yml file. Cron Expression Generator & Parser: Provides human-readable schedules from

2026-08-27 原文 →
AI 资讯

A Self-Correcting Solar System Baseline From Sunrise/Sunset Data

A fixed-schedule solar baseline drifts out of sync with the sun throughout the year. In Phoenix the sun is up for 13 hours 10 minutes in late August and 10 hours 2 minutes at the December solstice. A flat daily kWh target flags that entire winter as a fault, then stays quiet on the July afternoon when one string dies at 2pm under full sun. The fix is to anchor the baseline to the actual sun instead of the clock, and most of what you need for that does not require an irradiance forecast. One thing before any code: sun geometry tells you when a system should be producing and when it should peak. It does not tell you how much light actually reached the panels. That is irradiance, and cloud cover swamps it. If you want modeled output in kWh, reach for Forecast.Solar or Solcast, which fold in weather and your array's tilt and azimuth. What follows is the free, dependency-light layer underneath that: the daylight window, the solar-noon peak, and the day-length trend. TL;DR Sun geometry (sunrise, sunset, solar noon, day length) catches a specific class of solar underperformance with no irradiance data. Gate alerts to the real daylight window so your monitor stops crying "underperformance" before sunrise. Track the daily production peak relative to solar noon. A persistent shift across comparable days can reveal shading, orientation, or system changes that a total-kWh check misses. Normalize a flat kWh target by day length so winter stops tripping false alarms. First-order fix, not a physics model. One call to an astronomy endpoint returns all of it. Code below in curl, Python, and Node. For real production forecasting, use an irradiance API. Sun times are the sanity layer, not the forecaster. Sun times will not predict your kWh, but they eliminate common timing-based false alarms and can surface useful production-shape anomalies early. Pull sunrise, sunset, solar noon, and day length once a day, gate your alerts to daylight, watch the peak, and scale the target for season.

2026-08-27 原文 →
AI 资讯

Building Local-First Web Apps: Parsing HTML and PDFs to Markdown in the Browser

Local-first and privacy-focused web utilities are having a massive comeback. With browser engines becoming faster and WebAssembly/Web Workers maturing, there is rarely a reason to push sensitive user documents to an external backend for simple conversions. While building MD-Convert (a zero-upload document to Markdown converter), I explored how to parse real-world documents into clean Markdown entirely on the client side. Here is a breakdown of the core architecture and libraries that make purely in-browser document processing possible. 1. Converting Web Articles with Readability + Turndown Converting messy web markup into clean Markdown involves two distinct steps: Content Extraction: Stripping ads, navbars, sidebars, and trackers. HTML-to-Markdown Transformation: Translating semantic DOM nodes into markdown tokens. Mozilla’s @mozilla/readability paired with turndown is an incredible combination for this: import { Readability } from ' @mozilla/readability ' ; import TurndownService from ' turndown ' ; function htmlToCleanMarkdown ( rawHtmlDocument , sourceUrl ) { // 1. Extract pure article content const reader = new Readability ( rawHtmlDocument ); const article = reader . parse (); if ( ! article || ! article . content ) { throw new Error ( ' Unable to extract main content ' ); } // 2. Initialize Turndown const turndownService = new TurndownService ({ headingStyle : ' atx ' , codeBlockStyle : ' fenced ' }); // Ensure image URLs remain absolute turndownService . addRule ( ' absoluteImages ' , { filter : ' img ' , replacement : ( content , node ) => { const src = node . getAttribute ( ' src ' ); const alt = node . getAttribute ( ' alt ' ) || '' ; if ( ! src ) return '' ; try { const absoluteUrl = new URL ( src , sourceUrl ). href ; return `![ ${ alt } ]( ${ absoluteUrl } )\n\n` ; } catch { return `![ ${ alt } ]( ${ src } )\n\n` ; } } }); return turndownService . turndown ( article . content ); } Offloading Heavy PDF Parsing to Web Workers Parsing large PDFs using pdf

2026-08-27 原文 →