@effected/schema-org

中文文档
0.15.0发布于 2026/11/10MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2330

schema.org vocabulary as Effect Schema classes, with a script-safe JSON-LD serializer

原始使用文档
文档目录8 节
  1. Why @effected/schema-org
  2. Install
  3. Quick start
  4. The serializer is the point
  5. Validate offline
  6. What this package does not do
  7. Features
  8. License

@effected/schema-org

npm License: MIT Node.js %3E%3D24.11.0 TypeScript 7.0

schema.org as Effect Schema classes: build a JSON-LD graph, serialize it safely into a <script> element, and check it against schema.org's own vocabulary offline. effect is the only peer dependency and there are no runtime dependencies at all — the vocabulary is vendored as generated TypeScript, so nothing here reaches the network.

Pre-1.0.0. This package is part of the @effected/* kit, built on stable Effect v4 (effect ~4.1.0) and still in 0.x development. Stable Effect makes a kit 1.0.0 possible, not automatic. To keep your effect and @effect/* versions on the line the kit is built and tested against, install @effected/pnpm-plugin-effected.

Stability: unstable. This package's API surface is not yet considered complete and may change across 0.x releases. Pin an exact version — even a package marked stable before 1.0.0 can introduce a breaking change by accident, and an exact pin turns that into a type-check error rather than a runtime surprise. Full policy: release strategy.

Why @effected/schema-org

Structured data is usually assembled as an object literal and dropped into a template with JSON.stringify, which gets two things wrong at once. The first is a security bug: JSON.stringify does not escape <, so one description containing the literal </script> closes the JSON-LD block early and injects whatever follows into the page. The second is quieter — schema.org has a vocabulary, your object literal does not, and a plausible-looking property the vocabulary does not define on that type is simply ignored by every consumer, with no error anywhere.

This package fixes both at their source. toScriptBody() is the only text serializer and it is the escaped one, so the injection case cannot be reached by choosing the wrong function. The vocabulary ships vendored, so a build step can reject a misplaced property offline, in CI, with no network call and no crawler round trip.

Install

npm install @effected/schema-org effect
pnpm add @effected/schema-org effect

Requires Node.js >=24.11.0.

All @effected/* packages are ESM-only: the exports maps publish only import conditions, so require() — including tools that resolve in CJS mode — fails with Node's ERR_PACKAGE_PATH_NOT_EXPORTED rather than loading a CJS build that does not exist. Import from an ES module.

Quick start

Assemble nodes into a graph, then serialize it:

import { JsonLdDocument, NodeRef, SoftwareSourceCode, TechArticle } from "@effected/schema-org";
import { Result } from "effect";

const built = JsonLdDocument.buildResult([
  SoftwareSourceCode.make({ "@id": "https://example.com/pkg#source", name: "example", version: "1.2.3" }),
  TechArticle.make({
    "@id": "https://example.com/pkg/docs#intro",
    headline: "Getting started </script>",
    isPartOf: [NodeRef.to("https://example.com/pkg#source")],
  }),
]);

console.log(Result.getOrThrow(built).toScriptBody());
// {"@context":"https://schema.org","@graph":[{"@id":"https://example.com/pkg#source","@type":"SoftwareSourceCode","name":"example","version":"1.2.3"},{"@id":"https://example.com/pkg/docs#intro","@type":"TechArticle","isPartOf":[{"@id":"https://example.com/pkg#source"}],"headline":"Getting started </script>"}]}

Then embed the body verbatim:

<script type="application/ld+json">BODY</script>

buildResult is the entry point, not make: Schema.Class reserves make for its raw structural constructor, which runs none of the identity checks. JsonLdDocument.build is the Effect twin of buildResult, derived from it — the synchronous form is the primitive, so a static-site build never has to construct a runtime.

Three things fail a build, all of them caller error rather than something to report to a user: a malformed @id (InvalidNodeIdError), two nodes claiming one @id (DuplicateNodeIdError), and a catch-all key colliding with a typed field (ConflictingTermError). A dangling reference is legal — pointing at a node described on another page is correct JSON-LD — so read danglingReferences and decide for yourself whether your graph is meant to be closed.

The serializer is the point

Every string in your graph comes from prose someone wrote. A description containing the literal </script> closes the JSON-LD block early and injects markup into your page, and JSON.stringify does not escape it.

toScriptBody() escapes <, > and & after stringify. All three are valid JSON string escapes, so a parser sees the original characters; with no < in the body, neither </script nor <!-- can appear, and the element cannot be closed from inside. The & escape additionally keeps the output well-formed when the document is served as XHTML. The escaping is idempotent, so a second pass over the output is safe rather than double-escaped.

There is deliberately no unescaped text serializer. The escaped output is not a restricted form — it is semantically identical for anyone who parses it, which is everyone — so a second entry point could only ever be chosen wrongly.

toJsonLd() returns the wire form as a plain value, for a framework that serializes JSON-LD itself. If you are producing text, do not use it — JSON.stringify(doc.toJsonLd()) reintroduces exactly the bug this package exists to prevent.

Validate offline

import { Conformance, Vocabulary } from "@effected/schema-org/validate";

console.log(Vocabulary.version);
// 30.0

for (const issue of Conformance.check(doc)) {
  console.error(issue._tag, issue.message);
}
// PropertyNotOnType https://example.com/pkg#source: schema.org does not define "softwareVersion" on SoftwareSourceCode

Conformance.check is total — it never fails and never throws, because reporting is not a failure mode. Conformance.validateResult is the gate built on top of it, returning the graph back or a NonConformantGraphError carrying every issue, and Conformance.validate is its Effect twin. By default only the structural kinds close the gate; ConformanceOptions widens it to deprecations and dangling references.

The vocabulary is vendored, so the gate runs offline and in CI with no network call. It catches the failure that actually happens — not malformed JSON, but a plausible property schema.org does not define on that type. softwareVersion on a SoftwareSourceCode is the canonical example: it reads correct, serializes fine, and is silently ignored downstream. (The property is version.)

Legality is resolved through the full rdfs:subClassOf ancestor closure, so inherited properties are accepted — license is declared only on CreativeWork, and it is legal on SoftwareSourceCode because of that chain.

Import it from the ./validate subpath. The vocabulary table is the whole of what a graph-only consumer avoids: importing @effected/schema-org loads the node classes and the serializer and nothing else. ./validate re-exports the node classes type-only, for annotating a signature — reaching for a value through it, such as NodeRef.to, fails with "only refers to a type". Import values from the root.

Conformance is not Google rich-results eligibility. Google requires properties schema.org does not, and changes its policy on its own schedule. This tells you schema.org defines the term; it does not promise you a rich result.

What this package does not do

  • Mint @ids. They are IRIs in your namespace; the package cannot know your base URL and never derives one.
  • Map your domain onto nodes. That mapping is yours.
  • Decode JSON-LD back into typed nodes. The encode direction is the supported one in this release, and a test pins the asymmetry so it cannot start half-working by accident.
  • Model all of schema.org. Six node types, plus an open additional catch-all for any term — which the validator then checks.

Features

  • SoftwareSourceCode, TechArticle, APIReference, Person, Organization, CreativeWork — the six node classes. Every node requires @id and nothing else; every other field is optional and may be passed as undefined, which is dropped at serialization.
  • ThingFields, CreativeWorkFields, TechArticleFields — the shared field records the node classes are assembled from, exported for a consumer building a node type this package does not model.
  • NodeRef — a reference to another node by @id, with to (unchecked, validated at build time), the sync toCheckedResult and its Effect twin toChecked, and the isValidId predicate. A node-valued property holds a NodeRef, never an embedded node.
  • JsonLdDocument — the graph: buildResult / build to assemble and check identity, nodeIds and danglingReferences to inspect, toScriptBody() to serialize for a page and toJsonLd() for a framework that serializes JSON-LD itself.
  • Vocabulary — a read API over the vendored schema.org vocabulary: version, hasType, hasProperty, ancestorsOf, propertiesOf, isPropertyOn and supersededBy, usable on its own for tooling that never builds a graph.
  • Conformance — offline conformance checking, reporting UnknownTerm, PropertyNotOnType, DeprecatedType, DeprecatedProperty and DanglingReference issues, each with a one-line message.
  • A property that repeats is always an array, even at length one — including license, because MIT AND Apache-2.0 is a real dual-license and collapsing it would be silent data loss.

License

MIT

版本与发布记录

@effected/schema-org 共发布 17 个版本,首次发布于 2026/08/27;过去 12 个月发布了 17 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.15.0latest2026/11/103 天前
0.14.02026/11/0311 天前
0.13.02026/11/0113 天前
0.12.02026/10/2916 天前
0.11.02026/10/2322 天前
0.10.02026/10/1925 天前

关于 @effected/schema-org 的常见问题

@effected/schema-org 是什么?

schema.org vocabulary as Effect Schema classes, with a script-safe JSON-LD serializer

如何安装 @effected/schema-org?

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

@effected/schema-org 支持 TypeScript 吗?

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

@effected/schema-org 支持 ESM 还是 CommonJS?

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

@effected/schema-org 需要什么版本的 Node.js?

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

@effected/schema-org 可以商用吗?

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

@effected/schema-org 的下载量有多少?

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

@effected/schema-org 还在维护吗?

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