The experiment could not be started.
"; throw error; diff --git a/components/dashboard/script-tags.js b/components/dashboard/script-tags.js new file mode 100644 index 0000000..5e39fb2 --- /dev/null +++ b/components/dashboard/script-tags.js @@ -0,0 +1,22 @@ +// The `; +export const DATAPIPE_CLIENT_SCRIPT = ``; diff --git a/components/home/hero-snippets.js b/components/home/hero-snippets.js index f7a7fbb..8791612 100644 --- a/components/home/hero-snippets.js +++ b/components/home/hero-snippets.js @@ -51,7 +51,7 @@ export const snippets = [ { role: "fg", text: "await DataPipe." }, { role: "fn", text: "saveData" }, { role: "fg", text: "({\n" }, - { role: "fg", text: " experimentID: " }, + { role: "fg", text: " experiment_id: " }, { role: "string", text: '"your_id"' }, { role: "fg", text: ",\n" }, { role: "fg", text: " filename: " }, diff --git a/packages/client/.changeset/experiment-id-name.md b/packages/client/.changeset/experiment-id-name.md new file mode 100644 index 0000000..eb24094 --- /dev/null +++ b/packages/client/.changeset/experiment-id-name.md @@ -0,0 +1,7 @@ +--- +"datapipe-client": minor +--- + +Accept `experiment_id`, the name the jsPsych extension and plugin use for the experiment ID, so the same option is spelled the same way everywhere. + +`experimentID` still works and there are no plans to remove it. Give one or the other. If both are given and differ, `saveData`, `saveBase64Data` and `getCondition` throw, and a session starts inert with a console warning. Nothing changes on the wire. diff --git a/packages/client/.changeset/session-ready.md b/packages/client/.changeset/session-ready.md new file mode 100644 index 0000000..e22fc02 --- /dev/null +++ b/packages/client/.changeset/session-ready.md @@ -0,0 +1,7 @@ +--- +"datapipe-client": minor +--- + +Pass the session to `saveData` with `session` instead of flushing and reading `sessionId` yourself. `saveData` waits for the session to start and sends its id. + +Adds `session.ready()`, which resolves once the session has started (or failed to). It waits only for startup, not for staged writes, so a final submission from a background tab is no longer held up by a throttled flush. `sessionId` still works. diff --git a/packages/client/README.md b/packages/client/README.md index 589f17e..f2dd023 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -13,16 +13,18 @@ npm install datapipe-client Or in a plain HTML page, which exposes a `DataPipe` global: ```html - + ``` +Keep the version in the URL. Without one, unpkg serves the newest release, which could change a study that is already collecting data. + ## Sending data at the end ```js import { saveData } from "datapipe-client"; const result = await saveData({ - experimentID: "YOUR_EXPERIMENT_ID", + experiment_id: "YOUR_EXPERIMENT_ID", filename: "subject-01.csv", data: "rt,response\n204,1\n389,0", }); @@ -42,7 +44,7 @@ Staging each trial as it is produced means a participant who closes the tab at t import { createSession, saveData } from "datapipe-client"; const session = createSession({ - experimentID: "YOUR_EXPERIMENT_ID", + experiment_id: "YOUR_EXPERIMENT_ID", filename: "subject-01.csv", }); @@ -50,12 +52,11 @@ const session = createSession({ session.record(trialData); // ...at the end: -await session.flush(); const result = await saveData({ - experimentID: "YOUR_EXPERIMENT_ID", + experiment_id: "YOUR_EXPERIMENT_ID", filename: "subject-01.csv", data: allTrialsAsCSV, - sessionId: session.sessionId, + session, }); await session.close({ submitted: result.ok }); ``` @@ -63,7 +64,7 @@ await session.close({ submitted: result.ok }); Three things are worth knowing: - **`createSession()` returns immediately.** The request that starts the session is still in flight, and trials recorded before it lands are buffered and staged once it does. Use `await startSession(...)` instead if you would rather wait and check `session.enabled`. -- **Flush before reading `sessionId`.** `flush()` waits for the session to start, so until you have awaited it, `sessionId` may still be empty. Submitting without it leaves DataPipe unable to match your file to the staged copy, which it would then recover a second time. +- **Pass the session to `saveData`.** It tells DataPipe that this submission completes the staged copy, so the staged copy is discarded instead of being recovered as a second file. If you build the request yourself, `await session.ready()` and send `session.sessionId`. - **Tell `close()` what happened.** `{ submitted: true }` cancels the abandonment marker, so a completed session is never also reported as abandoned. `{ submitted: false }` marks it now, so the staged trials are recovered on DataPipe's normal sweep rather than waiting out the 24-hour expiry. Nothing about staging will break your experiment. If the session cannot be started, every method on it becomes a no-op and the data is still submitted at the end. @@ -75,7 +76,7 @@ import { getCondition } from "datapipe-client"; let condition; try { - condition = await getCondition({ experimentID: "YOUR_EXPERIMENT_ID" }); + condition = await getCondition({ experiment_id: "YOUR_EXPERIMENT_ID" }); } catch (error) { document.body.innerHTML = "The experiment could not be started.
"; throw error; @@ -90,7 +91,7 @@ try { import { saveBase64Data } from "datapipe-client"; await saveBase64Data({ - experimentID: "YOUR_EXPERIMENT_ID", + experiment_id: "YOUR_EXPERIMENT_ID", filename: "subject-01-recording.webm", data: base64EncodedString, }); @@ -114,7 +115,7 @@ setBaseURL("https://datapipe-test.web.app"); | `getCondition(options)` | `PromiseThe experiment could not be started.
"; * throw error; * } * ``` * - * @param options.experimentID The 12-character experiment ID. + * @param options.experiment_id The 12-character experiment ID. (`experimentID` + * is accepted too; see `ExperimentIDOption`.) * @param options.baseURL Override the DataPipe deployment for this call. * @throws If the request cannot be made, DataPipe refuses it (condition * assignment switched off, experiment closed), or the response carries no * condition. */ -export async function getCondition(options: { - experimentID: string; - baseURL?: string; -}): PromiseDataPipe global. If you use a bundler,{" "}
@@ -106,6 +107,13 @@ export default function SendingDataPage() {
saveData, createSession,{" "}
saveBase64Data, and getCondition.
flush() before you read sessionId.
- {" "}
- A session starts in the background, and until it has, the ID is an
- empty string. If you submit without it, DataPipe can't match
- your file to the staged copy, so it recovers that copy separately
- and you end up with a stray .partial.json next to a
- complete file.
+ At the end, pass the session to saveData as{" "}
+ session. That tells DataPipe the submission completes
+ the staged copy, so the staged copy is discarded rather than
+ recovered as a second file.