<?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: Elvin Ega</title>
    <description>The latest articles on DEV Community by Elvin Ega (@elvinega).</description>
    <link>https://dev.to/elvinega</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3437059%2Fb3d828da-4aee-4e43-a897-46365c325a7c.png</url>
      <title>DEV Community: Elvin Ega</title>
      <link>https://dev.to/elvinega</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elvinega"/>
    <language>en</language>
    <item>
      <title>What an offline-first point-of-sale app has to get right</title>
      <dc:creator>Elvin Ega</dc:creator>
      <pubDate>Wed, 30 Sep 2026 07:14:08 +0000</pubDate>
      <link>https://dev.to/elvinega/what-an-offline-first-point-of-sale-app-has-to-get-right-4ckn</link>
      <guid>https://dev.to/elvinega/what-an-offline-first-point-of-sale-app-has-to-get-right-4ckn</guid>
      <description>&lt;h2&gt;
  
  
  What an offline-first point-of-sale app has to get right
&lt;/h2&gt;

&lt;p&gt;A shop on a Saturday afternoon. The connection drops. The queue is six people deep. The cashier finishes the sale, prints the receipt, and the stock count updates.&lt;/p&gt;

&lt;p&gt;Weza POS is built on the assumption that the second half of that story is the normal case, not the outage case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint moves into the data layer
&lt;/h2&gt;

&lt;p&gt;Most point-of-sale apps are thin clients. The till asks a server for the price, the server records the sale, the server adjusts inventory. When the server stops answering, the till stops being a till.&lt;/p&gt;

&lt;p&gt;Weza POS puts the database on the counter computer. Checkout, barcode scanning, receipt printing, and stock deduction all run locally. Cloud sync is something you switch on, not something the checkout path waits for. Backup files are local, and data export is available from the app.&lt;/p&gt;

&lt;p&gt;That single decision pushes work into places a cloud-first design never has to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The local database is the source of truth, so reconciliation is your problem.&lt;/li&gt;
&lt;li&gt;Backups are the shop's responsibility unless they pay for off-site.&lt;/li&gt;
&lt;li&gt;Reports have to be computed from local data, not queried from a warehouse.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hardware is where the ugly parts live
&lt;/h2&gt;

&lt;p&gt;POS integrations get difficult at the device layer, and this is the part developers underestimate.&lt;/p&gt;

&lt;p&gt;Thermal receipt printers in this class speak ESC/POS, which is a command set rather than a driver. You are writing bytes to a device, whether that is a serial port, a USB endpoint, or a network socket. The 58mm and 80mm roll widths are the two common form factors.&lt;/p&gt;

&lt;p&gt;Barcode scanners in keyboard-emulation mode present as a keyboard, so they need no driver. That is why "works with a standard USB scanner" is achievable. Cash drawers are usually kicked by a pulse sent over RJ11 or RJ12, often through the printer rather than directly.&lt;/p&gt;

&lt;p&gt;None of that is hard on its own. It gets hard when you have to support the long tail of hardware a real shop already owns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping the same app to three platforms
&lt;/h2&gt;

&lt;p&gt;The builds are an .exe for Windows, a .dmg for macOS, and .deb or AppImage for Linux. ⟨verify⟩&lt;/p&gt;

&lt;p&gt;Desktop distribution means you own updates, code signing on two platforms, and whatever the Linux packaging story turns into. A web app avoids all of it, which is the trade you make for working offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The licence model is a product decision
&lt;/h2&gt;

&lt;p&gt;The Pro tier is a one-time payment per device. The Business tier is a subscription that adds cloud backup and sync, plus the multi-staff features. ⟨verify⟩&lt;/p&gt;

&lt;p&gt;That split is coherent. Offline resilience is what you buy once. Off-site durability is what you rent. A shop that wants both a local database and automatic remote backup pays monthly, and a shop that is willing to run its own backups does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions I would ask before picking it
&lt;/h2&gt;

&lt;p&gt;If you are evaluating this for a shop you support, these are the ones that decide it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How does sync resolve conflicts? If two tills sell the same last unit while both are offline, what does the merged inventory look like?&lt;/li&gt;
&lt;li&gt;Is the local database encrypted at rest, and where do the keys live?&lt;/li&gt;
&lt;li&gt;What is the restore path from a backup file, and how long does it take on a busy morning?&lt;/li&gt;
&lt;li&gt;Which printers and scanners have actually been tested? The compatibility list matters more than the feature list.&lt;/li&gt;
&lt;li&gt;Is sync one-way or two-way? One-way upload is easier to reason about and easier to recover from.&lt;/li&gt;
&lt;li&gt;What happens to reports and history if a subscription lapses?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The site answers some of these and not others. ⟨verify⟩&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this design fits
&lt;/h2&gt;

&lt;p&gt;One or two tills, a fixed counter, and an internet connection you cannot depend on. A pharmacy that cannot hold a queue while a connection resets. A bar on a Friday night.&lt;/p&gt;

