@event/schema は xdeal-lp / spikes-lp / ivs-cms の3実装を照合した単一のドメイン型セット。 最リッチな ivs-cms src/types.ts を起源に採取し、3つの規約に正規化した:
- ID は全域
id— spikes の_id、xdeal のレガシーSessionItem/VenueRoomを退役 - LOCKED フィールドは 4S が所有し、CMS は非破壊オーバーレイのみ書く
- イベント固有フィールドは型フォークで足さない — 拡張は extra-fields スロット(DB extension 層・第15章)へ
型の全体像
型はすべて @event/schema から直接 import する(internal-package なのでビルド成果物は無い):
import type {
Session, Speaker, Room, Venue, FloorPlan, Sponsor, PassTier,
Visibility, Track,
} from "@event/schema";
import { LOCKED_SESSION_FIELDS, CARD_OVERLAY_FIELDS } from "@event/schema";
| 型 | 役割 | id | 備考 |
|---|---|---|---|
Session | セッション(登壇枠) | id = 4S session id | LOCKED + CMS overlay + card overlay の3層 |
Speaker | 登壇者 | id | fourSUserId / fourSSlug で 4S user に連結 |
Room | ステージ / 部屋 | id | Session.roomId の参照先。code("M"/"UP")は timetable chip 用 |
Venue | 会場 | id | rooms: Room[] を内包。未マッピング stage は合成 venue に集約 |
FloorPlan | フロア図 | id | svgUrl / svgUrlEn / imageUrl |
Sponsor | スポンサー | id | tier は自由文字列(ivs PARTNERS の8段が superset) |
PassTier | 参加券種 | id | price / paymentType / salesLimit |
> 「Stage」は独立した型ではなく Room が担う。Session.roomId → Room.id で解決し、 > どの Venue にも属さない stage は Venue.rooms の合成エントリに畳み込まれる。
LOCKED(4S 所有)と CMS overlay の境界
Session は3層で構成される。LOCKED は 4S が正で、CMS からは書き込まない:
export const LOCKED_SESSION_FIELDS = [
"id", "title", "titleEn", "startsAt", "durationMin", "roomId", "speakerIds",
] as const satisfies readonly (keyof Session)[];
| 層 | フィールド | 誰が書くか |
|---|---|---|
| LOCKED | id title titleEn startsAt durationMin roomId speakerIds | 4S のみ — CMS の merge 層はこの集合への書き込みを拒否 |
| CMS overlay | track featured genreTags visibility description* coverImageUrl language | admin(content/*.json) |
| card overlay | cardVariant cardTitle cardSpeakerIds cardScale 他 | @event/session-card が所有 |
speakerIds は 4S sessionSpeakers[].order 順で固定。moderatorId は 4S role から毎回導出し保存しない (型上も「保存禁止」と注記される)。title を表示側で改行したいときは LOCKED を書き換えず、 overlay 側の cardTitle(\n で明示改行)で上書きする。
card overlay のドリフト防止
手動微調整オーバーレイのフィールドは CARD_OVERLAY_FIELDS に登録され、content 読み込みと KV ミラーが 同じ集合を参照する(ivs-cms の metaOverlay.ts / pickSessionOverlay パターン)。片方だけ増える ドリフトを防ぐための単一リストである:
export const CARD_OVERLAY_FIELDS = [
"cardVariant", "cardTheme", "cardTitle", "cardTitleEn", "cardSpeakerIds",
"cardRoles", "cardRolesEn", "cardScale", "cardTitleScale", "cardRoleScale",
"cardNameScales", "cardSpeakerIdsUpdatedAt",
] as const satisfies readonly (keyof Session)[];
cardSpeakerIdsUpdatedAt は 4S 側 lineup 編集との競合を last-writer-wins で解決するためのタイムスタンプ。
可視性と多言語
Visibility = "public" | "private" | "preview" | "draft"。draftは admin のみ、previewは
previewToken 保持者のみに見える。公開ページは s.visibility === "public" で絞る。
Trackはstringエイリアス — トラック語彙はイベント側に委ねる(enum 固定しない)。- 多言語は型フォークせず併置フィールドで持つ:
title/titleEn、description/descriptionEn、
nameJa/nameEn。Session.language は "ja" | "en" | "bilingual"。
Speaker / Sponsor の要点
Speaker は revealStatus("anonymous" | "revealed")で伏せ登壇に対応し、fourSUserId / fourSSlug(可変)で 4S 公開プロフィールへ連結する。Sponsor.tier / Room.code のように 語彙をイベント側に開いた文字列は型で縛らない — superset を持つ ivs を基準に、他イベントは部分集合で使う。 これが「型を1つに保ったまま各イベントが違う語彙で運用する」ための設計判断である。