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

imean-service-engine-htmx-plugin

v2.11.1

Published

HtmxAdminPlugin for IMean Service Engine

Downloads

2,963

Readme

HtmxAdminPlugin v2

基于 PageModel 和 Feature 的简化管理后台插件。

设计理念

核心原则

  1. 围绕模型设计:使用 PageModel 作为核心抽象,统一处理有数据模型和无数据模型的场景
  2. 权限与 Feature 绑定:每个 Feature 声明自己的权限,支持灵活的权限控制
  3. 函数式数据操作:移除数据源机制,直接使用函数处理数据操作
  4. 简洁统一:单一类、统一模式,降低学习成本

快速开始

安装

npm install imean-service-engine-htmx-plugin

基础使用

import { Factory } from "imean-service-engine";
import { HtmxAdminPlugin, PageModel, CustomFeature } from "imean-service-engine-htmx-plugin";

// 创建插件
const adminPlugin = new HtmxAdminPlugin({
  title: "管理后台",
  prefix: "/admin",
});

// 创建页面模型
class LoginPageModel extends PageModel {
  constructor() {
    super("login", undefined, {
      title: "登录",
      useAdminLayout: false,
    });

    this.features.custom("login", new CustomFeature({
      name: "login",
      permission: null,
      routes: [
        { method: "get", path: "" },
        { method: "post", path: "" },
      ],
      handler: async (context) => {
        // 处理逻辑
      },
    }));
  }
}

// 注册页面
adminPlugin.registerPage(new LoginPageModel());

// 创建引擎
const { Microservice } = Factory.create(adminPlugin);
const engine = new Microservice({ name: "admin-service" });
await engine.start(3000);

使用模式

有数据模型的页面

import { PageModel, type ModelSchema } from "imean-service-engine-htmx-plugin";
import { z } from "zod";

// 定义 Schema
const ArticleSchema = z.object({
  id: z.number().optional(),
  title: z.string().min(1).describe("标题"),
  content: z.string().min(10).describe("内容"),
});

const articleModelSchema: ModelSchema<Article> = {
  schema: ArticleSchema,
};

// 数据操作函数
async function getArticleList(params: ListParams): Promise<ListResult<Article>> {
  // 从数据库或其他数据源获取列表
}

async function getArticle(id: string | number): Promise<Article | null> {
  // 获取单条数据
}

async function createArticle(data: Partial<Article>): Promise<Article> {
  // 创建数据
}

async function updateArticle(id: string | number, data: Partial<Article>): Promise<Article | null> {
  // 更新数据
}

async function deleteArticle(id: string | number): Promise<boolean> {
  // 删除数据
}

// 定义 PageModel
class ArticlePageModel extends PageModel<Article> {
  constructor() {
    super("articles", articleModelSchema, {
      title: "文章管理",
      description: "管理文章内容",
    });

    // 注册 CRUD Feature
    this.features.crud({
      permissionPrefix: "articles",
      getList: getArticleList,
      getItem: getArticle,
      createItem: createArticle,
      updateItem: updateArticle,
      deleteItem: deleteArticle,
      // 可选:弹窗大小配置(sm, md, lg, xl, full)
      dialogSizes: {
        detail: "xl",
        edit: "xl",
        create: "xl",
      },
      // 可选:遮罩关闭配置(编辑/创建时建议设为 false,防止误操作)
      closeOnBackdropClick: {
        edit: false,
        create: false,
        detail: true,
      },
      // 可选:动态标题和描述配置(使用实际数据的标题作为页面标题)
      getTitles: {
        detail: (item) => item.title,  // 详情页使用文章标题
        edit: (item) => `编辑:${item.title}`,  // 编辑页使用 "编辑:{文章标题}"
      },
      getDescriptions: {
        detail: (item) => item.excerpt || `作者:${item.author}`,  // 详情页使用文章摘要
      },
    });
  }
}

无数据模型的页面

class LoginPageModel extends PageModel {
  constructor() {
    super("login", undefined, {
      title: "登录",
      useAdminLayout: false,
    });

    this.features.custom("login", new CustomFeature({
      name: "login",
      permission: null,
      routes: [
        { method: "get", path: "" },
        { method: "post", path: "" },
      ],
      handler: async (context) => {
        // 处理逻辑
      },
    }));
  }
}

只读场景

class ArticleReadonlyPageModel extends PageModel<Article> {
  constructor() {
    super("articles-readonly", articleModelSchema, {
      title: "文章查看(只读)",
    });

    this.features.crud({
      permissionPrefix: "articles",
      getList: getArticleList,
      getItem: getArticle,
      createItem: createArticle,  // 必须提供,但不会注册
      updateItem: updateArticle,
      features: {
        list: true,
        detail: true,
        create: false,  // 禁用创建
        edit: false,    // 禁用编辑
        delete: false,  // 禁用删除
      },
    });
  }
}

权限系统

权限格式

  • 默认格式:{modelName}.{featureName}
    • 例如:articles.listarticles.createarticles.delete
  • 自定义权限:可以在 Feature 配置中覆盖

权限绑定

  • 每个 Feature 声明自己的 permission
  • 插件在路由注册时自动进行权限检查
  • 支持 null 表示开放访问

API 参考

PageModel

class PageModel<T extends { id: string | number } = any> {
  constructor(
    modelName: string,
    schema?: ModelSchema<T>,
    metadata?: PageMetadata
  );
  
  getMetadata(): PageMetadata;
  hasModel(): boolean;
  validate(data: unknown): ValidationResult;
  getFormFields(): FormField[];
  getListFields(): string[];
  getDetailFields(): string[];
  getFieldLabel(fieldName: string): string;
}

FeatureRegistry

class FeatureRegistry<T> {
  crud(options: CRUDOptions): void;
  list(feature: Feature<T>): void;
  detail(feature: Feature<T>): void;
  create(feature: Feature<T>): void;
  edit(feature: Feature<T>): void;
  delete(feature: Feature<T>): void;
  custom(name: string, feature: Feature<T>): void;
}

HtmxAdminPlugin

class HtmxAdminPlugin {
  registerPage<T>(page: PageModel<T>): this;
  registerPages(...pages: PageModel<any>[]): this;
}

迁移指南

从 v1 迁移到 v2:

  1. 移除数据源:将数据源方法改为函数
  2. 移除装饰器:使用构造函数注册
  3. 合并元数据:将 getTitlegetDescription 等合并为构造函数参数
  4. 权限迁移:将页面级权限改为 Feature 级权限

设计文档

详细设计文档请参考 DESIGN.md

旧版本

旧版本代码已移至 src-old 目录,文档移至 README-old.md