@ayco/astro-resume

中文文档
1.1.1发布于 2026/08/13MITES Module零依赖构建来源已验证近 30 天下载 1895

Astro 中的安全服务器对客户端数据

中文使用文档
文档目录14 节
  1. 通过 npm 安装
  2. 使用量
  3. 类型安全
  4. 将全部 Astro.props 传递给客户端
  5. 将服务器数据序列化一次, 访问到任何地方
  6. 使用自定义序列器和解析器
  7. 逃脱 & XSS
  8. 错误警告( W) deserialize()
  9. 关于
  10. 贸易业务
  11. 发展
  12. 升级到 v1
  13. 路线图
  14. 报告问题

Astro 续作

Package information: NPM version Package information: NPM license Package information: NPM downloads

CI OpenSSF Scorecard Runtime dependencies

[! 警告] 在V0吗? 升级. 以前每个版本 1.0.0 将序列化的数据写入页面而不逃避 <因此,一个字符串包含 </script> 在数据中提早关闭元素,其余以 HTML 运行。 如果其中任何数据来自用户输入,则视其为可开发数据。 固定在 1.0.0,其唯一破碎的更改是 TypeScript 之一 — 见 逃脱 & XSS 财务报告和审定财务报表 升级到 v1。 。 。 .

[!注] V1 中新建 —— 说 deserialize<T> 现在默认为 unknown 改为 any,所以没有类型参数的调用在读取结果属性时不再进行键入检查。 见 升级到 v1。 。 。 .

用于序列化服务器数据供客户端使用的工具.

  1. Serialize - 需要Astro组件 id 财务报告和审定财务报表 data
  2. deserialize() - 用于客户端的功能 id 字符串并返回 data 对象

通过 npm 安装

在你身边 天体 项目:

npm i @ayco/astro-resume

需要Astro 4或更新.

使用量

基本原始数据的序列化和去序列化

---
import Serialize from "@ayco/astro-resume";

const data = {
    hello: 'world',
}
---

<Serialize id="my-data" data={data} />

<script>
import {deserialize} from '@ayco/astro-resume';
const data = deserialize('my-data');
console.log(data) // {hello: 'world'}
</script>

没有类型参数 data 实值 unknown 足够登入或传入,但不能读取属性。 说出要这样做的类型.

类型安全

deserialize<T>() 返回时 T,默认为 unknown。可以定义数据的类型,并在客户端脚本中使用.

---
import Serialize from "@ayco/astro-resume";

const data = {
    hello: 'world',
    isOkay: true
}
// define the type of data to be serialized
export type Data = typeof data;
---

<Serialize id="my-data" data={data} />

<script>
import {deserialize} from '@ayco/astro-resume';

/**
* reuse the type in the client
* assuming this component's name is `ThisComponent.astro`
*/
import type {Data} from './ThisComponent.astro';

const data = deserialize<Data>('my-data');

console.log(data) // {hello: 'world', isOkay: true}
</script>

自定义解析器已键入 (serialized: string) => T,同样的 T 呼叫回来。 这么说吧 deserialize<Data>('my-data', parse) 检查您的解析器是否生成 Data,以及 deserialize('my-data', myParser) 推断 T 从 myParser 而不是掉头 unknown。 。 。 .

将全部 Astro.props 传递给客户端

如果您需要将所有组件道具制成客户端脚本 :

---
import Serialize from "@ayco/astro-resume";
export interface Props {
    hello: string;
    isOkay: boolean;
}
---

<Serialize id="preferences" data={{...Astro.props}} />

<script>
import {deserialize} from '@ayco/astro-resume';
import type {Props} from './ThisComponent.astro';
const {hello, isOkay} = deserialize<Props>('preferences');
console.log(hello, isOkay);
</script>

将服务器数据序列化一次, 访问到任何地方

如果您共享了需要从服务器初始化并在客户端多个地方访问的数据,则可以使用 Serialize 一次和一次 deserialize 只要Astro组件在同一页中,它们就属于任何数量.

在这个例子中,一个 AppConfig 对象在index.astro 中被构建并序列化,在child Astro 组件中访问.

在索引.astro:

import Serialize from "@ayco/astro-resume";

const appConfig = {
    someClientSideKey: '1234hello',
}
export type AppConfig = typeof appConfig;
---

