Skip to content

POC Websocket types - #601

Open
C0D3-M4513R wants to merge 20 commits into
vrchatapi:mainfrom
C0D3-M4513R:feature/websocket
Open

C0D3-M4513R wants to merge 20 commits into
vrchatapi:mainfrom
C0D3-M4513R:feature/websocket

Conversation

@C0D3-M4513R

@C0D3-M4513R C0D3-M4513R commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Closes: #598

Please note: This bumps the Openapi Spec version to 3.1, which causes some issues with some language generators!

Copilot AI lite review requested due to automatic review settings September 16, 2026 17:47
@C0D3-M4513R
C0D3-M4513R force-pushed the feature/websocket branch 2 times, most recently from 742b44e to 91b5283 Compare September 16, 2026 17:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate schema issues remain in the WebSocket unions, discriminator modeling, and OpenAPI compatibility.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This proof-of-concept adds OpenAPI schemas for WebSocket messages, notifications, and related payloads to support client deserialization.

Changes:

  • Adds wrapper and non-wrapper WebSocket event schemas.
  • Adds notification, content-refresh, user, friend, group, badge, and queue payload schemas.
  • Registers the schemas in the components index.
File summaries
File Review status
openapi/components/schemas/WebsocketWrapperUserUpdate.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperUserLocation.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperUserBadgeUnassigned.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperUserBadgeAssigned.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperResponseNotification.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperNotificationV2Update.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperNotificationV2Delete.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperNotificationV2.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperNotification.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperInstanceQueueReady.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperInstanceQueueJoined.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperGroupRoleUpdated.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperGroupMemberUpdated.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperGroupLeft.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperGroupJoined.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendUpdate.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendOnline.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendOffline.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendLocation.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendDelete.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendAdd.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperFriendActive.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketWrapperContentRefresh.yaml Moderate finding recorded
openapi/components/schemas/WebsocketUserUpdate.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketUserLocation.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketUserBadgeUnassigned.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketUserBadgeAssigned.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketSeeNotification.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketResponseNotification.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketNotificationV2Update.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketNotificationV2Delete.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketNotificationV2.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketNotification.yaml Critical and moderate findings recorded
openapi/components/schemas/WebsocketMessage.yaml Critical and nit findings recorded
openapi/components/schemas/WebsocketInstanceQueueReady.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketInstanceQueueJoined.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketHideNotification.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketGroupRoleUpdated.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketGroupMemberUpdated.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketGroupLeft.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketGroupJoined.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendUpdate.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendOnline.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendOffline.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendLocation.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendDelete.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendAdd.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketFriendActive.yaml Reviewed; no final finding recorded
openapi/components/schemas/WebsocketContentRefresh.yaml Nit recorded
openapi/components/schemas/WebsocketClearNotification.yaml Critical finding recorded
openapi/components/schemas/_index.yaml Nit recorded
Review details

Suppressed comments (7)

openapi/components/schemas/WebsocketMessage.yaml:15

  • These alternatives include WebsocketWrapperNotification.yaml and other wrapper files that are currently empty in this checkout. Adding WebsocketMessage to _index.yaml makes the bundler resolve those references; empty targets are not usable Schema Objects (or become unconstrained branches), so the bundle will fail or the union will be ambiguous. Define each wrapper before referencing it, or remove unimplemented variants.
      when: jsonschema
      then:
        contentSchema:
          oneOf:
            - $ref: ./WebsocketNotification.yaml

openapi/components/schemas/WebsocketMessage.yaml:14

  • This publishes a 27-variant websocket union without a websocket workflow or capture in test/arazzo.yaml. Per the repository's evidence rule, an uncaptured property, type, or variant is a missing test rather than a schema fact; add workflows that record these frames before indexing the union, otherwise generated clients will be constrained by guessed variants.
    x-if:
      when: jsonschema
      then:
        contentSchema:
          oneOf:

openapi/components/schemas/WebsocketMessage.yaml:41

  • This discriminator maps on type, but WebsocketSeeNotification, WebsocketResponseNotification, and WebsocketClearNotification do not define that property. These branches therefore cannot be selected by the discriminator even after the conditional is placed at the intended schema level; add the actual wrapper type to each variant or map only schemas that carry it.
            - $ref: ./WebsocketGroupJoined.yaml
            - $ref: ./WebsocketGroupLeft.yaml
            - $ref: ./WebsocketGroupMemberUpdated.yaml
            - $ref: ./WebsocketGroupRoleUpdated.yaml
          discriminator:

openapi/components/schemas/WebsocketMessage.yaml:13

  • WebsocketSeeNotification.yaml and WebsocketResponseNotification.yaml currently define the same three required string properties. Any content object matching one therefore matches both branches, so this oneOf cannot distinguish the two message types. Add an evidence-backed schema distinction or discriminate at the enclosing message instead.
      then:
        contentSchema:

openapi/components/schemas/WebsocketMessage.yaml:14

  • Even after fixing the indentation, these branches are WebsocketWrapper... objects; for example, WebsocketWrapperContentRefresh already requires its own type and content. Placing that union under the outer message's content models an extra nested wrapper, so make the message itself the union (or make the branches content-only) and discriminate at the field that actually carries the event type.
    x-if:
      when: jsonschema
      then:
        contentSchema:
          oneOf:

openapi/components/schemas/WebsocketNotification.yaml:12

  • The websocket detail union omits NotificationEmpty, even though the existing SentNotification union models {} with that schema and NotificationType includes types such as friendRequest and message that do not have a listed detail shape. Those valid empty-detail cases are left without a branch. Add the existing empty-detail schema and make the other branches mutually exclusive, or explicitly constrain this websocket model to the covered notification types.
        oneOf:

openapi/components/schemas/_index.yaml:14

  • These newly published WebSocket schemas add properties, required entries, enum values, and examples without any Arazzo workflow/capture covering WebSocket frames. Add workflows that capture the relevant message states before publishing these branches; otherwise the schema is inferred rather than evidence-backed.
WebsocketMessage:
  $ref: ./WebsocketMessage.yaml
WebsocketNotification:
  $ref: ./WebsocketNotification.yaml
  • Files reviewed: 7/28 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread openapi/components/schemas/WebsocketClearNotification.yaml Outdated
Comment thread openapi/components/schemas/WebsocketMessage.yaml Outdated
Comment on lines +12 to +16
oneOf:
- $ref: ./NotificationDetailBoop.yaml
- $ref: ./NotificationDetailInvite.yaml
- $ref: ./NotificationDetailInviteResponse.yaml
- $ref: ./NotificationDetailRequestInvite.yaml
Comment on lines +19 to +23
discriminator:
propertyName: type
mapping:
boop: ./NotificationDetailBoop.yaml
invite: ./NotificationDetailInvite.yaml
Comment thread openapi/components/schemas/WebsocketContentRefresh.yaml Outdated
@C0D3-M4513R
C0D3-M4513R force-pushed the feature/websocket branch 2 times, most recently from 09c9cf1 to 5b4cd87 Compare September 16, 2026 17:59
@pkg-pr-new

pkg-pr-new Bot commented Sep 16, 2026

Copy link
Copy Markdown
pnpm add https://pkg.pr.new/vrchatapi/specification/vrchat@87e2c20

commit: 87e2c20

@C0D3-M4513R
C0D3-M4513R requested review from ariesclark and a lite review from Copilot September 16, 2026 18:58
@C0D3-M4513R
C0D3-M4513R marked this pull request as ready for review September 16, 2026 18:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical schema, discriminator, compatibility, and component-preservation issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

