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

@ahoo-wang/fetcher-wow

v3.13.4

Published

Support for Wow(https://github.com/Ahoo-Wang/Wow) in Fetcher

Readme

@ahoo-wang/fetcher-wow

npm version Build Status codecov License npm downloads npm bundle size Ask DeepWiki Storybook

Wow 框架提供支持。提供用于与 Wow CQRS/DDD 框架配合使用的 TypeScript 类型和工具。

🌟 特性

  • 📦 完整的 TypeScript 支持:为所有 Wow 框架实体提供完整的类型定义,包括命令、事件和查询
  • 🚀 命令客户端:用于向 Wow 服务发送命令的高级客户端,支持同步和流式响应
  • 🔍 强大的查询 DSL:丰富的查询条件构建器,支持全面的操作符用于复杂查询
  • 📡 实时事件流:内置对服务器发送事件的支持,用于接收实时命令结果和数据更新
  • 🔄 CQRS 模式实现:对命令查询责任分离架构模式的一流支持
  • 🧱 DDD 基础构件:基本的领域驱动设计构建块,包括聚合、事件和值对象
  • 🔍 查询客户端:专门用于查询快照和事件流数据的客户端,支持全面的查询操作:
    • 资源计数
    • 资源列表查询
    • 以服务器发送事件形式流式传输资源
    • 资源分页
    • 单个资源检索

🚀 快速开始

安装

# 使用 npm
npm install @ahoo-wang/fetcher-wow

# 使用 pnpm
pnpm add @ahoo-wang/fetcher-wow

# 使用 yarn
yarn add @ahoo-wang/fetcher-wow

📚 API 参考

命令模块

CommandResult

表示命令执行结果的接口:

import { CommandResult, CommandStage } from '@ahoo-wang/fetcher-wow';

CommandClient

用于向 Wow 框架发送命令的 HTTP 客户端。该客户端提供了同步或流式接收命令结果的方法。

import {
  Fetcher,
  FetchExchange,
  RequestInterceptor,
  URL_RESOLVE_INTERCEPTOR_ORDER,
} from '@ahoo-wang/fetcher';
import '@ahoo-wang/fetcher-eventstream';
import {
  CommandClient,
  CommandRequest,
  HttpMethod,
  CommandHttpHeaders,
  CommandStage,
} from '@ahoo-wang/fetcher-wow';
import { idGenerator } from '@ahoo-wang/fetcher-cosec';

// 使用基础配置创建 fetcher 实例
const exampleFetcher = new Fetcher({
  baseURL: 'http://localhost:8080/',
});

// 定义当前用户 ID
const currentUserId = idGenerator.generateId();

// 创建处理 URL 参数的拦截器
class AppendOwnerId implements RequestInterceptor {
  readonly name: string = 'AppendOwnerId';
  readonly order: number = URL_RESOLVE_INTERCEPTOR_ORDER - 1;

  intercept(exchange: FetchExchange) {
    const urlParams = exchange.ensureRequestUrlParams();
    urlParams.path['ownerId'] = currentUserId;
  }
}

// 注册拦截器
exampleFetcher.interceptors.request.use(new AppendOwnerId());

// 创建命令客户端
const cartCommandClient = new CommandClient({
  fetcher: exampleFetcher,
  basePath: 'owner/{ownerId}/cart',
});

// 定义命令端点
class CartCommandEndpoints {
  static readonly addCartItem = 'add_cart_item';
}

// 定义命令接口
interface AddCartItem {
  productId: string;
  quantity: number;
}

type AddCartItemCommand = CommandRequest<AddCartItem>;

// 创建命令请求
const addCartItemCommand: AddCartItemCommand = {
  method: HttpMethod.POST,
  headers: {
    [CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
  },
  body: {
    productId: 'productId',
    quantity: 1,
  },
};

// 发送命令并等待结果
const commandResult = await cartCommandClient.send(
  CartCommandEndpoints.addCartItem,
  addCartItemCommand,
);

// 发送命令并接收流式结果
const commandResultStream = await cartCommandClient.sendAndWaitStream(
  CartCommandEndpoints.addCartItem,
  addCartItemCommand,
);
for await (const commandResultEvent of commandResultStream) {
  console.log('收到命令结果:', commandResultEvent.data);
}
方法
  • send(path: string, commandRequest: CommandRequest): Promise<CommandResult> - 发送命令并等待结果。
  • sendAndWaitStream(path: string, commandRequest: CommandRequest): Promise<CommandResultEventStream> - 发送命令并以服务器发送事件的形式返回结果流。

查询模块

条件构建器

支持操作符的综合查询条件构建器:

import {
  and,
  or,
  eq,
  ne,
  gt,
  lt,
  contains,
  isIn,
  between,
  today,
  active,
} from '@ahoo-wang/fetcher-wow';

// 简单条件
const simpleConditions = [
  eq('name', 'John'),
  ne('status', 'inactive'),
  gt('age', 18),
  lt('score', 100),
];

// 复杂条件
const complexCondition = and(
  eq('tenantId', 'tenant-123'),
  or(
    contains('email', '@company.com'),
    isIn('department', 'engineering', 'marketing'),
  ),
  between('salary', 50000, 100000),
  today('createdAt'),
  active(),
);

// 日期条件
const dateConditions = [
  today('createdAt'),
  beforeToday('lastLogin', 7), // 最近7天内
  thisWeek('updatedAt'),
  lastMonth('createdDate'),
];

SnapshotQueryClient

用于查询物化快照的客户端,支持全面的查询操作:

import {
  Fetcher,
  FetchExchange,
  RequestInterceptor,
  URL_RESOLVE_INTERCEPTOR_ORDER,
} from '@ahoo-wang/fetcher';
import '@ahoo-wang/fetcher-eventstream';
import {
  SnapshotQueryClient,
  all,
  ListQuery,
  PagedQuery,
  SingleQuery,
} from '@ahoo-wang/fetcher-wow';
import { idGenerator } from '@ahoo-wang/fetcher-cosec';

interface CartItem {
  productId: string;
  quantity: number;
}

interface CartState extends Identifier {
  items: CartItem[];
}

// 使用基础配置创建 fetcher 实例
const exampleFetcher = new Fetcher({
  baseURL: 'http://localhost:8080/',
});

// 定义当前用户 ID
const currentUserId = idGenerator.generateId();

// 创建处理 URL 参数的拦截器
class AppendOwnerId implements RequestInterceptor {
  readonly name: string = 'AppendOwnerId';
  readonly order: number = URL_RESOLVE_INTERCEPTOR_ORDER - 1;

  intercept(exchange: FetchExchange) {
    const urlParams = exchange.ensureRequestUrlParams();
    urlParams.path['ownerId'] = currentUserId;
  }
}

// 注册拦截器
exampleFetcher.interceptors.request.use(new AppendOwnerId());

// 创建快照查询客户端
const cartSnapshotQueryClient = new SnapshotQueryClient<CartState>({
  fetcher: exampleFetcher,
  basePath: 'owner/{ownerId}/cart',
});

// 统计快照数量
const count = await cartSnapshotQueryClient.count(all());

// 列出快照
const listQuery: ListQuery = {
  condition: all(),
};
const list = await cartSnapshotQueryClient.list(listQuery);

// 以流的形式列出快照
const listStream = await cartSnapshotQueryClient.listStream(listQuery);
for await (const event of listStream) {
  const snapshot = event.data;
  console.log('收到快照:', snapshot);
}

// 列出快照状态
const stateList = await cartSnapshotQueryClient.listState(listQuery);

// 以流的形式列出快照状态
const stateStream = await cartSnapshotQueryClient.listStateStream(listQuery);
for await (const event of stateStream) {
  const state = event.data;
  console.log('收到状态:', state);
}

// 分页查询快照
const pagedQuery: PagedQuery = {
  condition: all(),
};
const paged = await cartSnapshotQueryClient.paged(pagedQuery);

// 分页查询快照状态
const pagedState = await cartSnapshotQueryClient.pagedState(pagedQuery);

// 查询单个快照
const singleQuery: SingleQuery = {
  condition: all(),
};
const single = await cartSnapshotQueryClient.single(singleQuery);

// 查询单个快照状态
const singleState = await cartSnapshotQueryClient.singleState(singleQuery);
方法
  • count(condition: Condition): Promise<number> - 统计匹配给定条件的快照数量。
  • list(listQuery: ListQuery): Promise<Partial<MaterializedSnapshot<S>>[]> - 检索物化快照列表。
  • listStream(listQuery: ListQuery): Promise<ReadableStream<JsonServerSentEvent<Partial<MaterializedSnapshot<S>>>>> - 以服务器发送事件的形式检索物化快照流。
  • listState(listQuery: ListQuery): Promise<Partial<S>[]> - 检索快照状态列表。
  • listStateStream(listQuery: ListQuery): Promise<ReadableStream<JsonServerSentEvent<Partial<S>>>> - 以服务器发送事件的形式检索快照状态流。
  • paged(pagedQuery: PagedQuery): Promise<PagedList<Partial<MaterializedSnapshot<S>>>> - 检索物化快照的分页列表。
  • pagedState(pagedQuery: PagedQuery): Promise<PagedList<Partial<S>>> - 检索快照状态的分页列表。
  • single(singleQuery: SingleQuery): Promise<Partial<MaterializedSnapshot<S>>> - 检索单个物化快照。
  • singleState(singleQuery: SingleQuery): Promise<Partial<S>> - 检索单个快照状态。

EventStreamQueryClient

用于查询领域事件流的客户端,支持全面的查询操作:

import {
  Fetcher,
  FetchExchange,
  RequestInterceptor,
  URL_RESOLVE_INTERCEPTOR_ORDER,
} from '@ahoo-wang/fetcher';
import '@ahoo-wang/fetcher-eventstream';
import {
  EventStreamQueryClient,
  all,
  ListQuery,
  PagedQuery,
} from '@ahoo-wang/fetcher-wow';
import { idGenerator } from '@ahoo-wang/fetcher-cosec';

// 使用基础配置创建 fetcher 实例
const exampleFetcher = new Fetcher({
  baseURL: 'http://localhost:8080/',
});

// 定义当前用户 ID
const currentUserId = idGenerator.generateId();

// 创建处理 URL 参数的拦截器
class AppendOwnerId implements RequestInterceptor {
  readonly name: string = 'AppendOwnerId';
  readonly order: number = URL_RESOLVE_INTERCEPTOR_ORDER - 1;

  intercept(exchange: FetchExchange) {
    const urlParams = exchange.ensureRequestUrlParams();
    urlParams.path['ownerId'] = currentUserId;
  }
}

// 注册拦截器
exampleFetcher.interceptors.request.use(new AppendOwnerId());

// 创建事件流查询客户端
const cartEventStreamQueryClient = new EventStreamQueryClient({
  fetcher: exampleFetcher,
  basePath: 'owner/{ownerId}/cart',
});

// 统计事件流数量
const count = await cartEventStreamQueryClient.count(all());

// 列出事件流
const listQuery: ListQuery = {
  condition: all(),
};
const list = await cartEventStreamQueryClient.list(listQuery);

// 以流的形式列出事件流
const listStream = await cartEventStreamQueryClient.listStream(listQuery);
for await (const event of listStream) {
  const domainEventStream = event.data;
  console.log('收到事件流:', domainEventStream);
}

// 分页查询事件流
const pagedQuery: PagedQuery = {
  condition: all(),
};
const paged = await cartEventStreamQueryClient.paged(pagedQuery);
方法
  • count(condition: Condition): Promise<number> - 统计匹配给定条件的领域事件流数量。
  • list(listQuery: ListQuery): Promise<Partial<DomainEventStream>[]> - 检索领域事件流列表。
  • listStream(listQuery: ListQuery): Promise<ReadableStream<JsonServerSentEvent<Partial<DomainEventStream>>>> - 以服务器发送事件的形式检索领域事件流。
  • paged(pagedQuery: PagedQuery): Promise<PagedList<Partial<DomainEventStream>>> - 检索领域事件流的分页列表。

🛠️ 高级用法

完整的命令和查询流程示例

import {
  Fetcher,
  FetchExchange,
  RequestInterceptor,
  URL_RESOLVE_INTERCEPTOR_ORDER,
} from '@ahoo-wang/fetcher';
import '@ahoo-wang/fetcher-eventstream';
import {
  CommandClient,
  CommandRequest,
  CommandHttpHeaders,
  CommandStage,
  HttpMethod,
  SnapshotQueryClient,
  all,
  ListQuery,
} from '@ahoo-wang/fetcher-wow';
import { idGenerator } from '@ahoo-wang/fetcher-cosec';

interface CartItem {
  productId: string;
  quantity: number;
}

interface CartState {
  id: string;
  items: CartItem[];
}

// 创建 fetcher 实例
const exampleFetcher = new Fetcher({
  baseURL: 'http://localhost:8080/',
});

// 定义当前用户 ID
const currentUserId = idGenerator.generateId();

// 创建处理 URL 参数的拦截器
class AppendOwnerId implements RequestInterceptor {
  readonly name: string = 'AppendOwnerId';
  readonly order: number = URL_RESOLVE_INTERCEPTOR_ORDER - 1;

  intercept(exchange: FetchExchange) {
    const urlParams = exchange.ensureRequestUrlParams();
    urlParams.path['ownerId'] = currentUserId;
  }
}

// 注册拦截器
exampleFetcher.interceptors.request.use(new AppendOwnerId());

// 创建客户端
const cartCommandClient = new CommandClient({
  fetcher: exampleFetcher,
  basePath: 'owner/{ownerId}/cart',
});

const cartSnapshotQueryClient = new SnapshotQueryClient<CartState>({
  fetcher: exampleFetcher,
  basePath: 'owner/{ownerId}/cart',
});

// 定义命令端点
class CartCommandEndpoints {
  static readonly addCartItem = 'add_cart_item';
}

// 定义命令接口
interface AddCartItem {
  productId: string;
  quantity: number;
}

type AddCartItemCommand = CommandRequest<AddCartItem>;

// 1. 发送命令添加商品到购物车
const addItemCommand: AddCartItemCommand = {
  method: HttpMethod.POST,
  headers: {
    [CommandHttpHeaders.WAIT_STAGE]: CommandStage.SNAPSHOT,
  },
  body: {
    productId: 'product-123',
    quantity: 2,
  },
};

const commandResult = await cartCommandClient.send(
  CartCommandEndpoints.addCartItem,
  addItemCommand,
);
console.log('命令执行完成:', commandResult);

// 2. 查询更新后的购物车
const listQuery: ListQuery = {
  condition: all(),
};
const carts = await cartSnapshotQueryClient.list(listQuery);

for (const cart of carts) {
  console.log('购物车:', cart.state);
}

// 3. 流式监听购物车更新
const listStream = await cartSnapshotQueryClient.listStream(listQuery);
for await (const event of listStream) {
  const cart = event.data;
  console.log('购物车更新:', cart.state);
}

🧪 测试

# 运行测试
pnpm test

# 运行带覆盖率的测试
pnpm test --coverage

🤝 贡献

欢迎贡献!请查看 贡献指南 获取更多详情。

📄 许可证

Apache-2.0