<Serialize id="app-config" data={appConfig} />
<Child />

在 Child.astro 中: (中文(简体) )

<h1>I'm a child. I have access to the appConfig in index!</h1>
<GrandChild />
<script>
import {deserialize} from '@ayco/astro-resume';
import type {AppConfig} from '..pages/index.astro';
const data = deserialize<AppConfig>('app-config');

// ... do something with the app config
</script>

在Grand Child.astro: (原始内容存档于2019-09-28)

<h1>I'm a grand child. I also have access to the appConfig in index!</h1>
<script>
import {deserialize} from '@ayco/astro-resume';
import type {AppConfig} from '..pages/index.astro';
const data = deserialize<AppConfig>('app-config');

// ... do something with the app config
</script>

使用自定义序列器和解析器

如果您想要选择更复杂的数据处理, 可以带自定义序列器/ 参数 .

以下是数据序列化的例子 JSON.stringify 无法使用 Rich Harris' (例如日期或 BigInt) devalue数字 :

---
import {stringify} from 'devalue';
import Serialize from "@ayco/astro-resume";
const data = {
    now: new Date(),
    age: BigInt('3218378192378')
}
export type Data = typeof data;
---

<Serialize data={data} id="my-data" use={stringify} />

<script>
import {parse} from 'devalue';
import {deserialize} from '@ayco/astro-resume';
import type {Data} from './index.astro';

const {age, now} = deserialize<Data>('my-data', parse);
console.log(typeof age); // 'bigint'
console.log(now instanceof Date); // true
</script>

注意自定义序列器的输出是写在页面的正文上,所以它必须自己逃出——见下文. devalue 已经 如果没有的话, Serialize 拒绝提供而不是运送突围.

逃脱 & XSS

序列化数据写入于 a <script> 元素,所以字符串类似 </script><img src=x onerror=alert(1)> 在数据中,否则会提前关闭该元素,让其余部分以 HTML 运行.

Serialize 避免这一切 < 输出为 \u003c这是有效的JSON字符串逃脱,所以没有损失.. deserialize() 原弦还给你 </script 绝对不能出现在序列有效载荷中.

<Serialize id="my-data" data={{evil: '</script><img src=x onerror=alert(1)>'}} />

<!-- renders as: -->
<script type="application/json" id="my-data">{"evil":"\u003c/script>\u003cimg src=x onerror=alert(1)>"}</script>

有两个限制值得了解:

  1. 自定义序列器不会为你逃脱. 当你通过 use,它的返回值不作修改地写到页面上,这样一个发布自己格式的序列器就不会被损坏。 用一个逃跑的 < 本身 - devalue 已经.

    作为后援, Serialize 如果串行输出仍然包含 </script,因此一个无法逃避的序列器在渲染时会响亮地出故障,而不是运输XSS. 那张支票是定向的,不是HTML脚本的完整模型解析——一个能逃脱的序列器 < 仍然值得信赖.

  2. 这能保护有效载荷,而不是你用它做什么. 数据你取出 deserialize() 回到DOM 仍然需要正常的护理 更喜欢 textContent 结束 innerHTML。 。 。 .

错误警告( W) deserialize()

这个 deserialize() 函数可以给出以下内容:

  1. 错误RR: 找不到匹配 - 没有 JSON 带有指定标识的脚本
  2. ERR: 分析数据失败 <id> - 发现一个脚本,但是 JSON.parse (或您的定制解析器) 扔在它的内容
  3. 警告: 多个匹配 <id> - 有多个 JSON 用同一ID找到的脚本

抛出的错误将潜在的失败保留为 cause因此,真正的问题仍然存在,以检查:

try {
  const data = deserialize<Data>('my-data');
} catch (err) {
  console.error(err.message); // astro-resume ERR: Failed to parse data for "my-data".
  console.error(err.cause);   // SyntaxError: Unexpected token ...
}

Serialize 在服务器上执行同样内容 : 如果您的数据无法被序列化, 则将“ 数据不可序列化 ” 与原始数据一起丢出 TypeError 圆形结构,或 BigInt 没有自定义序列器——作为 cause。 。 。 .

关于

这是一个快速而简单的模式,可以将序列化的信息嵌入到您的 HTML 中,并以客户端脚本提供. 这不需要客户端JS 直接将数据嵌入到您的HTML上.

