@decaf-ts/for-fabric

中文文档
0.19.3发布于 2026/11/09MPL-2.0TypeScriptESM + CJS构建来源已验证近 30 天下载 2323

抽象结构逻辑

中文使用文档
文档目录32 节
  1. DECAF的超高压燃料合同
  2. 说明
  3. 如何使用
  4. 安装
  5. 客户端用户端
  6. 连接到 Fabric 网络
  7. 正在收听链码事件
  8. 使用身份和证书
  9. 链码开发
  10. 创建模式
  11. 创建 CRUD 合同
  12. 直接使用合同适配器
  13. 接待和处理事件
  14. 合同 APIs (Chaincode)
  15. FabricCrud 合同
  16. 序列化 合同
  17. 包料存储器
  18. 工艺合同DB序列
  19. 配置状态<M,R>
  20. 合同
  21. 制造合同存储器
  22. 合同文本
  23. FabricERC20 合同(样本)
  24. 关于试验的说明作为例子
  25. 编码原则
  26. 释放文档钩
  27. 相关
  28. 社会
  29. 获得帮助
  30. 捐款
  31. 支助
  32. 许可证

Banner

DECAF的超高压燃料合同

用 TypeScript 写的 Hyperledger Fabric 的链码侧构件 。 该模块提供寄存器、CRUD合同(对象和序列化的JSON)、ERC20样本合同、Fabric背接序列、事件排放和上下文记录。 它使DECAF的数据访问模式适应了Fabric的世界状态,这样你就可以用DECAF其他地方使用的相同的存储器/模型抽象来写出智能合同.

2025-11-26. 发布docs刷新. 见 工作文件/报告/报告 罚单摘要.

Licence GitHub language count GitHub top language

Build & Test CodeQLSnyk Analysis Pages builder .github/workflows/release-on-tag.yaml

Open Issues Closed Issues Pull Requests Maintained

Forks Stars Watchers

Node Version NPM Version

现有文件 这里

说明

本模块侧重于@decaf-ts/ for-fabric的链码(合同)一面. 它将DECAF的存储器/模型/Adapter抽象化为Hyperledger Fabric的世界状态和执行背景,这样你就可以用熟悉的模式和最小的锅炉板执行智能合同.

主要想法:

  • 保留您的域名模式为注释类( 使用 @ decaf- ts/ decorator- validation) .
  • 通过Fabric-aware适配器使用存储器来持续/读取/平滑模型.
  • 编写可重复使用的CRUD合同和公用事业,而不是手写分机电话.
  • 从寄存器操作中释放出一等Fabric事件.
  • 在执行期间利用上下文感知到的日志和打入的旗帜.

行业合同/合同中确定的合同组成部分:

  1. 核心背景和类型
  • Fabric Contact Context:扩展了通用上下文,从分类账中曝光出Fabric特有的属性(stub,客户身份,日志)和时间戳分辨率.
  • FabricContractFlags:接口扩展 RepositoryFlags 并有分贝,客户端身份和对合同呼叫的日志.
  1. 日志
  • ContractLogger:与Fabric合同上下文相匹配的小型Logger. 它尊重日志水平,并转发给基本的Fabric日志.
  1. 适配器和存储器
  • FabricContactAdapter:可执行CRUD,批量操作,原始芒果查询,结果迭代,模型制备/重置,复合键前缀等功能的链码侧适配器,并进行序列创建. Bridges DECAF抽象到Fabric(ChancodeStub,客户端身份)和CouchDB风格的查询.
  • Fabric Contract Repositor:用于链码内部模型的 Repositor. 支持创建,更新,创建All,更新All,读取/读取All,原始查询(芒果),选择预测,以前缀为主的批量操作,并通过观察者汉德勒发布事件.
  • Fabric Contact Repository Observatory Handler:通过 stub. setEvent 发布 Fabric 事件的观察家 Handler 使用生成 FabricEvent 生成的名称Name.
  1. 顺序
  • FabricContractDBSequence:由Fabric支持的以当前,下行和范围执行序列. 通过 Fabric Contact Repository 在世界状态存储值, 并支持数字或 BigInt 序列的可配置启动 由来.
  1. CRUD合同
  • FabricCrud Contract: Base smart Contract 曝光 CRUD 端点(创建,读取,更新,删除,创建All,readAll,更新All,删除All,生取,入取,健康检查)为模型类型. 使用DeterministicSerializer和幕后FabricContractAdapter/存储器,并提供logFor(ctx).
  • 序列化Crud Contract:与FabricCrud Contract相同的端点,但取/回JSON字符串(去)被序列化为模型类. 这简化了客户端的相互作用,用于测试.
  1. ERC20样本
  • ERC20Token, ERC20Wallet,"津贴":ERC20类代币的样本域模型,钱包和津贴.
  • Fabric Statement <M,R>:一个可切入式DB状态桥,通过FabricContactAdapter运行芒果查询,在需要时处理主密钥投影.
  • FabricERC20 Contract:一个完整的ERC20智能合同显示基于寄存器的持久性和算术辅助. 执行初始化、 检查初始化、 托肯Name、 符号、 十进制、 总供应、 平衡、 转账、 转出、 批准、 津贴、 薄荷、 燃烧、 BurnFrom、 客户账户注册、 客户账户ID 和一个内部 转账帮助器 .

