# @estiva-app/platform 中文文档

规范地址：https://npmcn.com/package/%40estiva-app/platform/

版本：0\.7\.1
npm 元数据更新时间：2026-10-17T20:18:22.531Z
中文译文更新时间：2026-11-14T02:40:35.585Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40estiva-app/platform
原文对照：https://npmcn.com/package/%40estiva-app/platform/?lang=original
项目主页：https://github.com/estiva-app/estiva-foundation#readme
源码仓库：https://github.com/estiva-app/estiva-foundation

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：300 kB，49 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：0 个
- peer 依赖：@estiva-app/protocol \>=0\.15\.0
- 首次发布：2026-09-08T18:48:13.769Z
- 最新版本发布：2026-10-17T20:18:22.165Z
- 近 30 天下载量：2489（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：10（近 12 个月 10 个）

## 中文简介

浏览器寿命周期 2 Estiva apps share:一个活中继插座一个标签应该持有,它每个连接的证书读取,网络返回,当一个标签重新读取,设备上的复制取自重载.

## 安装

```sh
npm install @estiva-app/platform
```

也可以使用：`pnpm add @estiva-app/platform`、`yarn add @estiva-app/platform`、`bun add @estiva-app/platform`

## README

# @stiva-app/平台

浏览器使用周期 2 Estiva apps 共享.

`@estiva-app/protocol` 拥有电线——事件构造、身份证、签名和 两个中继客户端——并刻意包含无折叠. 这个包裹坐着 在它上面,并有关于它的东西 **在标签中运行**: 一个 设置一个标签, 它的证书读取在每个连接和网络上 回来.

```bash
npm install @estiva-app/platform
```

`@estiva-app/protocol` 是同级依赖.

## 为什么它存在

`createLiveRelay` 财务报告和审定财务报表 `createChannelSubscriptions` 已提取到 `protocol` 与一个呼叫者, 和管道a _第二届_ 应用程序不需要 和他们一起移动。 它留在这 `peek-app/src/nostr/liveTopics.ts`,哪个 [ADR 0002 (韩语)](https://github.com/estiva-app/estiva-docs/blob/main/decisions/0002-foundation-packages.md) §10分========================================================================================================================================================================================= (环境注入),并称"SHA-7法案正在支付".

3个行为,每个行为都失败了 作为一个健康的外观插座提供 无:

-   **每个标签一个实例.** `max_subscriptions` 是每个连接和中继器 认证一次连接, 因此每个组件挂载的连接错误 而不是只是浪费。 React Best Mode 使它变得具体:任何 由效果所拥有是两次构建的.
-   **证书读取每个连接,从来没有捕获.** 被俘的标志 模块载荷在无声更新后作为已死证书存活,并重新连接 然后签署其 `22242` 带着它.
-   **网相还来.** 重新连接由 `onclose`,则a 套接字,其对等点变得无法接触 `OPEN` 直到TCP放弃。 无 a 一个 `online`编号 :`offline` 信号它报告 `live`,没有带来什么,从来没有 重试.

## 使用量

```ts
import {
  browserOnlineSource,
  createLiveClientHolder,
} from '@estiva-app/platform'

// The app holds the one module-scope binding. This package holds none — that
// is the constraint-2 failure it exists to stop repeating.
const holder = createLiveClientHolder()

export function liveClient() {
  return holder.get({
    relayUrl: RELAY_URL,
    // Read fresh on every connect attempt.
    getCredential: () => {
      const token = validToken()
      return token && { accessToken: token.accessToken, pubkey: token.pubkey }
    },
    sign: signViaEstivaId,
    online: browserOnlineSource(window),
    subscriptionPrefix: 'my-app-',
    // Told what happened, never asked what to do — your freshness model stays
    // yours.
    observe: { onState: recordRelayState, onOnline: recordOnline },
  })
}
```

### 观看一个频道或整个工作空间

两道道相去相去相去相去,相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相相去相去相去相去相去相去相去相去相去相相去相去相去相去相相去相去相去相去相去相去相去相去相去相去相去相去相去相去相去相相相相相去相.

```ts
// Per channel, refcounted: one REQ each, every consumer sees every event.
// What a message projection wants.
client.subscriptions.subscribe(channelUuid, (event) => project(event))

// A whole workspace in one REQ per 128 Folders. You are told which Folder
// changed, never what changed.
client.watchFolders(folderIds, (folder) => refresh(folder), { kinds: [9, 1111] })
```

`REQ` 是一个客户端 – 服务器框架, 所以它是在中继器的调制 `LimitType::WsEvents`以生产计量: **50个无节奏的要求是 被接受,51号被拒绝** `rate-limited: quota exceeded`因此,一个 34-每个通道路径的Folder工作空间在每一个连接上花费34帧 _和每次连接_· ; `watchFolders` 花一个.

相反,持有订阅费没有多少可衡量的费用——探险回合 无论是1,34还是100米是开放的,行程是44.2米.

### 在依赖之前要知道一件事 `watchFolders`

宽滤波器是测量的,没有假定:一个滤波器携带34个 `#h` 已交付 34人中的34人反对 `wss://estiva.estiva.app`。 。 。 .

它也是人类的财产 **部署的中继分支**。 。 。 . `nfb-demo-kinds` 携带 复数 `extract_channel_ids_from_filters` 返回整个频道设置; 打开 `origin/main`单数版本的过滤器登记符与 _全球_ 订阅,和 `fan_out_scoped` 活动上的分会 `channel_id` 财务报告和审定财务报表 从来没有咨询它的任何携带 `h`。这种构建提供了 **34个中的0个, 完全没有出错** - 欧塞, `live`安静.

因此,任何取决于这一点的, 应该能够回答 _"有件事我没有 发表实际上到达?"_,因为没有其他东西能区分这两个案件. 过 `kinds` 当你在乐园里的时候,一个宽广的过滤器被拒绝 其他建筑与 `restricted: p-gated events require #p matching your pubkey`。 。 。 .

### 刷新,等待当接力说

巴斯计数器 `POST /query` 300分钟 **每桶**因此,每个标签和应用程序 一人签入,共用一项津贴。 构建一个预算和一个预算 每个标签的调度器 :

```ts
import { createRefreshScheduler, createRelayBudget } from '@estiva-app/platform'

// The transport writes it; the cadence reads it.
export const relayBudget = createRelayBudget({
  // Optional: the app's other tabs learn a pause from this one.
  channel: new BroadcastChannel('my-app-relay-budget'),
})
export const refreshScheduler = createRefreshScheduler({ document, window, budget: relayBudget })

// In the transport, on every answer:
if (status === 429 || isRateLimited(body)) relayBudget.noteRateLimited(body)

// In a hook: focus, becoming visible, and every 30 s while visible. A tab
// switch raises two events and costs one read; nothing runs while paused.
useEffect(() => refreshScheduler.subscribe(reload, { interval: !live }), [live])

// Before retrying a refused read: every waiter goes after the longest pause
// anybody learned, not its own shorter one.
await relayBudget.whenClear()
```

这里什么也没有打开读取。 点击请求读取的读物仍会外出 一次;只有节奏后退.

### 设备上的复制件,所以一次重新装入

`createEventSnapshot` 保留中继器告诉一个人的 签名事件, 从来没有一个折叠的视图——在下一个负载上回答同样的过滤器. 把它的答案叠起来,然后发送中继器读作 前后再折:接力的接力回答取代了副本的接力.

```ts
import { createEventSnapshot, indexedDbSnapshotStorage, snapshotScope } from '@estiva-app/platform'

const storage = indexedDbSnapshotStorage(indexedDB, 'my-app-copy')
const copy = createEventSnapshot({
  storage,
  scope: snapshotScope(relayUrl, pubkey),
  version: 1, // bump when what you fold from it changes meaning
})

// In the transport, on every answer the relay gives:
copy.absorb(filters, events)

// Before the relay answers: null means the copy cannot answer this read whole.
await copy.ready
const drawnFirst = copy.answer(filters)

// Signing out: every copy goes, and one still open — in this tab or another —
// can never write the person's data back (the storage counts sign-outs).
copy.discard()
await storage.clear()
```

它回答一个读数 它看到的答案, 和集合列表 它没有,何时 在所有其他领域读取相同(`limit` 答复全文 一起命名了其中的每一个价值—— `'#e': [the newest messages]` 之后 最新变化. `since` 财务报告和审定财务报表 `until` 是时钟而不是问题的一部分.

每一个答案也是一个更正:副本中包含的匹配 过滤刚才回答的中继器,而不在该答案中,被删除——a 删除, 或是丢失的文件夹 。 A 类 `kind:5` 放弃其作者 签名. `keepPlaintext` 保持 平话直说的人自己的blob 旁边 事件,所以一个不变的blob 不需要解密下一个负载。 从不 发布从副本中抽出的东西:它可能比中继器老.

### 正在装入下一个打开的, 不减慢点击

`createPacer` 持有标签的套接字读取到中继器的仪表( buz: 50 REQs) 每平方公尺5分),为两道. A 类 `click` 读取——该人要求的—— 并可能会在等待后溢出到HTTP。 一个 `ahead` 读取 只有窗口留下的东西: 永远不要等待, 永远不要过去 `perWindow - reserve`,仅在HTTP本身 `spill` 允许——应用程序 给它一个较小的 HTTP 允许比点击. `promote()` 移动 `ahead` 读取 仍在等待在点击道上 缓存会按下已经读出的内容来读取相同的内容. `createInFlight` 在电线上保持相同的分数.

`loadAhead` 运行一个应用程序的步骤列表顺序,一次几个 首先,抛出停止它, 和什么装载停留.

```ts
import { createInFlight, createPacer, loadAhead } from '@estiva-app/platform'

const pacer = createPacer({ perWindow: 40, windowMs: 5_000, reserve: 15 })
const wire = createInFlight(4) // two of them at most for `ahead`

// In the socket reader:
await pacer.take(reqs, { lane, spill })
const release = await wire.acquire(lane)

// Once the first page has drawn:
const loader = loadAhead([
  { label: 'Topics', run: () => readTopicsAhead() },
  { label: 'Open work 1', run: () => readTopicAhead(first) },
])
addEventListener('pointerdown', () => pacer.promote(), true)
const { loaded, stoppedBy } = await loader.done
```

如何一步读取和保存它 是应用程序的: 一步读入 点击时读取的相同缓存是使点击立即绘制的.

## 这刻意不知道的

在任何存储的地方,除了副本的存储,你交给它。 皮克的版本用了一个 `LiveProjectionApi`,以及 将康维克斯的假设制成第二包 应用程序取决于。 这能提供框架和连接状态 而不是别的,所以 如果Convex不留任何东西 改变,如果它走了,这已经是正确的.

你的新鲜度和复制品是产品的声音 留在你的应用程序里. `observe` 他们是如何得到他们需要的.

## 许可证

麻省理工学院

## 常见问题

### @estiva-app/platform 是什么？

浏览器寿命周期 2 Estiva apps share:一个活中继插座一个标签应该持有,它每个连接的证书读取,网络返回,当一个标签重新读取,设备上的复制取自重载\.

### 如何安装 @estiva-app/platform？

在项目目录运行 npm install @estiva-app/platform，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@estiva-app/protocol（\>=0\.15\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @estiva-app/platform 支持 TypeScript 吗？

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

### @estiva-app/platform 支持 ESM 还是 CommonJS？

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

### @estiva-app/platform 需要什么版本的 Node\.js？

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

### @estiva-app/platform 可以商用吗？

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

### @estiva-app/platform 的下载量有多少？

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

### @estiva-app/platform 还在维护吗？

最新版本 0\.7\.1 发布于 2026/10/18（27 天前）；至今共发布 10 个版本，其中过去 12 个月发布了 10 个；首次发布于 2026/09/09。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

