<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ray-D-Song</title>
    <description>The latest articles on DEV Community by Ray-D-Song (@ray-d-song).</description>
    <link>https://dev.to/ray-d-song</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1347491%2F9f1d8d38-7c2a-40af-b900-a2beddd4769b.jpeg</url>
      <title>DEV Community: Ray-D-Song</title>
      <link>https://dev.to/ray-d-song</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ray-d-song"/>
    <language>en</language>
    <item>
      <title>I built a Github user analysis and ranking website</title>
      <dc:creator>Ray-D-Song</dc:creator>
      <pubDate>Mon, 25 Nov 2024 05:08:34 +0000</pubDate>
      <link>https://dev.to/ray-d-song/i-built-a-github-user-analysis-and-ranking-website-5d3a</link>
      <guid>https://dev.to/ray-d-song/i-built-a-github-user-analysis-and-ranking-website-5d3a</guid>
      <description>&lt;p&gt;&lt;a href="https://github-persona.pages.dev" rel="noopener noreferrer"&gt;https://github-persona.pages.dev&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Sometimes I treat participating in Github as a game, like I will follow some big guys, see what they are doing recently, whether they have any new projects, any new ideas.  &lt;/p&gt;

&lt;p&gt;Then I realized that submitting code, participating in discussions, and solving problems can be quantified. I can make a website to analyze Github users, rank them, just like in the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Pages&lt;/li&gt;
&lt;li&gt;Cloudflare D1&lt;/li&gt;
&lt;li&gt;Cloudflare AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use Cloudflare Worker to develop it, because it has convenient CI/CD service and free quota.  &lt;/p&gt;

&lt;p&gt;One of the requirements is to use large models to summarize users. Cloudflare AI perfectly meets this requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project is completely open source, you can view the source code on &lt;a href="https://github.com/ray-d-song/github-persona" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Welcome to join and improve it together.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>cloudflarechallenge</category>
      <category>github</category>
    </item>
    <item>
      <title>How to detect code language in browser</title>
      <dc:creator>Ray-D-Song</dc:creator>
      <pubDate>Fri, 22 Nov 2024 01:47:57 +0000</pubDate>
      <link>https://dev.to/ray-d-song/how-to-detect-code-language-in-browser-424h</link>
      <guid>https://dev.to/ray-d-song/how-to-detect-code-language-in-browser-424h</guid>
      <description>&lt;p&gt;Repository: &lt;a href="https://github.com/ray-d-song/guesslang-js" rel="noopener noreferrer"&gt;https://github.com/ray-d-song/guesslang-js&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Demo: &lt;a href="https://ray-d-song.github.io/guesslang-js/" rel="noopener noreferrer"&gt;https://ray-d-song.github.io/guesslang-js/&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Recently, I'm working on a project called &lt;a href="https://github.com/Ray-D-Song/EchoRSS.git" rel="noopener noreferrer"&gt;EchoRSS&lt;/a&gt;, and I have a very wanted feature, which is to intercept external links in subscriptions (read full text, quote, etc.) and display them directly on the current page.  &lt;/p&gt;

&lt;p&gt;There is a problem that the returned HTML code block loses the language annotation (or the language was not annotated on the pre and code tags in the original code block), so it cannot be highlighted using tools like shiki or prism.js.  &lt;/p&gt;

&lt;p&gt;I found three solutions to detect code language:  &lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://github.com/github-linguist/linguist" rel="noopener noreferrer"&gt;linguist&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is a Ruby project deployed on the server, and Github uses it to detect the language composition of the repository. If you need extremely high accuracy and can be calculated on the server, this is the best solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://github.com/highlightjs/highlight.js" rel="noopener noreferrer"&gt;hljs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;highlight.js is a very famous web code highlighting library, and it is also the only library that provides automatic code detection.&lt;br&gt;&lt;br&gt;
The principle is very simple, which is to enumerate the keywords of the language, and then match them one by one with the text, and finally see which one has the highest matching degree.  &lt;/p&gt;

&lt;p&gt;hljs has four problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It requires a very long code length, and most languages require at least 300 characters to achieve a relatively good accuracy.
&lt;/li&gt;
&lt;li&gt;The part that detects the language is not a separate module, but tightly coupled with the parser and render, and the code is also very imperative, making it difficult to extract useful parts.&lt;/li&gt;
&lt;li&gt;If you don't extract the detection module, the original format (line breaks and indentation) of the code will be lost when using hljs to highlight.&lt;/li&gt;
&lt;li&gt;It requires a lot of regular matching, the performance is poor, and because of reason 2, it cannot be run in a web worker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://github.com/yoeo/guesslang" rel="noopener noreferrer"&gt;guesslang&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;guesslang is a machine learning project based on tensorflow.js.&lt;br&gt;&lt;br&gt;
Microsoft ported this project to node.js in 2021 and added the &lt;a href="https://code.visualstudio.com/updates/v1_60#_automatic-language-detection" rel="noopener noreferrer"&gt;automatic language detection&lt;/a&gt; function to vscode.  &lt;/p&gt;

&lt;p&gt;A Vietnamese guy &lt;a href="https://github.com/hieplpvip" rel="noopener noreferrer"&gt;hieplpvip&lt;/a&gt; three years ago also ported this project to the &lt;a href="https://github.com/hieplpvip/guesslang-js" rel="noopener noreferrer"&gt;browser&lt;/a&gt;, but there are also three problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory leak, memory leak...&lt;/li&gt;
&lt;li&gt;Only supports the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to introduce the umd format, does not support esm, does not support bundle&lt;/li&gt;
&lt;li&gt;Similarly, because of reason 2, it does not support web worker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The guy has not maintained this project, and the feat request to support esm in March has not been replied.  &lt;/p&gt;

&lt;p&gt;So I extracted the detection module from hljs, and forked guesslang-js to fix the above problems, and in the end guesslang won, the result is this:&lt;br&gt;
&lt;a href="https://github.com/ray-d-song/guesslang-js" rel="noopener noreferrer"&gt;https://github.com/ray-d-song/guesslang-js&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;I think it's too much to talk about, maybe someone will need it in the future, so I'll post it.&lt;/p&gt;

&lt;p&gt;If someone knows tensorflow.js, I hope they can recommend some learning materials, I want to further modify it to web gpu calculation to improve efficiency.&lt;/p&gt;

</description>
      <category>tensorflow</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The difference between Error and Exception in JavaScript</title>
      <dc:creator>Ray-D-Song</dc:creator>
      <pubDate>Sun, 17 Nov 2024 14:59:57 +0000</pubDate>
      <link>https://dev.to/ray-d-song/the-difference-between-error-and-exception-in-javascript-4mjl</link>
      <guid>https://dev.to/ray-d-song/the-difference-between-error-and-exception-in-javascript-4mjl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/Ray-D-Song" rel="noopener noreferrer"&gt;https://github.com/Ray-D-Song&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Error and exception are concepts born from practice, aiming to handle "programmable errors".  &lt;/p&gt;

&lt;h2&gt;
  
  
  Error
&lt;/h2&gt;

&lt;p&gt;From a code perspective, error tends to be manually handled precisely.&lt;br&gt;&lt;br&gt;
For example, fnA calls fnB and fnC. Both methods may encounter errors, and the processing code is roughly as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnA&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bErr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bRes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// error handling&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cErr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cRes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// error handling&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// normal logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key to "error" is to return an object or array from the function, with one field representing "an error occurred". As long as this field is not empty, programmers know that the normal flow has been interrupted.&lt;/p&gt;

&lt;p&gt;JavaScript has an internal &lt;code&gt;Error&lt;/code&gt; object and constructor, but the field representing the error is not required to be an Error object. Instead, the Error object is more often used in exception handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exception
&lt;/h2&gt;

&lt;p&gt;We already have error handling, why do we need exception?  &lt;/p&gt;

&lt;p&gt;Imagine a scenario where you have a button. When the button is clicked, it triggers function A. After going through multiple layers of calls (perhaps 10 layers), an error occurs in function X. You don't want to tell the user "unknown error", but rather want to provide specific information about what went wrong.&lt;/p&gt;

&lt;p&gt;You can achieve this effect with errors, but you need to write ten times this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnA&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// display error to user&lt;/span&gt;
    &lt;span class="nf"&gt;showErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnC&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// propagate error&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... 10 similar passes&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnX&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// propagate error&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of boilerplate code is very inefficient. A better method is to use exceptions.&lt;/p&gt;

&lt;p&gt;You only need to throw an exception when an error occurs in fnY. At the top level, you can catch it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnA&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fnB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fnY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fnX&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// 抛出&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, no matter where an error occurs, it can be caught at the top level, and the code in other layers is not affected.&lt;br&gt;&lt;br&gt;
Avoid polluting the entire code structure with errors at one place.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why distinguish between the two?
&lt;/h2&gt;

&lt;p&gt;We have already explained why we need exceptions, but why do we need to distinguish between errors and exceptions?&lt;/p&gt;

&lt;p&gt;The best practice is to strictly distinguish between the two. If an error does not need to be passed up layer by layer, it should be handled directly in the current layer. For example, the error of fnC does not need to be used in fnA, so it should be handled as an error directly in B.&lt;/p&gt;

&lt;p&gt;Assume that all errors are handled at the top level, then all logic is piled up in the catch block at the top level, which is difficult to maintain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;task2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;task3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I built a full-stack web archive tool running on Cloudflare</title>
      <dc:creator>Ray-D-Song</dc:creator>
      <pubDate>Sun, 10 Nov 2024 03:47:41 +0000</pubDate>
      <link>https://dev.to/ray-d-song/i-built-a-full-stack-web-archive-tool-running-on-cloudflare-5g0b</link>
      <guid>https://dev.to/ray-d-song/i-built-a-full-stack-web-archive-tool-running-on-cloudflare-5g0b</guid>
      <description>&lt;p&gt;Project address: &lt;a href="https://github.com/ray-d-song/web-archive" rel="noopener noreferrer"&gt;https://github.com/ray-d-song/web-archive&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build this tool
&lt;/h2&gt;

&lt;p&gt;I have been a loyal user of ArchiveBox for a long time. ArchiveBox is a very good web archiving tool, but it requires self-hosting and has high server requirements (requires headless browser). I used a Raspberry Pi before, and the performance was not good.&lt;/p&gt;

&lt;p&gt;And for websites like x and Medium, which require login, ArchiveBox needs to manually configure tokens or cookies, which is troublesome.&lt;/p&gt;

&lt;p&gt;So I thought, can there be a web archiving tool that doesn't require self-hosting, doesn't require headless browser, has no requirements for server, and can be cross-platform? Then I can access my archived pages anywhere, anytime, on any device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Cloudflare
&lt;/h2&gt;

&lt;p&gt;Cloudflare's Workers service is very powerful and free, with plenty of D1 databases and R2 storage buckets, which is very suitable for building this tool.&lt;/p&gt;

&lt;p&gt;More importantly, Cloudflare's ecosystem is complete, supports one-click deployment and data migration. Cloudflare's global CDN service can also be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can this tool do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[x] Folder classification&lt;/li&gt;
&lt;li&gt;[x] Page preview image&lt;/li&gt;
&lt;li&gt;[x] Title keyword search&lt;/li&gt;
&lt;li&gt;[x] Showcase, share the pages you captured&lt;/li&gt;
&lt;li&gt;[x] Mobile support&lt;/li&gt;
&lt;li&gt;[x] Tag classification system&lt;/li&gt;
&lt;li&gt;[x] Read mode&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;web-archive is composed of the following parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser extension: Save the page as a webpage snapshot and upload it to the server.&lt;/li&gt;
&lt;li&gt;Server: Receive the snapshot and metadata uploaded by the browser extension, and store them in the database and storage bucket.&lt;/li&gt;
&lt;li&gt;Web client: Query the snapshot and display it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used SingleFile's open-source code to save the page as a single html file (even including images and videos).&lt;/p&gt;

&lt;p&gt;The server is completely based on Cloudflare's Workers service, with D1 database for storing metadata and R2 storage bucket for storing snapshots.&lt;/p&gt;

&lt;p&gt;Although the number of interfaces is not small, I did not use ORM, actually I tried prisma and drizzle, because they caused a lot of trouble for deployment, so they were not used in the end.&lt;/p&gt;

&lt;p&gt;The web client is built with React, Vite, TailwindCSS, and shadcn/ui, and the packaged size is astonishingly small, only 1.5MB. The packaged product will be embedded in the assets folder of the server, so it does not need to be deployed separately when deploying the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;I really like Cloudflare's free services, but there are some limitations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The CPU calculation time of a single request cannot exceed 10 milliseconds, otherwise it will be forcibly terminated. (I was surprised to find that the paid account is 30 seconds 😂)&lt;/li&gt;
&lt;li&gt;The memory usage cannot exceed 256MB, otherwise it will be forcibly terminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These limitations have affected the construction of the website to some extent, such as ssr or dom parsing during crawling.&lt;/p&gt;

&lt;p&gt;However, no matter how it is said, thank you, Cloudflare!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