设计注释 :

  • 定型序列化用于确保世界态写作的稳定字节.
  • db-decors的创建/创建Update hooks被适配器所利用来设定主键和创建/拥有元数据.
  • 芒果查询(CouchDB)通过GetQueryResultWithPagination用于丰富的查询.
  • 事件排放是通过Fabric Contract Repositor Obbservatory Handler支持的Events列表选择每个操作类型的.

使用这些组件,您可以构建强大的链码,同时保持代码简洁,可测试,并与DECAF的架构保持一致.

如何使用

安装

npm install @decaf-ts/for-fabric

客户端用户端

连接到 Fabric 网络

import { FabricAdapter, PeerConfig } from "@decaf-ts/for-fabric";

// Configure connection to a Fabric peer
const config: PeerConfig = {
  mspId: "Org1MSP",
  peerEndpoint: "localhost:7051",
  channelName: "mychannel",
  chaincodeName: "mycc",
  contractName: "mycontract",
  tlsCertPath: "/path/to/tls/cert",
  certDirectoryPath: "/path/to/cert/dir",
  keyDirectoryPath: "/path/to/key/dir",
  cryptoPath: "/path/to/crypto",
  peerHostAlias: "peer0.org1.example.com",
  caEndpoint: "localhost:7054",
  caTlsCertificate: "/path/to/ca/tls/cert",
  caCert: "/path/to/ca/cert",
  caKey: "/path/to/ca/key",
  ca: "ca.org1.example.com",
};

// Create an adapter instance
const adapter = new FabricAdapter(config, "org1-adapter");

// Use the adapter to interact with the Fabric network
async function createAsset() {
  const asset = { id: "asset1", value: "Asset 1 Value" };
  return await adapter.create("assets", "asset1", asset, {}, mySerializer);
}

async function readAsset(id: string) {
  return await adapter.read("assets", id, mySerializer);
}

async function updateAsset(id: string, newValue: string) {
  const asset = await readAsset(id);
  asset.value = newValue;
  return await adapter.update("assets", id, asset, {}, mySerializer);
}

async function deleteAsset(id: string) {
  return await adapter.delete("assets", id, mySerializer);
}

async function queryAssets(owner: string) {
  const query = {
    selector: {
      owner: owner,
    },
  };
  return await adapter.raw(query, true);
}

正在收听链码事件

import { FabricAdapter, FabricDispatch } from "@decaf-ts/for-fabric";

async function setupEventListener(config: PeerConfig) {
  // Create a client
  const client = await FabricAdapter.getClient(config);

  // Create a dispatch instance
  const dispatch = new FabricDispatch(client);

  // Configure the dispatch with peer configuration
  dispatch.configure(config);

  // Register an observer for a specific table and event
  dispatch.observe("assets", "create", (id) => {
    console.log(`Asset created: ${id}`);
    // Fetch the new asset or update UI
  });

  // Start listening for events
  await dispatch.start();

  // When done, close the connection
  // await dispatch.close();
}

使用身份和证书

import {
  getIdentity,
  getSigner,
  readFile,
  getCAUser,
} from "@decaf-ts/for-fabric";

async function setupIdentity() {
  // Read a certificate file
  const tlsCert = await readFile("/path/to/tls/cert");

  // Get an identity from a certificate directory
  const identity = await getIdentity("Org1MSP", "/path/to/cert/dir");

  // Get a signer from a key directory
  const signer = await getSigner("/path/to/key/dir");

  // Create a CA user
  const user = await getCAUser(
    "user1",
    privateKeyPem,
    certificatePem,
    "Org1MSP"
  );

  return { identity, signer, user };
}

链码开发

创建模式

import { Model, id, property, table } from "@decaf-ts/decorator-validation";

@table("assets")
export class Asset extends Model {
  @id()
  id: string;

  @property()
  value: string;

  @property()
  owner: string;

  @property()
  createdAt: number;
}

创建 CRUD 合同

import { FabricCrudContract } from "@decaf-ts/for-fabric";
import { Context, Contract, Info, Transaction } from "fabric-contract-api";
import { Asset } from "./asset";

@Info({
  title: "AssetContract",
  description: "Smart contract for trading assets",
})
export class AssetContract extends FabricCrudContract<Asset> {
  constructor() {
    super("AssetContract", Asset);
  }

  // The base class already provides standard CRUD operations:
  // create, read, update, delete, createAll, readAll, updateAll, deleteAll

  // Add custom methods as needed
  @Transaction()
  async getAssetHistory(ctx: Context, id: string): Promise<any[]> {
    const stub = ctx.stub;
    const iterator = await stub.getHistoryForKey(id);

    const results = [];
    let result = await iterator.next();

    while (!result.done) {
      const value = result.value;
      results.push({
        txId: value.txId,
        timestamp: value.timestamp,
        value: JSON.parse(value.value.toString("utf8")),
      });

      result = await iterator.next();
    }

    await iterator.close();
    return results;
  }

  @Transaction()
  async transferAsset(
    ctx: Context,
    id: string,
    newOwner: string
  ): Promise<Asset> {
    const asset = await this.read(ctx, id);
    asset.owner = newOwner;
    return await this.update(ctx, asset);
  }
}

直接使用合同适配器

import { FabricContractAdapter } from "@decaf-ts/for-fabric";
import { Context, Contract, Transaction } from "fabric-contract-api";

export class CustomContract extends Contract {
  private adapter: FabricContractAdapter;

  constructor() {
    super("CustomContract");
    this.adapter = new FabricContractAdapter();
  }

  @Transaction()
  async createRecord(ctx: Context, id: string, data: string): Promise<any> {
    const record = { id, data, timestamp: Date.now() };
    return await this.adapter.create(
      "records",
      id,
      record,
      {},
      { stub: ctx.stub, logger: ctx.logging }
    );
  }

  @Transaction(false)
  async queryRecords(ctx: Context, owner: string): Promise<any[]> {
    const query = {
      selector: {
        owner: owner,
      },
    };

    return await this.adapter.raw(query, true, {
      stub: ctx.stub,
      logger: ctx.logging,
    });
  }
}

接待和处理事件

import {
  FabricContractRepositoryObservableHandler,
  generateFabricEventName,
  parseEventName,
} from "@decaf-ts/for-fabric";
import { Context } from "fabric-contract-api";
import { OperationKeys } from "@decaf-ts/db-decorators";

// In chaincode: Emit an event
async function emitEvent(ctx: Context, tableName: string, id: string) {
  const handler = new FabricContractRepositoryObservableHandler();
  const logger = ctx.logging.getLogger("EventHandler");

  await handler.updateObservers(logger, tableName, OperationKeys.CREATE, id, {
    stub: ctx.stub,
  });
}

