jspsych-datamanager
Version:
A package to manage data for jsPsych experiments. Currently Firebase and Supabase are supported.
141 lines (138 loc) • 4.88 kB
JavaScript
;
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
});