这个 Serialize 组件会将数据写入 JSON 中 <script type="application/json"> 用于按住字符串的元素.

这个 deserialize() 函数然后可以解析用于客户端脚本的值字符串.

还有一种模式 在 Astro 文档中给出 来使用自定义元素 data- 适当保护组件范围的道具。 它要求客户方联署材料.

贸易业务

其他一些框架本身会为你管理序列化的信息和ID, 但我们无法在Astro访问,因为我们并没有真正将一个框架传送到浏览器.

这很好,也是理想的(在我看来),因为我们知道HTML是如何形成的,以及我们正在向用户运送什么. 换句话说,我们必须自己去处理事情.

你必须管理身份证(即确保身份证是独一无二的),并了解这些证件是否具有特殊性 deserialize() 函数将爬行整个文档,其性能成本将视您的 HTML 大小而定.

身份证本身可以是任何字符串. 无效的 CSS 标识符 - my.data, (中文(简体) ), ns:data[0], (中文(简体) ), 1data 被逃脱 CSS.escape 在检查之前,所以他们决心而不是默默地不匹配.

发展

此寄存器是一个 pnpm 工作空间 : package/ 是已发布的软件包,并且 demo/ 是一个消耗它的Astro网站.

pnpm install
pnpm dev      # run the demo site
pnpm lint     # eslint
pnpm test     # vitest (happy-dom for deserialize, Astro Container API for Serialize)
pnpm check    # astro check on the demo
pnpm release  # bumpp: bump package/package.json, commit, tag, push

pnpm lint, (中文(简体) ), pnpm test 财务报告和审定财务报表 pnpm check 是三个命令 CI 运行.

升级到 v1

断裂变化: deserialize<T> 默认为 unknown 改为 any。 。 。 .

// v0 — `data` was `any`, so this compiled and checked nothing
const data = deserialize('my-data');
data.hello.toUpperCase();

// v1 — name the type
const data = deserialize<Data>('my-data');
data.hello.toUpperCase();

要求只移动价值——记录价值,将其交给别的东西——保持工作不受影响。 调用到结果需要类型参数或缩小检查。 现已输入自定义解析器 (serialized: string) => T 而不是返回 any,所以通过一个也给 T 某种可以推断的东西.

V1 中的其他所有内容都是向后兼容的 :

  • 安全修补——即使没有类型错误也要升级的原因. < 以 \u003c,因此数据无法再从 <script> 键。 v0完全没有逃脱,所以包含 </script> 可作为 HTML 运行 。 这改变了页面上的字节,而不是返回的值。 见 逃脱 & XSS。 。 。 .
  • 无法使用 CSS 标识符的ID 将使用 CSS.escape,因此它们现在匹配而不是扔出"找不到匹配".
  • 失败的解析器会抛出它自己命名ID的错误, 并带有解析器 SyntaxError 作为 cause,而不是光刻错误.
  • 错误来自 Serialize 将原始错误带入 cause. 此前,它作为第二个论点被通过: Error() 并悄悄地放弃.
  • 这个 astro 拓宽对等依赖 ^6 改为 >=4,因此软件包安装在Astro 4、5和6上.

路线图

见 TODO 跟踪器 已规划的工作项目.

报告问题

要报告问题或要求特性,请开罚单 GitHub 图像,或发送纯文本电子邮件给 ~ayoayco/[email protected]

版本与发布记录

@ayco/astro-resume 共发布 40 个版本,首次发布于 2023/07/16;过去 12 个月发布了 5 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.1.1latest2026/08/132 个月前
1.1.02026/08/122 个月前
1.0.02026/08/083 个月前
0.4.62026/04/047 个月前
0.4.52026/04/047 个月前
0.4.42025/03/061.7 年前

关于 @ayco/astro-resume 的常见问题

@ayco/astro-resume 是什么?

Astro 中的安全服务器对客户端数据

如何安装 @ayco/astro-resume?

在项目目录运行 npm install @ayco/astro-resume,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:astro(>=4)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@ayco/astro-resume 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/ayco__astro-resume 是否存在。

@ayco/astro-resume 支持 ESM 还是 CommonJS?

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

@ayco/astro-resume 可以商用吗?

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

@ayco/astro-resume 的下载量有多少?

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

@ayco/astro-resume 还在维护吗?

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