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

@currentjs/gen

v0.5.2

Published

CLI code generator

Readme

@currentjs/gen

A CLI code generator that transforms YAML specifications into fully functional TypeScript applications following clean architecture principles.

Table of Contents

Installation

npm install -g @currentjs/gen
# or use without installing
npx @currentjs/gen

Quick Start

Building an application from scratch:

  1. Initialize a new project:
currentjs init myapp
cd myapp
  1. Create a module:
currentjs create module Blog
  1. Run an interactive command:
currentjs create model Blog:Post

It will:

  • ask everything it needs,
  • generate yaml config,
  • generate a TypeScript source code,
  • and build it.

Alternatively, you can:

  • edit the generated module YAML at src/modules/Blog/blog.yaml. Define the domain model fields, use cases, API endpoints, and web routes.
  • Generate TypeScript files from the YAML configuration: currentjs generate Blog
  • If needed, make manual changes to generated files (domain entities, views, services).
  • Commit those manual changes so they survive regeneration: currentjs commit

To add custom (non-CRUD) behavior: define a method in the service, reference it in the module YAML as a handler, regenerate, and commit.

currentjs generate Blog
currentjs commit

Development Flow

                    ┌──────────────────┐
                    │   currentjs init │
                    └────────┬─────────┘
                             │
                             ▼
                ┌────────────────────────┐
                │ currentjs create module│
                └────────────┬───────────┘
                             │
                ┌────────────┴────────────┐
                │                         │
                ▼                         ▼
   ┌────────────────────┐   ┌────────────────────────┐
   │ Edit module YAML   │   │ currentjs create model │
   │ (define structure) │   │ (interactive wizard)   │
   └─────────┬──────────┘   └────────────┬───────────┘
             │                           │
             ▼                           │
   ┌───────────────────┐                 │
   │ currentjs generate│                 │
   └─────────┬─────────┘                 │
             │                           │
             └────────────┬──────────────┘
                          │
                          ▼
              ┌───────────────────────┐
              │ Modify generated files│
              │ (entities, views, etc)│
              │ (optional step)       │
              └───────────┬───────────┘
                          │
                          ▼
                ┌───────────────────┐
                │ currentjs commit  │
                └───────────────────┘

There are two paths after creating a module:

  • Interactive wizard (currentjs create model Blog:Post) — prompts for fields, use cases, routes, and permissions, then generates everything automatically.
  • Manual editing — edit the module YAML by hand, then run currentjs generate. This gives full control over every configuration option.

Both paths converge at the same point: once files are generated, you can optionally customize the generated code (business logic, templates, etc.) and run currentjs commit to preserve those changes across future regenerations.

When you need behavior beyond standard CRUD:

  1. Implement the custom method in the generated service class.
  2. Reference it in the module YAML as a handler (e.g., service:myMethod).
  3. Optionally add API/web endpoints for the new action.
  4. Regenerate and commit.

TLDR

  • module's YAML configurations (plus "commits") are the source of truth.
  • currentjs create module creates module's structure (empty folders, YAML configuration with one model without any fields)
  • currentjs create model: You > YAML > generate
  • currentjs generate: YAML + commits > TypeScript > JS
  • since YAML is the main source of truth, it's also the best place to make changes
  • if changes are beyond configuration (require some coding), the best place is (in descending order): model, service
  • in order to preserve changes in TypeScript files, use currentjs commit
  • templates can be changed freely, they are not regenerated by default.

Reference

For detailed documentation of all CLI commands, YAML configuration options, and field types, see the Reference.

Module Configuration Overview

Each module is configured through a single YAML file located at src/modules/<Name>/<name>.yaml. The configuration follows a layered structure inspired by Clean Architecture. Each layer in the YAML maps to a set of generated TypeScript files.

Layers at a Glance

| YAML Section | Purpose | Generated Files | |---|---|---| | domain | Define your data models (aggregates, value objects) | Entity classes, value object classes | | useCases | Define business operations, input/output shapes, handler chains | Use case orchestrators, services, DTOs | | api | Define REST API endpoints | API controller | | web | Define server-rendered pages and forms | Web controller, HTML templates |

A minimal module YAML needs at least domain and useCases. The api and web sections are optional.

→ Reference: Module Configuration


Domain Layer (domain)

The domain layer defines your data models. There are two kinds: aggregates (entities) and value objects.

Aggregates

Aggregates are the main entities. One aggregate should be marked as the root (root: true), which enables ownership tracking (auto-generated ownerId field).

domain:
  aggregates:
    Post:
      root: true
      fields:
        title: { type: string, required: true }
        content: { type: string, required: true }
        status: { type: enum, values: [draft, published, archived] }
        publishedAt: { type: datetime }

Fields like id, ownerId, created_at, updated_at, and deleted_at are added automatically — do not include them.

Available field types: string, number, integer, decimal, boolean, datetime, date, id, json, array, object, enum.

For enum fields, provide the allowed values with values: [...].

Model Relationships

Set the type to another aggregate's name to create a foreign key relationship:

domain:
  aggregates:
    Author:
      root: true
      fields:
        name: { type: string, required: true }
    Post:
      root: true
      fields:
        title: { type: string, required: true }
        author: { type: Author, required: true }

The generator automatically:

  • Creates a foreign key column authorId in the database.
  • Uses the full Author object in the domain model (not just the ID).
  • Uses authorId: number in DTOs for API transmission.
  • Generates a <select> dropdown with a "Create New" button in HTML forms.
  • Wires the related store as a dependency for loading relationships.

Foreign key naming follows the pattern fieldName + 'Id' (e.g., authorauthorId).

Child Entities

An aggregate can have child entities listed in the entities field:

domain:
  aggregates:
    Invoice:
      root: true
      fields:
        number: { type: string, required: true }
      entities: [InvoiceItem]

    InvoiceItem:
      fields:
        productName: { type: string, required: true }
        quantity: { type: integer, required: true }

Child entities get a getByParentId() method in their store and listByParent() in their service. Use input.parentId in child use cases to link them to the parent.

Value Objects

Value objects are reusable types embedded in aggregates, stored as JSON in the database:

domain:
  valueObjects:
    Money:
      fields:
        amount: { type: decimal, constraints: { min: 0 } }
        currency: { type: enum, values: [USD, EUR, PLN] }

→ Reference: aggregates · valueObjects · Field Types · Child Entities


Use Cases Layer (useCases)

Use cases define the operations available for each model. Each use case specifies its input shape, output shape, and a chain of handlers to execute.

useCases:
  Post:
    list:
      input:
        pagination: { type: offset, defaults: { limit: 20, maxLimit: 100 } }
      output: { from: Post, pagination: true }
      handlers: [default:list]
    get:
      input: { identifier: id }
      output: { from: Post }
      handlers: [default:get]
    create:
      input: { from: Post }
      output: { from: Post }
      handlers: [default:create]

Handlers

Handlers are listed in execution order. Each handler becomes a method on the service class.

Built-in handlers:

| Handler | Description | |---|---| | default:list | Paginated list of entities | | default:get | Fetch by ID | | default:create | Create with validation | | default:update | Update by ID | | default:delete | Soft-delete by ID |

Custom handlers — use methodName (or service:methodName). The generator creates a stub method that receives (result, input):

useCases:
  Post:
    publish:
      input: { identifier: id }
      output: { from: Post }
      handlers:
        - default:get
        - validateForPublish
        - updatePublishStatus

This generates three service methods called in sequence. Custom methods get a TODO comment for you to fill in.

Input Configuration

Inputs can derive fields from a model (from), pick/omit specific fields, add extra fields, define validation rules, enable pagination, filtering, and sorting. See the Reference for the full input specification.

Displaying Child Entities (withChild)

When an aggregate root has child entities, you can show them on the root's pages:

useCases:
  Invoice:
    list:
      withChild: true   # Adds a link column to child entities on the list page
      # ...
    get:
      withChild: true   # Shows a child entities table on the detail page
      # ...

→ Reference: useCases · handlers · input · output


API Layer (api)

Defines REST API endpoints. Each model gets its own section keyed by name:

api:
  Post:
    prefix: /api/posts
    endpoints:
      - method: GET
        path: /
        useCase: Post:list
        auth: all
      - method: POST
        path: /
        useCase: Post:create
        auth: authenticated
      - method: PUT
        path: /:id
        useCase: Post:update
        auth: [owner, admin]

Each endpoint references a use case with the format ModelName:actionName.

Auth / Roles

The auth field controls access:

| Value | Meaning | |---|---| | all | Public, no authentication required | | authenticated | Any logged-in user (valid JWT) | | owner | User must own the resource (matched via ownerId) | | admin, editor, etc. | User must have this role (from JWT) | | [owner, admin] | OR logic — user matches any of the listed roles |

When owner is combined with privileged roles (e.g., [owner, admin]), privileged roles bypass the ownership check.

→ Reference: api · auth


Web Layer (web)

Defines server-rendered pages and forms:

web:
  Post:
    prefix: /posts
    layout: main_view
    pages:
      - path: /
        useCase: Post:list
        view: postList
        auth: all
      - path: /create
        method: GET
        view: postCreate
        auth: authenticated
      - path: /create
        method: POST
        useCase: Post:create
        auth: authenticated
        onSuccess:
          redirect: /posts/:id
          toast: "Post created"
        onError:
          stay: true
          toast: error

Form submission results are handled with onSuccess / onError:

  • toast: "message" — show a toast notification
  • back: true — navigate back in browser history
  • redirect: /path — redirect to a URL (supports :id placeholder)
  • stay: true — stay on the current page

→ Reference: web · auth


Generated Source Code

When you run currentjs generate, the following files are produced for each model defined in a module:

src/modules/<ModuleName>/
  domain/
    entities/<EntityName>.ts           — Domain entity class with typed constructor and setters
    valueObjects/<ValueObject>.ts      — Value object class (if defined)
  application/
    dto/<Action>InputDto.ts            — Input DTO with parse() and validation
    dto/<Action>OutputDto.ts           — Output DTO with from() mapper
    useCases/<EntityName>UseCase.ts    — Use case orchestrator (calls service methods in sequence)
    services/<EntityName>Service.ts    — Service with handler implementations (CRUD + custom stubs)
  infrastructure/
    controllers/<EntityName>ApiController.ts  — REST endpoints with auth checks
    controllers/<EntityName>WebController.ts  — Page rendering with form handling
    stores/<EntityName>Store.ts        — Database access (CRUD, row-to-model conversion, relationships)
  views/
    <viewName>.html                    — HTML templates (list, detail, create, edit forms)

The generator also updates src/app.ts with dependency injection wiring between marker comments (// currentjs:controllers:start ... // currentjs:controllers:end). This section is fully regenerated each time — imports, instantiation order (topologically sorted), and the controllers array.

Each generated class is decorated with @Injectable() or @Controller(), so the DI system discovers and wires them automatically.

→ Reference: Generated File Structure · generate

Change Tracking: diff and commit

Generated code often needs small adjustments — custom business logic, template tweaks, validation rules. The generator includes a change tracking system so these adjustments survive regeneration.

How it works

  1. Registry — when files are generated, their content hashes are stored in registry.json.

  2. currentjs diff [module] — compares each generated file's current content against what the generator would produce. Reports files as [clean], [modified], or [missing].

  3. currentjs commit [files...] — records the differences between your current files and the generated baseline. Diffs are saved as JSON files in the commits/ directory.

  4. Regeneration — on the next currentjs generate, committed changes are reapplied to the freshly generated code. If a change cannot be applied cleanly (e.g., the generated code changed in the same area), you are prompted to resolve it (unless --force or --skip is set).

Practical workflow

# Generate code
currentjs generate

# Make your changes (service logic, templates, etc.)
# ...

# See what you changed
currentjs diff Blog

# Save your changes
currentjs commit

# Later, after modifying the YAML and regenerating:
currentjs generate
# Your committed changes are reapplied automatically

Repository strategy

You can choose to either commit generated source code to git normally, or keep your repository lean by only tracking YAML files, registry.json, and the commits/ directory. In the latter case, anyone can recreate the full source by running currentjs generate.

→ Reference: diff · commit · Notes — File Change Tracking · Notes — Commit Mechanism

Database Migrations

The generator can produce SQL migration files based on changes to your domain models.

migrate commit

currentjs migrate commit

Collects all aggregate definitions from module YAMLs, compares them against the stored schema state (migrations/schema_state.yaml), and generates a .sql migration file in the migrations/ directory.

The migration file contains CREATE TABLE, ALTER TABLE ADD/MODIFY/DROP COLUMN, and DROP TABLE statements as needed. Foreign keys, indexes, and standard timestamp columns (created_at, updated_at, deleted_at) are handled automatically.

After generating the file, the schema state is updated so the next migrate commit only produces a diff of subsequent changes.

migrate push (not yet implemented)

Will apply pending migration files to the database.

migrate update (not yet implemented)

Will compare the live database schema against the current model definitions and generate a migration to bring them in sync.

→ Reference: migrate commit

Template System

Generated HTML templates use the @currentjs/templating engine. Templates are placed in the module's views/ directory and referenced by name in the web section of the YAML.

Template header

Each template starts with a comment that declares its name:

<!-- @template name="postList" -->

Variables

{{ title }}
{{ post.authorName }}
{{ formData.email || '' }}

Loops

<tbody x-for="items" x-row="item">
  <tr>
    <td>{{ item.name }}</td>
    <td>{{ $index }}</td>
  </tr>
</tbody>

x-for specifies the data key to iterate over, x-row names the loop variable. $index gives the current iteration index.

Conditionals

<div x-if="user.isAdmin">Admin-only content</div>
<span x-if="errors.name">{{ errors.name }}</span>

Layouts

Templates are rendered inside a layout (specified per resource or per page in the web config). The layout receives the rendered template content as {{ content }}.

Forms

Generated forms include data-strategy attributes for the frontend JavaScript to handle submission via AJAX:

<form data-strategy='["toast", "back"]'
      data-entity-name="Post"
      data-field-types='{"age": "number", "active": "boolean"}'>
  <input name="title" type="text" required>
  <button type="submit">Save</button>
</form>

The data-field-types attribute tells the frontend how to convert form values before sending (e.g., string to number, checkbox to boolean).

Template regeneration behavior

By default, currentjs generate does not overwrite existing HTML templates. Only missing templates are created. Use --with-templates to force regeneration of all templates.

→ Reference: Notes — Template Regeneration · web

Authorship & Contribution

Vibecoded mostly with claude models by Konstantin Zavalny. Yes, it is a vibecoded solution, really.

Any contributions such as bugfixes, improvements, etc are very welcome.

License

GNU Lesser General Public License (LGPL)

It simply means, that you:

  • can create a proprietary application that uses this library without having to open source their entire application code (this is the "lesser" aspect of LGPL compared to GPL).
  • can make any modifications, but must distribute those modifications under the LGPL (or a compatible license) and include the original copyright and license notice.