// In client: Parse an event name
function handleEvent(eventName: string, payload: Buffer) {
  const { table, event, owner } = parseEventName(eventName);
  const data = JSON.parse(payload.toString());

  console.log(`Received ${event} event for ${table} with ID ${data.id}`);
  if (owner) {
    console.log(`Event owner: ${owner}`);
  }
}

关于更详细的实例和API文档,请参见: API 参考。 。 。 .

合同 APIs (Chaincode)

以下例子取材于成品/弧/合同中的合同,并反映了单位/整合测试所用的模式.

FabricCrud 合同

说明:Base Contracting 揭发CRUD一模范类的终点. 它在引擎盖下使用Repository和DeterministicSerializer.

import { Context, Transaction, Contract } from "fabric-contract-api";
import { model, ModelArg, required } from "@decaf-ts/decorator-validation";
import { BaseModel, pk } from "@decaf-ts/core";
import { FabricCrudContract } from "@decaf-ts/for-fabric/contracts";

@model()
class Person extends BaseModel {
  @pk({ type: "Number" })
  id!: number;
  @required() name!: string;
  constructor(arg?: ModelArg<Person>) {
    super(arg);
  }
}

export class PersonContract extends FabricCrudContract<Person> {
  constructor() {
    super("PersonContract", Person);
  }

  @Transaction(false)
  async ping(ctx: Context): Promise<string> {
    // Uses FabricCrudContract.logFor
    this.logFor(ctx).info("ping");
    return "pong";
  }
}

测试中的用法: 参见测试/单位/合同. test.ts模式,其中行使序列化Crud Contract子类; FabricCrud Contract相似,但取回对象而不是JSON字符串.

序列化 合同

描述: 与 FabricCrud Contract 相同的端点, 但取回 JSON 字符串 。 对简单的客户端有用. 根据测试/单位/合同.t.

import { Context } from "fabric-contract-api";
import { model, ModelArg, required } from "@decaf-ts/decorator-validation";
import { BaseModel, pk } from "@decaf-ts/core";
import { SerializedCrudContract } from "@decaf-ts/for-fabric/contracts";

@model()
class TestModel extends BaseModel {
  @pk({ type: "Number" }) id!: number;
  @required() name!: string;
  @required() nif!: string;
  constructor(arg?: ModelArg<TestModel>) {
    super(arg);
  }
}

export class TestModelContract extends SerializedCrudContract<TestModel> {
  constructor() {
    super("TestModelContract", TestModel);
  }
}

// Example invocation (mirrors unit test usage)
async function createExample(contract: TestModelContract, ctx: Context) {
  const payload = new TestModel({
    name: "Alice",
    nif: "123456789",
  }).serialize();
  const resultJson = await contract.create(ctx, payload);
  const created = new TestModel(JSON.parse(resultJson));
  return created;
}

包料存储器

说明:链码-侧存储器在合同方法内用于坚持和查询模型.

import { Context } from "fabric-contract-api";
import { Repo } from "@decaf-ts/core";
import { model, required, ModelArg } from "@decaf-ts/decorator-validation";
import { BaseModel, pk } from "@decaf-ts/core";
import { FabricContractRepository } from "@decaf-ts/for-fabric/contracts";

@model()
class Asset extends BaseModel {
  @pk() id!: string;
  @required() owner!: string;
  constructor(arg?: ModelArg<Asset>) {
    super(arg);
  }
}

export class AssetContract extends Contract {
  private repo: Repo<Asset, any, any, any, any>;
  constructor() {
    super("AssetContract");
    this.repo = new FabricContractRepository<Asset>(
      new (require("@decaf-ts/for-fabric").contracts.FabricContractAdapter)(),
      Asset
    );
  }

  @Transaction()
  async Create(ctx: Context, id: string, owner: string): Promise<void> {
    const m = new Asset({ id, owner });
    await this.repo.create(m, ctx as any);
  }

  @Transaction(false)
  async Read(ctx: Context, id: string): Promise<Asset> {
    return this.repo.read(id, ctx as any);
  }

  @Transaction(false)
  async QueryByOwner(ctx: Context, owner: string): Promise<Asset[]> {
    return this.repo.raw({ selector: { owner } } as any, true, ctx as any);
  }
}

