@fluxlabs/common-lib

中文文档
1.2.1发布于 2026/10/29ISCTypeScriptCommonJS近 30 天下载 4813

Shared building blocks for the Zoop backend services: errors and the Express error handler, response helpers, request validation, service-to-service auth tokens, HTTP helpers, the Objection and Knex instances, and an abuse filter for chat, names and usern

原始使用文档
文档目录10 节
  1. Usage
  2. Errors and responses
  3. Request validation and auth
  4. Service-to-service calls
  5. HTTP
  6. Database and Supabase
  7. Abuse filter
  8. Types
  9. Development
  10. Releasing

common-lib

Shared building blocks for the Zoop backend services: errors and the Express error handler, response helpers, request validation, service-to-service auth tokens, HTTP helpers, the Objection and Knex instances, and an abuse filter for chat, names and usernames.

npm install @fluxlabs/common-lib

Usage

Errors and responses

Throw a CustomError subclass and let errorHandler, registered after every route, turn it into a response:

import express from "express";
import { BadRequestError, errorHandler, okResponse } from "@fluxlabs/common-lib";

const app = express();

app.get("/stores/:id", async (req, res, next) => {
  try {
    const store = await Store.query().findById(req.params.id);
    if (!store) throw new BadRequestError("No such store");
    return okResponse(res, store, "Store fetched");
  } catch (err) {
    next(err);
  }
});

app.use(errorHandler);
CallStatusBody
okResponse(res, data, message)200{ success: true, statusCode: 200, data, message }
createdResponse(res, data, message)201{ success: true, statusCode: 201, data, message }
noContentResponse(res, message)204none
errorResponse(res, data, message, code), unAuthorisedResponse(...)code{ success: false, code, data, message }
errorHandler, for a CustomErrorits statusCode{ errors: [{ message, field? }] }
errorHandler, for anything else400{ errors: [{ message: "Something went wrong" }] }

The error classes, and the factories that build a ConcreteCustomError with any status:

ErrorStatus
new BadRequestError(message)400
new NotAuthorizedError()401
new NotFoundError()404
new RequestValidationError(errors)400, one entry per invalid field
new DatabaseConnectionError()500
createStatusCodeError(statusCode, message, body?)statusCode
badRequestError(message)422, unlike BadRequestError
unauthorizedError(message), forbiddenError(message), notFoundError(message)401, 403, 404
unverifiedError(message, body)412

Request validation and auth

import { body } from "express-validator";
import { currentUser, requireAuth, validateRequest } from "@fluxlabs/common-lib";

app.use(currentUser); // req.currentUser, from the JWT in req.session.jwt, verified with JWT_KEY

app.post("/orders", requireAuth, [body("store_id").isUUID()], validateRequest, createOrder);

requireAuth throws NotAuthorizedError when there is no req.currentUser, and validateRequest throws RequestValidationError when an express-validator check failed.

Service-to-service calls

Internal endpoints take a short-lived JWT, scoped to one service's internal API, in the x-internal-token header. The service that owns the endpoints verifies it:

import { createInternalServiceAuth, internalAudience } from "@fluxlabs/common-lib";

export const verifyInternalToken = createInternalServiceAuth({
  audience: internalAudience("order-service"),
  getSecret: () => environment_config.ORDER_INTERNAL_JWT_SECRET,
});

router.get("/internal/store-detail/:id", verifyInternalToken, handler); // sets req.internal_service

The service that calls them mints one per request:

import { axios, internalAudience, internalServiceAuthHeaders } from "@fluxlabs/common-lib";

await axios.get(url, {
  headers: internalServiceAuthHeaders({
    issuer: "payment-service",
    audience: internalAudience("order-service"),
    secret: environment_config.ORDER_INTERNAL_JWT_SECRET,
  }),
});

Tokens live 30 seconds. getSecret is a function so that a secret loaded after startup, or rotated, is picked up per request. src/utils/internal-service-token.ts has the details.

HTTP

handleData (POST), handlePatchData (PATCH) and getData (GET) never throw. They resolve to { result, error }, where result is the axios response and error what was thrown:

import { handleData } from "@fluxlabs/common-lib";

const { result, error } = await handleData(url, payload, { headers }, "json"); // "form" sends it form-encoded
if (error) { /* ... */ }

axios is the default axios instance.

Database and Supabase

Model is Objection's Model and Knex the knex factory, so that every service uses the same instances. initializeSupabase(url, key) returns a SupabaseClient.

Abuse filter

Checks text for abuse in English, Hindi and Hinglish, in Latin script or Devanagari. Each call returns where the abuse was found, "latin", "devanagari" or "emoji", or null when the text is clean:

import { findAbuse, findAbuseInName, findAbuseInUsername } from "@fluxlabs/common-lib";

