npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mikosoft/dodo

v1.1.4

Published

The DoDo is an easy to learn JavaScript framework which helps developers to build reactive, single page applications. It can be used for mobile applications, browser extensions, electron desktop apps, ...etc.

Downloads

599

Readme

DoDo Framework

A lightweight JavaScript framework for building reactive single-page applications — no build tools, no TypeScript, no complexity.

License: MIT Version

DoDo is a modern frontend framework built on the MVC pattern (Model-View-Controller). If you've used Angular 1 or similar frameworks, you'll feel right at home. Unlike React, Vue, or Angular 2+, DoDo does not use components — instead, a single Controller manages the whole page.


Why DoDo?

  • No build step — write plain ES6+ JavaScript, open in the browser, done
  • No TypeScript required — just JavaScript
  • No dependencies — zero external packages
  • Familiar MVC pattern — easy to reason about and test
  • Reactive $model — change a property, the HTML updates automatically
  • Built-in utilities — HTTP client, auth, forms, cookies, storage, events

Installation

npm install --save @mikosoft/dodo

Or scaffold a full project instantly:

npm init dodo

How It Works — The Big Picture

DoDo apps follow a simple flow:

URL changes  →  Router matches route  →  Controller runs  →  View renders
  1. The App listens for URL changes.
  2. When a URL matches a route, DoDo runs the corresponding Controller.
  3. The Controller loads HTML views, sets data on $model, and calls render().
  4. DoDo processes all dd-* directives in the HTML and updates the DOM.

Quick Start

1. Entry point — app.js

import { App } from '@mikosoft/dodo';
import { $auth, $httpClient, $debugOpts } from './conf/index.js';

import HomeCtrl from './controllers/HomeCtrl.js';
import AboutCtrl from './controllers/AboutCtrl.js';
import NotfoundCtrl from './controllers/NotfoundCtrl.js';

const $routes = [
  ['when', '/',       HomeCtrl],
  ['when', '/about',  AboutCtrl],
  ['notfound',        NotfoundCtrl]
];

const app = new App('myApp');
app
  .auth($auth)
  .httpClient($httpClient)
  .debug($debugOpts);

app
  .routes($routes)
  .listen();          // start listening for URL changes

2. Controller — HomeCtrl.js

import { Controller } from '@mikosoft/dodo';

class HomeCtrl extends Controller {

  // 1. Load HTML views from files
  async __loader(trx) {
    await this.loadView('#main', '/views/home.html');
  }

  // 2. Set initial data
  async __init(trx) {
    this.$model.title = 'Welcome to DoDo!';
    this.$model.users = [];
  }

  // 3. Render dd-* directives in the HTML
  async __rend(trx) {
    await this.render();
  }

  // 4. Run code after the page is rendered (attach plugins, fetch data, etc.)
  async __postrend(trx) {
    const resp = await this.$httpClient.get('/api/users');
    this.$model.users = resp.data;
    await this.render('users'); // re-render only the 'users' part
  }

}

export default HomeCtrl;

3. View — home.html

<h1 dd-text="$model.title"></h1>

<ul>
  <li dd-each="users" dd-text="$model.name"></li>
</ul>

<a dd-href="/about">Go to About</a>

That's it. When $model.users is set, calling render('users') updates only the dd-each="users" part of the page.


App vs AppOne

| | App | AppOne | |---|---|---| | Routing | Yes — multiple routes/pages | No — single page only | | Use case | SPAs, web panels | Browser extensions, simple tools |

// Multi-page app with routing
import { App } from '@mikosoft/dodo';
const app = new App('myApp');
app.routes($routes).listen();

// Single page, no routing needed
import { AppOne } from '@mikosoft/dodo';
const app = new AppOne('myApp');
app.controller(MyCtrl);

Controller Lifecycle

Every controller runs its hooks in this order:

__loader()  →  __init()  →  __rend()  →  __postrend()

| Hook | Purpose | |---|---| | __loader(trx) | Load HTML views/partials into the DOM | | __init(trx) | Set initial values on $model | | __rend(trx) | Call render() to process all dd-* directives | | __postrend(trx) | Run code after rendering (fetch data, init plugins) | | __destroy() | Cleanup listeners when navigating away |

The trx object carries route information:

async __loader(trx) {
  console.log(trx.uri);          // '/users/42'
  console.log(trx.params);       // { id: '42' }
  console.log(trx.query);        // { sort: 'asc' }
}

Directives Reference

Directives are special dd-* HTML attributes that DoDo processes during render().

Data Binding

| Directive | Description | Example | |---|---|---| | dd-text | Sets text content | <p dd-text="$model.name"></p> | | dd-html | Sets inner HTML | <div dd-html="$model.richContent"></div> | | dd-model | Two-way binding (input ↔ $model) | <input dd-model="$model.email"> | | dd-value | Sets the value attribute | <input dd-value="$model.count"> | | dd-set | One-way: input → $model, no re-render | <input dd-set="$model.search"> | | dd-label | Sets label text | <label dd-label="$model.fieldName"></label> | | dd-placeholder | Sets placeholder text | <input dd-placeholder="$model.hint"> | | dd-title | Sets title attribute | <span dd-title="$model.tooltip"></span> | | dd-data | Sets data-* attributes | <div dd-data="id::$model.userId"></div> |

