Version Packages - #35
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
4 times, most recently
from
July 13, 2026 18:37
6bc05ab to
5f21215
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
26 times, most recently
from
July 25, 2026 15:24
c860e69 to
c7f09c5
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
July 25, 2026 18:51
c7f09c5 to
642ee5e
Compare
Member
|
@ Once this merges and the
(Noted as a comment rather than in the PR body, since the changesets bot regenerates the description.) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@jspsych-multiplayer/adapter-multiplayer-firebase@0.2.0
Minor Changes
#43
565ff2fThanks @htsukamoto5! - Addadapter-multiplayer-firebase, a Firebase Realtime Database backend for the jsPsych multiplayer API — real cross-device multiplayer with essentially no server to write or host.It implements the same
MultiplayerAdaptercontract as the local and JATOS adapters, so plugins behave identically on any of them, and sits between them on the infrastructure spectrum: broader reach than the same-browser local adapter, far less setup than a self-hosted JATOS server. Because the contract'sgetAll()/get()are synchronous while Firebase reads are async, the adapter keeps an in-memory mirror of the session node (kept live by a singleonValuelistener) and does not resolveconnect()until the first snapshot arrives (rejecting on a rules denial or timeout). Each participant's payload is JSON-encoded as a string so pushes round-trip exactly over RTDB's JSON coercion, a.info/connectedhandler re-armsonDisconnectand re-pushes after a transient network blip so a still-present participant is never erased, and an optionaluseUidAsParticipantIdmode enables the recommended session-locked security rules: uid-as-slot-key (no participant can write another's slot) plus first-write-wins session binding —connect()registers amp-sessions-memberships/<uid> = sessionIdrecord the server-evaluated rules make immutable and then require on every session read/write, so a client identity can only ever touch the session it first joined. The recommended rules ship asdatabase.rules.json(with afirebase.jsonfor the emulator suite).firebaseis a peer dependency. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core, and the whole adapter is unit-tested against an in-memory backend fake with zero credentials.@jspsych-multiplayer/plugin-multiplayer-choice@0.2.0
Minor Changes
95ccc8aThanks @Mandyx22! - Addplugin-multiplayer-choice: a simultaneous group-decision primitive. Each participant picks one of the same options; the trial pushes that choice and waits (a barrier) until allexpected_playershave chosen, then optionally reveals the outcome. It is the engine under simultaneous-move paradigms (prisoner's dilemma, public-goods contributions, dictator/coordination games), packaging the choose → push → wait → reveal flow as one declarative trial. Two reveal modes:reveal_mode: "players"(default) lists who chose what;reveal_mode: "tally"shows only per-option counts and the plurality winner — an anonymous group poll, withrecord_choices_by_player: falseto keep the recorded data anonymous too (this subsumes the separately-proposedplugin-multiplayer-vote). Includes atimeoutthat degrades to a partial group, an optionalpayoff(choices, me)hook (off by default, so the plugin stays a pure decision primitive),player_label/button_htmldisplay hooks, always-recorded aggregate data (tally/winner/is_tie/tied_options), a barrier count bounded by the option range (a stale out-of-range pick can neither lift the barrier nor skewn_players), and static access to the pure core (collectChoices/countChosen/tally/plurality).Patch Changes
#37
0a25a8aThanks @Mandyx22! - Distinguish a genuine barrier timeout from otherwait()rejections. jsPsych#3694 rejects a timeout with a typedMultiplayerTimeoutError; await()can otherwise reject because the condition predicate threw or the backend failed. Only a timeout now proceeds with a partial snapshot (timed_out: true, runson_timeout); any other rejection rethrows so the trial halts loudly instead of masquerading as a timeout.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-countdown@0.2.0
Minor Changes
#41
b4d2faaThanks @htsukamoto5! - Addplugin-multiplayer-countdown, a synchronized group timer (countdown or count-up) for the jsPsych multiplayer API.Every participant pushes its own start timestamp into its own slot, and each client derives the displayed time from the minimum timestamp across all slots — a coordination-free consensus (no elected anchor, no single point of failure) in the same spirit as
plugin-multiplayer-role's ordering. Late joiners and refreshes resume at the group's actual remaining time for free, and the pure consensus core (startedAtKey/resolveStartedAt/computeRemaining/computeElapsed/formatTime) is exposed as statics on the default export so demos can render their own synced display. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core.Patch Changes
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.@jspsych-multiplayer/plugin-multiplayer-draw@0.2.0
Minor Changes
#34
d37bcb3Thanks @htsukamoto5! - Addplugin-multiplayer-draw, a real-time collaborative drawing canvas for the jsPsych multiplayer API.Every participant draws on one shared canvas; strokes from everyone appear live on everyone else's screen. Includes pen/eraser tools, a fixed color palette, brush sizes, and undo/redo buttons that only ever act on the participant's own strokes. Where
plugin-multiplayer-chatpushes once per message, this plugin pushes continuously while a stroke is active (throttled and point-decimated), making it the first plugin that stresses the multiplayer API'ssubscribeprimitive at a genuinely high rate. Full repaints (triggered by undo or a canvas resize) paint strokes in a global timestamp order so the eraser'sdestination-outcompositing behaves consistently across clients. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core.Patch Changes
#49
2fb283dThanks @htsukamoto5! - Internal: adopt jsPsych#3694'supdate()convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rollingget() ?? {}+ spread +pushfor a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's localMultiplayerApiLikemirror gained anupdatemember to match.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-match@0.2.0
Minor Changes
cda5411Thanks @Mandyx22! - Addplugin-multiplayer-match: partition a multiplayer group into matched sub-groups (pairs by default, or triads/larger) by deterministic consensus — every client independently computes the same partition from the shared group-session snapshot, with no coordinator. It is the foundational primitive under pairwise/small-group paradigms (trust game, ultimatum, dyadic negotiation) and composes withplugin-multiplayer-role(assign roles within a group viaposition). Runs as a short barrier (likeplugin-multiplayer-role), supportsordered/join_order/random(seeded, per-round) pairing strategies anderror/spectator/smaller_groupleftover policies for non-divisible counts, fails loud on timeout, and exposes the pure core (buildMatches) plus partner accessors (getMyPartners/getMyGroup/getMyPosition/getMatchMap) as statics for downstream trials.Patch Changes
#38
cd7f8feThanks @Mandyx22! - Adopt the push-then-wait pattern aftercommunicate()was removed from the jsPsych multiplayer API (jsPsych#3694). The match barrier now callspush()thenwait(), and distinguishes a genuine readiness timeout (MultiplayerTimeoutError, matched by error name) from other rejections: a real timeout ends the trial gracefully (timed_out: true), while a backend/push failure now propagates loudly instead of being mislabelled as a timeout.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.@jspsych-multiplayer/plugin-multiplayer-ready@0.1.0
Minor Changes
#30
d54c569Thanks @Mandyx22! - Addplugin-multiplayer-ready, a participant-facing ready / check-in barrier for the jsPsych multiplayer API.It packages the common lobby / waiting-room pattern into a single declarative trial: show a prompt and a ready button, push
{ ready: true }(optionally merged withpush_data) into the shared group session when the participant clicks, display a waiting message, and end the trial onceexpected_playersmembers are ready (or an optionaltimeoutelapses while waiting for the rest of the group). Unlikeplugin-multiplayer-sync, it owns the check-in UI and the "everyone is ready" condition, and standardizes on aready: trueflag so other plugins and examples can reliably gate on group readiness. Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core.Patch Changes
#45
21e0909Thanks @htsukamoto5! - Fixwait_error/timed_outmislabeling a non-timeoutwait()failure as a timeout. Both plugins previously treated everywait()rejection as a timeout (a leftover from before jsPsych#3694 exported a typedMultiplayerTimeoutError), so a throwingwait_forpredicate or an adapter/backend error would silently finish the trial withtimed_out: trueand callon_timeout, hiding the real failure inwait_error's message.Now only a rejection whose
error.name === "MultiplayerTimeoutError"is recorded as a timeout; any other rejection propagates and fails the trial, matching how apush()failure is already handled.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-reference-game@0.2.0
Minor Changes
#42
a836cd6Thanks @Mandyx22! - Addplugin-multiplayer-reference-game, a repeated referential communication game ("tangrams"; Hawkins, Frank & Goodman 2020) for two players on the jsPsych multiplayer API.Two players are paired as a fixed director and matcher and see the same objects, each in an independently scrambled layout; only the director sees which objects are targets (and, for more than one, in what order). They communicate over an integrated free-text chat, the matcher assigns objects to the director's ordered target slots (a single click when there is one target), and both then see feedback with the true answer revealed. The published "sequential" (one target, click) and "unconstrained" (all N objects are ordered targets, reproduce the whole board) conditions are the same task with two parameters turned differently —
stimulilength andtargetslength — so one configurable plugin covers both, plus everything in between. Likeplugin-multiplayer-chatit is a continuously-open,subscribe-driven trial: the matcher's submitted assignment is the shared trigger on which both clients score, show feedback, and end. Object/target counts, scramble mode, chat direction and limits, scoring rule, feedback content, and an optional pre-submit interaction log are all parameters. Composes withplugin-multiplayer-role(director/matcher) andplugin-multiplayer-sync(lobby). Built against a local interface mirroring the multiplayer API (jsPsych#3694), so it carries no build-time dependency on the unreleased core.#67
d6926f8Thanks @htsukamoto5! - Add arequire_message_before_responseparameter. When true, the matcher cannot commit a selection until the director has sent at least one chat message this round — while gated, matcher grid clicks are ignored and a brief hint is shown. This makes the plugin faithful to Hawkins, Frank & Goodman (2020) Exp. 2, whose client blocked the matcher's click behindmessageSent, guaranteeing a referring expression on every trial. Defaults tofalse(unchanged behavior); inert with a warning whenchat_enabledis false (gating with no channel would deadlock the matcher). Applies to both theclickandassign_slotsresponse modes. Only the partner's messages open the gate, so neither the matcher's own message nor a third participant's counts. Blocked clicks are recorded asgated_clickevents ininteraction_historywhensave_interaction_historyis on.Chat messages now also carry the
roundthey were sent during. This makes the gate exact underchat_persists: true, where every round shares one log: an earlier round's message never pre-opens the gate, and the director's message for the current round still counts when it lands before the matcher's trial is constructed (the two clients do not enter a round at the same moment). Savedchat_transcriptentries gain the sameroundfield; transcripts written by earlier versions still merge and render.Patch Changes
#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-scoreboard@0.2.0
Minor Changes
33d337eThanks @Mandyx22! - Addplugin-multiplayer-scoreboard: an end-of-game scoreboard for multiplayer experiments. Each client contributes its finalscore, the trial waits (a barrier) until the group has reported, then every client independently computes the same ranked leaderboard from the shared group-session snapshot — no coordinator, no extra round-trip — and renders it locally with its own row highlighted. Supports ascending/descending sort, standard/dense tie ranking, anon_timeouthook, timeouts that degrade to a partial board, and static accessors (getMyRank/getMyScore/getLeaderboard) for branching downstream trials.Patch Changes
#33
704e2a7Thanks @Mandyx22! - Drop the now-removedcommunicate()member from the local multiplayer API mirror and test mock (communicate()was removed from the jsPsych multiplayer API in jsPsych#3694). The plugin already pushed and waited as two separate calls, so there is no behavior change — this only trims dead interface surface.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-chat@0.1.1
Patch Changes
#49
2fb283dThanks @htsukamoto5! - Internal: adopt jsPsych#3694'supdate()convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rollingget() ?? {}+ spread +pushfor a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's localMultiplayerApiLikemirror gained anupdatemember to match.#29
2650fa9Thanks @htsukamoto5! - Fix unreadable chat transcript: the plugin shipped no CSS, so sender and message text rendered as bare unstyled<span>s with nothing between them (e.g. "AliceHello"). Inject minimal scoped styles (boxed log, one message per line, bold sender label with a colon separator, own-message highlight) so the transcript is legible out of the box.Also clarifies the chat-room example's name prompt ("Choose a display name — this is what other participants will see you as in the chat") since testers read the original wording as naming the chat room itself.
#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.@jspsych-multiplayer/plugin-multiplayer-role@0.1.1
Patch Changes
#45
fc1a842Thanks @htsukamoto5! - Update for jsPsych#3694's removal ofMultiplayerAPI.communicate(): the plugin now callspush()followed bywait()directly instead of the removed fused convenience method. Also fixes the same timeout-mislabeling bug already patched inplugin-multiplayer-sync/plugin-multiplayer-ready— only a rejection whoseerror.name === "MultiplayerTimeoutError"is now recorded astimed_out: true; any otherwait()/push()rejection (a throwingreadypredicate, an adapter/backend error) propagates and fails the trial loudly instead.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.@jspsych-multiplayer/plugin-multiplayer-sync@0.1.1
Patch Changes
#45
21e0909Thanks @htsukamoto5! - Fixwait_error/timed_outmislabeling a non-timeoutwait()failure as a timeout. Both plugins previously treated everywait()rejection as a timeout (a leftover from before jsPsych#3694 exported a typedMultiplayerTimeoutError), so a throwingwait_forpredicate or an adapter/backend error would silently finish the trial withtimed_out: trueand callon_timeout, hiding the real failure inwait_error's message.Now only a rejection whose
error.name === "MultiplayerTimeoutError"is recorded as a timeout; any other rejection propagates and fails the trial, matching how apush()failure is already handled.#53
57ea69dThanks @htsukamoto5! - Resolve the multiplayer API viaresolveMultiplayerApi(), preferringjsPsych.multiplayer(jsPsych#3694's current namespace) and falling back tojsPsych.pluginAPI, with a directing error when neither is present.#45
a587023Thanks @htsukamoto5! - Finish gracefully whengetAll()throws on the timeout path. On a genuine timeout the adapter may already be torn down (getAll()then throws"connect() must be called…"), which would otherwise escape and reject the trial instead of finishing it astimed_out: true. The snapshot read now falls back to an empty group session, matching thesafeGetAllguardplugin-multiplayer-readyalready had.#62
d1552c0Thanks @jodeleeuw! - Register trial timers throughjsPsych.pluginAPI.setTimeoutso they are cancelled when a trial is ended externally (abortExperiment,endCurrentTimeline, forcedfinishTrial), instead of firing into a finished trial. The plugins previously used baresetTimeoutand only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.