@endo/patterns

中文文档
2.1.0发布于 2026/08/14Apache-2.0TypeScriptES Module近 30 天下载 3.5万

通过对象的匹配模式,以通过数据表示

中文使用文档
文档目录26 节
  1. 概览
  2. 快速启动
  3. M 命名空间
  4. 初级火柴
  5. 集装箱火柴
  6. 结构匹配器
  7. 逻辑运算符
  8. 比较匹配符
  9. 特殊火柴
  10. 模式匹配
  11. 匹配( specimen,图案)
  12. mustMatch( specimen, 模式, 标签 )
  13. 复制收藏
  14. 复制集
  15. 复制文件夹
  16. 复制地图
  17. 界面守护
  18. 创建界面保护
  19. 方法守护结构
  20. 与 Exo 合并
  21. 密钥比较
  22. 键EQ( 键 1, 键2)
  23. 比较 Keys( key1, key2)
  24. 密钥、图案和可通行等级
  25. 与 Endo 软件包集成
  26. 深潜

@endo/patterns

可通过数据的匹配和验证模式,包括复制收集以及 界面保护.

概览

这个 @ endo/ patters( 签名) 软件包提供 M 创建模式的命名空间 匹配者可以验证可通过的数据,并描述行为合同. 这是上面的验证层 @ endo/ pass 风格, (中文(简体) ), 允许您在使用前检查与预期形状相匹配的数据.

模式使:

  • 数据验证: 检查符合预期类型和结构的值
  • 接口合同: 描述带有界面守护符的方法签名
  • 复制收藏: CopySet, CopyBag, CopyMap 可通过的数据结构
  • 关键比较: 分布式平等,用于比较不同瓦特的数值

快速启动

import { M, mustMatch } from '@endo/patterns';

const specimen = harden({ foo: 3, bar: 4 });

const pattern = M.splitRecord(
  { foo: M.number() },                        // required properties
  { bar: M.string(), baz: M.number() }       // optional properties
);

mustMatch(specimen, pattern);
// throws: 'bar?: number 4 - Must be a string'

为了最佳渲染效果,请使用 Endo 参考文件 网站.

M 命名空间

这个 M 对象提供了创建图案匹配器的方法 几类:

初级火柴

匹配特定的 JavaScript 类型 :

M.any()           // Matches any passable
M.undefined()     // Matches undefined
M.null()          // Matches null
M.boolean()       // Matches true or false
M.number()        // Matches any number (including NaN, Infinity)
M.bigint()        // Matches any bigint
M.string()        // Matches any string
M.symbol()        // Matches registered/well-known symbols

// Constrained primitives
M.nat()           // Non-negative bigint
M.gte(5)          // Number >= 5
M.lte(100)        // Number <= 100

集装箱火柴

匹配复制本、复制记录和其他结构 :

M.array()         // Any CopyArray
M.record()        // Any CopyRecord
M.set()           // Any CopySet
M.bag()           // Any CopyBag
M.map()           // Any CopyMap

// With constraints
M.array({ maxSize: 10 })          // Array with at most 10 elements
M.string({ maxSize: 100 })        // String with at most 100 characters

// Structured content
M.arrayOf(M.number())             // Array of numbers only
M.recordOf(M.string(), M.number()) // Record with string keys, number values
M.setOf(M.string())               // Set of strings only

结构匹配器

匹配特定形状 :

// Split patterns: required, optional, rest
M.splitArray(
  [M.string(), M.number()],      // required elements
  [M.boolean()],                  // optional elements
  M.any()                         // rest elements
)

M.splitRecord(
  { name: M.string() },           // required properties
  { age: M.number() },            // optional properties
  M.any()                         // rest properties
)

// Partial matches
M.partial({ name: M.string() })  // Has at least 'name' property

// Split auto-detects array vs record
M.split({ x: M.number() }, M.any())

逻辑运算符

组合匹配者 :

M.and(M.number(), M.gte(0), M.lte(100))   // 0 <= n <= 100
M.or(M.string(), M.number())               // String or number
M.not(M.undefined())                       // Anything except undefined
M.opt(M.string())                          // undefined or string (optional)

比较匹配符

匹配比对密钥的值 :

M.eq('hello')     // Equal to 'hello'
M.neq(0)          // Not equal to 0
M.lt(10)          // Less than 10
M.lte(100)        // Less than or equal to 100
M.gte(0)          // Greater than or equal to 0
M.gt(-1)          // Greater than -1

特殊火柴

M.remotable()           // Any remotable object
M.remotable('Counter')  // Remotable with specific label
M.error()               // Any error
M.promise()             // Any promise
M.eref(M.number())      // Number or promise for number (eventual reference)
M.kind('copyArray')     // Specific pass style
M.pattern()             // Any valid pattern
M.key()                 // Any valid Key
M.scalar()              // Any primitive or remotable

模式匹配

匹配( specimen,图案)

回返 true 如果标本符合模式, false 否则:

import { M, matches } from '@endo/patterns';

matches(42, M.number());                    // true
matches('hello', M.number());               // false
matches([1, 2, 3], M.arrayOf(M.number())); // true

mustMatch( specimen, 模式, 标签 )

如果标本不匹配, 则带有描述错误 :

import { mustMatch } from '@endo/patterns';

mustMatch(42, M.string());
// throws: "number 42 - Must be a string"

mustMatch(-5, M.and(M.number(), M.gte(0)), 'count');
// throws: "count: number -5 - Must be >= 0"

错误消息旨在帮助您准确理解错误 与数据.

复制收藏

模式引入了基于三种可通行的收藏类型 makeTagged()数字 :

复制集

一组独有的密钥(原始或可再移动的密钥):

import { makeCopySet } from '@endo/patterns';

const colors = makeCopySet(['red', 'blue', 'green']);

// Elements are sorted in rank order
// Duplicates are removed
// Can be passed between vats

// Pattern for sets
const ColorSet = M.setOf(M.string());
mustMatch(colors, ColorSet);  // passes

为什么不使用 JavaScript 集 ? Java 脚本 设定不能通过. CopySet被冻结,通过 keyEQ,可以高效地序列化.

复制文件夹

多集( 有计数要素) :

import { makeCopyBag } from '@endo/patterns';

const inventory = makeCopyBag([
  ['apples', 5n],
  ['oranges', 3n],
  ['apples', 2n]   // counts are combined
]);

// Result: [['apples', 7n], ['oranges', 3n]]

const InventoryPattern = M.bagOf(M.string(), M.bigint());
mustMatch(inventory, InventoryPattern);

复制地图

从密钥到通过值的映射 :

import { makeCopyMap } from '@endo/patterns';

const balances = makeCopyMap([
  ['alice', 100],
  ['bob', 50]
]);

// Keys are sorted in rank order
// Can use any Key as a key (not just strings!)

const remotableKey = Far('Key', {});
const map = makeCopyMap([[remotableKey, 'value']]);

const BalancesPattern = M.mapOf(M.string(), M.number());
mustMatch(balances, BalancesPattern);

为什么不使用普通物体? 复制映射支持 :

  • 任意密钥作为密钥( 对象, 可调动, 不仅仅是字符串)
  • 使用有效的密钥比较 compareKeys()
  • 用于部分排序的子集关系

界面守护

界面保护描述对象的行为契约, 尤其有用 与 @ endo/ exo (帮助)数字 :

创建界面保护

import { M } from '@endo/patterns';

const CounterI = M.interface('Counter', {
  // Synchronous method
  increment: M.call(M.number()).returns(M.number()),

  // Method with optional arguments
  reset: M.call().optional(M.number()).returns(),

  // Method with rest arguments
  add: M.call(M.number()).rest(M.number()).returns(M.number()),

  // Async method (awaits arguments)
  asyncOp: M.callWhen(M.string()).returns(M.string())
});

方法守护结构

// Basic call: call(required args...)
M.call(M.string(), M.number())

// With optional args
M.call(M.string()).optional(M.number())

// With rest args
M.call(M.string()).rest(M.any())

// Specify return type
M.call(M.string()).returns(M.number())

// Async method (awaits promise args)
M.callWhen(M.remotable()).returns(M.string())

与 Exo 合并

界面保护由 exos 自动执行 :

import { makeExo } from '@endo/exo';
import { M } from '@endo/patterns';

const CounterI = M.interface('Counter', {
  increment: M.call(M.number()).returns(M.number())
});

const counter = makeExo('Counter', CounterI, {
  increment(n) {
    // n is guaranteed to be a number by the guard
    return count += n;
  }
});

counter.increment(5);      // OK
counter.increment('5');    // throws: Must be a number

这是远藤防御性编程的基础:卫兵验证输入 自动,因此您的方法可以专注于商业逻辑.

密钥比较

平等与秩序的密钥可以比较:

键EQ( 键 1, 键2)

使用分布式平等语义测试两个密钥是否相等 :

import { keyEQ } from '@endo/patterns';

keyEQ('hello', 'hello');        // true
keyEQ(42, 42);                  // true
keyEQ([1, 2], [1, 2]);          // true (compares content)

const r1 = Far('Obj', {});
const r2 = Far('Obj', {});
keyEQ(r1, r1);                  // true (same remotable)
keyEQ(r1, r2);                  // false (different remotables)

比较 Keys( key1, key2)

返回比较结果 部分顺序数字 :

  • 0: 键相等
  • -1: 键 1 < 键 2
  • 1: 键 1 > 键 2
  • NaN: 密钥不可比
import { compareKeys, keyLT, keyGT } from '@endo/patterns';

compareKeys('a', 'b');          // -1
compareKeys(5, 5);              // 0
compareKeys(10, 3);             // 1

// Convenience functions
keyLT('a', 'b');                // true
keyGT(10, 3);                   // true

// Incomparable keys
const r1 = Far('A', {});
const r2 = Far('B', {});
compareKeys(r1, r2);            // NaN (different remotables)

为什么是局部命令? 并不是所有的键都可以比较. 例如,不同的可移动性没有定义的顺序, CopySets 使用 子集关系.

密钥、图案和可通行等级

了解类型等级 :

Passable (everything that can pass)
├── Error
├── Promise
├── Key (stable, comparable)
│   ├── Primitives (null, undefined, boolean, number, bigint, string, symbol)
│   ├── Remotable
│   ├── CopyArray<Key>
│   ├── CopyRecord<Key>
│   ├── CopySet<Key>
│   ├── CopyBag<Key>
│   └── CopyMap<Key, Passable>
└── Pattern (describes a set of Passables)
    ├── Key (matches itself)
    └── Key-like with Matcher leaves
  • 可通行: 能够跨越瓦特边界(来自 @endo/pass-tyle)
  • 密钥: 可通行性中稳定和可比较的子集
  • 图案: 描述匹配的可通性子集

与 Endo 软件包集成

完成教学: 见 消息传递 页:1 综合指南,说明模式如何与通行证式、exo和 最终发送.

深潜

详细执行情况:

版本与发布记录

@endo/patterns 共发布 30 个版本,首次发布于 2023/03/08;过去 12 个月发布了 4 个版本。

版本发布日期距今
2.1.0latest2026/08/142 个月前
1.9.12026/06/125 个月前
1.9.02026/04/176 个月前
1.8.02026/02/278 个月前
1.7.02025/07/121.3 年前
1.6.12025/06/181.4 年前

关于 @endo/patterns 的常见问题

@endo/patterns 是什么?

通过对象的匹配模式,以通过数据表示

如何安装 @endo/patterns?

在项目目录运行 npm install @endo/patterns,或使用 pnpm add / yarn add / bun add。

@endo/patterns 支持 TypeScript 吗?

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

@endo/patterns 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@endo/patterns 可以商用吗?

@endo/patterns 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@endo/patterns 的下载量有多少?

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

@endo/patterns 还在维护吗?

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