findAbuse("tu chutiya hai");        // "latin" — for sentences, such as chat comments
findAbuseInName("iamchutiya99");    // "latin" — for full names
findAbuseInName("harshit.dikshit"); // null
findAbuseInUsername("jiya_bkl");    // "latin" — for usernames
CallUse it forCatches
findAbuse(text)sentencesabuse and insults, mild ones included, however they are disguised: case, stretched letters (fuuuck), leetspeak (chut1ya), look-alike characters, letters spelled out with separators (c.h.u.t.i.y.a), masked letters (f**k), long words split by spaces (chu tiya), Devanagari phrases, and abusive emoji
findAbuseInName(text)full namessevere abuse only, so that no real name is refused, but also when it is joined to other letters (bigchutiya) or split by _, . or - (big_chutiya)
findAbuseInUsername(text)usernameseverything findAbuseInName catches, plus short forms (bkl, mkc, bsdk, bc) when they stand as a word of their own (jiya_bkl, mkc99, b.k.l). Not inside a word, since ramkc (Ram KC) holds mkc, and not in full names, where they could be initials (BC Roy)

All three run in memory, in well under a millisecond, and return null when the check itself fails, so a broken filter never blocks a comment or a signup. The matchers are built on the first call, so a service that never checks text never pays for them.

The word lists are in src/services/moderation-service/abuse-words.ts, one set for each call. Chat's are only the defaults: setAbuseLists replaces them while the service runs, and notification-service does that from the Firestore document chat_moderation/abuse_words, so chat words change without a release.

import { DEFAULT_ABUSE_LISTS, setAbuseLists } from "@fluxlabs/common-lib";

setAbuseLists({ ...DEFAULT_ABUSE_LISTS, hinglish: [...DEFAULT_ABUSE_LISTS.hinglish, "|newword"] }); // true

A list that cannot be built is refused, setAbuseLists returns false, and the lists in use stay. findAbuseInName's and findAbuseInUsername's lists are fixed, because chat's include words that are also names ("Kamini"). It allows real names that a pattern matches, such as the surnames Chutia and Bhadwal; when a real name is blocked, add it to NAME_ALLOWLIST.

Types

  • Importing the package adds currentUser?: UserPayload and internal_service?: string to Express's Request.
  • Model, Knex, axios, the HTTP helpers' result, and the response helpers' res and return value are typed any on purpose. Typing them properly breaks code in the services that compiles today only because they are any, so it needs a major version. The comments in src/instances/index.ts and src/utils/function.ts explain.

Development

nvm use        # Node 22, from .nvmrc
npm ci
npm run build  # tsc, into build/

Every export has to declare its type in source: the build fails otherwise (isolatedDeclarations). That keeps the published .d.ts from changing with whatever dependencies the publishing machine has installed, which is how 1.0.14 broke four services' builds.

A change to a public type can break the services that use this package even when this package builds, so check them before releasing one.

A change reaches the services only in a new version: see Releasing.

Releasing

Bump the version, or nothing is released. A merge to main publishes only when it leaves package.json with a version npm doesn't have yet. So a pull request whose change should reach the services has to bump the version itself, or be followed by one that does. That includes a change to this README, since npm shows the README of the latest published version.

  1. In the pull request, bump the version: npm version patch --no-git-tag-version updates package.json and package-lock.json. Use patch for a fix, minor for an addition such as a new export, and major for anything that can break a service's build, such as narrowing a type.
  2. Write the pull request's description as the release notes: everything above its ## Test plan heading becomes them.
  3. Merge it. The Release workflow builds the package, publishes it to npm through trusted publishing, with no npm token, and creates the v<version> GitHub release with those notes and a link to the version on npm.

A prerelease version, such as 1.1.0-beta.0, is published under the next tag instead of latest. Publish only through this workflow, never with npm publish or npm run pub from your own machine.

版本与发布记录

@fluxlabs/common-lib 共发布 21 个版本,首次发布于 2023/12/15;过去 12 个月发布了 8 个版本。

版本发布日期距今
1.2.1latest2026/10/2917 天前
1.2.02026/10/2719 天前
1.1.02026/10/2719 天前
1.0.172026/10/2323 天前
1.0.162026/10/2323 天前
1.0.152026/08/043 个月前

关于 @fluxlabs/common-lib 的常见问题

@fluxlabs/common-lib 是什么?

Shared building blocks for the Zoop backend services: errors and the Express error handler, response helpers, request validation, service-to-service auth tokens, HTTP helpers, the Objection and Knex instances, and an abuse filter for chat, names and usern

如何安装 @fluxlabs/common-lib?

在项目目录运行 npm install @fluxlabs/common-lib,或使用 pnpm add / yarn add / bun add。

@fluxlabs/common-lib 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fluxlabs/common-lib 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@fluxlabs/common-lib 可以商用吗?

@fluxlabs/common-lib 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@fluxlabs/common-lib 的下载量有多少?

根据 npm 官方下载统计,@fluxlabs/common-lib 在 2026/10/15 – 2026/11/13 这 30 天内被下载 4,813 次,日均约 160 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fluxlabs/common-lib 还在维护吗?

最新版本 1.2.1 发布于 2026/10/29(17 天前);至今共发布 21 个版本,其中过去 12 个月发布了 8 个;首次发布于 2023/12/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。