diff --git a/docs/design/contract-check.md b/docs/design/contract-check.md index 9b76e06..51db507 100644 --- a/docs/design/contract-check.md +++ b/docs/design/contract-check.md @@ -167,5 +167,6 @@ issue が閉じたら該当行を消す。消し忘れは次のスナップシ 「降格対象なのに違反が出ない」状態になるだけで安全側だが、`contract-drift` の issue 本文に known-gaps の件数を出すので気づける。 -初期エントリ: `meta` / `i` / `emojis` / `stats`(notedeck#1250。14 件の配布物が NoteDeck 上で -該当呼び出しに到達すると止まる)。 +初期エントリだった `meta` / `i` / `emojis` / `stats`(notedeck#1250)は notedeck 側の欠落ではなく、 +同期スクリプトが引用符つきのキーしか読んでいなかったための誤検知だった(biome は識別子として +書けるキーの引用符を外す)。同期スクリプトは引用符なしのキーも読み、読めない行があれば失敗する。 diff --git a/scripts/contract/known-gaps.json b/scripts/contract/known-gaps.json index 7b2da77..1286582 100644 --- a/scripts/contract/known-gaps.json +++ b/scripts/contract/known-gaps.json @@ -1,9 +1,4 @@ { "$comment": "notedeck 側の既知の欠落。ここに挙げた違反は verify-contract が warning に降格する。issue が閉じたら該当行を消すこと (消し忘れは contract-drift が拾う)。", - "endpoints": { - "meta": { "issue": "https://github.com/notedeck-dev/notedeck/issues/1250", "note": "misskeyEndpoints.generated.ts に無い (本家は公開 endpoint)。14 件の配布物が NoteDeck で止まる" }, - "i": { "issue": "https://github.com/notedeck-dev/notedeck/issues/1250", "note": "同上。本家は read:account" }, - "emojis": { "issue": "https://github.com/notedeck-dev/notedeck/issues/1250", "note": "同上。本家は公開 endpoint" }, - "stats": { "issue": "https://github.com/notedeck-dev/notedeck/issues/1250", "note": "同上。本家は公開 endpoint" } - } + "endpoints": {} } diff --git a/scripts/contract/notedeck-contract.json b/scripts/contract/notedeck-contract.json index 57d1321..17d2354 100644 --- a/scripts/contract/notedeck-contract.json +++ b/scripts/contract/notedeck-contract.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-10-12T10:42:54.123Z", + "generatedAt": "2026-10-13T11:45:02.243Z", "source": { "repo": "notedeck-dev/notedeck", "ref": "25a2567f07744eeea648468412614b61f7831205", @@ -1788,6 +1788,7 @@ "admin/update-meta": "deny:admin", "admin/update-proxy-account": "deny:admin", "admin/update-user-note": "deny:admin", + "announcements": "allow", "announcements/show": "allow", "antennas/create": "account.write", "antennas/delete": "account.write", @@ -1873,6 +1874,7 @@ "clips/show": "account.read", "clips/unfavorite": "clips.write", "clips/update": "account.write", + "drive": "drive.read", "drive/files": "drive.read", "drive/files/attached-chat-messages": "drive.read", "drive/files/attached-notes": "drive.read", @@ -1893,6 +1895,10 @@ "drive/folders/update": "drive.write", "drive/stream": "drive.read", "email-address/available": "allow", + "emoji": "allow", + "emojis": "allow", + "endpoint": "allow", + "endpoints": "allow", "export-custom-emojis": "deny:credential-required-without-kind", "federation/followers": "allow", "federation/following": "allow", @@ -1940,6 +1946,7 @@ "hashtags/show": "allow", "hashtags/trend": "allow", "hashtags/users": "allow", + "i": "account.read", "i/2fa/done": "deny:credential-required-without-kind", "i/2fa/key-done": "deny:credential-required-without-kind", "i/2fa/password-less": "deny:credential-required-without-kind", @@ -2000,11 +2007,13 @@ "invite/delete": "deny:no-mapped-key(write:invite-codes)", "invite/limit": "account.read", "invite/list": "account.read", + "meta": "allow", "miauth/gen-token": "deny:credential-required-without-kind", "mute/create": "account.write", "mute/delete": "account.write", "mute/list": "account.read", "my/apps": "account.read", + "notes": "allow", "notes/children": "allow", "notes/clips": "allow", "notes/conversation": "allow", @@ -2052,6 +2061,7 @@ "pages/show": "allow", "pages/unlike": "deny:no-mapped-key(write:page-likes)", "pages/update": "deny:no-mapped-key(write:pages)", + "ping": "allow", "pinned-users": "allow", "promo/read": "account.write", "renote-mute/create": "account.write", @@ -2060,6 +2070,7 @@ "request-reset-password": "allow", "reset-db": "allow", "reset-password": "allow", + "retention": "allow", "reversi/cancel-match": "account.write", "reversi/games": "allow", "reversi/invitations": "account.read", @@ -2072,11 +2083,14 @@ "roles/show": "allow", "roles/users": "allow", "server-info": "allow", + "stats": "allow", "sw/register": "deny:credential-required-without-kind", "sw/show-registration": "deny:credential-required-without-kind", "sw/unregister": "allow", "sw/update-registration": "deny:credential-required-without-kind", + "test": "allow", "username/available": "allow", + "users": "allow", "users/achievements": "allow", "users/clips": "allow", "users/featured-notes": "allow", diff --git a/scripts/sync-notedeck-contract.mjs b/scripts/sync-notedeck-contract.mjs index f1760c6..a05e9ce 100644 --- a/scripts/sync-notedeck-contract.mjs +++ b/scripts/sync-notedeck-contract.mjs @@ -143,8 +143,14 @@ function parseEndpoints(text) { if (start < 0) throw new Error('misskeyEndpoints.generated.ts に MISSKEY_ENDPOINT_RULES が無い') const body = text.slice(text.indexOf('{', start), text.indexOf('\n}', start)) const endpoints = {} - for (const m of body.matchAll(/^\s*'([^']+)':\s*'([^']+)',?\s*$/gm)) { - endpoints[m[1]] = m[2] + // biome は識別子として書けるキー (meta / i / emojis …) の引用符を外す (notedeck#1250) + const entry = /^\s*(?:'([^']+)'|([A-Za-z_$][\w$]*)):\s*'([^']+)',?\s*$/ + for (const line of body.split('\n').slice(1)) { + if (!line.trim()) continue + const m = line.match(entry) + // 読めない行を黙って落とすと「表に無い」と誤判定するので止める + if (!m) throw new Error(`MISSKEY_ENDPOINT_RULES の行を読めない: ${line.trim()}`) + endpoints[m[1] ?? m[2]] = m[3] } return sortObject(endpoints) }