GitHub

Project Structure

Pulse enforces a single convention: one spec per page, one directory per concern. The structure is not a recommendation — it is how the framework discovers and registers your pages. There is nothing to configure because there is nothing to decide.

Layout

my-app/
├── package.json
├── public/                # static assets served directly
│   ├── app.css
│   └── dist/              # generated bundles (pulse build)
│       ├── manifest.json
│       ├── runtime-[hash].js
│       └── [name].boot-[hash].js
└── src/
    ├── pages/             # one file per page — auto-discovered
    │   ├── home.js
    │   ├── about.js
    │   └── contact.js
    ├── lib/               # shared helpers
    │   └── db.js
    └── components/        # shared view fragments (optional)
        └── card.js

src/pages/

Each file in src/pages/ exports a single spec as the default export. pulse dev auto-discovers every file in this directory and registers it as a route. There is no route registry to maintain — the file is the registration.

Routes are derived from filenames by default (about.js/about). For dynamic segments, route is set explicitly in the spec.

public/

Static files served directly — CSS, fonts, images. Referenced via meta.styles in the spec. The dist/ subdirectory is generated by pulse build. Its contents are content-hashed and must not be edited manually.

PathPurpose
public/app.cssYour global stylesheet — reference it in spec meta.styles
public/dist/Generated bundles from npm run build — do not edit manually
public/dist/manifest.jsonMaps spec hydrate paths to hashed bundle filenames

src/lib/

Shared helpers used across multiple pages — database clients, API wrappers, utility functions. Imported directly into specs. Plain JavaScript modules with no framework coupling — they work identically in tests, scripts, or other contexts.

src/components/ (optional)

Reusable view fragments. Since views are just functions that return HTML strings, a component is simply a function:

src/components/card.js
// src/components/card.js
export function card({ title, body }) {
  return `
    <div class="card">
      <h2>${title}</h2>
      <p>${body}</p>
    </div>
  `
}
src/pages/home.js
// src/pages/home.js
import { card } from '../components/card.js'

export default {
  route: '/',
  state: {},
  view: () => card({ title: 'Hello', body: 'Welcome to Pulse' }),
}

One file per page

Pulse enforces one spec per file. Every page is self-contained — state, view, mutations, actions, and validation in one place. This is not a style preference. It is how the framework eliminates the question of where things live.

For large pages, view fragments and helpers can be imported from other modules. The spec file remains the coordination point — the structure is always clear regardless of how the implementation is organised.