# @ema-unlimited/frontier-apps-sdk 中文文档

规范地址：https://npmcn.com/package/%40ema-unlimited/frontier-apps-sdk/

版本：0\.3\.0
npm 元数据更新时间：2026-10-21T18:33:16.580Z
中文译文更新时间：2026-11-13T14:26:31.457Z
软件包许可：未声明

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

原始 npm 页面：https://www.npmjs.com/package/%40ema-unlimited/frontier-apps-sdk
原文对照：https://npmcn.com/package/%40ema-unlimited/frontier-apps-sdk/?lang=original

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

- 包体积（解压后）：70.1 kB，14 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：mongoose ^8\.0\.0
- 首次发布：2026-05-20T20:00:57.405Z
- 最新版本发布：2026-10-21T18:33:16.313Z
- 近 30 天下载量：6951（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：9（近 12 个月 9 个）

## 中文简介

SDK for Frontier Apps——数据库、存储和Ema API通过Frontier-apps-Gateway访问

## 安装

```sh
npm install @ema-unlimited/frontier-apps-sdk
```

也可以使用：`pnpm add @ema-unlimited/frontier-apps-sdk`、`yarn add @ema-unlimited/frontier-apps-sdk`、`bun add @ema-unlimited/frontier-apps-sdk`

## README

# @ ema- 无限制/ frontier- apps- sdk

SDK for Frontier Apps ——通过该程序提供数据库,存储和Ema API访问 `frontier-apps-gateway`。每种能力都是一种单独的子路径导入,所以应用程序 只能拉他们用的东西.

