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

@dokebi/module

v0.4.1

Published

Feature modules for Dokebi apps: controllers, models, views and routes in one folder mounted under a prefix.

Readme

@dokebi/module 0.4.1

An optional package for Node >=24.0.0 and core ^0.4.0 (public on npm). A module is one feature as an MVC unit of business code (D-0022, D-0032). It keeps controllers, services, models, views, routes and sockets in one folder, and apps mount it under a prefix. It is not an HTTP-only bundle, so business code without a web side (job processing, event subscriptions) is a module too.

Folder

src/shared/modules/<module>/
  module.ts          export default defineModule({ ... })
  routes.ts          routes registered when an app mounts the module
  controllers/       this module's controllers (created once in the module's setup)
  models/            this module's models (plain code)
  views/default/     (modules with pages only) EJS views; other templates hold only changed files
  sockets/           (when needed) defineSocket

dokebi module add <name> creates the skeleton and adds it to the generated registration code. Create a module without pages with --no-views; its routes then return JSON. dokebi module remove <name> removes only the registration and leaves the folder. dokebi module list shows the list. dokebi add @dokebi/module only creates the src/shared/modules/ folder.

defineModule

export default defineModule({
  name: 'board',                        // the folder name; the ID is dokebi-module.board
  requires: [db, other],                // plugin instances, descriptors, other modules
  controllers: [postsController],       // created once with the module context
  contributes: [mcpTools],              // points of plugins it contributes to; module.contribute in setup
  views: new URL('./views/', import.meta.url),   // default/ required; template names get the prefix module.board
  routes(route, module) {               // registered per app (when mounted). page = { app, module, base, view, inertia }
    route.get('/', (request, reply, page) =>
      page.view.reply(reply, 'index', module.controller(postsController).list(page)));
  },
  sockets: (module) => [boardSocket(module)],     // registered per app under the prefix; tenant is <namespace>:<tenant>
  events: (module) => [boardStream(module)],      // (0.1.3) SSE routes (@dokebi/sse); same rules as sockets
  setup(module) { return api; },        // optional; the return value is the API of whoever requires this module
  start(module) {}, drain(module) {},
});
  • module (ModuleContext): PluginContext plus module (the name) and controller(token).

    • It reads only the declared requires.
    • When a controller factory passes a freshly built descriptor object, the declared descriptor with that name is found.
  • version: an exact semver, default '0.1.0'. Requires with duplicate names are refused.

  • views: adds a template-ejs dependency automatically. Without views/default/ the start fails.

  • controllers: uses @dokebi/controller (optional peer). Calling module.controller() without controllers fails with declares no controllers.

  • setup: optional. The older setup(context) form still works.

  • contributes (D-0130): the points of other plugins this module contributes to (for example mcpTools of @dokebi/mcp). setup calls module.contribute(point, value) with objects that use the module's own services; core sets the module up before every collector of the point. A point that nothing collects drops its values with warn POINT_NOT_COLLECTED; an undeclared point is UNDECLARED_POINT; modules never collect. See AI.md §3a.

  • Shared APIs: APIs that must answer on every domain are registered directly with router or web in setup, for example the playground's /api/*.

  • Mounting on apps: defineApp({ modules: { '/board': board } }). Routes and sockets answer only on that app's domains and prefix. A mounted module's API is readable with app.get(board) without listing it in the app's requires. Details are in @dokebi/app.

  • View lookup order:

    1. <app>/<template>/<module>/…
    2. <app>/default/<module>/…
    3. the module's <template>/…
    4. the module's default/…
    5. the app's views (layouts and partials)
    6. shared src/shared/views

    So an app can change a single file of a module page, for example src/apps/<app>/views/christmas/<module>/heading.ejs.

  • Inertia apps: the module produces props with page.inertia.render(request, reply, 'Component', props). The app keeps the Vue pages in views/<template>/Pages/.

  • Sockets and SSE: the return types of sockets and events (0.1.3) are defined by shape only (ModuleSocketRoute, ModuleEventRoute), so projects that do not use them need neither @dokebi/websocket nor @dokebi/sse. The mounting app registers them under its namespace rules (D-0061): the tenant is <namespace>:<authenticated tenant>, and an authenticated namespace must be in that app's list.

  • moduleMount(plugin) and MODULE_MOUNT: how the app plugin reads the mounting information. App code never needs them.

Group plugin

createPlugin({ app: app.view, entry: { modules: [...] } }) groups several modules into a dependency graph and exposes the frozen names (plugin contract v2, D-0028).

  • Generated code: passes the default exports of the modules registered with dokebi module add to this entry. Projects with the old entry file src/shared/modules/index.ts get them appended after its modules list.
  • Accepted modules: only what defineModule made (IDs starting with dokebi-module.). Duplicates are INVALID_PLUGIN_INPUT. A single module may also be registered directly with app.register.
  • Lifecycle: setup registers routes, start prepares to run, drain stops new input and waits for work in progress, and context.defer cleans up after the whole drain.
  • Drain order: reverse dependency order. A module's drain starts after the drains of the modules that depend on it have finished. Unrelated modules drain at the same time.
  • Instances: module names cannot repeat. One module instance is used by one Application only.
  • Not provided: automatic discovery, dynamic import, hot reload and folder scanning while running. The CLI writes registrations into the generated code.
  • Configuration: the YAML settings are an empty object.

Examples:

See the AI guide for errors and cautions.

Service composition

Use Controller -> Service -> Model for application data. Controllers handle HTTP input/output; services provide data and coordinate workflows through models; models own persistence. Generated app/module starters include this chain. Keep services free of request/reply and rendering objects, and reuse them from jobs. Compose dependencies once during setup. App-specific services belong in src/apps/<app>/services, cross-app services in src/shared/services, and module services in src/shared/modules/<module>/services. Shared services must not import app-specific code. These are ordinary application classes, not a new framework plugin or service registry. Existing projects are not moved automatically.