openapi/components/schemas/Notification.yaml:31

  • These discriminator keys do not match the repository's NotificationType values (inviteResponse, requestInvite, requestInviteResponse, votetokick in NotificationType.yaml:4-11); the mapping uses hyphenated spellings instead. Even after fixing the discriminator placement, those notification variants will not resolve to a branch. Use the actual enum values as mapping keys.
              invite-response: ./NotificationDetailInviteResponse.yaml
              request-invite: ./NotificationDetailRequestInvite.yaml
              request-invite-response: ./NotificationDetailRequestInviteResponse.yaml
              vote-to-kick: ./NotificationDetailVoteToKick.yaml

openapi/components/schemas/WebsocketMessage.yaml:7

  • No Arazzo workflow or captured response accompanies these new WebSocket branches, while this repository derives schema properties, enum values, and required entries from traffic. As written, this publishes guessed shapes (the TODOs in the new schemas reinforce that); add capture-backed workflow coverage and derive the union from it before adding these schemas.
    oneOf:
      - $ref: ./WebsocketMessageNormal.yaml
      - $ref: ./WebsocketMessageDoubleJson.yaml

openapi/components/schemas/WebsocketNotification.yaml:19

  • These oneOf branches are not mutually exclusive: the issue's invite payload has worldId and worldName but no type, and NotificationDetailBoop has no required properties, so it also accepts that object. The oneOf therefore matches both Boop and Invite and rejects the notification instead of fixing deserialization. Make the branches mutually exclusive using capture-backed requirements, or use a non-oneOf representation if the wire payload has no discriminator.
        oneOf:
          - $ref: ./NotificationDetailBoop.yaml
          - $ref: ./NotificationDetailInvite.yaml
          - $ref: ./NotificationDetailInviteResponse.yaml
          - $ref: ./NotificationDetailRequestInvite.yaml
          - $ref: ./NotificationDetailRequestInviteResponse.yaml
          - $ref: ./NotificationDetailVoteToKick.yaml
        discriminator:

openapi/components/schemas/WebsocketNotification.yaml:27

  • These discriminator keys do not match the repository's NotificationType values (inviteResponse, requestInvite, requestInviteResponse, votetokick in NotificationType.yaml:4-11); the mapping uses hyphenated spellings instead. Even after fixing the discriminator placement, those notification variants will not resolve to a branch. Use the actual enum values as mapping keys.
            invite-response: ./NotificationDetailInviteResponse.yaml
            request-invite: ./NotificationDetailRequestInvite.yaml
            request-invite-response: ./NotificationDetailRequestInviteResponse.yaml
            vote-to-kick: ./NotificationDetailVoteToKick.yaml
  • Files reviewed: 33/33 changed files
  • Comments generated: 19
  • Review effort level: Lite

Comment on lines +23 to +27
discriminator:
propertyName: type
mapping:
boop: ./NotificationDetailBoop.yaml
invite: ./NotificationDetailInvite.yaml
Comment thread openapi/components/schemas/WebsocketContentRefresh.yaml Outdated
Comment thread openapi/components/schemas/WebsocketGroupJoined.yaml Outdated
Comment thread openapi/components/schemas/WebsocketGroupLeft.yaml Outdated
Comment thread openapi/components/schemas/WebsocketGroupMemberUpdated.yaml Outdated
Comment thread openapi/components/schemas/WebsocketInstanceQueueReady.yaml Outdated
Comment on lines +7 to +11
- notification
- response-notification
- notification-v2
- notification-v2-update
- notification-v2-delete
Comment thread openapi/components/schemas/WebsocketContentRefresh.yaml Outdated
Comment thread openapi/components/schemas/WebsocketContentRefreshContentType.yaml Outdated
Comment thread openapi/components/schemas/WebsocketNotificationV2Update.yaml Outdated
@C0D3-M4513R

Copy link
Copy Markdown
Contributor Author

This approach does not work for actually deserializing the double-json encoded json, sadly.

Also the discriminator mappings do not work (at least in rust).

If possible I'd like someone more skilled at Openapi spec to look at this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants