Skip to content

Version Packages - #35

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main
Open

Version Packages#35
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

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 565ff2f Thanks @htsukamoto5! - Add adapter-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 MultiplayerAdapter contract 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's getAll()/get() are synchronous while Firebase reads are async, the adapter keeps an in-memory mirror of the session node (kept live by a single onValue listener) and does not resolve connect() 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/connected handler re-arms onDisconnect and re-pushes after a transient network blip so a still-present participant is never erased, and an optional useUidAsParticipantId mode 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 a mp-sessions-memberships/<uid> = sessionId record 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 as database.rules.json (with a firebase.json for the emulator suite). firebase is 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

  • #37 95ccc8a Thanks @Mandyx22! - Add plugin-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 all expected_players have 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, with record_choices_by_player: false to keep the recorded data anonymous too (this subsumes the separately-proposed plugin-multiplayer-vote). Includes a timeout that degrades to a partial group, an optional payoff(choices, me) hook (off by default, so the plugin stays a pure decision primitive), player_label/button_html display 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 skew n_players), and static access to the pure core (collectChoices/countChosen/tally/plurality).

Patch Changes

  • #37 0a25a8a Thanks @Mandyx22! - Distinguish a genuine barrier timeout from other wait() rejections. jsPsych#3694 rejects a timeout with a typed MultiplayerTimeoutError; a wait() can otherwise reject because the condition predicate threw or the backend failed. Only a timeout now proceeds with a partial snapshot (timed_out: true, runs on_timeout); any other rejection rethrows so the trial halts loudly instead of masquerading as a timeout.

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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 b4d2faa Thanks @htsukamoto5! - Add plugin-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

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

@jspsych-multiplayer/plugin-multiplayer-draw@0.2.0

Minor Changes

  • #34 d37bcb3 Thanks @htsukamoto5! - Add plugin-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-chat pushes 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's subscribe primitive at a genuinely high rate. Full repaints (triggered by undo or a canvas resize) paint strokes in a global timestamp order so the eraser's destination-out compositing 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 2fb283d Thanks @htsukamoto5! - Internal: adopt jsPsych#3694's update() convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rolling get() ?? {} + spread + push for a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's local MultiplayerApiLike mirror gained an update member to match.

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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

  • #38 cda5411 Thanks @Mandyx22! - Add plugin-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 with plugin-multiplayer-role (assign roles within a group via position). Runs as a short barrier (like plugin-multiplayer-role), supports ordered/join_order/random (seeded, per-round) pairing strategies and error/spectator/smaller_group leftover 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 cd7f8fe Thanks @Mandyx22! - Adopt the push-then-wait pattern after communicate() was removed from the jsPsych multiplayer API (jsPsych#3694). The match barrier now calls push() then wait(), 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 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

@jspsych-multiplayer/plugin-multiplayer-ready@0.1.0

Minor Changes

  • #30 d54c569 Thanks @Mandyx22! - Add plugin-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 with push_data) into the shared group session when the participant clicks, display a waiting message, and end the trial once expected_players members are ready (or an optional timeout elapses while waiting for the rest of the group). Unlike plugin-multiplayer-sync, it owns the check-in UI and the "everyone is ready" condition, and standardizes on a ready: true flag 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 21e0909 Thanks @htsukamoto5! - Fix wait_error/timed_out mislabeling a non-timeout wait() failure as a timeout. Both plugins previously treated every wait() rejection as a timeout (a leftover from before jsPsych#3694 exported a typed MultiplayerTimeoutError), so a throwing wait_for predicate or an adapter/backend error would silently finish the trial with timed_out: true and call on_timeout, hiding the real failure in wait_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 a push() failure is already handled.

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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 a836cd6 Thanks @Mandyx22! - Add plugin-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 — stimuli length and targets length — so one configurable plugin covers both, plus everything in between. Like plugin-multiplayer-chat it 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 with plugin-multiplayer-role (director/matcher) and plugin-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 d6926f8 Thanks @htsukamoto5! - Add a require_message_before_response parameter. 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 behind messageSent, guaranteeing a referring expression on every trial. Defaults to false (unchanged behavior); inert with a warning when chat_enabled is false (gating with no channel would deadlock the matcher). Applies to both the click and assign_slots response 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 as gated_click events in interaction_history when save_interaction_history is on.

    Chat messages now also carry the round they were sent during. This makes the gate exact under chat_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). Saved chat_transcript entries gain the same round field; transcripts written by earlier versions still merge and render.