Conditionals

| Directive | Description | Example | |---|---|---| | dd-if | Show element if truthy | <div dd-if="$model.isAdmin">...</div> | | dd-elseif | Else-if branch | <div dd-elseif="$model.isMod">...</div> | | dd-else | Else branch | <div dd-else>...</div> | | dd-visible | Toggle CSS display (element stays in DOM) | <div dd-visible="$model.show">...</div> |

Loops

| Directive | Description | Example | |---|---|---| | dd-each | Iterate an array | <li dd-each="users" dd-text="$model.name"></li> | | dd-each2 | Iterate a sub-array inside a dd-each row | nested lists | | dd-entries | Iterate an object's key/value pairs | <li dd-entries="settings"></li> | | dd-repeat | Repeat element N times | <div dd-repeat="5">★</div> |

Attributes

| Directive | Description | |---|---| | dd-class | Add/remove CSS classes dynamically | | dd-style | Set inline styles | | dd-disabled | Disable form elements | | dd-readonly | Set readonly attribute | | dd-required | Set required attribute | | dd-checked | Checkbox / radio checked state | | dd-selected | Select option selected state | | dd-src | Image or media src attribute | | dd-attr | Any attribute dynamically | | dd-min / dd-max | Input min/max attributes |

Events

| Directive | Description | Example | |---|---|---| | dd-click | Click event | <button dd-click="save()">Save</button> | | dd-change | Change event | <select dd-change="onSelect()"> | | dd-keyup | Keyup event (optional key filter --13) | <input dd-keyup="search()"> | | dd-enter | Enter key shortcut | <input dd-enter="submit()"> | | dd-evt | Any DOM event via --<eventName> | <div dd-evt--mouseover="onHover()"> | | dd-outclick | Fires when clicking outside the element | dropdowns, modals | | dd-intersect | Fires when element enters the viewport | lazy loading, animations | | dd-swipe | Fires on touch swipe (optional direction filter) | carousels, drawers |

Navigation & DOM

| Directive | Description | Example | |---|---|---| | dd-href | Client-side navigation (no page reload) | <a dd-href="/about">About</a> | | dd-elem | Expose DOM element as this.$elem.<name> | <canvas dd-elem="chart"></canvas> | | dd-setinitial | Read element value into $model on load | <input dd-setinitial="$model.lang"> |


Built-in Libraries

All libraries are available from corelib:

import { corelib } from '@mikosoft/dodo';
const { Auth, HTTPClient, HTTPClientFetch, Form, Cookie, BrowserStorage, Paginator, eventEmitter, navig, util } = corelib;

Auth

JWT-based authentication with cookie support, route guards, auto-login, and role-based access control. Inject it into the app via app.auth($auth) — then use this.$auth inside any controller.

HTTPClient / HTTPClientFetch

Two HTTP clients (XMLHttpRequest and Fetch API). Support GET, POST, PUT, DELETE, PATCH, request/response interceptors, automatic retries, and custom headers. Inject via app.httpClient($httpClient).

Form

Reads and writes HTML form values to/from $model. Handles text, checkbox, radio, select, and file inputs with optional validation and type conversion.

Cookie & BrowserStorage

  • Cookie — get/set/delete cookies with expiry, secure, and domain options
  • BrowserStorage — simple wrappers around localStorage and sessionStorage

Paginator

Handles paginated data: calculates page ranges, total pages, and navigation state.

EventEmitter

Pub/sub event bus for decoupled communication between controllers:

eventEmitter.emit('user:loggedIn', userData);
eventEmitter.on('user:loggedIn', (data) => { ... });

Navig

Programmatic navigation and URL utilities:

navig.goto('/dashboard');
navig.getCurrentURI(); // '/dashboard?tab=stats'

Util

General-purpose helpers for type checking, object cloning, string manipulation, and more.


App Configuration

const app = new App('myApp');

app.auth($auth)              // inject Auth instance → available as this.$auth in controllers
app.httpClient($httpClient)  // inject HTTP client → available as this.$httpClient
app.debug($debugOpts)        // control which debug messages appear in the console
app.fridge($fridge)          // shared data object that persists across route changes → this.$fridge
app.i18n($i18n)              // translations object → used by View.loadI18n(langCode)
app.preflight([fn1, fn2])    // functions that run before every controller's __loader()
app.postflight([fn1, fn2])   // functions that run after every controller's __postrend()
app.destroyflight(fn)        // function that runs when any controller is destroyed (route change)
app.ssr()                    // enable SSR mode — dispatches 'ssr-ready' window event after first route renders

Route definitions

const $routes = [
  ['when',     '/users/:id',  UserCtrl,     { authGuards: ['isLogged'] }],
  ['when',     '/login',      LoginCtrl],
  ['redirect', '/home',       '/'],         // redirect /home → /
  ['do',       [logFn]],                    // run on every route change
  ['notfound', NotfoundCtrl]
];

Supported Environments

  • Standard browsers (Chrome, Firefox, Safari, Edge)
  • Browser extensions
  • Electron desktop apps
  • Cordova / PhoneGap mobile apps
  • Progressive Web Apps (PWAs)

Documentation

Full documentation, tutorials, and live examples:

http://dodo.mikosoft.info


Links