工艺合同DB序列

说明:用于生成递增值的世界状态后置序列.

import { Context } from "fabric-contract-api";
import { FabricContractDBSequence } from "@decaf-ts/for-fabric/contracts";
import { FabricContractAdapter } from "@decaf-ts/for-fabric/contracts";

const adapter = new FabricContractAdapter();

export class OrderContract extends Contract {
  private orderSeq = new FabricContractDBSequence(
    {
      name: "orderSeq",
      type: "Number",
      startWith: 1,
      incrementBy: 1,
    },
    adapter
  );

  @Transaction()
  async CreateOrder(ctx: Context): Promise<number> {
    const next = await this.orderSeq.next(ctx as any);
    // use next as order id
    return next as number;
  }

  @Transaction(false)
  async NextRange(ctx: Context, count: number): Promise<number[]> {
    return (await this.orderSeq.range(count, ctx as any)) as number[];
  }
}

配置状态<M,R>

描述:通过Fabric适配器运行芒果查询的桥并取回打入的型号;由寄存器内部使用并直接用于高级病例. 见测试/单元/erc20contract.ts 嘲弄 CouchDB状态处理.

import { FabricStatement } from "@decaf-ts/for-fabric/contracts";
import { FabricContractAdapter } from "@decaf-ts/for-fabric/contracts";
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
import { MangoQuery } from "@decaf-ts/for-couchdb";
import { Model } from "@decaf-ts/decorator-validation";

class MyModel extends Model {}

const adapter = new FabricContractAdapter();

async function query(ctx: FabricContractContext) {
  const stmt = new FabricStatement<MyModel, MyModel[]>(adapter, ctx);
  const models = await stmt.raw<MyModel[]>({
    selector: { type: "MyModel" },
  } as MangoQuery);
  return models;
}

合同

描述: 联系到 Fabric 的上下文, 尊重日志级别 .

import { Context, Transaction } from "fabric-contract-api";
import { Contract } from "fabric-contract-api";
import { ContractLogger } from "@decaf-ts/for-fabric/contracts";

export class LoggableContract extends Contract {
  @Transaction()
  async DoWork(ctx: Context): Promise<void> {
    const log = new ContractLogger(
      "LoggableContract",
      { level: "info" },
      ctx as any
    );
    log.info("Starting work");
    // ... work ...
    log.debug("Finished");
  }
}

制造合同存储器

描述: Emits Fabric 事件用于寄存器操作. 也可以直接使用它来发布自定义事件.

import { FabricContractRepositoryObservableHandler } from "@decaf-ts/for-fabric/contracts";
import { OperationKeys } from "@decaf-ts/db-decorators";
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";
import { MiniLogger } from "@decaf-ts/logging";

async function emitExample(ctx: FabricContractContext) {
  const handler = new FabricContractRepositoryObservableHandler();
  const log = new MiniLogger("obs");
  await handler.updateObservers(
    log as any,
    "assets",
    OperationKeys.CREATE,
    "asset1",
    ctx
  );
}

合同文本

说明:在合同内有特定内容.

import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";

function readContext(ctx: FabricContractContext) {
  const ts = ctx.timestamp; // Date from stub.getDateTimestamp()
  const id = ctx.identity.getID();
  ctx.logger.info(`Tx by ${id} at ${ts.toISOString()}`);
}

FabricERC20 合同(样本)

说明:测试中使用的ERC20全面实施(见测试/单元/erc20contract.ts.

import { FabricERC20Contract } from "@decaf-ts/for-fabric/contracts";
import { FabricContractContext } from "@decaf-ts/for-fabric/contracts";

const contract = new FabricERC20Contract("TestToken");

async function initAndRead(ctx: FabricContractContext) {
  const created = await contract.Initialize(ctx, "TestToken", "TT", 18);
  if (created) {
    const name = await contract.TokenName(ctx);
    const decimals = await contract.Decimals(ctx);
    return { name, decimals };
  }
  throw new Error("Init failed");
}

关于试验的说明作为例子

  • 测试/单位/合同.test.ts显示创建了串行化的Crud Contact,并用被嘲笑的Fabric上下文调用创建(ctx,jsonPayload).
  • 测试/单元/erc20contract.test.ts演示ERC20合同初始化并读取了TokenName.
  • 测试/整合/序列化-Contact.test.ts显示基于JSON的端到端CRUD流经序列化合同,包括创建,读取,更新和丰富的查询.

上述例子反映了这些模式,以确保正确性和与存储器测试套件的一致性.

编码原则

  • 在文件夹中分组类似的功能( 与命名空间对应, 但没有任何命名空间声明)
  • 每个文件一个类别;
  • 每个文件一个接口(除非接口只是用作类型);
  • 组合类型作为每个文件夹的.ts文件类型中的其他接口;
  • 每个文件夹中的常数或常数组;
  • 每个文件夹中的装饰器组;
  • 总是从特定文件导入, 绝不从文件夹或索引文件导入( 其他软件包的依赖性除外 );
  • 在适用的情况下,更倾向于使用既定的设计样板:
    • Singleton(可以是反派. * 谨慎使用;
    • 工厂;
    • 观察员;
    • 战略;
    • 建造者;
    • 诸如此类;

释放文档钩

通过审查与自动释放管道保持一致 发布注释 财务报告和审定财务报表 依赖关系 在尝试这些食谱后(更新于2025-11-26).

Readme Card

社会

LinkedIn

语文

TypeScript JavaScript NodeJS ShellScript

获得帮助

如果您有错误报告, 请回答问题或建议 创建一个新问题。 。 。 .

捐款

我感谢对这一项目作出的任何贡献。 请阅读 这个 要开始.

支助

第一种最简单的方法就是支持它 捐款. 甚至在文档中找到打字很重要.

财政支持总是受到欢迎,帮助我和该项目保持生命和健康.

所以,如果你可以, 如果这个项目以任何方式。 要么通过学习 要么只是帮助你 节省宝贵的时间, 请考虑捐赠.

许可证

这个项目将在 Mozilla 公共许可证2.0。 。 。 .

由开发者为开发者...

版本与发布记录

@decaf-ts/for-fabric 共发布 290 个版本,首次发布于 2025/10/22;过去 12 个月发布了 275 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.19.3latest2026/11/093 天前
0.18.42026/10/3014 天前
0.18.32026/10/081 个月前
0.18.22026/09/231 个月前
0.18.02026/09/221 个月前
0.17.72026/09/211 个月前

其他发布通道:0.5.1.query → 0.5.1-query,0.5.1.query.2 → 0.5.1-query.2,0.5.1.query.3 → 0.5.1-query.3,experimental → 0.13.31-experimental.3,v0.4.8.hotfix.1 → 0.4.8-hotfix.1。

关于 @decaf-ts/for-fabric 的常见问题

@decaf-ts/for-fabric 是什么?

抽象结构逻辑

如何安装 @decaf-ts/for-fabric?

在项目目录运行 npm install @decaf-ts/for-fabric,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:fabric-shim(^2.5.8)、@grpc/grpc-js(^1.14.5)、@noble/curves(^2.2.0)、fabric-common(^2.2.20)、fabric-network(^2.2.20)、fabric-ca-client(^2.2.20)、@peculiar/webcrypto(^2.0.0)、fabric-contract-api(^2.5.8)、@hyperledger/fabric-gateway(^1.7.1)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 for-fabric,可以用 npx for-fabric 直接运行。

@decaf-ts/for-fabric 支持 TypeScript 吗?

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

@decaf-ts/for-fabric 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@decaf-ts/for-fabric 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@decaf-ts/for-fabric 可以商用吗?

@decaf-ts/for-fabric 使用 MPL-2.0 许可。弱 Copyleft:可以商用,也可以和闭源代码一起使用;但如果分发时修改了 MPL 授权的文件,这些文件的修改需以 MPL 开源。以上为概括,不构成法律意见,请以许可原文为准。

@decaf-ts/for-fabric 的下载量有多少?

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

@decaf-ts/for-fabric 还在维护吗?

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