UNPKG

jspsych-datamanager

Version:

A package to manage data for jsPsych experiments. Currently Firebase and Supabase are supported.

141 lines (138 loc) 4.88 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { FirebaseManager: () => FirebaseManager }); module.exports = __toCommonJS(index_exports); // src/firebase.ts var import_app = require("firebase/app"); var import_firestore = require("firebase/firestore"); var import_core = require("@jspsych-datamanager/core"); var FirebaseManager = class extends import_core.DataManager { /** * Creates a new FirebaseManager instance * @param firebaseConfig Firebase configuration object * @param options Additional options for initialization */ constructor(firebaseConfig, options = {}) { super(options.metadata); this.numberOfWrites = 0; this.app = (0, import_app.initializeApp)(firebaseConfig); this.db = (0, import_firestore.getFirestore)(this.app); const collectionName = options.collectionName || "experiments"; const documentId = options.documentId || void 0; this.docRef = documentId ? (0, import_firestore.doc)(this.db, collectionName, documentId) : (0, import_firestore.doc)((0, import_firestore.collection)(this.db, collectionName)); } /** * Initializes the experiment document in Firestore * @param additionalData Additional data to include in the experiment document * @throws {Error} If initialization fails */ async initializeExperiment(additionalData = {}) { const initialData = { ...this.metadata, trials: [], ...additionalData }; try { await (0, import_firestore.setDoc)(this.docRef, initialData); this.numberOfWrites++; console.log("[FirebaseManager] Document successfully created!"); } catch (error) { console.error("[FirebaseManager] Error creating document:", error); throw new Error("Failed to initialize experiment document"); } } /** * Adds a new trial to the experiment document * @param trialData The trial data to add * @throws {Error} If storing the trial fails */ async addTrialData(trialData) { const flattenedData = this.flattenNestedArrays(trialData); try { await (0, import_firestore.updateDoc)(this.docRef, { trials: (0, import_firestore.arrayUnion)(flattenedData) }); this.numberOfWrites++; console.log("[FirebaseManager] Added trial data:", flattenedData); } catch (error) { console.error("[FirebaseManager] Error storing trial data:", error); throw new Error("Failed to store trial data"); } } /** * Gets the total number of writes to Firestore * @returns The number of write operations performed */ getNumberOfOperations() { return this.numberOfWrites; } /** * Creates a callback function for jsPsych's on_data_update event * @returns A function that handles trial data updates */ createDataUpdateCallback() { return (data) => { if (data.no_upload) { delete data.no_upload; return data; } this.addTrialData(data).catch((error) => { console.error("[FirebaseManager] Error in data update callback:", error); }); return data; }; } /** * Creates a callback function for jsPsych's on_finish event * @returns A function that handles experiment completion */ createFinishCallback() { return () => { console.log("[FirebaseManager] Total writes to Firestore:", this.getNumberOfOperations()); }; } /** * Flattens nested arrays in an object to make it Firestore-compatible * @param obj The object to flatten * @returns A new object with flattened arrays */ flattenNestedArrays(obj) { const result = { ...obj }; for (const key in result) { const value = result[key]; if (Array.isArray(value)) { result[key] = value.reduce((acc, val, i) => { acc[i] = val; return acc; }, {}); } else if (typeof value === "object" && value !== null) { result[key] = this.flattenNestedArrays(value); } } return result; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { FirebaseManager });