Skip to content
This repository was archived by the owner on Jun 12, 2025. It is now read-only.

AAStarCommunity/AAStar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

AAStar

Deprecated🛑❤️‍🩹👶

merge into https://github.com/AAStarCommunity/AAStar_SDK

AAStar npm packages collection: You can use

pnpm install @aastar/airaccount
@aastar/superpaymaster
@aastar/cometens
@aastar/openpnts
@aastar/opencards
@aastar/arcadia
@aastar/cos72

or

pnpm install aastar

For developers

Why do you install this package?

airaccount

Provide a moveable\self-custody\crypto account with life cycle service. Permissionless. Trustless. Decentralized. In 3 steps: bind, send, claim, move(recover)

superpaymaster

Embbeded into AirAccount, but provide ERC20 gas token ability for any community individually.

cometens

Embbeded into AirAccount, but provide set your own ENS name for your community individually.

从零开始开发和发布 npm 包:完整指南

目录

  1. 前期准备
  2. 初始化项目
  3. 配置开发环境
  4. 开发 SDK
  5. 测试
  6. 构建和打包
  7. 发布到 npm
  8. 文档和示例
  9. 版本管理和更新
  10. 持续集成和自动发布
  11. SDK 开发最佳实践
  12. 问题排查

前期准备

1. 安装必要工具

# 安装 Node.js (推荐使用 nvm 管理 Node.js 版本)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
nvm install node # 安装最新版
nvm install --lts # 或安装长期支持版

# 确认安装
node --version
npm --version

# 安装一些全局工具
npm install -g typescript ts-node np

2. 创建 npm 账号

  1. 访问 npm 官网 并注册
  2. 在命令行登录 npm (后续发布时需要)
npm login

3. 规划你的 SDK

在开始编码前,确定以下内容:

  • SDK 的目标和用途
  • 核心功能和 API 设计
  • 依赖关系
  • 支持的平台 (浏览器、Node.js、React Native 等)
  • 命名规范 (包名必须唯一)
  • 核心 API 结构

初始化项目

1. 创建项目目录

# 创建项目目录 (使用你希望的包名)
mkdir my-awesome-sdk
cd my-awesome-sdk

2. 初始化 npm 项目

npm init

按照提示填写信息:

  • name: 包名 (可以使用 @组织名/包名 格式)
  • version: 版本号 (推荐从 0.1.0 开始)
  • description: 简短描述
  • main: 入口文件 (通常是 dist/index.js)
  • scripts: 构建脚本
  • repository: 代码仓库地址
  • keywords: 关键词,帮助用户发现你的包
  • author: 作者信息
  • license: 许可证 (如 MIT)

3. 初始化 Git 仓库

git init
echo "node_modules\ndist\n.env\n*.log" > .gitignore

4. 创建基本目录结构

mkdir src tests examples docs
touch src/index.ts README.md LICENSE

配置开发环境

1. 配置 TypeScript

# 安装 TypeScript
npm install typescript --save-dev

# 初始化 TypeScript 配置
npx tsc --init

编辑 tsconfig.json

{
    "compilerOptions": {
        "target": "es2018",
        "module": "commonjs",
        "declaration": true,
        "outDir": "./dist",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "rootDir": "./src"
    },
    "include": ["src"],
    "exclude": ["node_modules", "tests", "examples", "dist"]
}

2. 配置代码格式化和 Lint

# 安装 ESLint 和 Prettier
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier --save-dev

# 创建 ESLint 配置

创建 .eslintrc.js

module.exports = {
    parser: "@typescript-eslint/parser",
    extends: [
        "plugin:@typescript-eslint/recommended",
        "plugin:prettier/recommended",
    ],
    parserOptions: {
        ecmaVersion: 2018,
        sourceType: "module",
    },
    rules: {
        // 自定义规则
    },
};

创建 .prettierrc

{
    "singleQuote": true,
    "trailingComma": "es5",
    "printWidth": 100,
    "tabWidth": 2,
    "semi": true
}