> **状态 :** v.x- DB 路线 (`/v1/db/*`(已部署) 储存路线 (单位:千美元)`/v1/storage/*`添加到 **0.2.0 (中文(简体) )** (目前为 `rc` 频道; 见 `rc` (原始内容存档于2019-09-31). Dutt-tag. Ema API线路计划未来发布.

## 为什么

每个部署的边境App 之前直接与蒙戈DB交谈。 单人 运行后查询可以饱和每个应用程序的复制集. 这个SDK部队 所有通过安全屏蔽列表的网关的流量,每个应用程序连接 游泳池,审计线索现场直播.

App代码只更改其导入行——调用表面( Mongoose) `Schema`, (中文(简体) ), `model()`,查询链)被保留.

## 安装

```bash
pnpm add @ema-unlimited/frontier-apps-sdk mongoose
```

`mongoose` 是 DB 子路径所需的对等依赖。 SDK再出口 `mongoose.Schema` 财务报告和审定财务报表 `InferSchemaType` 因此,从一个直接的迁移 Mongoose连接是一个单行导入互换. SDK从不打开 MongoDB连接本身——Schema建筑纯粹是JS道路建设.

该软件包发布在 npm 公共注册处——不需要认证:

```bash
npm install @ema-unlimited/frontier-apps-sdk
```

## 子路径

| 导入 | 状态 | 它会做什么 |
| --- | --- | --- |
| `@ema-unlimited/frontier-apps-sdk` | 可用 | 网关输入( E)`initGatewayClient`\+ 基准错误类 |
| `@ema-unlimited/frontier-apps-sdk/mongodb` | 可用 | MongoDB 式 DB 访问( 完整的 Mongoose 表面) |
| `@ema-unlimited/frontier-apps-sdk/storage` | 可用 | 通过 V4 签名的 URL 文件存储 (`put` 编号 : `get` 编号 : `list` / .) (中文(简体) ) |

`@ema-unlimited/frontier-apps-sdk/ema` (Ema API 代理)已经计划,但尚未添加.

* * *

## 根 (E)`@ema-unlimited/frontier-apps-sdk`(中文(简体) )

根出口只是网关基础设施,而不是一种能力.

```ts
import { initGatewayClient, FrontierError } from '@ema-unlimited/frontier-apps-sdk'
import type { GatewayClientConfig } from '@ema-unlimited/frontier-apps-sdk'
```

### 一次性客户端输入

```ts
initGatewayClient({
  gatewayUrl: process.env.FRONTIER_GATEWAY_URL!,  // injected by publisher
  appId: process.env.APP_ID!,                      // injected by publisher
})
```

`APP_ID` 是出版商提供的值:用于出版范围的完整UUID, `preview-{shortId}` 用于预览范围。 一次在应用靴子上叫这个——它有线 所有子路径使用的共享运输.

* * *

## 蒙戈DB (英语:`@ema-unlimited/frontier-apps-sdk/mongodb`(中文(简体) )

### 定义一个方案( Mongoose 的机械交换)

```ts
// Before
import mongoose, { Schema, type InferSchemaType } from 'mongoose'

// After — only the import line changes
import { Schema, model, type InferSchemaType } from '@ema-unlimited/frontier-apps-sdk/mongodb'

const noteSchema = new Schema(
  {
    userId: { type: String, required: true, index: true },
    title: { type: String, required: true },
    content: { type: String, default: '' },
  },
  { timestamps: true },
)

export type NoteDocument = InferSchemaType<typeof noteSchema>
export const Note = model<NoteDocument>('Note', noteSchema)
```

### 查询

```ts
const notes = await Note.find({ userId }).sort({ createdAt: -1 }).limit(20)

const note = await Note.findOneAndUpdate(
  { _id: id, userId },
  { $set: { title: 'New' } },
  { new: true },
)

await Note.deleteOne({ _id: id, userId })
```

### 支助行动

| 方法 | 回返 |
| --- | --- |
| `find(filter?, options?)` | `QueryBuilder<T[]>` (可兑换) |
| `findOne(filter)` | `QueryBuilder<T | null>` |
| `findById(id)` | `QueryBuilder<T | null>` |
| `findOneAndUpdate(filter, update, ?)` | `QueryBuilder<T | null>` |
| `findByIdAndUpdate(id, update, ?)` | `QueryBuilder<T | null>` |
| `findOneAndDelete(filter)` | `QueryBuilder<T | null>` |
| `findByIdAndDelete(id)` | `QueryBuilder<T | null>` |
| `create(doc | docs[])` | `Promise<T | T[]>` |
| `insertOne(doc)` | `Promise<T>` |
| `insertMany(docs[])` | `Promise<T[]>` |
| `updateOne(filter, update)` | `Promise<UpdateResult>` |
| `updateMany(filter, update)` | `Promise<UpdateResult>` |
| `deleteOne(filter)` | `Promise<DeleteResult>` |
| `deleteMany(filter)` | `Promise<DeleteResult>` |
| `countDocuments(filter?)` | `Promise<number>` |
| `distinct(field, filter?)` | `Promise<unknown[]>` |
| `aggregate(pipeline[])` | `Promise<unknown[]>` |
| `bulkWrite(ops[], options?)` | `Promise<BulkWriteResult>` |
| `createIndex(keys, options?)` | `Promise<string>` (索引名称) |
| `listIndexes()` | `Promise<IndexInfo[]>` |

查询链很懒—— `.sort()`, (中文(简体) ), `.limit()`, (中文(简体) ), `.skip()`, (中文(简体) ), `.lean()` 返回时 同样的 `QueryBuilder` 而 HTTP 仅在 `await`编. `.lean()` 是一个无操作( 网关总是返回普通 JSON ).

### 批量写入

`bulkWrite` 使用相同的操作阵列 `mongoose.Model.bulkWrite` 选择:

```ts
await LineItem.bulkWrite([
  { updateOne: { filter: { _id: id }, update: { $set: { verdict: 'pass' } } } },
  { insertOne: { document: { sku: 'new' } } },
  { deleteOne: { filter: { sku: 'old' } } },
])
```

**序与孟oose相异.** 此默认为 `ordered: false` 地点 默认值为 `true`,所以失败的行动不会丢弃剩下的 批号。 过 `{ ordered: true }` 用于止于一时的语义.

一个批量的上限为1000 行动, 整个请求机构在10 MiB——块 以上为两个。 即使一些操作落地, 批次也会丢出失败; 错误 信件带有应用过的计数器.

### 索引

```ts
// Idempotent: safe to call at every boot.
await Note.createIndex({ userId: 1, createdAt: -1 })
await Person.createIndex({ email: 1 }, { unique: true })
const live = await Note.listIndexes()
```

密钥顺序对复合指数很重要 并且保存在电线上, 因此,将密钥谱构建为对象字元。 支持的选项 : `name`, (中文(简体) ), `unique`, (中文(简体) ), `sparse`, (中文(简体) ), `hidden`, (中文(简体) ), `expireAfterSeconds`, (中文(简体) ), `partialFilterExpression`。 。 。 . 任何其他选项是: **被拒绝**而不是丢弃,所以一个应用永远不会 认为它有限制 数据库没有强制.

重新创建一个具有相同光谱的索引是没有的。 一样 _名称_ 与 不同的规格是 `IndexOptionsConflict` 从服务器。 一个收藏是 上限为64个指数,MongoDB自己的极限.

`createIndex` 使用5分钟而非通常的30秒: a 索引构建运行到命令返回前完成 .

### 屏蔽在外层

这些在任何网络呼叫前同步抛出 :

-   `.populate()` • 使用 `aggregate` 与 `$lookup`
-   `.drop()` 编号 : `dropCollection()` - 计划管理没有暴露
-   `dropIndex()` • 使用 `createIndex` 财务报告和审定财务报表 `listIndexes`

额外屏蔽的网关 `$where`, (中文(简体) ), `$function`, (中文(简体) ), `$accumulator`, (中文(简体) ), `$out`, (中文(简体) ), `mapReduce`,且无约束 `find({})` 打电话 这些返回 HTTP 400,即: SDK 表面为 `FrontierDbError`。 。 。 .

### 处理错误

```ts
import { FrontierError } from '@ema-unlimited/frontier-apps-sdk'
import { FrontierDbError } from '@ema-unlimited/frontier-apps-sdk/mongodb'

try {
  await Note.find({})
} catch (err) {
  if (err instanceof FrontierDbError) {
    console.error(err.status, err.code, err.message)
  }
  // Or catch any SDK error across subpaths:
  if (err instanceof FrontierError) { ... }
}
```

* * *

## 储存`@ema-unlimited/frontier-apps-sdk/storage`(中文(简体) )

每个应用程序文件存储由GCS支持. SDK从不触及GCS证书—— 网关薄荷短命 **V4 签名的 URL** (5-min PUT, 15-min Get) (中文(简体) ) 浏览器/节点客户端直接向 `storage.googleapis.com`控制平面(自动、内容类型验证, 配额,审计)运行于网关;数据平面跳过.

存储是一个纯字节存储器——它从不分析您的文件——所以它接受 **任意 结构良好的内容类型**。没有上传允许列表可以生长。 安全问题 对活性内容执行 _服务_ 时间: 网关服务 `text/html`, (中文(简体) ), `image/svg+xml`,以及 `*/xml` 对象为 `Content-Disposition: attachment` 因此,他们下载,而不是在共享的GCS上执行内置 源代码。 (这不影响 `<img>` 编号 : `<video>` 编号 : `<audio>` 嵌入- 浏览器 忽略 `Content-Disposition` 用于子资源负载。 )

### 密钥布局

SDK发送的每个密钥都经过了消毒和前缀的服务器侧:

```
tenants/<tenantId>/frontier-apps/<appId>/uploads/<your-key>
```

租户+应用由网关解决(每个应用K8s秘密)——SDK 不能影响他们。 用户提供的密钥必须匹配 `[A-Za-z0-9._/-]` 财务报告和审定财务报表 无法从 `/` 或包含 `..` / NUL (英语).

### 高级上传( 为您签名)

```ts
import { put } from '@ema-unlimited/frontier-apps-sdk/storage'

const blob = new Blob([fileBytes], { type: 'image/png' })
const meta = await put(`users/${userId}/avatar.png`, blob, {
  contentType: 'image/png',
})
// → { key, size, contentType }
```

在车盖下 `put()` 签名 + PUT(直接对GCS) + 确认 . 如果PUT失败, `confirm-put` 实值 **没有** 没有孤儿记录.

### 浏览器直接上传( 大文件, 节点代理 hop)

当上传来自浏览器时, 将 URL 服务器端薄荷并让 浏览器执行 PUT. 一样 `Content-Type` + 大小被钉入 签名,所以调用者无法在 网关授权.

```ts
import { signPut, confirmPut } from '@ema-unlimited/frontier-apps-sdk/storage'

// 1. Server side — mint the signed URL.
const signed = await signPut(`users/${userId}/big.zip`, {
  contentType: 'application/zip',
  size: file.size,
})

// 2. Browser side — PUT bytes direct to GCS.
await fetch(signed.url, {
  method: 'PUT',
  headers: signed.requiredHeaders,  // includes Content-Type + X-Goog-Content-Length-Range
  body: file,
})

// 3. Server side — record the upload.
const meta = await confirmPut(`users/${userId}/big.zip`)
```

### 回读 / 列表 / 删除

```ts
import { get, getMeta, list, del } from '@ema-unlimited/frontier-apps-sdk/storage'

// Signed GET URL — usable directly in <img src>, <video src>, <a download>.
const { url, expiresAt, size, contentType } = await get(`users/${userId}/avatar.png`)

// Metadata only (no signing, no transfer).
const meta = await getMeta(`users/${userId}/avatar.png`)

// Paginated list scoped to the app's prefix.
const { items, nextCursor } = await list(`users/${userId}/`, { limit: 50 })

// Idempotent delete.
await del(`users/${userId}/avatar.png`)
```

### 制约因素(服务器强制)

| 限制 | 默认 |
| --- | --- |
| 每个对象大小 | 1个GIB |
| 已签名 PUT TTL | 5分钟 |
| 签名获取 TTL | 15分钟 |
| 内容类型 | 任何身材不错的 `type/subtype` 已接受。 活动类型( A)`text/html`, (中文(简体) ), `image/svg+xml`, (中文(简体) ), `*/xml`)作为下载服务. |
| 密钥字符集 | `[A-Za-z0-9._/-]` 没有 `..`无线索 `/`,无 NUL |

