Skip to content

Fix TypeScript-in-JS, security, and timing bugs in sports use-case snippets - #520

Merged
techwritermat merged 4 commits into
masterfrom
docs-snippets-sports-review-fixes
Sep 23, 2026
Merged

techwritermat merged 4 commits into
masterfrom
docs-snippets-sports-review-fixes

Conversation

@techwritermat

Copy link
Copy Markdown
Contributor

What

Fixes defects found by the Platform documentation release review (preview 961) in docs-snippets/use-cases/, which feeds the 11 Use cases → Sports, Media & Entertainment pages.

Finding File Change
G01 all 11 files Extracted regions are now valid plain JavaScript (the docs render them as javascript). (error as PubNubError) casts, type annotations, type declarations, and Record<> generics removed. npm run test:snippets still type-checks every file.
AI-17 live-commentary.ts unsubscribe() moved into a SIGINT handler. A status listener logs readiness on PNConnectedCategory before the reader starts the publisher.
AI-19, AI-21 fan-behavior-management.ts Runnable server.js (grant, mute, unmute, ban via process.argv) and fan.js (node fan.js <token>). A JSON-file token registry survives separate CLI runs. Mute revokes every tracked writable token, ban revokes every token, grant refuses writable tokens while muted and any token while banned. A failed revoke keeps the token tracked so a repeated command retries it. The status listener is installed before subscribing, and publish has its own try/catch.
AI-22 game-chat-moderation.ts Hiding a message now goes through a moderator-only control channel (game.chat.moderation, write granted only to the moderator token). Live and history views both read it, instead of trusting any client's moderation/hidden message action.
AI-23 game-chat-moderation.ts The delete bound is computed as (BigInt(timetoken) - BigInt(1)).toString(), with the timetoken kept as a string. The start/end direction is unchanged and is under separate review.
AI-24 fan-re-engagement.ts Presence monitoring runs as match-service. The alert is published only when whereNow shows the fan is absent, to a per-user channel game.moment-alerts.<userId>. New fan-viewer.js for a two-client test.
AI-25 score-alerts.ts One APNS_ENVIRONMENT constant is used for registration, listing, removal, and the payload target. The list and remove snippets are split into FCM and APNs variants.

New snippet names that the docs pages already reference: fanBehaviorStateStore, fanBehaviorBanFan, fanBehaviorUnmuteFan, fanBehaviorServerDispatch, fanBehaviorSubscribeAndPublish, reEngagementFanViewer, chatModerationGrantControlChannelAccess, chatModerationApplyModeratorToken, scoreAlertsEnvironmentConstant, scoreAlertsListDeviceRegistrationsAPNs, scoreAlertsListDeviceRegistrationsFCM, scoreAlertsRemoveDeviceRegistrationAPNs, scoreAlertsRemoveDeviceRegistrationFCM.

Validation

  • npm run test:snippets passes.
  • All 60 JavaScript regions embedded by the 11 pages were extracted with the docs site's extractSnippets from this branch and pass node --check as ES modules.
  • Offline mocked harnesses (stubbed PubNub, no network): the mute/ban/unmute sequence across separate node runs sharing the state file, a failed revoke being retried, and the three-process re-engagement test (only the absent fan is alerted).
  • Not run: no live PubNub, push, or delete calls. Revocation propagation and APNs delivery are described from the docs, not tested.

Docs dependency

The docs pages point at refs/heads/master. Four pages (fan-behavior-management, fan-re-engagement, game-chat-moderation, real-time-score-alerts) reference the new snippet names above, so this needs to merge before those docs changes ship.

techwritermat and others added 2 commits September 23, 2026 09:34
…ippets

The platform review found that every sports/media/entertainment snippet file
cast errors with `as PubNubError`, which is TypeScript syntax and fails a
plain-JS parser when the docs extract these regions as JavaScript. Some files
also carried type annotations, `type`/`interface` declarations, and `as`
casts inside the extracted regions. Replace the cast pattern everywhere with
a runtime `instanceof Error && 'status' in error` check, drop now-unused
`PubNubError` imports, and remove or rework every other type-only construct
inside a snippet marker (Map instead of a `Record<...>`-annotated object,
default-valued parameters instead of typed ones, `typeof`/`in`-narrowed
property reads instead of `as` casts on a subscribed message).

Fix the specific defects the review's SDK-review findings pointed at:

- live-commentary.ts: `unsubscribe()` was top-level code that ran the moment
  the file loaded. Move it into a `SIGINT` handler, and add a status
  listener that logs on `PNConnectedCategory` so the docs can tell the
  reader when the viewer is actually ready to receive.

- fan-behavior-management.ts: mute and ban only revoked the most recently
  issued token, leaving any earlier writable token usable. Track every
  token issued per fan in a small JSON file store, revoke every writable
  one on mute and every token on ban, and refuse to grant write back while
  muted or the fan at all while banned. Add real `process.argv` dispatch
  (`grant`/`mute`/`unmute`/`ban`) and a `fan.js` that actually parses its
  token argument, installs its status listener before subscribing, and
  reports a rejected publish from its own `try`/`catch` rather than the
  status listener.

- game-chat-moderation.ts: any client with write access to `game.chat`
  could publish the same `moderation`/`hidden` message action a moderator
  would, since Access Manager scopes permissions to channels, not to
  action type/value strings. Move the hide decision to its own
  `game.chat.moderation` channel that only a moderator-scoped token can
  write to, and fetch that channel's history alongside `game.chat`'s so a
  late-joining client still renders the hide. Also stop rounding a
  17-digit timetoken through `Number` arithmetic before the single-message
  delete; keep it as a string and use `BigInt` for the exclusive-start
  calculation.

- fan-re-engagement.ts: the occupancy/leave logic ran under the same
  `userId` it was checking, and the alert published unconditionally to a
  shared channel every registered device received. Separate the
  monitoring identity from any fan's, gate the publish behind a `whereNow`
  check for the specific fan who triggered the presence event, and target
  a per-fan `game.moment-alerts.<userId>` channel instead of a shared one.
  Add a `fan-viewer.js` so the two-client case (one fan stays, one leaves)
  is actually runnable.

- score-alerts.ts: device registration set `environment: 'production'`
  while the notification payload's APNs target left `environment` at its
  SDK default of `'development'`, so a registered device could never
  receive what got published. Introduce one `APNS_ENVIRONMENT` constant
  used by registration, the payload target, and new APNs-specific listing
  and removal snippets.

Verified with `npm run test:snippets` (tsc against the whole
docs-snippets tree) and by replicating EmbeddedCode's exact snippet
extraction to `node --check` every region, and the per-page combined
assembly, for all 11 sports use-case pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A failed revokeToken call dropped the token from the registry, leaving a
valid writable token untracked. Mute also saved its status only after the
read-only grant succeeded. Record the status first and keep unrevoked tokens
so a repeated mute or ban retries them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (4)
  • docs-snippets/use-cases/automated-polling.ts is excluded by none and included by none
  • docs-snippets/use-cases/fan-behavior-management.ts is excluded by none and included by none
  • docs-snippets/use-cases/fan-re-engagement.ts is excluded by none and included by none
  • docs-snippets/use-cases/score-alerts.ts is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Repository: pubnub/javascript/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: bf480d56-7452-40dc-9e21-39347772cd76

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pubnub-ops-terraform

pubnub-ops-terraform commented Sep 23, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@techwritermat
techwritermat merged commit f84d50f into master Sep 23, 2026
10 checks passed
@techwritermat
techwritermat deleted the docs-snippets-sports-review-fixes branch September 23, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants