<h1 id="id-klaf">Klaf</h1> <p><a href="https://www.jsdelivr.com/package/npm/klaf.js"><img src="https://data.jsdelivr.com/v1/package/npm/klaf.js/badge" alt="jsdelivr"></a> <img src="https://github.com/izure1/klaf/actions/workflows/node.js.yml/badge.svg" alt="node.js workflow"></p> <p>Very simple read/write database with a <strong>NoSQL</strong>.<br>It's written in JavaScript using pure Node.js API and pretty easy and small.<br>This database works seamlessly with <a href="https://nodejs.org/">Node.js</a>, <a href="https://bun.sh/">Bun</a> <a href="https://deno.com/">Deno</a>, and Browser, and Web worker.</p> <p>Choose the <a href="#database">database</a> and <a href="#engine">engine</a> that best fit your needs to handle your data efficiently!</p> <h2 id="id-database">Database</h2> <p><strong>klaf</strong> comes in two flavors: <strong>key-value</strong> database and <strong>document-oriented</strong> database.<br>You have the freedom to choose based on your needs, <strong>but most users will likely prefer the <em>document-oriented</em> database.</strong></p> <p>For details on how to use each database, please refer to the links below.</p> <ul> <li><a href="./docs/document/README.md"><strong>Document-oriented Database</strong></a></li> <li><a href="./docs/core/README.md"><strong>Key-value Database</strong></a></li> </ul> <h2 id="id-engine"><a href="./docs/engine/README.md">Engine</a></h2> <p>Klaf.js introduces the concept of an engine, which is an instance responsible for handling how data is stored. Currently, three types of engines are supported by default: <strong>FileSystem</strong>, <strong>InMemory</strong>, and <strong>WebWorker</strong>. If needed, you can also create your own custom engine.<br>Choose the engine that best fits your needs.</p> <p>For a detailed list of the supported engines and more information, refer to <a href="./docs/engine/README.md">this link</a>.<br>If you're unsure what to choose, select the <strong>FileSystem</strong> engine.</p> <h2 id="id-example">Example</h2> <p>This example shows how to build a database using the document-oriented database <strong>KlafDocument</strong> and the <strong>FileSystem</strong> engine.</p> <pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">KlafDocument</span>, <span class="hljs-title class_">DataJournal</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'klaf.js'</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">FileSystemEngine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'klaf.js/engine/FileSystem'</span> <span class="hljs-keyword">const</span> db = <span class="hljs-keyword">await</span> <span class="hljs-title class_">KlafDocument</span>.<span class="hljs-title class_">Open</span>({ <span class="hljs-attr">path</span>: <span class="hljs-string">'my-database-path.db'</span>, <span class="hljs-attr">version</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">engine</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileSystemEngine</span>(), <span class="hljs-attr">journal</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataJournal</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileSystemEngine</span>()), <span class="hljs-attr">scheme</span>: { <span class="hljs-attr">id</span>: { <span class="hljs-attr">default</span>: <span class="hljs-function">() =></span> crypto.<span class="hljs-title function_">randomUUID</span>() }, <span class="hljs-attr">nickname</span>: { <span class="hljs-attr">default</span>: <span class="hljs-function">() =></span> <span class="hljs-string">'Anonymous'</span>, <span class="hljs-attr">validate</span>: <span class="hljs-function">(<span class="hljs-params">v</span>) =></span> <span class="hljs-keyword">typeof</span> v === <span class="hljs-string">'string'</span>, }, <span class="hljs-attr">gender</span>: { <span class="hljs-attr">default</span>: <span class="hljs-function">() =></span> <span class="hljs-string">'other'</span>, <span class="hljs-attr">validate</span>: <span class="hljs-function">(<span class="hljs-params">v</span>) =></span> [<span class="hljs-string">'male'</span>, <span class="hljs-string">'female'</span>, <span class="hljs-string">'other'</span>].<span class="hljs-title function_">includes</span>(v), } } }) <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">put</span>({ <span class="hljs-attr">nickname</span>: <span class="hljs-string">'faker'</span>, <span class="hljs-attr">gender</span>: <span class="hljs-string">'male'</span> }) <span class="hljs-keyword">const</span> [err, documents] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">pick</span>({ <span class="hljs-attr">gender</span>: <span class="hljs-string">'male'</span> }) </code></pre> <p>The following example demonstrates how to build a database using the key-value database <strong>Klaf</strong> and the <strong>WebWorker</strong> engine.</p> <pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Klaf</span>, <span class="hljs-title class_">DataJournal</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'klaf.js'</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">WebWorkerEngine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'klaf.js/engine/WebWorker'</span> <span class="hljs-keyword">const</span> db = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Klaf</span>.<span class="hljs-title class_">Open</span>({ <span class="hljs-attr">path</span>: <span class="hljs-string">'my-database-path.db'</span>, <span class="hljs-attr">engine</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebWorkerEngine</span>(), <span class="hljs-attr">journal</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataJournal</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">WebWorkerEngine</span>()) }) <span class="hljs-keyword">const</span> [errPut, key] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">put</span>(<span class="hljs-string">'Faker, GOAT.'</span>) <span class="hljs-keyword">const</span> [errPick, row] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">pick</span>(key) row.<span class="hljs-property">record</span>.<span class="hljs-property">payload</span> <span class="hljs-comment">// "Faker, GOAT."</span> </code></pre> <h2 id="id-install">Install</h2> <h3 id="id-nodejs-npm">Node.js (NPM)</h3> <pre><code class="hljs language-bash">npm i klaf.js </code></pre> <h3 id="id-deno-jsr">Deno (JSR)</h3> <pre><code class="hljs language-bash">deno add jsr:@izure/klaf </code></pre> <h3 id="id-browser-esm">Browser (ESM)</h3> <pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Klaf</span>, <span class="hljs-title class_">KlafDocument</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/klaf.js/dist/esm/index.mjs'</span> <span class="hljs-comment">// engines</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">InMemoryEngine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/klaf.js/dist/esm/engine/InMemory.mjs'</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">WebWorkerEngine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/klaf.js/dist/esm/engine/WebWorker.mjs'</span> </code></pre> <h2 id="id-why">Why</h2> <h3 id="id-why-use-klaf">Why use <strong>klaf</strong>?</h3> <p>JavaScript has numerous fantastic database libraries available, but at times, they can seem overly complex.<br>This particular solution is ideal for situations where you need to store data for an extended period, making it well-suited for less critical data that doesn't require a rigid structure.</p> <p>Since it is implemented in pure JavaScript, there is no need for pre-builds or configuration based on the Node.js version. It is compatible with all versions!</p> <h3 id="id-why-should-i-use-this-instead-of-json">Why should I use this instead of <strong>JSON</strong>?</h3> <p>When the <strong>JSON</strong> files get large, quick data read and write operations can become challenging.<br><strong>klaf</strong> handles data input and output in real-time, ensuring fast and lightweight performance. Check the performance tests below.</p> <h2 id="id-how">How</h2> <h3 id="id-how-does-klaf-work">How does <strong>klaf</strong> work?</h3> <p><strong>klaf</strong> manages files by breaking them into logical blocks called pages. You can set the page size when creating the database.</p> <p>When you insert data, the ID you get back includes information about where the data is stored on the page. This makes it possible to work with large files quickly. This value could be seen by users, but it's encrypted to make it hard to predict. This way, it stops users from trying to steal data by requesting fake record IDs.</p> <h3 id="id-how-many-can-i-own-data">How many can I own data?</h3> <p><strong>klaf</strong> can make a unsigned 32bit range of page block. This is a <strong>4,294,967,296</strong>. And each page can own unsigned 32bit range of records also. So you can theoretically insert <strong>4,294,967,295</strong> * <strong>4,294,967,295</strong> records.</p> <h2 id="id-performance-test">Performance Test</h2> <p>The test result is the average value from 10 attempts.<br>The latest performance benchmarking was conducted based on version <strong>2.0.6</strong>. This test was conducted using the <strong>FileSystem</strong> engine, and it was tested without the <strong>Journal</strong> feature.</p> <p><strong>klaf</strong> supports two databases, and this test tested the core functions of data reading/writing of the two databases. Therefore, it's not a perfect test result, but it's enough to show the time complexity.</p> <p>If you're adding data to the database in real-time, the results would be as follows:</p> <h3 id="id-write">WRITE</h3> <p>Overall, Klaf supports faster writes than JSON. As the size increases, this gap becomes even larger.</p> <div class="table-responsive"><table class="table table-striped"><tr> <th><code>WRITE</code></th> <th>JSON</th> <th>KLAF</th> <th><code>RESULT</code></th> </tr> <tr> <td>1,000 times</td> <td>1014ms</td> <td>864ms</td> <td><em><strong>+15% Faster</strong></em></td> </tr> <tr> <td>2,000 times</td> <td>2200ms</td> <td>1700ms</td> <td><em><strong>+23% Faster</strong></em></td> </tr> <tr> <td>4,000 times</td> <td>5674ms</td> <td>3163ms</td> <td><em><strong>+44% Faster</strong></em></td> </tr> <tr> <td>8,000 times</td> <td>15332ms</td> <td>5925ms</td> <td><em><strong>+61% Faster</strong></em></td> </tr> </table></div><h3 id="id-read">READ</h3> <p><strong>klaf</strong> maintains a steady reading speed no matter the database size. In contrast, JSON files slow down as they get bigger.</p> <div class="table-responsive"><table class="table table-striped"><tr> <th><code>READ</code></th> <th>JSON</th> <th>KLAF</th> <th><code>RESULT</code></th> </tr> <tr> <td>from 8,000 records</td> <td>1.8ms</td> <td>2ms</td> <td><em>-10% Slower</em></td> </tr> <tr> <td>from 16,000 records</td> <td>4ms</td> <td>2ms</td> <td><em><strong>+100% Faster</strong></em></td> </tr> <tr> <td>from 32,000 records</td> <td>5.4ms</td> <td>2ms</td> <td><em><strong>+170% Faster</strong></em></td> </tr> <tr> <td>from 64,000 records</td> <td>11.4ms</td> <td>2ms</td> <td><em><strong>+470% Faster</strong></em></td> </tr> <tr> <td>from 128,000 records</td> <td>26.4ms</td> <td>2ms</td> <td><em><strong>+1220% Faster</strong></em></td> </tr> </table></div><h3 id="id-result">RESULT</h3> <p><img src="./docs/asset/image/svg_perf_write.svg" alt="WRITE"> <img src="./docs/asset/image/svg_perf_read.svg" alt="READ"></p> <p><strong>NOTICE!</strong></p> <p>*This is the usual case, but the results can be different depending on programming optimizations. Please note that this test takes a square of the sample size to easily show the error with a small number of tests. Therefore, the graph appears to increase exponentially, but in terms of time complexity, JSON has <strong>O(n)</strong>, and klaf has a speed of <strong>O(1)</strong> or <strong>O(log n)</strong>.*</p> <p>*The Journal feature is designed to enhance database stability. However, it may slightly reduce performance, approximately by <strong>25~30%</strong>.*</p> <h2 id="id-repository">Repository</h2> <div class="table-responsive"><table class="table table-striped"><tr> <th>Site</th> <th>Link</th> </tr> <tr> <td><strong>NPM</strong></td> <td><a href="https://www.npmjs.com/package/klaf.js">View</a></td> </tr> <tr> <td><strong>JSR</strong></td> <td><a href="https://jsr.io/@izure/klaf">View</a></td> </tr> <tr> <td><strong>jsdelivr</strong></td> <td><a href="https://www.jsdelivr.com/package/npm/klaf.js">View</a></td> </tr> <tr> <td><strong>Github</strong></td> <td><a href="https://github.com/izure1/klaf">View</a></td> </tr> </table></div><h2 id="id-migration">Migration</h2> <p>The Klaf library is the new name for the TissueRoll library.</p> <div class="table-responsive"><table class="table table-striped"><tr> <th>Version</th> <th>Link</th> </tr> <tr> <td>From Klaf 2.x to Klaf 3.x</td> <td><a href="./docs/migration/8.md">Link</a></td> </tr> <tr> <td>From Klaf 1.x to Klaf 2.x</td> <td><a href="./docs/migration/7.md">Link</a></td> </tr> <tr> <td>From TissueRoll 5.x.x to Klaf 1.x</td> <td><a href="./docs/migration/6.md">Link</a></td> </tr> </table></div><h2 id="id-license">License</h2> <p>MIT LICENSE</p>