Patch Changes

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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

  • #33 33d337e Thanks @Mandyx22! - Add plugin-multiplayer-scoreboard: an end-of-game scoreboard for multiplayer experiments. Each client contributes its final score, 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, an on_timeout hook, timeouts that degrade to a partial board, and static accessors (getMyRank/getMyScore/getLeaderboard) for branching downstream trials.

Patch Changes

  • #33 704e2a7 Thanks @Mandyx22! - Drop the now-removed communicate() 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 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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 2fb283d Thanks @htsukamoto5! - Internal: adopt jsPsych#3694's update() convenience (shallow-merge into own slot, then push) at the four call sites that were hand-rolling get() ?? {} + spread + push for a top-level-key overwrite (draw's stroke pushes on flush/undo/redo, chat's message send). No behavior change; each package's local MultiplayerApiLike mirror gained an update member to match.

  • #29 2650fa9 Thanks @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 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and 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 fc1a842 Thanks @htsukamoto5! - Update for jsPsych#3694's removal of MultiplayerAPI.communicate(): the plugin now calls push() followed by wait() directly instead of the removed fused convenience method. Also fixes the same timeout-mislabeling bug already patched in plugin-multiplayer-sync/plugin-multiplayer-ready — only a rejection whose error.name === "MultiplayerTimeoutError" is now recorded as timed_out: true; any other wait()/push() rejection (a throwing ready predicate, an adapter/backend error) propagates and fails the trial loudly instead.

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

@jspsych-multiplayer/plugin-multiplayer-sync@0.1.1

Patch Changes

  • #45 21e0909 Thanks @htsukamoto5! - Fix wait_error/timed_out mislabeling a non-timeout wait() failure as a timeout. Both plugins previously treated every wait() rejection as a timeout (a leftover from before jsPsych#3694 exported a typed MultiplayerTimeoutError), so a throwing wait_for predicate or an adapter/backend error would silently finish the trial with timed_out: true and call on_timeout, hiding the real failure in wait_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 a push() failure is already handled.

  • #53 57ea69d Thanks @htsukamoto5! - Resolve the multiplayer API via resolveMultiplayerApi(), preferring jsPsych.multiplayer (jsPsych#3694's current namespace) and falling back to jsPsych.pluginAPI, with a directing error when neither is present.

  • #45 a587023 Thanks @htsukamoto5! - Finish gracefully when getAll() 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 as timed_out: true. The snapshot read now falls back to an empty group session, matching the safeGetAll guard plugin-multiplayer-ready already had.

  • #62 d1552c0 Thanks @jodeleeuw! - Register trial timers through jsPsych.pluginAPI.setTimeout so they are cancelled when a trial is ended externally (abortExperiment, endCurrentTimeline, forced finishTrial), instead of firing into a finished trial. The plugins previously used bare setTimeout and only cleared handles on their own end paths, so external termination — exactly what multiplayer sync timeouts and host-ended sessions do — left timers alive.

@github-actions
github-actions Bot force-pushed the changeset-release/main branch 4 times, most recently from 6bc05ab to 5f21215 Compare July 13, 2026 18:37
@github-actions
github-actions Bot force-pushed the changeset-release/main branch 26 times, most recently from c860e69 to c7f09c5 Compare July 25, 2026 15:24
@github-actions
github-actions Bot force-pushed the changeset-release/main branch from c7f09c5 to 642ee5e Compare July 25, 2026 18:51
@htsukamoto5

Copy link
Copy Markdown
Member

@
Post-merge follow-up (not a blocker for this PR):

Once this merges and the @jspsych-multiplayer/* packages actually publish, reconcile the README in jspsych/multiplayer-test-experiments:

  • Its script tags are pinned to 0.1.0 on jsDelivr, but its "Running it" steps still describe building from a local checkout and swapping in relative dist/ paths. The two halves currently disagree; pick whichever is true post-publish and drop the other to a fallback note.
  • Confirm the published version actually is 0.1.0 — if the changesets here bump past it, those pinned URLs are wrong and the files won't load.

(Noted as a comment rather than in the PR body, since the changesets bot regenerates the description.)
@

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