AI 资讯
Blume: Zero-Config Docs Framework That Turns a Markdown Folder into an AI-Ready Website
Blume is an open-source documentation framework that converts Markdown into a complete documentation site. Built with Astro and Vite, it requires only Node.js and a single Markdown file for setup. The framework supports various configurations, offers automatic SEO features, and includes tools for document testing. It facilitates migration from other documentation systems. By Daniel Curtis
AI 资讯
Designing Type-Safe Multi-Calendar Primitives in TypeScript Without 'any'
Handling dates in JavaScript is notoriously error-prone. While ECMAScript's native Date object has well-documented pitfalls—uncontrolled mutability, 0-indexed months, and automatic local-timezone conversions—there is an even larger blind spot in existing libraries like date-fns , dayjs , and luxon : non-Gregorian calendar systems and regional legal date semantics. Global and regional enterprise applications (e.g., banking, fintech, tax compliance, healthcare, public sector, and international travel) frequently operate under official non-Gregorian legal rules: 🇹🇭 Thai Buddhist Era ( พ.ศ. = CE + 543) with official government numbering and Royal Gazette formatting presets. 🇯🇵 Japanese Imperial Era (Reiwa 令和, Heisei 平成, Showa 昭和) with exact historical day-of-event rollover boundaries (e.g., May 1, 2019 Reiwa 1 Gannen). 🇹🇼 Taiwan Minguo (民國紀年) used across municipal and legal filings. 🇸🇦 Islamic Hijri (Astronomical Umm al-Qura, Islamic Civil, and Tabular systems). 🇮🇷 Persian / Solar Hijri (Jalali Khayyami 33-year astronomical leap cycle). 🇮🇳 Indian National Saka Calendar adopted as the official civil calendar of India. To solve this without bloating runtime bundles, dragging in heavy astronomical dependencies, or resorting to loose string parsing and any , we engineered Chronera — an open-source, zero-dependency date and multi-calendar engine written in strict TypeScript. In this deep dive, we'll examine the architectural design decisions, mathematical foundations, and type-level techniques used to model complex multi-calendar domains safely. 1. The Architectural Dilemma: Monolithic Objects vs. Tagged Primitives Most date libraries wrap a native timestamp inside a single monolithic object. The instant you create a date to represent someone's birth date (e.g., 1995-05-15 ), the engine binds it to an hour, minute, second, and UTC timezone offset. When that object is serialized to JSON or transferred across servers in different timezones, classic off-by-one errors happen: //
AI 资讯
CodePen: CryptoCap Landing Page
Crypto landing page with a sleek dark/light mode toggle, stylish Chart.js market graph, and smooth scroll animations using sal.js. Built with Tailwind CSS for a pixel-perfect, fully responsive design. Design inspired by: https://www.figma.com/community/file/1047142300578798855/cryptocurrency-landing-page-dark-mode
AI 资讯
useEffect Fired Twice and It Found a Real Bug
useEffect fired twice, on mount, every single time, in development only. The API call inside it — a POST that created a resource — ran twice, and for about a day we had duplicate records showing up in a table that should have had exactly one insert per page load. The first reaction, and why it was wrong The instinct is to assume a bug — a rerender loop, a missing dependency, something actually broken. React 18's Strict Mode, in development, deliberately mounts, unmounts, and remounts every component once, specifically to surface effects that aren't properly cleaned up. It's not a bug in your code causing a double-fire; it's a bug in your code being caught by a feature built to catch exactly this. useEffect (() => { console . log ( ' mount ' ); // logs twice in dev, once in production const subscription = subscribeToUpdates (); // no cleanup — this is the actual problem Strict Mode is surfacing }, []); Production builds don't do this double-invocation — it's development-only, and specifically Strict-Mode-only, which is why the duplicate inserts we saw locally would eventually have shown up in production too, just less predictably, under a race condition instead of a guaranteed double-fire. Why this is a feature and not noise to suppress An effect that safely tolerates being mounted, torn down, and mounted again is an effect that correctly declares its dependencies and cleans up after itself — which is exactly the property you need for effects to behave correctly under React's concurrent features generally, not just under Strict Mode specifically. The double-invocation in development is a cheap, automatic test for that property, running on every single page load without you writing a test for it. The actual fix useEffect (() => { const subscription = subscribeToUpdates (); return () => subscription . unsubscribe (); // cleanup makes remounting safe }, []); For our specific case — a POST that shouldn't fire twice regardless of mount behavior — the deeper fix was recogn
AI 资讯
Stop changing your sprite sheet to fix animation speed
An eight-frame animation does not have a fixed duration. At 8 fps it lasts one second; at 12 fps it lasts two-thirds of a second; at 16 fps it lasts half a second. Before drawing or generating more frames, check whether the problem is missing poses or the time each pose stays on screen. We maintain FrameSprite, a browser workspace for game assets. This is a timing and export note, not a claim that a particular frame count makes AI animation reliable. The equations work with hand-drawn sprites too. Three numbers that are easy to mix up Source FPS describes how a recording was sampled. Frame count is the number of entries you put in an animation. Playback FPS controls how fast those entries advance in the game. A 24 fps source video can provide eight selected poses that you play at 12 fps. You do not need to preserve every source frame. For equal holds, forward playback and a speed multiplier of 1: duration_seconds = frame_count / playback_fps frame_hold_ms = 1000 / playback_fps fps_for_target = frame_count * 1000 / target_duration_ms Same eight frames Hold per frame Full loop 8 fps 125 ms 1.000 s 12 fps 83.333… ms 0.667 s 16 fps 62.5 ms 0.500 s You changed the cadence without changing one pixel of the sprite sheet. A test you can reproduce Use the public eight-frame sample . Keep the same frames, order, canvas and pivot for all three trials. Change only playback FPS between 8, 12 and 16. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. If the pause happens only at the seam, look for an accidental duplicate endpoint. The sample makes the arithmetic test repeatable. It is not evidence that eight frames is the right budget for every character or action. Do not accumulate rounded timestamps At 24 fps, one hold is 41.666… milliseconds. St
开发者
What a Language Needs Before It Can Compile Itself
Code: Megapixel99/lambda-language lm is a small low-level language I wrote: static types, explicit memory, no closures, no garbage collector, and four independent backends that emit C, WebAssembly, ARM64 and bytecode for a VM. Its compiler is about 4,400 lines of JavaScript. The obvious next question is whether the language can compile itself, and the obvious first step is the lexer, which is 129 lines. In lm the same lexer is 355 lines. That ratio is the finding, because almost none of it is lm being a verbose language. Six specific absences account for nearly all of it, and writing them down was a planned milestone rather than an afterthought: the point of porting the lexer first was to find out what the language could not do while the port was still small enough to abandon. The one that cost the most src/lexer.js has a single advance(n) that moves pos , line and col together, called from 14 places. lm had no way to take the address of a scalar local, so a function could not mutate a caller's variable, and a function returning three values would need a struct allocated on every call. So advance does not exist. All 14 sites write pos += 1; col += 1; inline, and the newline case writes the three-line variant. That is the single largest source of the size difference, and it also caused the only correctness bug in the port. Column counting inside a string literal has to skip UTF-8 continuation bytes, and because the logic is inlined rather than centralised there is no one place to fix it. The two comment scanners over-count a column in exactly the same way. They get away with it only because a comment always ends at a newline, which resets the column before anything reads it. That is worth sitting with. A centralised advance would have been fixed once and been right in all three places. Instead the code is right in one place by correction and in two others by luck, and the luck is load-bearing: change what terminates a comment and two latent bugs become live ones. Dup
AI 资讯
Solid-Vue | The Minimalist Vue + Vite Web Frameworks (No relation to SolidJS or SolidStart at all)
Solid-Vue is a lightweight Vue + Vite framework for small and growing businesses. File-based routing, a built-in server layer powered by h3, and zero extra config to wire together. Features File-based routing — every file in src/pages becomes a route automatically, via unplugin-vue-router. A server, built in — src/server/api holds your API endpoints, served through h3 alongside your frontend. One dev server, one deploy. Vite underneath — instant startup and near-instant HMR. State management ready — Pinia is wired in out of the box. Extensible via add-ons — install Tailwind CSS, icon sets, form validation, i18n, and more with the companion solid-vue-cli. Quick start Don't install this package directly — scaffold a new project instead: npm create solid-vue@latest my-app cd my-app npm install npm run dev Usage vite.config.ts import { defineConfig } from ' vite ' import { solidVue } from ' solid-vue ' export default defineConfig ({ plugins : [ solidVue ({ mode : ' spa ' }) ] }) src/main.ts import { createSolidApp } from ' solid-vue/client ' import App from ' ./App.vue ' const { app , router } = createSolidApp ( App ) router . isReady (). then (() => { app . mount ( ' #app ' ) }) src/server/api/hello.ts import { defineEventHandler } from ' solid-vue/server ' export default defineEventHandler (() => { return { message : ' Hello from Solid-Vue! ' } }) Plugin options solidVue ({ mode : ' spa ' , // 'spa' | 'ssr' | 'ssg' — default: 'spa' apiPrefix : ' /api ' , // prefix for file-based API routes — default: '/api' optimizeCWV : true , // inject Core Web Vitals meta/preconnect tags — default: true }) Package exports Entry Use solid-vue The Vite plugin ( solidVue ), used in vite.config.ts solid-vue/client createSolidApp() — bootstraps Vue, Vue Router, and Pinia solid-vue/server Re-exported h3 utilities ( defineEventHandler , readBody , useSession , etc.) for your API routes Add-ons Add optional integrations to an existing project with the CLI: npx solid-vue add tailwind npx so
AI 资讯
Architecting Enterprise Angular with Signals: Zoneless Reactivity and 60fps Performance
Architecting Enterprise Angular with Signals: Zoneless Reactivity and 60fps Performance For nearly a decade, Angular relied on Zone.js to intercept asynchronous browser events and trigger top-down dirty checking across the entire component tree. In large enterprise dashboards displaying live telemetry, grid streams, and complex forms, this model leads directly to frame drops and memory leaks. With Angular 19+, fine-grained Signals provide a reactive paradigm where the framework tracks exact DOM dependencies at compile-time and updates only the precise DOM nodes that changed, unlocking 60fps zoneless execution . Architecture & Interview Cheat Sheet Feature Legacy RxJS / Zone.js Angular Signals (Modern) Change Detection Dirty-checks entire component tree Fine-grained single DOM node updates Memory Lifecycle Manual takeUntilDestroyed subscriptions Automatic graph cleanup without memory leaks Derivations Complex combineLatest / switchMap Lazy, memoized computed(() => ...) Zone.js Overhead Monkey-patches all browser async APIs 0 overhead ( provideExperimentalZonelessChangeDetection() ) 1: Clean Reactive State with Signals import { Component , computed , signal , effect , inject } from ' @angular/core ' ; export interface TelemetryPacket { id : string ; latencyMs : number ; status : ' healthy ' | ' degraded ' | ' critical ' ; } @ Component ({ selector : ' app-telemetry-monitor ' , standalone : true , template : ` <div class="card"> <h3>Live Ingestion Monitor</h3> <p>Total Packets: {{ packetCount() }}</p> <p>Average Latency: {{ averageLatency().toFixed(2) }}ms</p> <span [class.badge-warn]="isDegraded()"> {{ isDegraded() ? 'DEGRADED PERFORMANCE' : 'NOMINAL' }} </span> </div> ` }) export class TelemetryMonitorComponent { // Primary Writable Signal readonly packets = signal < TelemetryPacket [] > ([]); // Derived Computed Signals (Memoized, evaluated lazily on read) readonly packetCount = computed (() => this . packets (). length ); readonly averageLatency = computed (() => {
AI 资讯
The CORS Header Was Right There and the Browser Blocked It Anyway
The browser console showed exactly what CORS errors always show — a request blocked for violating the same-origin policy — except the response headers, visible in the network tab, clearly included Access-Control-Allow-Origin: * . The header the browser wanted was right there. The browser rejected the request anyway. The detail that's easy to miss in the network tab Chrome's network inspector, by default, coalesces duplicate header names into a single display line — so Access-Control-Allow-Origin: * shown once in the UI can actually mean the header was sent twice by the server, and the browser is showing you a merged, deduplicated view rather than the literal wire response. curl -s -D - https://api.example.com/data -o /dev/null | grep -i access-control Access-Control-Allow-Origin: * Access-Control-Allow-Origin: https://app.example.com Two separate headers, both valid individually, sent by two different layers that each thought they were the one responsible for CORS: our nginx reverse proxy had a blanket add_header Access-Control-Allow-Origin *; for general API access, and the application server behind it independently set a specific origin for authenticated routes. Neither config was wrong on its own. Together, they produced a response with the header appearing twice — and per the Fetch spec, a response with multiple Access-Control-Allow-Origin values is treated as invalid, so the browser blocks the request rather than guessing which one you meant. Why this is worse than a missing header A missing CORS header fails immediately, obviously, the same way every time. A duplicate header fails in a way that looks, from the response body alone, like the header is present and correct — because it is present, twice, which is precisely the state that trips the spec's validation. Every piece of evidence you'd normally check says "this should work," and it still doesn't. The fix Removed the blanket nginx header and let the application server be the single source of truth for COR
AI 资讯
Does That "Free Online PDF" Tool Upload Your File? How to Tell.
Most free online PDF tools work by uploading your document to a server, processing it there, and sending it back. For a lot of files that's fine. For a signed contract, a payslip, a medical form, or a scanned ID, it's the entire privacy problem: your document now lives on someone else's machine, subject to their logging, retention, and breach exposure. It doesn't have to work that way. A modern browser can split, merge, compress, sign, and even OCR a PDF without the file ever leaving your device — using libraries like pdf-lib , pdf.js , jsPDF and SheetJS that run entirely in JavaScript. How to tell an uploader from a client-side tool You don't have to trust a marketing claim. Two checks settle it: Watch the network. Open your browser's DevTools → Network tab, then run the tool on a file. If you see your file leave in a POST/PUT request, it uploaded. A client-side tool shows no upload of the document itself. Pull the plug. Load the page, then turn off Wi-Fi and try the tool again. A client-side tool keeps working offline. An uploader breaks the moment the network is gone. The honest tools pass both tests. If a site can't work offline, your file is going somewhere. The trade-offs, stated honestly Client-side processing isn't a free lunch, and any tool that pretends it is should make you suspicious: Memory. Very large PDFs are held in browser memory, so there's a ceiling a server wouldn't have. Speed. OCR in WebAssembly is slower than a server GPU. It's private, not fast. Fidelity. Converting PDF → Word transfers the text , not the layout — the same is true of every converter, but a client-side one can't hide it behind a server. Compression limits. A PDF shrinks by downsampling embedded images or rasterizing pages; a small or text-only PDF may not shrink at all, and rasterizing removes selectable text. We built 24 client-side PDF tools on exactly this principle and wrote down where each limit is, rather than papering over them. If you're evaluating any online PDF tool
AI 资讯
Twenty Years of jQuery: How a Little Library Rewired Web Development
jQuery, created by John Resig and released in 2006, is a JavaScript library that simplifies HTML manipulation, event handling, animation, and Ajax. It enabled easier web development by providing an accessible API across browsers. While its use has declined with the rise of modern frameworks, jQuery remains prevalent on a significant portion of websites today. By Daniel Curtis
AI 资讯
Protótipos: como a herança realmente funciona no JavaScript
Introdução Muitas linguagens como C#, Java, entre outras são descritas como orientadas a objeto, possibilitando o paradigma Programação Orientada a Objeto (POO). No entanto, quando falamos de JS, sabemos que por mais que existam objetos, ela é dita como uma linguagem orientada a protótipos, mas o que de fato isso significa, qual problema isso resolve e como muda a maneira como programamos? O problema Tanto a orientação a objeto quanto a orientação a protótipo lidam, entre outras coisas, com a questão de como a herança vai funcionar em determinada linguagem e é justamente nesse ponto que as duas abordagens mais se diferem. Em linguagens orientadas a objetos as classes de fato existem, contendo propriedades, métodos e servem como molde para a criação de objetos. Com isso, todo objeto criado a partir de uma classe herda suas propriedades e métodos ficando acessíveis para uso. Como não existem Classes de fato em JavaScript, a herança ocorre de maneira diferente, de objeto para objeto, ligados através da propriedade [[Prototype]] que possui uma referência ao seu protótipo, fazendo com que determinado objeto herde de seu protótipo propriedades e métodos que nunca foram definidos nele. Exemplo com array Quando criamos um array, seja de forma literal com [], ou de forma explícita com new Array(), o resultado final é o mesmo: um array cujo [[Prototype]] aponta para o Array.prototype. Essa propriedade .prototype possui um objeto contendo todas as propriedades e métodos que o [[Prototype]] referencia, possibilitando que todos os arrays possam usar métodos como push, pop, map, filter… Com isso, se irmos além e conferirmos o [[Prototype]] do Array.prototype vamos perceber que ele aponta para o Object.prototype que contém propriedades e métodos também disponível em todo essa cadeia que chamamos de prototype chain . Por fim, se tentarmos visualizar o protótipo do Object.prototype veremos que é null, pois ele representa o último elo dessa cadeia. Teste o código abaixo para ver na p
AI 资讯
AI Can Write Your Code. Can It Actually Debug It?
AI Can Write Your Code. Can It Actually Debug It? AI coding assistants have changed how developers write software. You can describe a feature, generate a function, refactor a component, write a test, or explain an unfamiliar codebase in seconds. But there is one part of software development that is still surprisingly difficult: figuring out why something broke. Writing code and investigating a failure are two very different problems. When an application crashes, the answer usually isn't sitting inside the error message. You have to reconstruct what happened. The Problem With "Just Read the Stack Trace" Consider this Node.js error: TypeError: Cannot read properties of undefined (reading 'email') at getUser (/app/services/user.js:42:18) at processRequest (/app/controllers/auth.js:87:12) at async handler (/app/routes/auth.js:31:5) The immediate problem appears obvious. Something is undefined. But what caused it? Maybe: A database query returned no user. An API returned an unexpected response. Authentication middleware failed. A promise returned an unexpected value. A user record exists but its profile doesn't. An earlier function silently produced invalid state. The stack trace tells you where the program finally failed . It doesn't necessarily tell you where the bug began . That's the difference between error reporting and debugging investigation. AI Coding vs AI Debugging Most AI coding workflows look something like this: Developer ↓ Prompt ↓ AI ↓ Code Debugging is different: Failure ↓ Error ↓ Stack trace ↓ Execution path ↓ Application state ↓ Root cause ↓ Fix The AI needs to reason across that chain. Simply asking: "What does this error mean?" usually produces a list of possible explanations. That's useful, but it's not necessarily an investigation. A better question is: "Given this failure and its context, what is the most likely root cause, what evidence supports it, and how can I reproduce it?" That's a much more interesting problem for AI. A Simple JavaScript De
AI 资讯
Safely parsing email files in the browser
An email file is not just text plus a few attachments. It can contain HTML, nested MIME parts, misleading filenames, inline resources, remote tracking pixels, malformed encodings, and enough data to exhaust a browser tab. Moving parsing into the browser removes an upload from the architecture, but it does not automatically make the viewer safe. It changes the security job: untrusted content is now being interpreted next to the user’s active web session. This is the checklist I use for a local EML and winmail.dat/TNEF reader. Treat every parsed field as untrusted The sender, subject, recipient, filename, MIME type, and message body all came from a file. Render headers and filenames as text, never by concatenating HTML. The same applies to errors. A parser exception can include a filename or fragment of malformed input. Showing that message verbatim may leak data into logs or turn it into markup. Map parser failures to stable error categories, then display a controlled explanation. Normalize into one internal model EML and TNEF have different container structures, but the UI should not contain two independent security implementations. Both parsers can produce a common message model: subject, sender, to, cc, date, plain body, sanitized HTML candidate, attachments[] { safe filename, MIME type, bytes, inline flag, content ID, content location } The normalization layer is the right place to enforce per-source limits and reject unsupported structures. The viewer and download code then work against the same constrained data regardless of input format. Sanitize HTML as hostile input Email HTML was designed for mail clients, not for direct insertion into an application DOM. A conservative policy removes: scripts and event handlers; forms and interactive controls; iframe , object , and embed elements; styles and CSS URLs; unsafe protocols; executable or unexpected embedded content. Use a maintained sanitizer with a pinned version, but do not stop at its default configuration.
AI 资讯
eBay's Browse API Doesn't Return Sold Listings. Here Is a Node.js Alternative
If you are building a pricing, resale, or inventory tool, active listings answer the wrong question. The price a seller asks for an item is not necessarily the price a buyer paid. eBay's public Browse API returns active inventory. Marketplace Insights covers sold history, but access is restricted. That leaves many developers maintaining search-page parsers or using a sold-data provider. This example uses CompSniper because it returns completed listings and a price summary through one GET request. Disclosure: I am Marc, the owner of CompSniper. Make one sold-listings request Node.js 20 and newer already include fetch , URLSearchParams , and request timeouts, so this example does not need an HTTP package. const params = new URLSearchParams ({ keyword : " sony wh-1000xm5 " , count : " 10 " , ebaySite : " ebay.com " , itemCondition : " used " , }); const response = await fetch ( `https://api.compsniper.com/v1/scrape? ${ params } ` , { headers : { Authorization : `Bearer ${ process . env . COMPSNIPER_API_KEY } ` , }, signal : AbortSignal . timeout ( 75 _000 ), }, ); const data = await response . json (); if ( ! response . ok ) { throw new Error ( data . error ?? `HTTP ${ response . status } ` ); } console . log ( " Listings: " , data . totalItems ); console . log ( " Median: " , data . summary . median , data . summary . currency ); console . log ( " Range: " , data . summary . p25 , " to " , data . summary . p75 ); for ( const item of data . items . slice ( 0 , 5 )) { console . log ( item . title , item . soldPrice , item . endedAt ); } Keep the API key in a server, worker, or serverless function. Do not place it in browser JavaScript. Select the buyer's marketplace The marketplace matters. A UK reseller normally wants UK sold listings and prices in pounds, not US listings in dollars. const params = new URLSearchParams ({ keyword : " iphone 15 pro -case -charger " , ebaySite : " ebay.co.uk " , count : " 100 " , itemCondition : " used " , minPrice : " 250 " , maxPrice :
AI 资讯
Namaste JavaScript — Complete Notes
Full interview-prep notes, ##Episode 1 through 29. Episode 1 : Execution Context ============================== Everything in JS happens inside the execution context. Imagine a sealed-off container inside which JS runs. It is an abstract concept that hold info about the env. within the current code is being executed. In the container the first component is memory component and the 2nd one is code component Memory component has all the variables and functions in key value pairs. It is also called Variable environment. Code component is the place where code is executed one line at a time. It is also called the Thread of Execution. JS is a synchronous, single-threaded language Synchronous:- In a specific synchronous order. Single-threaded:- One command at a time. Episode 2 : How JS is executed & Call Stack ============================================= When a JS program is ran, a global execution context is created. The execution context is created in two phases. Memory creation phase - JS will allocate memory to variables and functions. Code execution phase Let's consider the below example and its code execution steps: var n = 2 ; function square ( num ) { var ans = num * num ; return ans ; } var square2 = square ( n ); var square4 = square ( 4 ); The very first thing which JS does is memory creation phase, so it goes to line one of above code snippet, and allocates a memory space for variable 'n' and then goes to line two, and allocates a memory space for function 'square'. When allocating memory for n it stores 'undefined', a special value for 'n'. For 'square', it stores the whole code of the function inside its memory space. Then, as square2 and square4 are variables as well, it allocates memory and stores 'undefined' for them, and this is the end of first phase i.e. memory creation phase. Now, in 2nd phase i.e. code execution phase, it starts going through the whole code line by line. As it encounters var n = 2 , it assigns 2 to 'n'. Until now, the value of 'n' wa
AI 资讯
pnpm 12 Rewrites Package Manager in Rust, Accelerating Installs While Preserving pnpm 11 Workflows
pnpm 12 has transitioned to a native Rust implementation, maintaining compatibility with pnpm 11 commands, flags, and formats. This update enhances startup and filesystem performance, particularly when using existing caches. Community feedback highlights the performance gains while noting some trade-offs in larger artifact sizes. By Daniel Curtis
AI 资讯
JsonFabrica vs. Mockaroo vs. Faker.js for Test Data Generation
If you're generating test data today, you've probably landed on one of three approaches: click through a UI like Mockaroo, pull in a library like Faker.js and write generation code yourself, or call a hosted API like JsonFabrica. Comparing these test data generation tools side by side, the real differences aren't about which one produces "better" fake data — Faker.js, Mockaroo, and JsonFabrica are all capable of that. The differences are about where the tool lives, how it handles relationships between records, and who's responsible for running it. Three test data generation tools compared, shape by shape Mockaroo is a browser-based UI: you define columns and types through a web form, preview rows, and export a file — or hit its API directly, which is available even on the free tier (paid tiers raise the volume ceiling rather than gate API access itself). Faker.js is a JavaScript library: you import it into your own code and call functions like faker.person.fullName() or faker.internet.email() to build up objects yourself, one field at a time. JsonFabrica is an API-first hosted service: you send a schema (or use a template) to an endpoint and get structured, schema-conformant JSON back, with no UI step and no library to install in your own codebase. That distinction matters more than it sounds. A UI tool is something a person operates by hand. A library is something a developer owns and maintains inside their own project — you write the loops, the relationships, the edge cases. An API-first tool is infrastructure: something your CI pipeline, your seed script, or an AI coding agent can call directly, without a human in the loop or generation logic living in your repo. UI vs. library vs. API, in practice Mockaroo's UI is genuinely fast for a one-off task — sketch a schema, click generate, download a CSV or JSON file. What it isn't built for is wiring generation into an automated pipeline where nobody is clicking anything. Its API can cover that, but at free-tier volume
AI 资讯
Your JavaScript Code Works. But How Fast Does It Scale?
Sometimes a simple line of JavaScript can do more work than you expect. For example, array.includes() is fine for small arrays, but using it again and again with large datasets can affect performance. Things get even more interesting when it is used inside another loop. I recently wrote about this with simple JavaScript and React examples, including when using Set or Map can be a better choice. 👉 Read the full article: https://nirmitkotadiya.dev/dsa/big-o-javascript-array-includes You don't need to optimize everything. The important part is knowing where a small change in your data structure can make your code much more efficient.
AI 资讯
Making three years of a Telegram group chat queryable
A three-year group chat is a knowledge base nobody can read. Somewhere in it is how long the tax office actually took, which form replaced the old one, which accountant people quietly stopped recommending. Telegram's search finds a word you already know. It cannot answer a question. The fix is boring in outline: get the history out, turn it into documents, hand them to something that reads — NotebookLM, in my case. I built that pipeline for real chats. The parsing has traps, and I list them below, but the design problem is elsewhere: packing, and making the second run idempotent. Two clients, two shapes Export exists in exactly two places. Telegram Desktop has had it since 2018 and offers JSON or HTML. The native Telegram for macOS app — the Mac-only client, not Desktop — added Export Chat History… in 12.10 (24 August 2026) and writes HTML only; the Mac App Store build was still on 12.9, without the menu item, at the end of August. Telegram Web and the phone apps have nothing. So you have to read both formats: JSON — one result.json with the chat's name at the root and a messages array: {id, date, from, text, text_entities} per message. HTML — paginated. messages.html , messages2.html , messages3.html , one page per file, each a few MB. text is not a string In the JSON export, a plain message has text: "hello" . A message with a link, a bold run or a code span has an array of runs : "text" : [ "see " , { "type" : "bold" , "text" : "section 4" }, " first" ] String(msg.text) on that gives "[object Object]" in the middle of your document, and it does it silently. Join the runs instead: // Telegram writes a formatted message as // text: ["plain ", {type: "bold", text: "…"}, …] — String() gives "[object Object]". function contentValueToString ( v : unknown ): string { if ( v === undefined || v === null ) return '' ; if ( Array . isArray ( v )) { return v . map (( x ) => x !== null && typeof x === ' object ' && ' text ' in x && typeof ( x as { text : unknown }). text ===