Skip to content

Support the 0.1.x plugin SDK (BigBlueButton 4.0) - #18

Open
imdt-claudiop wants to merge 2 commits into
bigbluebutton:v0.1.xfrom
imdt-claudiop:sdk-compat-0.1.x
Open

Support the 0.1.x plugin SDK (BigBlueButton 4.0)#18
imdt-claudiop wants to merge 2 commits into
bigbluebutton:v0.1.xfrom
imdt-claudiop:sdk-compat-0.1.x

Conversation

@imdt-claudiop

Copy link
Copy Markdown

Why

BigBlueButton 4.0 ships version 0.1.26 of the plugin SDK. The server compares that
version against the requiredSdkVersion declared in the plugin manifest and refuses to
load anything that does not match, so on a 4.0 server this plugin is rejected before its
bundle is ever fetched:

Cannot load plugin [CodeHighlight]: system SDK version [0.1.26] does not satisfy plugin requirement [~0.0.59].

This is the v0.1.x line, which is the one that targets the 0.1.x SDK, so this makes the plugin load and work on BigBlueButton 4.0.

What changed

  • manifest.json: requiredSdkVersion from ~0.0.59 to ^0.1.2
  • package.json and package-lock.json: bigbluebutton-html-plugin-sdk from 0.0.66 to 0.1.26
  • src/code-highlighter/component.tsx: guard the chat message filter against a null message body, so a deleted chat message no longer kills the plugin. Details below.

The plugin version itself was deliberately left alone. It is cut by the publish-tag workflow, which also updates package.json and package-lock.json, so it belongs to the release process rather than to this change.

The null guard

Running the plugin on a live 4.0 server surfaced a crash that is worth fixing here, because without it the plugin does not survive a normal session.

BigBlueButton soft deletes chat messages: when a message is deleted the row stays and its body becomes NULL. The schema states it directly, in bbb-graphql-server/bbb_schema.sql:

--resolve the deleter's display name so a soft-deleted last message (message=NULL,
--deletedByUserId set) can reuse the existing "deleted by {userName}" preview label.

That row reaches the plugin through useLoadedChatMessages, and the filter calls .search() on the body with no guard:

(message) => message.message.search(CODE_BLOCK_REGEX) !== -1,

so it throws TypeError: Cannot read properties of null (reading 'search'). The throw is caught by the client global error boundary, which unmounts the plugin. The practical effect is that after any chat message is deleted, no code block sent from that point on is highlighted, for the rest of the session. Code blocks highlighted before the delete keep their highlighting, which makes the failure easy to miss.

The fix is one line:

(message) => message.message?.search(CODE_BLOCK_REGEX) > -1,

undefined > -1 is false, so a null body simply drops out of the filter. Optional chaining is already the idiom in this file, on the two lines that follow. The .search(CODE_LANGUAGE_REGEX) call in the map right below is deliberately left unguarded: it only runs on entries that already passed the filter, so the body is a string there.

Pre existing upstream behaviour, not a regression from the SDK bump

This is not something the SDK bump introduced. The same unguarded line is on the upstream v0.0.x and v0.1.x branches, byte for byte, and the crash reproduces on both lines.

It is also easy to see how it got there. The SDK types the field as non nullable, in data-consumption/domain/chat/loaded-chat-messages/types.d.ts:

export interface LoadedChatMessage {
    createdAt: string;
    message: string;
    ...
}

so nothing in the type checker or the linter points at the null case, while the server sends NULL for every soft deleted message. Flagging that mismatch rather than folding an SDK change into this pull request.

How to test

  1. Build and host the bundle:
    npm ci
    npm run build-bundle
    
    Serve manifest.json and dist/BbbPluginCodeHighlight.js from a URL the BigBlueButton server can reach. They have to sit next to each other, since javascriptEntrypointUrl is relative.
  2. Attach the plugin to a single meeting, so no server side configuration is touched. In API-Mate, add this Create custom parameter:
    pluginManifests=[{"url":"https://<your-host>/<folder>/manifest.json"}]
    
  3. Join the meeting as moderator. Send a fenced code block in the public chat, for example a block opened with three backticks followed by javascript. The message renders with syntax highlighting.
  4. Send an ordinary message, then delete it from the message toolbar and confirm. The browser console stays clean, and a fenced code block sent after the delete is still highlighted. Before this change, the delete raised a TypeError and every later code block rendered unhighlighted.
  5. Optionally confirm the server accepted the plugin, in the bbb_graphql database:
    SELECT name, "loadFailureReason" FROM plugin;
    loadFailureReason must be empty for this plugin.

Validation

Checked against a BigBlueButton 4.0 server built from source on v4.0.x-develop at commit 93bc4864ab, with the plugin attached per meeting and the bundle built from this branch.

SDK bump:

  • npm ci, npx tsc and npm run lint all run clean, matching what the pull request workflows run
  • the plugin loads with an empty loadFailureReason and the client reports no page error
  • a JavaScript chat snippet rendered as code.hljs with highlight spans

Null guard, same server, same scripted run before and after the change:

Step Before After
Fenced code block, plugin renders it code.hljs = 1 code.hljs = 1
Delete a plain chat message 1 page error, 5 console errors 0 page errors, 0 console errors
Fenced code block sent after the delete not highlighted, code.hljs stays 1 highlighted, code.hljs = 2

The error observed before the change, thrown from the plugin bundle inside Array.filter:

TypeError: Cannot read properties of null (reading 'search')
    at .../BbbPluginCodeHighlight.js?version=0.0.1:2:1180027
    at Array.filter (<anonymous>)

npx tsc and npm run lint were re run on the final state of this branch and both are clean.

Evidence

Chat panel after deleting a message, then sending a new fenced code block. Same script, same server, only the bundle differs:

Before and after the null guard

Full client for each side: before, after.

Notes

  • requiredSdkVersion is set to ^0.1.2, so any 0.1.x SDK from 0.1.2 up is accepted. The build pins 0.1.26, the SDK that 4.0 currently ships, while the wider floor also lets the plugin load on 4.0 servers that are still on an earlier 0.1.x SDK. Caret rather than tilde because on a 0.x floor the two operators accept exactly the same versions, and caret is the one that still expresses the intended range once the SDK reaches 1.0.0; note that ^0.1.2 by itself does not admit a 1.0.0 SDK.

Co-authored with Guilherme Leme, who created the v0.1.x branches for these plugins.

imdt-claudiop and others added 2 commits August 21, 2026 17:53
…ton 4.0)

Pin the build to the SDK that BigBlueButton 4.0 currently ships (0.1.26) and
widen requiredSdkVersion to ^0.1.2, so the plugin also loads on 4.0 servers
running an earlier 0.1.x SDK instead of being rejected at load time.

Co-Authored-By: Guilherme Leme <leme.guilherme.p@gmail.com>
Soft deleted chat messages reach the plugin with a null message body, so
calling search() on it throws. The throw is caught by the client global
error boundary, which unmounts the plugin: after any chat message is
deleted, no further code block is highlighted for the rest of the
session. Optional chaining keeps those entries out of the filter.

Co-Authored-By: Guilherme Leme <leme.guilherme.p@gmail.com>
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.

1 participant