### 处理错误

```ts
import { FrontierStorageError } from '@ema-unlimited/frontier-apps-sdk/storage'

try {
  await put('uploads/x.pdf', blob, { contentType: 'application/pdf' })
} catch (err) {
  if (err instanceof FrontierStorageError) {
    console.error(err.status, err.code, err.message)
    // err.status: HTTP status (e.g. 422 for a denied/invalid content-type or oversize)
    // err.code:   the gateway error code (e.g. "VALIDATION_ERROR") or a client
    //             classifier ("network_error" | "gcs_put_failed" | "timeout" | "aborted")
  }
}
```

### 哪里的东西降落在GCS

```
gs://<project-id>-frontier-apps-uploads/
  tenants/
    <tenantId>/
      frontier-apps/
        <appId>/
          uploads/
            users/<userId>/avatar.png           ← written by the storage SDK
            users/<userId>/reports/2026-Q1.pdf
```

该桶是专门用于每个应用程序用户上传的,与 共享 `<env>-emu-blobs` 那边的桶 `frontier-apps-builder` 保留它 即兴表演 租户下船是单人 `gsutil -m rm -r gs://<bucket>/tenants/<tenantId>/` 对抗这个桶.

* * *

## 发展

```bash
pnpm install
pnpm typecheck
pnpm test
pnpm build
```

## 设计

见 [`docs/design-frontier-apps-db-proxy.md`](https://npmcn.com/docs/design-frontier-apps-db-proxy.md)。 。 。 .

## 常见问题

### @ema-unlimited/frontier-apps-sdk 是什么？

SDK for Frontier Apps——数据库、存储和Ema API通过Frontier-apps-Gateway访问

### 如何安装 @ema-unlimited/frontier-apps-sdk？

在项目目录运行 npm install @ema-unlimited/frontier-apps-sdk，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：mongoose（^8\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @ema-unlimited/frontier-apps-sdk 支持 TypeScript 吗？

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

### @ema-unlimited/frontier-apps-sdk 支持 ESM 还是 CommonJS？

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

### @ema-unlimited/frontier-apps-sdk 可以商用吗？

@ema-unlimited/frontier-apps-sdk 使用 未声明 许可。package\.json 没有声明许可证。没有许可证的代码默认保留全部权利，商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括，不构成法律意见，请以许可原文为准。

### @ema-unlimited/frontier-apps-sdk 的下载量有多少？

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

### @ema-unlimited/frontier-apps-sdk 还在维护吗？

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