&lt;p&gt;If you are running a cloud POS in a place with reliable connectivity, the offline architecture buys you less. If you are not, it is the difference between a lost sale and a recorded one.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>offlinefirst</category>
      <category>desktop</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Supercharging AI Development with code-contextify</title>
      <dc:creator>Elvin Ega</dc:creator>
      <pubDate>Fri, 15 Aug 2025 11:20:19 +0000</pubDate>
      <link>https://dev.to/elvinega/supercharging-ai-development-with-code-contextify-53ih</link>
      <guid>https://dev.to/elvinega/supercharging-ai-development-with-code-contextify-53ih</guid>
      <description>&lt;p&gt;As AI-powered coding becomes the norm, one challenge keeps popping up — &lt;strong&gt;how do we give AI models complete context about our codebase?&lt;/strong&gt; If you’ve ever tried pasting a dozen files into ChatGPT, Claude, or Gemini, you know the pain.&lt;/p&gt;

&lt;p&gt;That’s exactly where &lt;strong&gt;&lt;a href="https://github.com/ElvinEga/code-contextify" rel="noopener noreferrer"&gt;code-contextify&lt;/a&gt;&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;This nifty CLI tool takes your &lt;strong&gt;entire codebase&lt;/strong&gt;, cleans it up, and turns it into a &lt;strong&gt;single AI-ready text file&lt;/strong&gt; — perfect for sending to &lt;em&gt;any&lt;/em&gt; large language model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jy4mhck9vlclb8qozqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jy4mhck9vlclb8qozqa.png" alt="Ai code-contextify" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What is code-contextify?
&lt;/h2&gt;

&lt;p&gt;Simply put, &lt;strong&gt;code-contextify&lt;/strong&gt; scans your project, applies smart filters (respects &lt;code&gt;.gitignore&lt;/code&gt;, skips binaries, removes irrelevant noise), and outputs a &lt;strong&gt;formatted, readable text file&lt;/strong&gt; that contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📊 &lt;strong&gt;Project statistics&lt;/strong&gt; (total files, total size)&lt;/li&gt;
&lt;li&gt;🌳 &lt;strong&gt;Directory tree&lt;/strong&gt; with excluded file markers&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Full file contents&lt;/strong&gt; (only relevant code and docs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that instead of AI guessing about your project, you can hand over &lt;strong&gt;full, precise context&lt;/strong&gt; in one shot.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Why Developers Love It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full AI Context&lt;/strong&gt; — Feed GPT, Claude, or Gemini the &lt;em&gt;entire&lt;/em&gt; codebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfect for Code Reviews&lt;/strong&gt; — Share with teammates without sending raw repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for Debugging&lt;/strong&gt; — AI can see the full picture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Filtering&lt;/strong&gt; — No more handpicking files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy First&lt;/strong&gt; — Runs locally, no data leaves your machine&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; code-contextify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or without installing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx code-contextify /path/to/project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Basic Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Convert current directory into AI context&lt;/span&gt;
code-contextify

&lt;span class="c"&gt;# Convert a specific directory&lt;/span&gt;
code-contextify /path/to/project

&lt;span class="c"&gt;# Custom output file&lt;/span&gt;
code-contextify my-context.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-contextify review-context.txt &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"node_modules,dist,*.min.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates &lt;code&gt;review-context.txt&lt;/code&gt; containing &lt;strong&gt;only the files that matter&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 What the Output Looks Like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project Statistics:
Total Files: 42
Total Size: 1.25 MB

Folder Structure (Tree)
=====================
Legend: ✗ = Excluded
├── src/
│   ├── components/
│   │   ├── Header.js
│   │   └── Footer.js
│   └── utils/
├── node_modules/ ✗
├── package-lock.json (123.45 KB) ✗
└── README.md (2.34 KB)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Followed by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/components/Header.js
------------------------
import React from 'react';
// ... actual file content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📏 Counting Tokens Before Sending to AI
&lt;/h2&gt;

&lt;p&gt;Before sending your context file to an AI model, it’s smart to check token usage — especially for models with smaller context windows.&lt;/p&gt;

&lt;p&gt;You can use &lt;strong&gt;&lt;a href="https://quizgecko.com/tools/token-counter" rel="noopener noreferrer"&gt;QuizGecko’s Token Counter&lt;/a&gt;&lt;/strong&gt; to paste your generated file and see how many tokens it contains.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Sending to Any AI Model
&lt;/h2&gt;

&lt;p&gt;Once you have your &lt;code&gt;.txt&lt;/code&gt; file, you can paste it directly into an AI chat or send it via API.&lt;/p&gt;

&lt;p&gt;Example with OpenAI API:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;project-context.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a senior developer reviewing a project.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Here is the full project context:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can swap &lt;code&gt;gpt-4&lt;/code&gt; for &lt;strong&gt;Claude&lt;/strong&gt;, &lt;strong&gt;Gemini&lt;/strong&gt;, or any other LLM.&lt;/p&gt;




&lt;h2&gt;
  
  
  📥 Try It Now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/ElvinEga/code-contextify" rel="noopener noreferrer"&gt;github.com/ElvinEga/code-contextify&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NPM:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/code-contextify" rel="noopener noreferrer"&gt;npmjs.com/package/code-contextify&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stop pasting files one by one — let your AI see &lt;em&gt;everything&lt;/em&gt; at once.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Turn your codebase into conversation — one context file at a time.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
  </channel>
</rss>