3. 配置测试环境

# 安装 Jest
npm install jest ts-jest @types/jest --save-dev

创建 jest.config.js

module.exports = {
    preset: "ts-jest",
    testEnvironment: "node",
    testMatch: ["**/tests/**/*.test.ts"],
    collectCoverage: true,
    coverageDirectory: "coverage",
    collectCoverageFrom: ["src/**/*.ts"],
};

4. 更新 package.json 脚本

编辑 package.json 添加以下脚本:

"scripts": {
  "build": "tsc",
  "test": "jest",
  "lint": "eslint 'src/**/*.ts'",
  "format": "prettier --write 'src/**/*.ts'",
  "prepare": "npm run build",
  "prepublishOnly": "npm test && npm run lint",
  "preversion": "npm run lint",
  "version": "npm run format && git add -A src",
  "postversion": "git push && git push --tags"
}

开发 SDK

1. 创建基本结构

src/index.ts 创建 SDK 的入口点:

// 导出所有公共 API
export * from "./client";
export * from "./types";
// ... 其他导出

// 导出默认客户端
import { Client } from "./client";
export default Client;

2. 创建核心模块

例如,创建 src/client.ts 实现主要客户端类:

import { ApiResponse, ClientOptions } from "./types";

export class Client {
    private apiKey: string;
    private baseUrl: string;

    constructor(options: ClientOptions) {
        this.apiKey = options.apiKey;
        this.baseUrl = options.baseUrl || "https://api.example.com/v1";
    }

