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

@webx-ui/module-admin

v0.33.0

Published

The frame a WebX UI admin panel runs in: bootstrap, the shell, the HTTP client and the module registry that pairs with webx-ui/module-admin on the server.

Readme

@webx-ui/module-admin

The frame a WebX UI admin panel runs in: the bootstrap, the shell, the HTTP client and the module registry.

It is the front-end half of the Composer package webx-ui/module-admin, and the two are paired by the manifest the server publishes — so a section appears in the panel when both halves have it, and the menu is what the installation actually has rather than a list written twice.

Install

pnpm add @webx-ui/module-admin @webx-ui/core @webx-ui/tokens vue vue-router

On the Laravel side, php artisan webx:panel writes the entry file below and wires it into Vite for you.

The panel

import { createAdmin } from '@webx-ui/module-admin'
import { auth, WxUserMenu } from '@webx-ui/module-auth'
import { pages } from '@webx-ui/module-pages'

createAdmin({
  modules: [pages],
  plugins: [auth()],
  userMenu: WxUserMenu,
}).mount()

createAdmin() mounts into #webx-app — the element the server's Blade shell renders — and reads the manifest address out of the webx-manifest meta tag beside it. Both are overridable for a panel served some other way.

The name in the head of the sidebar, an arrow on the rail and an entry of the account menu lead to the site, in a new tab: to the home page in its main language the server names, to the address in siteLink when the project gives one, and nowhere at all with siteLink: false.

Mounting does not wait for the server. The manifest needs a signed-in session, so a visit that starts at the sign-in screen would otherwise stare at a blank page waiting for a request it is bound to lose.

A module

import type { AdminModule } from '@webx-ui/module-admin'

export const pages: AdminModule = {
  id: 'pages', // the same id the server-side module answers to
  path: '/pages',
  routes: [{ path: '/pages', component: () => import('./PagesScreen.vue') }],
}

A module the server reports with no front end installed has nowhere to send anybody, so it stays out of the menu; one installed but not reported is not there at all.

Language

The panel draws itself in the language of whoever is reading it — their choice, stored against them on the server, not a setting of the site. A site published only in Ukrainian can still be maintained by somebody who wants English menus.

import { useI18n, useTranslate } from '@webx-ui/module-admin'

const t = useTranslate('webx-admin') // t('shell.retry')
const i18n = useI18n() // i18n.state.locale, i18n.state.panelLocales

The words come from the Composer package's lang files, fetched as one dictionary at boot and merged over the English this package ships in its own code. So a module is translated once, in the half that also writes the server's validation messages, and a panel with no server behind it still has labels.

i18n.state.contentLocales is the other list: the languages the site publishes content in. That is what an editing screen builds its tabs from, and it has nothing to do with the language of the interface around them.

Dates

One way to say when something happened, for every screen in the panel.

<script setup lang="ts">
import { useDates, WxDate } from '@webx-ui/module-admin'

const dates = useDates() // dates.short(row.updated_at), dates.exact(…), dates.iso(…)
</script>

<template>
  <wx-date :value="row.last_login_at" />
</template>

| When | What it says | | ------------ | ----------------------- | | today | today at 08:10 | | yesterday | yesterday at 14:03 | | this year | 16 September at 14:03 | | earlier | 16 September 2025 | | never at all | never |

Twenty-four hours, no seconds; WxDate keeps the exact moment in a tip and in <time datetime>, so a column is read at a glance and an argument about the exact second still has an answer. The month names and the order of the parts come from Intl in the panel's language, not the browser's; today is counted in the reader's own day, which is why a server sending UTC does not turn somebody's morning into yesterday.

Nothing here touches ordering: a sortable column sorts on the value the server sent.

Talking to the backend

import { useAdmin, HttpError } from '@webx-ui/module-admin'

const { http, can, state } = useAdmin()

const body = await http.get<{ data: Page[] }>(`${state.manifest?.apiPath}/pages`)

The client speaks this backend's conventions rather than HTTP in general: a session cookie, a CSRF token fetched when needed and refreshed once if the server says it has gone stale, 422 arriving as errors ready for WxForm, 429 carrying retryAfter.

try {
  await http.post('/api/cms/pages', page)
} catch (error) {
  if (error instanceof HttpError && error.isValidation) {
    formErrors.value = error.errors
  }
}

A host that refuses PUT

Some shared hosting lets nothing but GET and POST past the web server: the panel opens and reads, and every save or delete gets a 403 or 405 before PHP is reached. methodOverride sends PUT, PATCH and DELETE as POST and says what they really are:

createAdmin({
  modules: [pages],
  plugins: [auth()],
  methodOverride: true, // or 'header', or 'field'
}).mount()

| Value | On the wire | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | false (default) | Every method as it is | | true or 'header' | POST with X-HTTP-Method-Override: PUT | | 'field' | POST with _method: "PUT" in the JSON body, in a form, or in the query for raw bytes — for a firewall that strips the header too |

GET and POST are never touched, and the CSRF token goes with every overridden request as it did before. Laravel reads both forms on its own; the server needs nothing. The option belongs to the panel's own client — a project that passes its own http passes methodOverride to createHttp() instead.

php artisan webx:doctor says whether the site's host is one of them: it sends a PUT and a DELETE to its own APP_URL and warns when the web server, not Laravel, refuses them.

can('pages.manage') answers from the signed-in session — a super administrator passes everything, the same rule the server applies.

Exports

| Export | What it is | | ------------------------- | ---------------------------------------------- | | createAdmin | Assembles and mounts the panel | | useAdmin | The context: http, state, nav, can | | createHttp, HttpError | The client, usable on its own | | AdminShell, AdminNav | The layout, for a panel that assembles its own | | useDates, WxDate | When something happened, in words |

Styles come with @webx-ui/core; this package adds a little of its own:

import '@webx-ui/module-admin/style.css'

Licence

MIT.