Lexicon
TL; DR:
Lexiconはグローバルなスキーマシステムである
"com.example.ping() "のような逆DNS名を使用します。
定義は主にJSON-Schema文書です
現在はRPCメソッドとrepo Recordsに使用されています
Intro to Lexicon
Lexicon はRPCメソッドとレコードタイプを定義するために使われるスキーマシステム
スキーマは逆引きDNS形式であるNSIDを使って識別される。 メソッドの例:
code:js
com.atproto.repo.getRecord()
com.atproto.identity.resolveHandle()
app.bsky.feed.getPostThread()
app.bsky.notification.list()
レコードタイプの例:
app.bsky.feed.post
app.bsky.feed.like
app.bsky.actor.profile
app.bsky.graph.follow
何故Lexicon が必要?
相互運用性 atproto のようなオープン ネットワークには、動作と意図について合意する方法が必要です。 Lexicon はこれを解決し、開発者が新しいスキーマを導入することを比較的簡単にします。
LexiconはRDFではありません。 RDF はデータの記述には効果的ですが、スキーマの適用には理想的ではありません。 Lexiconは、RDF が提供する汎用性を必要としないため、使いやすくなっています。 実際、Lexicon のスキーマを使用すると、型と検証を使用したコード生成が可能になり、作業がはるかに簡単になります!
HTTP API methods
ATプロトコルのAPIシステム XRPCは、実質HTTPSの薄いラッパーです。その目的は、LexiconをHTTPSに適用することです。 呼び出し:
com.example.getProfile()
は、実際には単なるHTTP requestです:
GET /xrpc/com.example.getProfile
schemaは、有効なクエリパラメータ、リクエストボディ、およびレスポンスボディを規定する。
code:json
{
"lexicon": 1,
"id": "com.example.getProfile",
"type": "query",
"parameters": {
"user": {"type": "string", "required": true}
},
"output": {
"encoding": "application/json",
"schema": {
"type": "object",
"properties": {
"did": {"type": "string"},
"name": {"type": "string"},
"displayName": {"type": "string", "maxLength": 64},
"description": {"type": "string", "maxLength": 256}
}
}
}
}
コード生成することで、これらのスキーマは非常に使いやすくなります。
code:ts
await client.com.example.getProfile({user: 'bob.com'})
// => {name: 'bob.com', did: 'did:plc:1234', displayName: '...', ...}`
Record types
スキーマは、レコードの使用可能な値を定義します。 すべてのレコードには、スキーマにマップされ、レコードの URL を確立する "type" があります。
たとえば、この "follow" レコードの場合:
code:json
{
"$type": "com.example.follow",
"subject": "at://did:plc:12345",
"createdAt": "2022-10-09T17:51:55.043Z"
}
...次のようなURLを持つことになります:
at://bob.com/com.example.follow/12345
...そして次のようなスキーマ:
code:json
{
"lexicon": 1,
"id": "com.example.follow",
"type": "record",
"description": "A social follow",
"record": {
"type": "object",
"properties": {
"subject": { "type": "string" },
"createdAt": {"type": "string", "format": "date-time"}
}
}
}
Tokens
token はデータで使用するグローバル識別子(global identifier)を宣言します。
例えば、あるレコードスキーマが信号機の状態(state)を「赤」「黄」「緑」の3種類で指定することを想定しています。
code:json
{
"lexicon": 1,
"id": "com.example.trafficLight",
"type": "record",
"record": {
"type": "object",
"properties": {
}
}
}
これは完全に許容範囲ですが、拡張性がありません。「黄色に点滅」や「紫」のような新しいstateを追加することはできません(何が起きるかは誰にもわかりません)。
柔軟性を持たせるために、列挙型(enum)の制約を取り除き、可能な値だけを文書化することができます:
code:json
{
"lexicon": 1,
"id": "com.example.trafficLight",
"type": "record",
"record": {
"type": "object",
"properties": {
"state": {
"type": "string",
"description": "Suggested values: red, yellow, green"
},
}
}
}
これは悪くありませんが、具体性に欠けます。 stateの新しい値を発明する人々は互いに衝突する可能性が高く、各状態に関する明確なドキュメントはありません。
代わりに、使用する値のLexicon トークンを定義できます。
code:json
{
"lexicon": 1,
"id": "com.example.green",
"type": "token",
"description": "Traffic light state representing 'Go!'.",
}
{
"lexicon": 1,
"id": "com.example.yellow",
"type": "token",
"description": "Traffic light state representing 'Stop Soon!'.",
}
{
"lexicon": 1,
"id": "com.example.red",
"type": "token",
"description": "Traffic light state representing 'Stop!'.",
}
これにより、trafficLight stateで使用する明確な値が得られます。 最終的なスキーマは引き続き柔軟な検証を使用しますが、他のチームが値の出所と独自の値を追加する方法をより明確にすることができます。
code:json
{
"lexicon": 1,
"id": "com.example.trafficLight",
"type": "record",
"record": {
"type": "object",
"properties": {
"state": {
"type": "string",
"knownValues": [
"com.example.green",
"com.example.yellow",
"com.example.red"
]
},
}
}
}
バージョニング
スキーマは一度公開されると、その制約を変更することはできません。 制約を緩める (可能な値を追加する) と、古いソフトウェアが新しいデータの検証に失敗し、制約を厳しくする (可能な値を削除する) と、新しいソフトウェアが古いデータの検証に失敗します。 その結果、スキーマは、以前は制約のなかったフィールドにオプションの制約を追加することしかできません。
スキーマが以前に公開された制約を変更する必要がある場合は、新しい NSID の下で新しいスキーマとして公開する必要があります。
スキーマの配布
スキーマは、機械的に読むことができ、ネットワークからアクセスできるように設計されています。スキーマがネットワーク上で利用可能であることは必須ではありませんが、メソッドの利用者が単一の正規表現と権威ある表現を利用できるようにスキーマを公開することを強くおすすめします。