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

haze-cms

v0.3.3

Published

Vue 3 UI for a generic data-table CMS shell (layout, tables, login, column config). This repo is both a **standalone app** (for development) and an **npm-style library** you can mount inside another Vue project for a unified CMS look.

Readme

haze-cms

Vue 3 UI for a generic data-table CMS shell (layout, tables, login, column config). This repo is both a standalone app (for development) and an npm-style library you can mount inside another Vue project for a unified CMS look.


Requirements

  • Node.js and npm
  • Vue 3, Vue Router 4, and Axios in the host project when you consume the library (they are listed as peerDependencies)

Develop in this repo

npm install
npm run serve

Production bundle for deploying this UI as a full SPA (not the library artifact):

npm run build

Lint:

npm run lint

Optional: point the default REST adapter at your API (see Environment).


Build the library

npm run build:lib

Output goes to dist-lib/:

| File | Purpose | |------|---------| | haze-cms.es.js | ESM entry (import / exports.import) | | haze-cms.cjs.js | CommonJS entry (require / exports.require) | | haze-cms.umd.js | UMD bundle for CDN (unpkg / jsdelivr) | | haze-cms.css | Extracted styles — must be imported in the host (same build as the JS, so scoped data-v-* matches) |

All of the above are produced in one vite build (vite.lib.config.mjs). Do not combine this with vue-cli-service --target lib for the same package, or scoped SFC CSS will no longer match the compiled components.

package.json main, module, and exports point at haze-cms.cjs.js / haze-cms.es.js. The files field ships dist-lib.


Use in another Vue project

1. Add the package

Examples:

  • Local path (monorepo): "haze-cms": "file:../haze-cms"
  • Packed tarball: run npm pack in this repo, then depend on the generated .tgz
  • npm / private registry: publish this package, then npm install haze-cms

Install peers in the host if they are not already present:

npm install vue@^3 vue-router@^4 axios@^1

2. Import styles

The CMS layout and components rely on bundled CSS:

import 'haze-cms/dist-lib/haze-cms.css';

(If your resolver maps the package root differently, adjust the path so it resolves to haze-cms.css inside dist-lib.)

3. Bootstrap the app

The UI expects:

  1. A data adapter (api) — object with async methods the screens call (see API adapter).
  2. Auth helpers (auth) — isLoggedIn, persistLogin, getUser, etc. (see Auth).
  3. createCms (recommended) — resolves tables once and returns both router and plugin so routes, sidebar, home dashboard, and table-config labels never disagree on how many entities you have.

Alternatively, call createCmsRouter and createCmsPlugin separately only if you need full control; you must pass the same tables array to both.

Minimal host entry (same idea as src/main.js in this repo):

import { createApp } from 'vue';
import { createWebHistory } from 'vue-router';
import {
  App,
  createCms,
  defaultCmsTables,
  defaultAuthHelpers,
  createRestApiAdapter
} from 'haze-cms';
import 'haze-cms/dist-lib/haze-cms.css';

const api = createRestApiAdapter({
  baseURL: import.meta.env.VITE_API_URL ?? '/api' // or process.env.VUE_APP_API_URL in Vue CLI
});

const tables = defaultCmsTables();
// Or: const tables = getTables() from your host cms/schema.js

const { router, plugin } = createCms({
  api,
  auth: defaultAuthHelpers(),
  tables,
  history: createWebHistory(import.meta.env.BASE_URL)
});

createApp(App).use(plugin).use(router).mount('#app');

If you split router and plugin manually, use the same auth and tables references for both so route names and sidebar stay in sync.


Public API (src/index.js)

| Export | Role | |--------|------| | App | Root component (<router-view /> only) | | createCms({ api, auth?, tables?, history?, loginPath?, rootPath?, extraChildren? }) | Returns { router, plugin, tables, auth } with one shared tables list for routes + inject (recommended for hosts) | | createCmsPlugin({ api, auth?, tables? }) | Registers provide for the adapter, auth, and sidebar tables (defaults to defaultCmsTables()) | | createCmsRouter({ history?, tables?, loginPath?, rootPath?, auth, extraChildren? }) | Creates the CMS router and navigation guards | | defaultCmsTables() | Demo default table definitions (path, name, db, title); hosts usually pass their own tables array | | buildDefaultLayoutChildren(tables) | Builds child route records (advanced composition) | | defaultAuthHelpers() | LocalStorage-based session helpers | | createRestApiAdapter(options) | Reference REST client matching the original server routes | | CMS_API_KEY, CMS_AUTH_KEY, CMS_TABLES_KEY | inject keys for adapter, auth, and table nav config |


API adapter

All table/login/config views use inject to receive the adapter. Your object should expose the same method names the UI calls.

If you use createRestApiAdapter, it expects a backend shaped like the original haze-cms server (REST paths under baseURL).

| Method | Typical use | |--------|-------------| | login(credentials) | Login form | | register(user?) | Optional dev helper | | getItems(db, params?) | Table rows | | getItem(db, id) | Sidebar / detail | | createItem(db, data) | Create row | | updateItem(db, id, data) | Update row | | getTitles(db) | Column metadata (see Column types) | | getAvailableTables() | Table picker | | saveTableConfig(entity, payload) | Column visibility, and optionally full column definitions (see below) | | getDBOptions(db, db_col) | Sync options for type: 'db' fields (default implementation returns a static list; override via adapter options) |

saveTableConfig accepts either:

  • Legacy: visibleColumns as a string[] — same as { visibleColumns: [...] }.
  • Extended: { visibleColumns: string[], columns?: object[] } — when columns is present, the host should persist those definitions (keys, labels, type, relation, db / db_col, etc.) and keep visible in sync with visibleColumns. The REST adapter sends this object as the JSON body; your backend must accept optional columns alongside visibleColumns.

Replace createRestApiAdapter entirely with your own implementation when the host API differs; method names and semantics should stay compatible with the table and config screens.

Layout and home dashboard

  • Mobile (viewport ≤ 720px): A top bar with a menu button opens the same sidebar as on desktop as an off-canvas drawer (backdrop tap closes it). Main content is full width with top padding for the bar.
  • Wide tables: Entity table views wrap the grid in a horizontal scroll area; columns use a minimum width so many columns scroll sideways instead of shrinking.
  • Home (/): Users can checkbox which entities appear on the dashboard. The choice is stored in localStorage under haze-cms-dashboard-tables (JSON array of route name strings). First visit defaults to all configured tables.
  • “Recent” rows on home: For each selected table, the UI loads getItems(db), sorts client-side by updatedAt / updated_at, then createdAt / created_at (newest first), then _id, and shows the first five rows with up to four visible columns per preview. For large collections, implement getItems(db, params) in your adapter (e.g. limit, sort) so the server returns a small sorted page; the UI still works if you only return unsorted rows (sorting happens in the browser).

Column types

Each object from getTitles can include:

| type | Meaning | |--------|---------| | (omit) or 'text' | Plain text; rendered as <input type="text"> unless you set another HTML input type via type: 'email', etc. | | 'boolean' | Toggle switch; stored value should be boolean (the UI tolerates "true" / "false" strings when reading). | | 'relation' | Foreign key: include relation: { targetDb, valueKey, labelKey }. targetDb is the related entity id passed to getItems. valueKey is the field stored on the current row (e.g. _id on the related row). labelKey is the field from the related row shown in the dropdown and in the table grid. Options are loaded with getItems(targetDb) and filtered client-side by search text (fine for small/medium catalogs; large catalogs may need a dedicated search API later). | | 'db' | Existing static dropdown: db, db_col, and getDBOptions(db, db_col). |

Example relation column:

{
  key: 'vendor_id',
  label: 'Vendor',
  visible: true,
  type: 'relation',
  relation: { targetDb: 'vendors', valueKey: '_id', labelKey: 'name' }
}

Auth

defaultAuthHelpers() implements:

  • isLoggedIn() — used by the router guard (default: token in localStorage)
  • persistLogin(response) — called after successful login (default: stores token and user JSON)
  • clearSession() — optional logout cleanup
  • getUser() — used by the admin card in the sidebar

Pass auth into createCmsPlugin({ api, auth: { ...defaultAuthHelpers(), ...yourOverrides } }) and the same merged object into createCmsRouter({ auth, ... }) if you use cookies, memory tokens, or OIDC instead of localStorage.


Router customization

createCmsRouter options:

| Option | Default | Notes | |--------|---------|--------| | history | createWebHistory() | Pass createWebHistory(base) when the app lives under a subpath | | tables | defaultCmsTables() | Each entry: { path, name, db, title } — drives routes and Table props. Optional navLabel or label for a shorter sidebar label (else title is shown). Pass the same array to createCmsPlugin({ tables }). | | loginPath | '/login' | Login route path | | rootPath | '/' | Where to send an already logged-in user who hits the login page | | auth | required | Same helpers as the plugin | | extraChildren | [] | Extra child routes under MainLayout |

Named routes used in the default sidebar include Home, Leads, Products, Vendors, Users, Tables, and Login. If you rename name fields in tables, update navigation in your fork or pass custom layout (advanced).


Environment

When running this repo in dev / SPA build, src/main.js uses:

  • VUE_APP_API_URL — base URL for createRestApiAdapter (falls back to http://localhost:3000)
  • BASE_URL — passed to createWebHistory for a public path prefix

Host apps should set their own env convention (e.g. Vite VITE_*) and pass the value into createRestApiAdapter({ baseURL }).


Architecture notes

  • No backend is bundled in the library build; the server in a full-stack repo is only needed if you point the REST adapter at it.
  • Vue, Vue Router, and Axios are not bundled as application code for library consumers in the usual setup; keep them as peers in the host.
  • After changing components or styles, run npm run build:lib again before publishing or packing.

Vue CLI

This project uses Vue CLI. See the Configuration Reference for vue.config.js customization.