    async makeRequest<T>(
        endpoint: string,
        method: string = "GET",
        data?: any,
    ): Promise<ApiResponse<T>> {
        const url = `${this.baseUrl}/${endpoint}`;

        try {
            const response = await fetch(url, {
                method,
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${this.apiKey}`,
                },
                body: data ? JSON.stringify(data) : undefined,
            });

            const responseData = await response.json();

            if (!response.ok) {
                throw new Error(responseData.message || "API request failed");
            }

            return {
                data: responseData,
                status: response.status,
                headers: response.headers,
            };
        } catch (error) {
            throw error;
        }
    }

    // 公共 API 方法
    async getUser(userId: string) {
        return this.makeRequest<any>(`users/${userId}`);
    }

    // 更多 API 方法...
}

3. 定义类型

创建 src/types.ts 定义类型:

export interface ClientOptions {
    apiKey: string;
    baseUrl?: string;
}

export interface ApiResponse<T> {
    data: T;
    status: number;
    headers: Headers;
}

// 更多类型定义...

4. 使用模块化设计

将 SDK 拆分为多个模块,例如:

src/
  index.ts          # 主入口
  client.ts         # 核心客户端类
  types.ts          # 类型定义
  modules/
    auth.ts         # 认证相关功能
    users.ts        # 用户相关功能
    products.ts     # 产品相关功能
  utils/
    request.ts      # 请求工具
    validation.ts   # 验证工具

测试

1. 编写单元测试

tests 目录中创建测试文件,例如 tests/client.test.ts

import { Client } from "../src/client";

// 模拟 fetch API
global.fetch = jest.fn();

describe("Client", () => {
    let client: Client;

    beforeEach(() => {
        client = new Client({ apiKey: "test-api-key" });
        (global.fetch as jest.Mock).mockClear();
    });

    test("makeRequest should call fetch with correct parameters", async () => {
        (global.fetch as jest.Mock).mockResolvedValueOnce({
            ok: true,
            status: 200,
            json: async () => ({ id: "123", name: "Test User" }),
            headers: new Headers(),
        });

        const result = await client.makeRequest("users/123");

        expect(global.fetch).toHaveBeenCalledWith(
            "https://api.example.com/v1/users/123",
            expect.objectContaining({
                method: "GET",
                headers: expect.objectContaining({
                    "Authorization": "Bearer test-api-key",
                }),
            }),
        );

        expect(result.data).toEqual({ id: "123", name: "Test User" });
    });

    // 更多测试...
});

2. 运行测试

npm test

3. 编写集成测试

创建与真实 API 交互的集成测试,例如 tests/integration.test.ts

import { Client } from "../src/client";

// 这些测试需要一个有效的 API 密钥
// 通常通过环境变量提供
const apiKey = process.env.API_KEY;

// 只在提供 API 密钥时运行集成测试
(apiKey ? describe : describe.skip)("Integration tests", () => {
    let client: Client;

    beforeAll(() => {
        client = new Client({ apiKey });
    });

    test("can get a user", async () => {
        const result = await client.getUser("test-user-id");
        expect(result.status).toBe(200);
        expect(result.data).toHaveProperty("id");
    });

    // 更多集成测试...
});

构建和打包

1. 配置 npm 包文件

编辑 package.json 指定要包含的文件:

{
    "main": "dist/index.js",
    "types": "dist/index.d.ts",
    "files": [
        "dist",
        "LICENSE",
        "README.md"
    ]
}

2. 支持不同的模块格式 (可选)

如果你想同时支持 CommonJS 和 ES 模块,可以使用 rolluptsup 等工具。

使用 tsup 示例:

# 安装 tsup
npm install tsup --save-dev

更新 package.json

{
    "main": "dist/index.js",
    "module": "dist/index.mjs",
    "types": "dist/index.d.ts",
    "exports": {
        ".": {
            "require": "./dist/index.js",
            "import": "./dist/index.mjs",
            "types": "./dist/index.d.ts"
        }
    },
    "scripts": {
        "build": "tsup src/index.ts --format cjs,esm --dts --clean"
    }
}

发布到 npm

1. 准备发布

确保你的包名是唯一的,并已登录 npm。如果使用组织名称,确保已创建组织。

# 检查是否登录
npm whoami

# 如果未登录
npm login

2. 测试包

在发布前,使用 npm pack 创建一个本地包进行测试:

npm pack

这将创建一个 .tgz 文件,可以在另一个项目中使用它进行测试:

# 在测试项目中
npm install ../path/to/your-package-1.0.0.tgz

3. 发布包

# 首次发布
npm publish

# 如果是组织包并想设为公共
npm publish --access public

4. 更新包

修改 package.json 中的版本号或使用 npm version 命令:

# 更新补丁版本 (1.0.0 -> 1.0.1)
npm version patch

# 更新小版本 (1.0.0 -> 1.1.0)
npm version minor

# 更新大版本 (1.0.0 -> 2.0.0)
npm version major

然后发布更新:

npm publish

文档和示例

1. 编写 README

一个好的 README 应包含:

  • 项目描述
  • 安装指南
  • 基本用法示例
  • API 文档链接
  • 配置选项
  • 常见问题

示例 README 结构:

# My Awesome SDK

A JavaScript/TypeScript SDK for interacting with the Example API.

## Installation

```bash
npm install my-awesome-sdk
```

Quick Start

import Client from "my-awesome-sdk";

const client = new Client({ apiKey: "your-api-key" });

// Get a user
client.getUser("user123")
    .then((response) => console.log(response.data))
    .catch((error) => console.error(error));

Documentation

For full documentation, visit docs.example.com.

Configuration

The client accepts the following options:

Option Type Required Default Description
apiKey string Yes - Your API key
baseUrl string No https://api.example.com/v1 The API base URL

License

MIT

### 2. 示例代码

在 `examples` 目录中创建示例代码:

```typescript
// examples/basic-usage.ts
import Client from '../src';

async function main() {
  const client = new Client({ apiKey: 'your-api-key' });
  
  try {
    const user = await client.getUser('user123');
    console.log('User:', user.data);
    
    // More examples...
  } catch (error) {
    console.error('Error:', error);
  }
}

main();

3. 生成 API 文档

可以使用 TypeDoc 生成 API 文档:

npm install typedoc --save-dev

添加 package.json 脚本:

"scripts": {
  "docs": "typedoc --out docs src/index.ts"
}

版本管理和更新

1. 遵循语义化版本规范

  • 补丁版本(1.0.x):修复 bug,小改进
  • 小版本(1.x.0):添加向后兼容的新功能
  • 大版本(x.0.0):不向后兼容的变更

2. 维护更新日志

创建 CHANGELOG.md 并记录每个版本的变更:

# Changelog

## [1.1.0] - 2023-05-01

### Added

- New feature X
- Support for Y

### Changed

- Improved error handling
- Better performance for Z

### Fixed

- Bug in getUser method

## [1.0.0] - 2023-04-01

Initial release

持续集成和自动发布

1. 设置 GitHub Actions

创建 .github/workflows/ci.yml

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x, 16.x, 18.x]
    
    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm run build
    - run: npm test
    - run: npm run lint

2. 设置自动发布

创建 .github/workflows/publish.yml

name: Publish

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: '16.x'
        registry-url: 'https://registry.npmjs.org'
    - run: npm ci
    - run: npm run build
    - run: npm test
    - run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

在 GitHub 仓库设置中,添加 NPM_TOKEN 秘密。

SDK 开发最佳实践

1. API 设计原则

  • 简单直观:API 应该易于理解和使用
  • 一致性:保持命名和模式一致
  • 错误处理:提供清晰的错误信息和类型
  • 文档完善:每个公共方法都应有文档
  • 链式调用:考虑支持方法链接

2. 错误处理

创建自定义错误类:

// src/errors.ts
export class ApiError extends Error {
    statusCode: number;
    data: any;

    constructor(message: string, statusCode: number, data?: any) {
        super(message);
        this.name = "ApiError";
        this.statusCode = statusCode;
        this.data = data;
        Object.setPrototypeOf(this, ApiError.prototype);
    }
}

export class ValidationError extends Error {
    errors: any[];

    constructor(message: string, errors: any[]) {
        super(message);
        this.name = "ValidationError";
        this.errors = errors;
        Object.setPrototypeOf(this, ValidationError.prototype);
    }
}

3. 浏览器兼容性

如果 SDK 需要在浏览器中运行,考虑:

  • 使用 fetch 时提供兼容性封装
  • 考虑使用 axios 等库处理请求
  • 使用 polyfills 确保兼容性

4. 安全性

  • 不要在客户端代码中存储敏感信息
  • 使用 HTTPS
  • 避免将敏感信息记录到控制台
  • 提供 API 令牌轮换机制

问题排查

1. 常见问题

  • 包名冲突:在 npm 上查找是否已存在同名包
  • 构建错误:检查 TypeScript 配置和依赖
  • 发布失败:确保已登录 npm 并有权限发布

2. 调试技巧

// 添加调试配置
export class Client {
    private debug: boolean;

    constructor(options: ClientOptions) {
        this.debug = options.debug || false;
        // ...
    }

    private log(...args: any[]) {
        if (this.debug) {
            console.log("[SDK]", ...args);
        }
    }

    async makeRequest<T>(
        endpoint: string,
        method: string = "GET",
        data?: any,
    ): Promise<ApiResponse<T>> {
        this.log("Request:", method, endpoint, data);

        // ...请求逻辑...

        this.log("Response:", responseData);
        return response;
    }
}

3. 提供用户支持

  • 在 GitHub 上设置 Issues 模板
  • 提供常见问题和解决方案
  • 考虑设置讨论区或社区渠道

NPM包组织

您想要建立一个名为"aastar"的NPM组织,其中包含多个子包,既可以单独安装也可以整体安装。这是一个非常好的组织方式,类似于许多流行的库如Next.js、Ant Design等采用的结构。

要实现这样的结构,您需要采用"Monorepo"(单体仓库)架构,以下是详细的规划和实现步骤:

1. 创建Monorepo项目结构

首先,建立基本的项目结构:

aastar/
├── packages/
│   ├── airaccount/        # 子包1
│   ├── superpaymaster/    # 子包2
│   └── ...                # 其他子包
├── package.json           # 根项目配置
├── pnpm-workspace.yaml    # 工作区配置
├── lerna.json             # (可选)Lerna配置
└── tsconfig.json          # 基础TypeScript配置

2. 配置工作区

在根目录创建pnpm-workspace.yaml文件:

packages:
  - 'packages/*'

3. 配置每个子包

在每个子包目录中创建package.json,例如packages/airaccount/package.json

{
    "name": "@aastar/airaccount",
    "version": "0.1.0",
    "description": "AIR账户管理模块",
    "main": "dist/index.js",
    "types": "dist/index.d.ts",
    "scripts": {
        "build": "tsc",
        "test": "jest"
    },
    "dependencies": {
        // 此包的依赖
    },
    "peerDependencies": {
        // 可选的同级依赖
    },
    "publishConfig": {
        "access": "public"
    }
}

4. 创建主包作为入口点

packages目录下创建一个集合包,例如packages/aastar/package.json

{
    "name": "aastar",
    "version": "0.1.0",
    "description": "AASTAR SDK全集合",
    "main": "dist/index.js",
    "types": "dist/index.d.ts",
    "scripts": {
        "build": "tsc"
    },
    "dependencies": {
        "@aastar/airaccount": "workspace:*",
        "@aastar/superpaymaster": "workspace:*"
        // 添加其他所有子包作为依赖
    },
    "publishConfig": {
        "access": "public"
    }
}

然后创建相应的入口文件packages/aastar/src/index.ts,导出所有子包:

export * from "@aastar/airaccount";
export * from "@aastar/superpaymaster";
// 导出其他所有子包

5. 使用工具管理版本和发布

您可以使用以下工具之一来管理版本和发布:

选项1: 使用pnpm + changesets

  1. 安装changesets:
pnpm add -D -w @changesets/cli
pnpm changeset init
  1. 配置发布脚本:在根目录的package.json中添加:
{
    "scripts": {
        "build": "pnpm -r build",
        "version": "changeset version",
        "publish": "pnpm build && changeset publish"
    }
}

选项2: 使用Lerna

  1. 安装Lerna:
pnpm add -D -w lerna
  1. 创建lerna.json
{
    "version": "independent",
    "npmClient": "pnpm",
    "useWorkspaces": true,
    "command": {
        "publish": {
            "ignoreChanges": ["*.md"],
            "message": "chore(release): publish"
        }
    }
}

6. 处理包之间的依赖关系

如果子包之间有依赖关系,在依赖包的package.json中添加:

{
    "dependencies": {
        "@aastar/some-dependency": "workspace:*"
    }
}

7. 统一配置和构建

创建一个根目录的tsconfig.json作为基础配置:

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "node",
        "declaration": true,
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    }
}

然后,每个子包可以扩展此配置:

{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "outDir": "./dist",
        "rootDir": "./src"
    },
    "include": ["src/**/*"]
}

8. 发布流程

  1. 创建npm组织:

    • 在npm网站上注册aastar组织
  2. 发布所有包:

# 使用changesets
pnpm changeset
pnpm version
pnpm publish

# 或使用Lerna
pnpm lerna publish

使用示例

用户可以按您希望的方式安装和使用SDK:

# 安装单个包
pnpm install @aastar/airaccount @aastar/superpaymaster

# 或安装全部功能
pnpm install aastar

使用示例:

// 单个包使用方式
import { someFunction } from "@aastar/airaccount";

// 全部功能使用方式
import { airaccount, superpaymaster } from "aastar";

这种结构的主要优点是:

  1. 用户可以根据需求选择安装单个包或全部功能
  2. 包之间可以共享代码,但又保持独立发布
  3. 可以统一管理版本,简化发布流程
  4. 便于维护和扩展

您对这种结构有什么特别的需求或问题吗?

About

aastar npm packages collection

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •