From 8a109c6f76023ccf26759a39a5519debcd45f7a3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 02:52:52 +0000 Subject: [PATCH 1/7] Update AGENTS.md with improved Events-Backend stub instructions and backend test commands Co-authored-by: James Liu --- AGENTS.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9d9c2dc2..de666bf3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,13 +34,16 @@ Meridian (formerly Study Compass) is a full-stack web app for university campus ``` - **Fallback (stub mode)**: If you do NOT have access to the private repo, you can create minimal stubs so the server starts (but events functionality will not work): 1. Remove the symlink: `rm backend/events` - 2. Create the directory: `mkdir -p backend/events/schemas` + 2. Create the directory: `mkdir -p backend/events/schemas backend/events/utilities backend/events/routes` 3. Create `backend/events/index.js` exporting an Express router - 4. Create stub Mongoose schemas in `backend/events/schemas/` for: `rssFeed`, `approvalFlowDefinition`, `approvalInstance`, `event`, `form`, `formResponse`, `eventAnalytics`, `eventSystemConfig`, `stakeholderRole`, `domain`, `analyticsEvent`, `eventQR` - - **Note**: The stubs allow the server to start but all events-related API endpoints will be non-functional. Full development requires the actual Events-Backend repo. + 4. Create `backend/events/backendRoot.js` exporting `{ require: (...args) => require(...args) }` + 5. Create stub Mongoose schemas in `backend/events/schemas/` for: `rssFeed`, `approvalFlowDefinition`, `approvalInstance`, `event`, `form`, `formResponse`, `eventAnalytics`, `eventSystemConfig`, `stakeholderRole`, `domain`, `analyticsEvent`, `eventQR` (each exports `new mongoose.Schema({}, { strict: false })`) + 6. Create `backend/events/utilities/workflowUtilities.js` with `getDomainSpaceGovernance` and `scopeMatchesEventContext` functions (see existing stubs for reference) + 7. Create `backend/events/routes/analyticsDashboardRoutes.js` with stub analytics dashboard routes (see existing stubs for reference) + - **Note**: The stubs allow the server to start and backend tests to pass, but all events-related API endpoints will be non-functional. Full development requires the actual Events-Backend repo. - **`.env` file** is needed in both `/workspace/.env` and `/workspace/backend/.env` with at minimum: `JWT_SECRET`, `MONGO_URI_RPI` (or `MONGODB_URI`), `NODE_ENV=development`. - **ESLint** for the frontend requires `NODE_ENV` to be set: `NODE_ENV=development npx eslint src/` -- The frontend test suite (`npm test` in `frontend/`) has a pre-existing failure in `App.test.js` related to analytics imports. This is not caused by environment setup. +- The frontend test suite (`CI=true npm test` in `frontend/`) passes all tests (5 suites, 16 tests). The worker process may report a force-exit warning due to open handles — this is benign. ### Lint / test / build commands @@ -48,5 +51,6 @@ See `README.md` for standard setup instructions. Quick reference: - **Lint frontend**: `cd frontend && NODE_ENV=development npx eslint src/` - **Test frontend**: `cd frontend && CI=true npm test` - **Build frontend**: `cd frontend && npm run build` +- **Test backend**: `cd backend && NODE_ENV=test npx jest --runInBand` (uses mongodb-memory-server, no running mongod needed) - **Backend health check**: `curl http://localhost:5001/health` - **Backend greet**: `curl http://localhost:5001/api/greet` From faaeb8c99e731f55230b4ed6bdc5e58b6f91909d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 02:54:05 +0000 Subject: [PATCH 2/7] Update frontend package-lock.json from npm install Co-authored-by: James Liu --- frontend/package-lock.json | 2522 +++++++++++++++++++++++++++++++++++- 1 file changed, 2497 insertions(+), 25 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fac6147a..69e6a2a8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2705,6 +2705,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3846,6 +3856,380 @@ } } }, + "node_modules/@react-native/assets-registry": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.85.2.tgz", + "integrity": "sha512-kauC/oPaxklU4Y+u9gBfCBJm51qX6WBZq4xx0USCdimtp+G8+554kpygfSWIjoqCJa2o06bWxBEjesiuCv+LzA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.2.tgz", + "integrity": "sha512-XCginmxh0//++EXVOEJHBVZxHla294FzLCFF6jXwAUjvXVhqyIKyxhABfz+r4OOmaiuWk4Rtd4arqdAzeHeprg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.29.0", + "hermes-parser": "0.33.3", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@react-native/codegen/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@react-native/codegen/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.85.2.tgz", + "integrity": "sha512-3KLgSg1kHvBpr93zMaQhvfYTgnCw7yZRED+3J4dMcYjfSjtD0Wf8SofU6uBmAw9JaVYvP43lpdwUpI4p0+ABsg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/dev-middleware": "0.85.2", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.84.0", + "metro-config": "^0.84.0", + "metro-core": "^0.84.0", + "semver": "^7.1.3" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "0.85.2" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.85.2.tgz", + "integrity": "sha512-j+0b9H5f5hGTLQxHIhJU/b/W6ijuxJF+ZTLHB0se2kzUBNxFKd7DkIc6753qk3CJdiv55vxG3XDgmlpbHxOpmA==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/debugger-shell": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.85.2.tgz", + "integrity": "sha512-r5BkhqPMfg3LmaZS5zadHmBNVH5h4bhSpv4BEPGfK4gat9HABAMzUzybi+2wpgU3SoHxnyKGdExEJvoqVcjeRg==", + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "debug": "^4.4.0", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/debugger-shell/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-shell/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.85.2.tgz", + "integrity": "sha512-3J+NaDUg+QEfDeLAUzgaWhpaxEg78g+KwbydlDCewh2G6WnHpsty8XooruxNHzyAsqVWywZMrzmbn78Ctc1O9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.85.2", + "@react-native/debugger-shell": "0.85.2", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.3.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@react-native/dev-middleware/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/dev-middleware/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-native/dev-middleware/node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "peer": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.85.2.tgz", + "integrity": "sha512-YXBOLeAqFrv7XwUeBPTKZeOV1FIxn4AW7UAEitScf3ibC8bu8+6NpJu4HWgbNQHg7vDbbTZVbcOl8EwGxsSq2w==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.2.tgz", + "integrity": "sha512-esGEAmKVM40DV/yVmNljCKZTIeUo7qXqc+Hwffkv3TG+b3E24xyFovHrbP98gGxZr2ZsEyx+2sKLdXF5asY5nw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.85.2.tgz", + "integrity": "sha512-svuOLtjbFGXDdHsriHXuND5FgHg7XlkOXCbH/8+X4t76YLH6qSTffSIQQrKLDL5mn4EFU+Oh/PNO0/FfpnTOTg==", + "license": "MIT", + "peer": true + }, "node_modules/@react-oauth/google": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.1.tgz", @@ -3979,6 +4363,105 @@ "zdog": ">=1.0" } }, + "node_modules/@react-three/fiber": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.6.1.tgz", + "integrity": "sha512-zF0rsKcVYpcJwbFEnv2HkHX9cvOEgsfQo/X8lwmR2dn13S4qEQJXir9fxf5js2LQFoXqxOY7MDkOkYx2uZ4gSg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.27.0", + "suspend-react": "^0.1.3", + "use-sync-external-store": "^1.4.0", + "zustand": "^5.0.3" + }, + "peerDependencies": { + "expo": ">=43.0", + "expo-asset": ">=8.4", + "expo-file-system": ">=11.0", + "expo-gl": ">=11.0", + "react": ">=19 <19.3", + "react-dom": ">=19 <19.3", + "react-native": ">=0.78", + "three": ">=0.156" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-asset": { + "optional": true + }, + "expo-file-system": { + "optional": true + }, + "expo-gl": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber/node_modules/its-fine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/react-reconciler": "^0.28.9" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/@react-three/fiber/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-three/fiber/node_modules/zustand": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.13.tgz", + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, "node_modules/@remirror/core-constants": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz", @@ -4357,12 +4840,32 @@ "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/jest-dom": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", - "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "license": "MIT", + "peer": true, "dependencies": { - "@adobe/css-tools": "^4.0.1", + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dependencies": { + "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", "@types/testing-library__jest-dom": "^5.9.1", "aria-query": "^5.0.0", @@ -5680,6 +6183,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-window": { "version": "1.8.8", "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", @@ -5777,6 +6290,13 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT", + "peer": true + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -6561,6 +7081,19 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "deprecated": "Use your platform's native atob() and btoa() methods instead" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "peer": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -6718,6 +7251,13 @@ "ajv": "^6.9.1" } }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT", + "peer": true + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -7467,6 +8007,16 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz", + "integrity": "sha512-/Z9xYdaJ1lC0pT9do6TqCqhOSLfZ5Ot8D5za1p+feEfWYupCOfGbhhEXN9r2ZgJtDNUNRw/Z+T2CvAGKBqtqWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-parser": "0.33.3" + } + }, "node_modules/babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", @@ -7858,6 +8408,27 @@ "node": ">= 0.6.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -8048,6 +8619,31 @@ "node": ">= 0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -8291,6 +8887,38 @@ "node": ">= 6" } }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -8299,6 +8927,46 @@ "node": ">=6.0" } }, + "node_modules/chromium-edge-launcher": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.3.0.tgz", + "integrity": "sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4" + } + }, + "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-edge-launcher/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -8521,6 +9189,22 @@ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -8529,6 +9213,65 @@ "node": ">=0.8" } }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -10716,6 +11459,16 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -10773,6 +11526,13 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -10914,6 +11674,19 @@ "node": ">=0.8.0" } }, + "node_modules/fb-dotslash": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", + "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", + "license": "(MIT OR Apache-2.0)", + "peer": true, + "bin": { + "dotslash": "bin/dotslash" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -11090,6 +11863,13 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT", + "peer": true + }, "node_modules/follow-redirects": { "version": "1.15.5", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", @@ -11797,6 +12577,30 @@ "he": "bin/he" } }, + "node_modules/hermes-compiler": { + "version": "250829098.0.10", + "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.10.tgz", + "integrity": "sha512-TcRlZ0/TlyfJqquRFAWoyElVNnkdYRi/sEp4/Qy8/GYxjg8j2cS9D4MjuaQ+qimkmLN7AmO+44IznRf06mAr0w==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-estree": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz", + "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-parser": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz", + "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.33.3" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -12132,6 +12936,27 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -12140,6 +12965,22 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, "node_modules/immer": { "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", @@ -12884,6 +13725,19 @@ "set-function-name": "^2.0.1" } }, + "node_modules/its-fine": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz", + "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/react-reconciler": "^0.28.0" + }, + "peerDependencies": { + "react": ">=18.0" + } + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -14884,6 +15738,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD", + "peer": true + }, "node_modules/jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -15089,6 +15950,27 @@ "node": ">= 8" } }, + "node_modules/konva": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/konva/-/konva-10.3.0.tgz", + "integrity": "sha512-gt19K2gzY4lHbnkvsku7eSmB+A9PTS2jG4F9coBMsdjM1UKfJNxJbDbXVpeCW1wjEGRwBD3nBamcHnqJhAeKlg==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT", + "peer": true + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -15134,18 +16016,46 @@ "node": ">= 0.8.0" } }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "engines": { - "node": ">=10" + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/linkify-it": { "version": "5.0.0", @@ -15222,6 +16132,13 @@ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -15370,6 +16287,13 @@ "node": ">= 20" } }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/math-expression-evaluator": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz", @@ -15422,20 +16346,1116 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz", + "integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.35.0", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.84.4", + "metro-cache": "0.84.4", + "metro-cache-key": "0.84.4", + "metro-config": "0.84.4", + "metro-core": "0.84.4", + "metro-file-map": "0.84.4", + "metro-resolver": "0.84.4", + "metro-runtime": "0.84.4", + "metro-source-map": "0.84.4", + "metro-symbolicate": "0.84.4", + "metro-transform-plugins": "0.84.4", + "metro-transform-worker": "0.84.4", + "mime-types": "^3.0.1", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.4.tgz", + "integrity": "sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.84.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro-cache": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.4.tgz", + "integrity": "sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.84.4" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-cache-key": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.4.tgz", + "integrity": "sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-cache/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/metro-cache/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/metro-config": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz", + "integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.84.4", + "metro-cache": "0.84.4", + "metro-core": "0.84.4", + "metro-runtime": "0.84.4", + "yaml": "^2.6.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-config/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-config/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-config/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-config/node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/metro-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro-config/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-config/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro-config/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro-config/node_modules/yaml": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/metro-core": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz", + "integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.84.4" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-file-map": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.4.tgz", + "integrity": "sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-file-map/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-file-map/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-file-map/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-file-map/node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/metro-file-map/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro-file-map/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro-file-map/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro-file-map/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro-file-map/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-file-map/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/metro-file-map/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro-file-map/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-file-map/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro-file-map/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-file-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.4.tgz", + "integrity": "sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-resolver": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.4.tgz", + "integrity": "sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-runtime": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz", + "integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-source-map": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz", + "integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.84.4", + "nullthrows": "^1.1.1", + "ob1": "0.84.4", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.4.tgz", + "integrity": "sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.84.4", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-symbolicate/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.4.tgz", + "integrity": "sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.4.tgz", + "integrity": "sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "metro": "0.84.4", + "metro-babel-transformer": "0.84.4", + "metro-cache": "0.84.4", + "metro-cache-key": "0.84.4", + "metro-minify-terser": "0.84.4", + "metro-source-map": "0.84.4", + "metro-transform-plugins": "0.84.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, + "node_modules/metro/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/metro/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/metro/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/metro/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/metro/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro/node_modules/jest-util/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/metro/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/metro/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/metro/node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/metro/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, "engines": { - "node": ">= 0.6" + "node": ">=12" } }, "node_modules/micromatch": { @@ -15772,11 +17792,31 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT", + "peer": true + }, "node_modules/nwsapi": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" }, + "node_modules/ob1": { + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz", + "integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -18015,6 +20055,16 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -18249,6 +20299,17 @@ "node": ">=8" } }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -18367,6 +20428,272 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/react-konva": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/react-konva/-/react-konva-18.2.14.tgz", + "integrity": "sha512-lBDe/5fTgquMdg1AHI0B16YZdAOvEhWMBWuo12ioyY0icdxcz9Cf12j86fsCJCHdnvjUOlZeC0f5q+siyHbD4Q==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@types/react-reconciler": "^0.28.2", + "its-fine": "^1.1.1", + "react-reconciler": "~0.29.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "konva": "^8.0.1 || ^7.2.5 || ^9.0.0 || ^10.0.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/react-konva/node_modules/react-reconciler": { + "version": "0.29.2", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz", + "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-native": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.85.2.tgz", + "integrity": "sha512-GFWEPwLYirfj5X8gMtXOWtqX0cqUEURRHETZfFk37VCa4++izrKvGvv24anvuyulXV87NAhVkfNw93rLg3HByw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/assets-registry": "0.85.2", + "@react-native/codegen": "0.85.2", + "@react-native/community-cli-plugin": "0.85.2", + "@react-native/gradle-plugin": "0.85.2", + "@react-native/js-polyfills": "0.85.2", + "@react-native/normalize-colors": "0.85.2", + "@react-native/virtualized-lists": "0.85.2", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-plugin-syntax-hermes-parser": "0.33.3", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "hermes-compiler": "250829098.0.10", + "invariant": "^2.2.4", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.84.0", + "metro-source-map": "^0.84.0", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.27.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.15", + "whatwg-fetch": "^3.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@react-native/jest-preset": "0.85.2", + "@types/react": "^19.1.1", + "react": "^19.2.3" + }, + "peerDependenciesMeta": { + "@react-native/jest-preset": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.85.2", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.85.2.tgz", + "integrity": "sha512-wmVKpAlcr+UB0L5SpbrV865EdleUP7I5+X+48e1aRsQK8q+wsTRBXeUwWVip/1l+HZwlZFeO8iOILJ16VRu0Cw==", + "license": "MIT", + "peer": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "0.85.2" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-native/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-native/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/react-native/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/react-native/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-native/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-native/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/react-optimized-image": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/react-optimized-image/-/react-optimized-image-0.4.1.tgz", @@ -18544,6 +20871,18 @@ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-zdog": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/react-zdog/-/react-zdog-1.2.2.tgz", + "integrity": "sha512-Ix7ALha91aOEwiHuxumCeYbARS5XNpc/w0v145oGkM6poF/CvhKJwzLhM5sEZbtrghMA+psAhOJkCTzJoseicA==", + "license": "MIT", + "peer": true, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "resize-observer-polyfill": "^1.5.1" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -18828,6 +21167,13 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT", + "peer": true + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -19438,6 +21784,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -19805,6 +22161,29 @@ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/static-eval": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", @@ -20277,6 +22656,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -20591,6 +22980,13 @@ "node": ">=0.8" } }, + "node_modules/three": { + "version": "0.184.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", + "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", + "license": "MIT", + "peer": true + }, "node_modules/throat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", @@ -20601,6 +22997,54 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "license": "MIT", + "peer": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -20859,6 +23303,20 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -21113,6 +23571,13 @@ "node": ">= 0.8" } }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT", + "peer": true + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -22153,6 +24618,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zdog": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/zdog/-/zdog-1.1.3.tgz", + "integrity": "sha512-raRj6r0gPzopFm5XWBJZr/NuV4EEnT4iE+U3dp5FV5pCb588Gmm3zLIp/j9yqqcMiHH8VNQlerLTgOqL7krh6w==", + "license": "MIT", + "peer": true + }, "node_modules/zustand": { "version": "4.5.7", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", From acf8c1d69275ab57449a554676c769a99e625adf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 03:36:45 +0000 Subject: [PATCH 3/7] Add event demo seed script with realistic org, events, and interaction data Creates scripts/seed-event-demo.js that populates the local MongoDB with: - 16 users with natural names and RPI emails - 1 org (RPI Innovators Hub) with roles and positions - 6 events (4 past, 2 upcoming) spanning showcase, talk, social, workshop, gala, study types - Event analytics (views, registrations, engagement rates) - Registration forms and 12 form responses - Event agenda with 7 items for the flagship showcase event - Volunteer roles (Stage Manager, AV Tech, Registration Desk) with assignments - Equipment allocations - Org followers Login: demo@meridian.test / password123 Dashboard: /club-dashboard/RPI-Innovators-Hub Co-authored-by: James Liu --- .gitignore | 3 +- scripts/seed-event-demo.js | 726 +++++++++++++++++++++++++++++++++++++ 2 files changed, 728 insertions(+), 1 deletion(-) create mode 100644 scripts/seed-event-demo.js diff --git a/.gitignore b/.gitignore index d4064b53..608c7dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,8 @@ venv/ .vscode/ -scripts/ +scripts/* +!scripts/seed-event-demo.js .env* .flaskenv* !.env.project diff --git a/scripts/seed-event-demo.js b/scripts/seed-event-demo.js new file mode 100644 index 00000000..5143bf7f --- /dev/null +++ b/scripts/seed-event-demo.js @@ -0,0 +1,726 @@ +#!/usr/bin/env node +/** + * Seed script: creates a realistic org with events, members, analytics, + * registrations, volunteer signups, equipment, and agenda data. + * + * Usage: node scripts/seed-event-demo.js + * + * Connects to local MongoDB at mongodb://localhost:27017/rpi + */ + +const mongoose = require('mongoose'); +const bcrypt = require('bcrypt'); + +const MONGO_URI = process.env.MONGO_URI_RPI || 'mongodb://localhost:27017/rpi'; +const PLATFORM_URI = MONGO_URI.replace(/\/([^/]+)(\?|$)/, '/meridian_platform$2'); + +async function main() { + const conn = await mongoose.createConnection(MONGO_URI).asPromise(); + const platformConn = await mongoose.createConnection(PLATFORM_URI).asPromise(); + console.log('Connected to rpi + platform databases'); + + // ─── Schemas (minimal, strict:false lets us insert any shape) ─── + const flexSchema = new mongoose.Schema({}, { strict: false, timestamps: true, id: false }); + const flexSchemaNoTs = new mongoose.Schema({}, { strict: false, id: false }); + + const User = conn.model('User', flexSchema, 'users'); + const Org = conn.model('Org', flexSchema, 'orgs'); + const OrgMember = conn.model('OrgMember', flexSchema, 'members'); + const Event = conn.model('Event', flexSchemaNoTs, 'events'); + const EventAnalytics = conn.model('EventAnalytics', flexSchemaNoTs, 'eventAnalytics'); + const EventAgenda = conn.model('EventAgenda', flexSchema, 'eventAgendas'); + const EventJob = conn.model('EventJob', flexSchema, 'eventRoles'); + const OrgEventRole = conn.model('OrgEventRole', flexSchema, 'orgEventRoles'); + const VolunteerSignup = conn.model('VolunteerSignup', flexSchema, 'volunteerSignups'); + const EventEquipment = conn.model('EventEquipment', flexSchema, 'eventEquipment'); + const OrgEquipment = conn.model('OrgEquipment', flexSchema, 'orgEquipment'); + const FormModel = conn.model('Form', flexSchemaNoTs, 'forms'); + const FormResponse = conn.model('FormResponse', flexSchemaNoTs, 'formResponses'); + const OrgFollower = conn.model('OrgFollower', flexSchemaNoTs, 'followers'); + const Classroom = conn.model('Classroom', flexSchemaNoTs, 'classrooms1'); + const Building = conn.model('Building', flexSchemaNoTs, 'buildings'); + + const GlobalUser = platformConn.model('GlobalUser', flexSchema, 'globalusers'); + const TenantMembership = platformConn.model('TenantMembership', flexSchema, 'tenantmemberships'); + const Session = platformConn.model('Session', flexSchema, 'sessions'); + + // ─── Clean previous seed data ─── + const seedMarker = { _seedDemo: true }; + for (const M of [User, Org, OrgMember, Event, EventAnalytics, EventAgenda, + EventJob, OrgEventRole, VolunteerSignup, EventEquipment, OrgEquipment, + FormModel, FormResponse, OrgFollower, Classroom, Building]) { + await M.deleteMany(seedMarker); + } + await GlobalUser.deleteMany(seedMarker); + await TenantMembership.deleteMany(seedMarker); + await Session.deleteMany(seedMarker); + console.log('Cleaned previous seed data'); + + // ─── Buildings & Rooms ─── + const empacBuilding = await Building.create({ + ...seedMarker, + name: 'EMPAC', + code: 'EMPAC', + address: '110 8th St, Troy, NY', + }); + const unionBuilding = await Building.create({ + ...seedMarker, + name: 'Rensselaer Union', + code: 'UNION', + address: '110 8th St, Troy, NY', + }); + const empacRoom = await Classroom.create({ + ...seedMarker, + name: 'EMPAC Concert Hall', + building: empacBuilding._id, + capacity: 1200, + type: 'auditorium', + }); + const unionRoom = await Classroom.create({ + ...seedMarker, + name: 'McNeil Room', + building: unionBuilding._id, + capacity: 200, + type: 'conference', + }); + + // ─── Users ─── + const hashedPw = await bcrypt.hash('password123', 10); + + const adminUser = await User.create({ + ...seedMarker, + username: 'demo_admin', + name: 'Jordan Rivera', + email: 'demo@meridian.test', + password: hashedPw, + admin: true, + roles: ['admin', 'developer'], + onboarded: true, + picture: '', + clubAssociations: [], + }); + + const memberUsers = []; + const memberData = [ + { username: 'alex_chen', name: 'Alex Chen', email: 'alex.chen@rpi.edu' }, + { username: 'priya_patel', name: 'Priya Patel', email: 'priya.patel@rpi.edu' }, + { username: 'marcus_johnson', name: 'Marcus Johnson', email: 'marcus.j@rpi.edu' }, + { username: 'sofia_rodriguez', name: 'Sofia Rodriguez', email: 'sofia.r@rpi.edu' }, + { username: 'liam_oconnor', name: 'Liam O\'Connor', email: 'liam.oc@rpi.edu' }, + { username: 'aisha_williams', name: 'Aisha Williams', email: 'aisha.w@rpi.edu' }, + { username: 'david_kim', name: 'David Kim', email: 'david.kim@rpi.edu' }, + { username: 'emma_davis', name: 'Emma Davis', email: 'emma.d@rpi.edu' }, + { username: 'noah_brown', name: 'Noah Brown', email: 'noah.b@rpi.edu' }, + { username: 'olivia_martinez', name: 'Olivia Martinez', email: 'olivia.m@rpi.edu' }, + { username: 'ethan_wright', name: 'Ethan Wright', email: 'ethan.w@rpi.edu' }, + { username: 'mia_thompson', name: 'Mia Thompson', email: 'mia.t@rpi.edu' }, + { username: 'james_garcia', name: 'James Garcia', email: 'james.g@rpi.edu' }, + { username: 'ava_anderson', name: 'Ava Anderson', email: 'ava.a@rpi.edu' }, + { username: 'ben_taylor', name: 'Benjamin Taylor', email: 'ben.t@rpi.edu' }, + ]; + + for (const md of memberData) { + const u = await User.create({ + ...seedMarker, + ...md, + password: hashedPw, + onboarded: true, + roles: ['user'], + clubAssociations: [], + }); + memberUsers.push(u); + } + console.log(`Created ${memberUsers.length + 1} users`); + + // ─── Organization ─── + const orgPositions = [ + { + name: 'owner', + displayName: 'President', + permissions: ['manage_events', 'manage_members', 'manage_roles', 'view_analytics', + 'manage_settings', 'manage_announcements', 'manage_forms', 'manage_budgets', + 'view_events', 'create_events', 'edit_events', 'delete_events'], + isDefault: false, + canManageMembers: true, + canManageRoles: true, + canManageEvents: true, + canViewAnalytics: true, + order: 0, + color: '#3B82F6', + }, + { + name: 'vice_president', + displayName: 'Vice President', + permissions: ['manage_events', 'manage_members', 'view_analytics', 'manage_announcements', + 'view_events', 'create_events', 'edit_events'], + isDefault: false, + canManageMembers: true, + canManageRoles: false, + canManageEvents: true, + canViewAnalytics: true, + order: 1, + color: '#8B5CF6', + }, + { + name: 'events_coordinator', + displayName: 'Events Coordinator', + permissions: ['manage_events', 'view_analytics', 'view_events', 'create_events', 'edit_events'], + isDefault: false, + canManageMembers: false, + canManageRoles: false, + canManageEvents: true, + canViewAnalytics: true, + order: 2, + color: '#EC4899', + }, + { + name: 'member', + displayName: 'Member', + permissions: ['view_events'], + isDefault: true, + canManageMembers: false, + canManageRoles: false, + canManageEvents: false, + canViewAnalytics: false, + order: 3, + color: '#6B7280', + }, + ]; + + const org = await Org.create({ + ...seedMarker, + org_name: 'RPI-Innovators-Hub', + org_profile_image: 'https://ui-avatars.com/api/?name=RPI+Innovators+Hub&background=3B82F6&color=fff&size=200', + org_banner_image: 'https://images.unsplash.com/photo-1523240795612-9a054b0db644?w=1200&h=400&fit=crop', + org_description: 'RPI Innovators Hub is a student-run organization dedicated to fostering innovation, entrepreneurship, and technology leadership on campus. We host hackathons, tech talks, startup showcases, and networking events that connect students with industry professionals and fellow innovators.', + positions: orgPositions, + owner: adminUser._id, + verified: true, + verifiedAt: new Date('2024-09-01'), + verificationType: 'manual', + verificationStatus: 'verified', + approvalStatus: 'approved', + approvedAt: new Date('2024-09-01'), + lifecycleStatus: 'active', + orgTypeKey: 'student_org', + messageSettings: { + enabled: true, + visibility: 'members', + postingPermissions: ['owner', 'vice_president', 'events_coordinator'], + allowReplies: true, + allowLikes: true, + requireApproval: false, + characterLimit: 1000, + }, + socialLinks: [ + { type: 'instagram', username: 'rpi_innovators', order: 0 }, + { type: 'website', url: 'https://innovators.rpi.edu', title: 'Our Website', order: 1 }, + ], + betaFeatureKeys: ['tasks'], + taskBoardStatuses: [ + { key: 'backlog', label: 'Backlog', category: 'backlog', order: 0 }, + { key: 'todo', label: 'To Do', category: 'active', order: 1 }, + { key: 'in_progress', label: 'In Progress', category: 'active', order: 2 }, + { key: 'done', label: 'Done', category: 'done', order: 3 }, + ], + isDeleted: false, + }); + + // Update admin's club associations + await User.updateOne({ _id: adminUser._id }, { $set: { clubAssociations: [org._id] } }); + + // ─── Org Members ─── + const roleAssignments = ['vice_president', 'events_coordinator', 'member', 'member', + 'member', 'member', 'member', 'member', 'member', 'member', + 'member', 'member', 'member', 'member', 'member']; + + const orgMembers = []; + for (let i = 0; i < memberUsers.length; i++) { + const member = await OrgMember.create({ + ...seedMarker, + org_id: org._id, + user_id: memberUsers[i]._id, + role: roleAssignments[i], + roles: [roleAssignments[i]], + status: 'active', + joinedAt: new Date(Date.now() - (180 - i * 10) * 86400000), + }); + orgMembers.push(member); + await User.updateOne({ _id: memberUsers[i]._id }, { $push: { clubAssociations: org._id } }); + } + // Admin as owner member + await OrgMember.create({ + ...seedMarker, + org_id: org._id, + user_id: adminUser._id, + role: 'owner', + roles: ['owner'], + status: 'active', + joinedAt: new Date('2024-09-01'), + }); + console.log(`Created org "${org.org_name}" with ${orgMembers.length + 1} members`); + + // ─── Org followers ─── + for (let i = 0; i < 8; i++) { + await OrgFollower.create({ + ...seedMarker, + org_id: org._id, + user_id: memberUsers[i]._id, + }); + } + + // ─── Org Event Roles (reusable role templates) ─── + const eventRoleSetup = await OrgEventRole.create({ + ...seedMarker, + orgId: org._id, + name: 'Stage Manager', + description: 'Oversees event logistics and manages the venue setup/teardown', + isActive: true, + createdBy: adminUser._id, + }); + const eventRoleAV = await OrgEventRole.create({ + ...seedMarker, + orgId: org._id, + name: 'AV Technician', + description: 'Handles audio/visual equipment and livestream', + isActive: true, + createdBy: adminUser._id, + }); + const eventRoleGreeter = await OrgEventRole.create({ + ...seedMarker, + orgId: org._id, + name: 'Registration Desk', + description: 'Manages check-in and welcomes attendees at the door', + isActive: true, + createdBy: adminUser._id, + }); + + // ─── Org Equipment ─── + const projector = await OrgEquipment.create({ + ...seedMarker, + orgId: org._id, + id: 'EQ-001', + name: 'Portable Projector', + quantity: 2, + storageLocation: 'Union Room 3602', + managedByRole: 'events_coordinator', + createdBy: adminUser._id, + }); + const speakers = await OrgEquipment.create({ + ...seedMarker, + orgId: org._id, + id: 'EQ-002', + name: 'PA Speaker System', + quantity: 1, + storageLocation: 'Union Room 3602', + managedByRole: 'events_coordinator', + createdBy: adminUser._id, + }); + const banners = await OrgEquipment.create({ + ...seedMarker, + orgId: org._id, + id: 'EQ-003', + name: 'Event Banners & Signage Kit', + quantity: 3, + storageLocation: 'Union Room 3602', + managedByRole: 'member', + createdBy: adminUser._id, + }); + + // ─── Helper: create dates relative to now ─── + const now = new Date(); + const daysFromNow = (d) => new Date(now.getTime() + d * 86400000); + const daysAgo = (d) => new Date(now.getTime() - d * 86400000); + + // ─── EVENTS ─── + + // Event 1: Past - Spring Innovation Showcase (flagship event, lots of data) + const event1 = await Event.create({ + ...seedMarker, + name: 'Spring Innovation Showcase 2026', + description: 'Our flagship spring event featuring student startup pitches, interactive technology demos, and a keynote by Dr. Sarah Mitchell from Google DeepMind on "The Future of AI in Education." Open to all RPI students, faculty, and invited guests from the Capital Region tech community.', + type: 'showcase', + hostingId: org._id, + hostingType: 'Org', + start_time: daysAgo(14), + end_time: new Date(daysAgo(14).getTime() + 5 * 3600000), + location: 'EMPAC Concert Hall', + classroom_id: empacRoom._id, + status: 'approved', + visibility: 'public', + expectedAttendance: 250, + going: memberUsers.slice(0, 10).map(u => u._id), + rsvpEnabled: true, + rsvpRequired: false, + checkInEnabled: true, + checkInToken: 'showcase2026', + isDeleted: false, + createdAt: daysAgo(45), + createdBy: adminUser._id, + contact: 'innovators@rpi.edu', + attendees: [], + }); + + // Populate attendees with realistic check-in data + const attendeePool = [...memberUsers]; + const event1Attendees = []; + for (let i = 0; i < 12; i++) { + const u = attendeePool[i % attendeePool.length]; + event1Attendees.push({ + userId: u._id, + registeredAt: new Date(daysAgo(14).getTime() - (30 - i) * 86400000), + guestCount: i < 3 ? 1 : 0, + checkedIn: i < 10, + checkedInAt: i < 10 ? new Date(daysAgo(14).getTime() + (i * 5 + 10) * 60000) : null, + }); + } + await Event.updateOne({ _id: event1._id }, { $set: { attendees: event1Attendees, registrationCount: event1Attendees.length } }); + + // Event 2: Past - Tech Talk: Building Scalable Systems + const event2 = await Event.create({ + ...seedMarker, + name: 'Tech Talk: Building Scalable Systems', + description: 'A deep-dive technical talk by Ethan Brooks (RPI \'22, now SRE at Stripe) on designing distributed systems that handle millions of transactions. Includes Q&A and networking reception afterward.', + type: 'talk', + hostingId: org._id, + hostingType: 'Org', + start_time: daysAgo(28), + end_time: new Date(daysAgo(28).getTime() + 2 * 3600000), + location: 'McNeil Room, Rensselaer Union', + classroom_id: unionRoom._id, + status: 'approved', + visibility: 'public', + expectedAttendance: 80, + going: memberUsers.slice(0, 6).map(u => u._id), + rsvpEnabled: true, + checkInEnabled: true, + isDeleted: false, + createdAt: daysAgo(60), + createdBy: memberUsers[1]._id, + attendees: memberUsers.slice(0, 8).map((u, i) => ({ + userId: u._id, + registeredAt: new Date(daysAgo(28).getTime() - (20 - i * 2) * 86400000), + checkedIn: i < 7, + checkedInAt: i < 7 ? new Date(daysAgo(28).getTime() + i * 3 * 60000) : null, + })), + registrationCount: 8, + }); + + // Event 3: Past - Startup Networking Mixer + const event3 = await Event.create({ + ...seedMarker, + name: 'Startup Networking Mixer', + description: 'Casual networking evening connecting student founders with local angel investors, mentors from the RPI Lally School of Management, and alumni entrepreneurs. Light refreshments provided.', + type: 'social', + hostingId: org._id, + hostingType: 'Org', + start_time: daysAgo(7), + end_time: new Date(daysAgo(7).getTime() + 3 * 3600000), + location: 'McNeil Room, Rensselaer Union', + classroom_id: unionRoom._id, + status: 'approved', + visibility: 'public', + expectedAttendance: 60, + going: memberUsers.slice(2, 9).map(u => u._id), + rsvpEnabled: true, + checkInEnabled: true, + isDeleted: false, + createdAt: daysAgo(21), + createdBy: adminUser._id, + attendees: memberUsers.slice(0, 7).map((u, i) => ({ + userId: u._id, + registeredAt: new Date(daysAgo(7).getTime() - (10 - i) * 86400000), + checkedIn: true, + checkedInAt: new Date(daysAgo(7).getTime() + i * 4 * 60000), + })), + registrationCount: 7, + }); + + // Event 4: Upcoming - HackRPI Kickoff Workshop + const event4 = await Event.create({ + ...seedMarker, + name: 'HackRPI Kickoff: Intro to Full-Stack Development', + description: 'Beginner-friendly workshop to prepare for HackRPI 2026. Learn React, Node.js, and MongoDB basics while building a mini project from scratch. Laptops required, all skill levels welcome.', + type: 'workshop', + hostingId: org._id, + hostingType: 'Org', + start_time: daysFromNow(5), + end_time: new Date(daysFromNow(5).getTime() + 3 * 3600000), + location: 'EMPAC Concert Hall', + classroom_id: empacRoom._id, + status: 'approved', + visibility: 'public', + expectedAttendance: 120, + going: memberUsers.slice(0, 5).map(u => u._id), + rsvpEnabled: true, + rsvpRequired: true, + maxAttendees: 120, + checkInEnabled: false, + isDeleted: false, + createdAt: daysAgo(10), + createdBy: memberUsers[2]._id, + attendees: memberUsers.slice(0, 5).map((u, i) => ({ + userId: u._id, + registeredAt: daysAgo(10 - i), + })), + registrationCount: 5, + }); + + // Event 5: Upcoming - Annual Gala + const event5 = await Event.create({ + ...seedMarker, + name: 'Innovators Hub Annual Gala & Awards', + description: 'Our end-of-year celebration recognizing outstanding members, successful projects, and community impact. Features dinner, awards ceremony, and a surprise keynote speaker. Semi-formal attire requested.', + type: 'gala', + hostingId: org._id, + hostingType: 'Org', + start_time: daysFromNow(21), + end_time: new Date(daysFromNow(21).getTime() + 4 * 3600000), + location: 'EMPAC Concert Hall', + classroom_id: empacRoom._id, + status: 'approved', + visibility: 'public', + expectedAttendance: 200, + going: [], + rsvpEnabled: true, + rsvpRequired: true, + maxAttendees: 200, + isDeleted: false, + createdAt: daysAgo(3), + createdBy: adminUser._id, + attendees: memberUsers.slice(0, 3).map((u, i) => ({ + userId: u._id, + registeredAt: daysAgo(3 - i), + })), + registrationCount: 3, + }); + + // Event 6: Past - weekly study session (shows variety) + const event6 = await Event.create({ + ...seedMarker, + name: 'Weekly Code Review & Study Session', + description: 'Open study and code review session. Bring your projects for peer feedback, pair programming, or just a productive study environment with fellow innovators.', + type: 'study', + hostingId: org._id, + hostingType: 'Org', + start_time: daysAgo(3), + end_time: new Date(daysAgo(3).getTime() + 2 * 3600000), + location: 'McNeil Room, Rensselaer Union', + classroom_id: unionRoom._id, + status: 'approved', + visibility: 'internal', + expectedAttendance: 20, + going: memberUsers.slice(5, 10).map(u => u._id), + isDeleted: false, + createdAt: daysAgo(10), + createdBy: memberUsers[0]._id, + attendees: memberUsers.slice(5, 10).map((u, i) => ({ + userId: u._id, + registeredAt: daysAgo(5 - i), + checkedIn: true, + checkedInAt: new Date(daysAgo(3).getTime() + i * 2 * 60000), + })), + registrationCount: 5, + isStudySession: true, + }); + + const allEvents = [event1, event2, event3, event4, event5, event6]; + console.log(`Created ${allEvents.length} events`); + + // ─── Event Analytics ─── + const analyticsEntries = [ + { eventId: event1._id, views: 847, uniqueViews: 423, anonymousViews: 312, registrations: 89, uniqueRegistrations: 76, engagementRate: 42 }, + { eventId: event2._id, views: 312, uniqueViews: 198, anonymousViews: 87, registrations: 45, uniqueRegistrations: 42, engagementRate: 38 }, + { eventId: event3._id, views: 256, uniqueViews: 167, anonymousViews: 64, registrations: 38, uniqueRegistrations: 35, engagementRate: 35 }, + { eventId: event4._id, views: 189, uniqueViews: 134, anonymousViews: 45, registrations: 28, uniqueRegistrations: 25, engagementRate: 31 }, + { eventId: event5._id, views: 94, uniqueViews: 78, anonymousViews: 22, registrations: 15, uniqueRegistrations: 14, engagementRate: 28 }, + { eventId: event6._id, views: 67, uniqueViews: 45, anonymousViews: 12, registrations: 8, uniqueRegistrations: 8, engagementRate: 22 }, + ]; + for (const a of analyticsEntries) { + await EventAnalytics.create({ ...seedMarker, ...a }); + } + console.log('Created event analytics'); + + // ─── Registration Forms & Responses (for showcase event) ─── + const regForm = await FormModel.create({ + ...seedMarker, + title: 'Spring Innovation Showcase Registration', + orgId: org._id, + eventId: event1._id, + fields: [ + { label: 'Full Name', type: 'text', required: true }, + { label: 'RPI Email', type: 'email', required: true }, + { label: 'Year', type: 'select', required: true, options: ['Freshman', 'Sophomore', 'Junior', 'Senior', 'Graduate'] }, + { label: 'Dietary Restrictions', type: 'text', required: false }, + { label: 'What excites you most about innovation?', type: 'textarea', required: false }, + ], + isActive: true, + }); + await Event.updateOne({ _id: event1._id }, { $set: { registrationFormId: regForm._id } }); + + const yearOptions = ['Freshman', 'Sophomore', 'Junior', 'Senior', 'Graduate']; + const excitementAnswers = [ + 'Building products that solve real problems', + 'The intersection of AI and creativity', + 'Meeting other students who are passionate about tech', + 'Learning from industry professionals', + 'The energy of hackathons and pitch competitions', + 'Exploring new technologies and frameworks', + 'The potential to create something impactful', + ]; + for (let i = 0; i < 12; i++) { + const u = memberUsers[i % memberUsers.length]; + await FormResponse.create({ + ...seedMarker, + form: regForm._id, + event: event1._id, + submittedBy: u._id, + answers: [ + { label: 'Full Name', value: u.name }, + { label: 'RPI Email', value: u.email }, + { label: 'Year', value: yearOptions[i % yearOptions.length] }, + { label: 'Dietary Restrictions', value: i % 4 === 0 ? 'Vegetarian' : '' }, + { label: 'What excites you most about innovation?', value: excitementAnswers[i % excitementAnswers.length] }, + ], + submittedAt: new Date(daysAgo(14).getTime() - (30 - i) * 86400000), + }); + } + console.log('Created registration form + 12 responses'); + + // ─── Event Agenda (for showcase event) ─── + const showcaseAgenda = await EventAgenda.create({ + ...seedMarker, + eventId: event1._id, + orgId: org._id, + items: [ + { id: 'a1', title: 'Registration & Welcome Coffee', type: 'Setup', startTime: daysAgo(14), endTime: new Date(daysAgo(14).getTime() + 30 * 60000), location: 'Main Lobby', isPublic: true, order: 0 }, + { id: 'a2', title: 'Opening Remarks', description: 'Welcome by Jordan Rivera, President', type: 'Speaker', startTime: new Date(daysAgo(14).getTime() + 30 * 60000), endTime: new Date(daysAgo(14).getTime() + 45 * 60000), location: 'Main Stage', isPublic: true, order: 1 }, + { id: 'a3', title: 'Keynote: The Future of AI in Education', description: 'Dr. Sarah Mitchell, Google DeepMind', type: 'Speaker', startTime: new Date(daysAgo(14).getTime() + 45 * 60000), endTime: new Date(daysAgo(14).getTime() + 105 * 60000), location: 'Main Stage', isPublic: true, order: 2 }, + { id: 'a4', title: 'Coffee Break & Demo Setup', type: 'Break', startTime: new Date(daysAgo(14).getTime() + 105 * 60000), endTime: new Date(daysAgo(14).getTime() + 120 * 60000), isPublic: true, order: 3 }, + { id: 'a5', title: 'Student Startup Pitches (Round 1)', description: '5 teams, 5 minutes each + Q&A', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 120 * 60000), endTime: new Date(daysAgo(14).getTime() + 180 * 60000), location: 'Main Stage', isPublic: true, order: 4 }, + { id: 'a6', title: 'Interactive Demo Fair', description: 'Hands-on demos from 12 student projects', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 180 * 60000), endTime: new Date(daysAgo(14).getTime() + 240 * 60000), location: 'Exhibition Hall', isPublic: true, order: 5 }, + { id: 'a7', title: 'Networking Reception & Awards', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 240 * 60000), endTime: new Date(daysAgo(14).getTime() + 300 * 60000), location: 'Lobby', isPublic: true, order: 6 }, + ], + publicNotes: 'All attendees welcome. Refreshments provided.', + internalNotes: 'AV setup must be complete by 8:30am. Catering arrives at 8:00am.', + isPublished: true, + }); + console.log('Created event agenda'); + + // ─── Event Jobs (roles for showcase event) ─── + const jobStageManager = await EventJob.create({ + ...seedMarker, + orgRoleId: eventRoleSetup._id, + eventId: event1._id, + orgId: org._id, + name: 'Stage Manager', + description: 'Coordinate speaker transitions, manage timing, and oversee AV cues', + requiredCount: 2, + shiftStart: daysAgo(14), + shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + assignments: [ + { memberId: memberUsers[0]._id, status: 'confirmed', assignedAt: daysAgo(20), confirmedAt: daysAgo(18) }, + { memberId: memberUsers[1]._id, status: 'confirmed', assignedAt: daysAgo(20), confirmedAt: daysAgo(17) }, + ], + }); + + const jobAV = await EventJob.create({ + ...seedMarker, + orgRoleId: eventRoleAV._id, + eventId: event1._id, + orgId: org._id, + name: 'AV Technician', + description: 'Set up projectors, microphones, and manage livestream', + requiredCount: 2, + shiftStart: new Date(daysAgo(14).getTime() - 1 * 3600000), + shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + assignments: [ + { memberId: memberUsers[6]._id, status: 'confirmed', assignedAt: daysAgo(19), confirmedAt: daysAgo(16) }, + ], + }); + + const jobGreeter = await EventJob.create({ + ...seedMarker, + orgRoleId: eventRoleGreeter._id, + eventId: event1._id, + orgId: org._id, + name: 'Registration Desk', + description: 'Welcome attendees, manage check-in process, hand out name badges', + requiredCount: 3, + shiftStart: daysAgo(14), + shiftEnd: new Date(daysAgo(14).getTime() + 2 * 3600000), + assignments: [ + { memberId: memberUsers[3]._id, status: 'confirmed', assignedAt: daysAgo(18), confirmedAt: daysAgo(15) }, + { memberId: memberUsers[4]._id, status: 'confirmed', assignedAt: daysAgo(18), confirmedAt: daysAgo(14) }, + { memberId: memberUsers[7]._id, status: 'assigned', assignedAt: daysAgo(16) }, + ], + }); + console.log('Created event jobs (roles + assignments)'); + + // ─── Volunteer Signups ─── + const volunteerData = [ + { memberId: memberUsers[0]._id, roleId: jobStageManager._id, checkedIn: true, checkedOut: true }, + { memberId: memberUsers[1]._id, roleId: jobStageManager._id, checkedIn: true, checkedOut: true }, + { memberId: memberUsers[6]._id, roleId: jobAV._id, checkedIn: true, checkedOut: true }, + { memberId: memberUsers[3]._id, roleId: jobGreeter._id, checkedIn: true, checkedOut: false }, + { memberId: memberUsers[4]._id, roleId: jobGreeter._id, checkedIn: true, checkedOut: false }, + ]; + for (const v of volunteerData) { + await VolunteerSignup.create({ + ...seedMarker, + eventId: event1._id, + memberId: v.memberId, + roleId: v.roleId, + shiftStart: daysAgo(14), + shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + status: 'approved', + checkedIn: v.checkedIn, + checkedInAt: v.checkedIn ? daysAgo(14) : null, + checkedOut: v.checkedOut, + checkedOutAt: v.checkedOut ? new Date(daysAgo(14).getTime() + 5 * 3600000) : null, + }); + } + console.log('Created volunteer signups'); + + // ─── Event Equipment ─── + await EventEquipment.create({ + ...seedMarker, + eventId: event1._id, + orgId: org._id, + items: [ + { equipmentId: 'EQ-001', name: 'Portable Projector', quantity: 2 }, + { equipmentId: 'EQ-002', name: 'PA Speaker System', quantity: 1 }, + { equipmentId: 'EQ-003', name: 'Event Banners & Signage Kit', quantity: 2 }, + ], + }); + await EventEquipment.create({ + ...seedMarker, + eventId: event4._id, + orgId: org._id, + items: [ + { equipmentId: 'EQ-001', name: 'Portable Projector', quantity: 1 }, + ], + }); + console.log('Created event equipment allocations'); + + // ─── Summary ─── + console.log('\n════════════════════════════════════════'); + console.log(' SEED COMPLETE'); + console.log('════════════════════════════════════════'); + console.log(` Admin login: demo@meridian.test / password123`); + console.log(` Org slug: ${org.org_name}`); + console.log(` Org ID: ${org._id}`); + console.log(` Events: ${allEvents.length}`); + console.log(` Members: ${memberUsers.length + 1}`); + console.log(` Dashboard: http://localhost:3000/club-dashboard/${org.org_name}`); + console.log('════════════════════════════════════════\n'); + + await conn.close(); + await platformConn.close(); + process.exit(0); +} + +main().catch(err => { + console.error('Seed failed:', err); + process.exit(1); +}); From afc6ab346ab1f163fae3dd86b25e99adf8977608 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 03:36:51 +0000 Subject: [PATCH 4/7] Fix ObjectId casting in buildOrgEventScope for aggregate queries The orgId from req.params is a string, which doesn't match ObjectId fields in MongoDB aggregate pipelines (unlike Mongoose find() which auto-casts). This caused event analytics and dashboard aggregations to return zero results even with valid data. Co-authored-by: James Liu --- backend/routes/orgEventManagementRoutes.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/routes/orgEventManagementRoutes.js b/backend/routes/orgEventManagementRoutes.js index 8a9c216a..2ff634dc 100644 --- a/backend/routes/orgEventManagementRoutes.js +++ b/backend/routes/orgEventManagementRoutes.js @@ -10,10 +10,17 @@ const ExternalRoomSyncService = require('../services/externalRoomSyncService'); const { resolveAnonymousEmail, resolveAnonymousName } = require('../services/eventAnnouncementService'); const { getEffectivePolicy, assertOrgAllowsEventCreation, assertEventReservationReady } = require('../services/atlasPolicyService'); -const buildOrgEventScope = (orgId) => ([ - { hostingId: orgId }, - { collaboratorOrgs: { $elemMatch: { orgId, status: 'active' } } } -]); +const mongoose = require('mongoose'); +const toObjectId = (id) => { + try { return new mongoose.Types.ObjectId(String(id)); } catch { return id; } +}; +const buildOrgEventScope = (orgId) => { + const oid = toObjectId(orgId); + return [ + { hostingId: oid }, + { collaboratorOrgs: { $elemMatch: { orgId: oid, status: 'active' } } } + ]; +}; const buildScopedEventQuery = (orgId, eventId) => ({ _id: eventId, From dd795349ccae530a6e13725a327f55bdb213a436 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 06:19:04 +0000 Subject: [PATCH 5/7] Enhance seed script with funnel analytics data and fix schema stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add viewHistory to EventAnalytics seed entries for legacy analytics - Seed analytics_events collection with platform-level events: event_view, event_registration_form_open, event_registration, event_checkin (with referrer source attribution) - Update event schema stub to include attendees, registrationFormId, and collaboratorOrgs for Mongoose populate support - Update eventAnalytics schema stub with viewHistory and rsvpHistory - Update formResponse schema stub with submittedBy ref for populate Funnel data for Spring Innovation Showcase: 15 unique viewers → 12 form opens → 12 registrations → 10 check-ins Sources: 6 direct, 6 explore, 3 org_page Co-authored-by: James Liu --- scripts/seed-event-demo.js | 146 ++++++++++++++++++++++++++++++++++--- 1 file changed, 137 insertions(+), 9 deletions(-) diff --git a/scripts/seed-event-demo.js b/scripts/seed-event-demo.js index 5143bf7f..dfa8b9d3 100644 --- a/scripts/seed-event-demo.js +++ b/scripts/seed-event-demo.js @@ -527,19 +527,147 @@ async function main() { const allEvents = [event1, event2, event3, event4, event5, event6]; console.log(`Created ${allEvents.length} events`); - // ─── Event Analytics ─── + // ─── Event Analytics (with viewHistory for funnel) ─── + function generateViewHistory(eventDate, totalViews, uniqueLoggedIn, anonymousViews, users, daysSpread) { + const history = []; + const seenUsers = new Set(); + for (let i = 0; i < totalViews - anonymousViews; i++) { + const u = users[i % users.length]; + const isUnique = !seenUsers.has(String(u._id)); + seenUsers.add(String(u._id)); + history.push({ + userId: u._id, + isAnonymous: false, + anonymousId: null, + timestamp: new Date(eventDate.getTime() - Math.random() * daysSpread * 86400000), + userAgent: 'Mozilla/5.0 Chrome/125', + ipAddress: `10.0.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`, + }); + } + for (let i = 0; i < anonymousViews; i++) { + history.push({ + userId: null, + isAnonymous: true, + anonymousId: `anon-${i}-${Math.random().toString(36).slice(2, 8)}`, + timestamp: new Date(eventDate.getTime() - Math.random() * daysSpread * 86400000), + userAgent: 'Mozilla/5.0 Chrome/125', + ipAddress: `10.0.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`, + }); + } + return history; + } + const analyticsEntries = [ - { eventId: event1._id, views: 847, uniqueViews: 423, anonymousViews: 312, registrations: 89, uniqueRegistrations: 76, engagementRate: 42 }, - { eventId: event2._id, views: 312, uniqueViews: 198, anonymousViews: 87, registrations: 45, uniqueRegistrations: 42, engagementRate: 38 }, - { eventId: event3._id, views: 256, uniqueViews: 167, anonymousViews: 64, registrations: 38, uniqueRegistrations: 35, engagementRate: 35 }, - { eventId: event4._id, views: 189, uniqueViews: 134, anonymousViews: 45, registrations: 28, uniqueRegistrations: 25, engagementRate: 31 }, - { eventId: event5._id, views: 94, uniqueViews: 78, anonymousViews: 22, registrations: 15, uniqueRegistrations: 14, engagementRate: 28 }, - { eventId: event6._id, views: 67, uniqueViews: 45, anonymousViews: 12, registrations: 8, uniqueRegistrations: 8, engagementRate: 22 }, + { eventId: event1._id, views: 847, uniqueViews: 423, anonymousViews: 312, uniqueAnonymousViews: 198, registrations: 89, uniqueRegistrations: 76, engagementRate: 42, eventDate: daysAgo(14), daysSpread: 30 }, + { eventId: event2._id, views: 312, uniqueViews: 198, anonymousViews: 87, uniqueAnonymousViews: 64, registrations: 45, uniqueRegistrations: 42, engagementRate: 38, eventDate: daysAgo(28), daysSpread: 20 }, + { eventId: event3._id, views: 256, uniqueViews: 167, anonymousViews: 64, uniqueAnonymousViews: 42, registrations: 38, uniqueRegistrations: 35, engagementRate: 35, eventDate: daysAgo(7), daysSpread: 14 }, + { eventId: event4._id, views: 189, uniqueViews: 134, anonymousViews: 45, uniqueAnonymousViews: 31, registrations: 28, uniqueRegistrations: 25, engagementRate: 31, eventDate: daysFromNow(5), daysSpread: 10 }, + { eventId: event5._id, views: 94, uniqueViews: 78, anonymousViews: 22, uniqueAnonymousViews: 18, registrations: 15, uniqueRegistrations: 14, engagementRate: 28, eventDate: daysFromNow(21), daysSpread: 5 }, + { eventId: event6._id, views: 67, uniqueViews: 45, anonymousViews: 12, uniqueAnonymousViews: 9, registrations: 8, uniqueRegistrations: 8, engagementRate: 22, eventDate: daysAgo(3), daysSpread: 7 }, ]; for (const a of analyticsEntries) { - await EventAnalytics.create({ ...seedMarker, ...a }); + const viewHistory = generateViewHistory(a.eventDate, a.views, a.uniqueViews, a.anonymousViews, memberUsers, a.daysSpread); + await EventAnalytics.create({ + ...seedMarker, + eventId: a.eventId, + views: a.views, + uniqueViews: a.uniqueViews, + anonymousViews: a.anonymousViews, + uniqueAnonymousViews: a.uniqueAnonymousViews, + registrations: a.registrations, + uniqueRegistrations: a.uniqueRegistrations, + engagementRate: a.engagementRate, + rsvps: a.registrations, + uniqueRsvps: a.uniqueRegistrations, + viewHistory, + rsvpHistory: [], + }); + } + console.log('Created event analytics with viewHistory'); + + // ─── Platform Analytics Events (analytics_events collection for funnel) ─── + const AnalyticsEvent = conn.model('AnalyticsEvent', flexSchemaNoTs, 'analytics_events'); + await AnalyticsEvent.deleteMany(seedMarker); + + async function seedPlatformAnalytics(eventDoc, uniqueViewers, formOpens, registrations, checkins) { + const evId = String(eventDoc._id); + const eventStart = eventDoc.start_time || daysAgo(14); + const entries = []; + const viewerUsers = memberUsers.slice(0, Math.min(uniqueViewers, memberUsers.length)); + + // event_view entries + for (let i = 0; i < uniqueViewers; i++) { + const u = viewerUsers[i % viewerUsers.length]; + const sources = ['direct', 'explore', 'org_page', 'direct', 'explore']; + entries.push({ + ...seedMarker, + event: 'event_view', + user_id: u._id, + anonymous_id: null, + ts: new Date(eventStart.getTime() - Math.random() * 20 * 86400000), + properties: { event_id: evId, source: sources[i % sources.length] }, + context: { referrer: i % 3 === 0 ? 'events-dashboard' : i % 3 === 1 ? 'club-dashboard/RPI-Innovators-Hub' : '' }, + }); + } + + // event_registration_form_open entries + for (let i = 0; i < formOpens; i++) { + const u = viewerUsers[i % viewerUsers.length]; + entries.push({ + ...seedMarker, + event: 'event_registration_form_open', + user_id: u._id, + anonymous_id: null, + ts: new Date(eventStart.getTime() - Math.random() * 15 * 86400000), + properties: { event_id: evId }, + }); + } + + // event_registration entries + for (let i = 0; i < registrations; i++) { + const u = viewerUsers[i % viewerUsers.length]; + entries.push({ + ...seedMarker, + event: 'event_registration', + user_id: u._id, + anonymous_id: null, + ts: new Date(eventStart.getTime() - Math.random() * 10 * 86400000), + properties: { event_id: evId }, + }); + } + + // event_checkin entries + for (let i = 0; i < checkins; i++) { + const u = viewerUsers[i % viewerUsers.length]; + entries.push({ + ...seedMarker, + event: 'event_checkin', + user_id: u._id, + anonymous_id: null, + ts: new Date(eventStart.getTime() + i * 5 * 60000), + properties: { event_id: evId }, + }); + } + + if (entries.length > 0) { + await AnalyticsEvent.insertMany(entries); + } } - console.log('Created event analytics'); + + // Showcase: 15 unique viewers → 12 form opens → 12 registrations → 10 check-ins + await seedPlatformAnalytics(event1, 15, 12, 12, 10); + // Tech Talk: 12 viewers → 10 form opens → 8 registrations → 7 check-ins + await seedPlatformAnalytics(event2, 12, 10, 8, 7); + // Networking Mixer: 10 viewers → 8 form opens → 7 registrations → 7 check-ins + await seedPlatformAnalytics(event3, 10, 8, 7, 7); + // HackRPI: 8 viewers → 6 form opens → 5 registrations → 0 check-ins (upcoming) + await seedPlatformAnalytics(event4, 8, 6, 5, 0); + // Annual Gala: 5 viewers → 4 form opens → 3 registrations → 0 check-ins (upcoming) + await seedPlatformAnalytics(event5, 5, 4, 3, 0); + // Study Session: 6 viewers → 0 form opens → 5 registrations → 5 check-ins + await seedPlatformAnalytics(event6, 6, 0, 5, 5); + + console.log('Created platform analytics events (funnel data)'); // ─── Registration Forms & Responses (for showcase event) ─── const regForm = await FormModel.create({ From b23fa8c6711fc3f1cd45d1a884bdf7b68ac31cad Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 19:18:33 +0000 Subject: [PATCH 6/7] Scale seed data to realistic event sizes and fix funnel unique counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Showcase: 250 expected → 178 registered, 142 checked in Funnel: 312 viewers → 245 form opens → 178 regs → 142 check-ins - Tech Talk: 80 expected → 62 registered, 54 checked in - Networking Mixer: 60 expected → 47 registered, 43 checked in - HackRPI (upcoming): 120 expected → 34 registered - Annual Gala (upcoming): 200 expected → 18 registered - Study Session: 20 expected → 15 registered, 13 checked in Generate synthetic ObjectIds for platform analytics events so unique user counts are accurate at scale (previously capped at 15 because of reusing the same 15 member users). Co-authored-by: James Liu --- scripts/seed-event-demo.js | 152 ++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 80 deletions(-) diff --git a/scripts/seed-event-demo.js b/scripts/seed-event-demo.js index dfa8b9d3..dc1e232d 100644 --- a/scripts/seed-event-demo.js +++ b/scripts/seed-event-demo.js @@ -361,20 +361,25 @@ async function main() { attendees: [], }); - // Populate attendees with realistic check-in data - const attendeePool = [...memberUsers]; - const event1Attendees = []; - for (let i = 0; i < 12; i++) { - const u = attendeePool[i % attendeePool.length]; - event1Attendees.push({ - userId: u._id, - registeredAt: new Date(daysAgo(14).getTime() - (30 - i) * 86400000), - guestCount: i < 3 ? 1 : 0, - checkedIn: i < 10, - checkedInAt: i < 10 ? new Date(daysAgo(14).getTime() + (i * 5 + 10) * 60000) : null, - }); + // Helper: generate N attendees with realistic registration spread + function generateAttendees(users, count, eventDate, spreadDays, checkedInPct) { + const attendees = []; + for (let i = 0; i < count; i++) { + const u = users[i % users.length]; + const isCheckedIn = i < Math.floor(count * checkedInPct); + attendees.push({ + userId: u._id, + registeredAt: new Date(eventDate.getTime() - (spreadDays - (i / count) * spreadDays) * 86400000), + guestCount: i % 7 === 0 ? 1 : 0, + checkedIn: isCheckedIn, + checkedInAt: isCheckedIn ? new Date(eventDate.getTime() + (i * 2 + 5) * 60000) : null, + }); + } + return attendees; } - await Event.updateOne({ _id: event1._id }, { $set: { attendees: event1Attendees, registrationCount: event1Attendees.length } }); + + const event1Attendees = generateAttendees(memberUsers, 178, daysAgo(14), 30, 0.80); + await Event.updateOne({ _id: event1._id }, { $set: { attendees: event1Attendees, registrationCount: 178 } }); // Event 2: Past - Tech Talk: Building Scalable Systems const event2 = await Event.create({ @@ -397,13 +402,8 @@ async function main() { isDeleted: false, createdAt: daysAgo(60), createdBy: memberUsers[1]._id, - attendees: memberUsers.slice(0, 8).map((u, i) => ({ - userId: u._id, - registeredAt: new Date(daysAgo(28).getTime() - (20 - i * 2) * 86400000), - checkedIn: i < 7, - checkedInAt: i < 7 ? new Date(daysAgo(28).getTime() + i * 3 * 60000) : null, - })), - registrationCount: 8, + attendees: generateAttendees(memberUsers, 62, daysAgo(28), 20, 0.87), + registrationCount: 62, }); // Event 3: Past - Startup Networking Mixer @@ -427,13 +427,8 @@ async function main() { isDeleted: false, createdAt: daysAgo(21), createdBy: adminUser._id, - attendees: memberUsers.slice(0, 7).map((u, i) => ({ - userId: u._id, - registeredAt: new Date(daysAgo(7).getTime() - (10 - i) * 86400000), - checkedIn: true, - checkedInAt: new Date(daysAgo(7).getTime() + i * 4 * 60000), - })), - registrationCount: 7, + attendees: generateAttendees(memberUsers, 47, daysAgo(7), 14, 0.91), + registrationCount: 47, }); // Event 4: Upcoming - HackRPI Kickoff Workshop @@ -459,11 +454,8 @@ async function main() { isDeleted: false, createdAt: daysAgo(10), createdBy: memberUsers[2]._id, - attendees: memberUsers.slice(0, 5).map((u, i) => ({ - userId: u._id, - registeredAt: daysAgo(10 - i), - })), - registrationCount: 5, + attendees: generateAttendees(memberUsers, 34, daysFromNow(5), 10, 0), + registrationCount: 34, }); // Event 5: Upcoming - Annual Gala @@ -488,11 +480,8 @@ async function main() { isDeleted: false, createdAt: daysAgo(3), createdBy: adminUser._id, - attendees: memberUsers.slice(0, 3).map((u, i) => ({ - userId: u._id, - registeredAt: daysAgo(3 - i), - })), - registrationCount: 3, + attendees: generateAttendees(memberUsers, 18, daysFromNow(21), 5, 0), + registrationCount: 18, }); // Event 6: Past - weekly study session (shows variety) @@ -514,13 +503,8 @@ async function main() { isDeleted: false, createdAt: daysAgo(10), createdBy: memberUsers[0]._id, - attendees: memberUsers.slice(5, 10).map((u, i) => ({ - userId: u._id, - registeredAt: daysAgo(5 - i), - checkedIn: true, - checkedInAt: new Date(daysAgo(3).getTime() + i * 2 * 60000), - })), - registrationCount: 5, + attendees: generateAttendees(memberUsers, 15, daysAgo(3), 7, 0.87), + registrationCount: 15, isStudySession: true, }); @@ -558,12 +542,12 @@ async function main() { } const analyticsEntries = [ - { eventId: event1._id, views: 847, uniqueViews: 423, anonymousViews: 312, uniqueAnonymousViews: 198, registrations: 89, uniqueRegistrations: 76, engagementRate: 42, eventDate: daysAgo(14), daysSpread: 30 }, - { eventId: event2._id, views: 312, uniqueViews: 198, anonymousViews: 87, uniqueAnonymousViews: 64, registrations: 45, uniqueRegistrations: 42, engagementRate: 38, eventDate: daysAgo(28), daysSpread: 20 }, - { eventId: event3._id, views: 256, uniqueViews: 167, anonymousViews: 64, uniqueAnonymousViews: 42, registrations: 38, uniqueRegistrations: 35, engagementRate: 35, eventDate: daysAgo(7), daysSpread: 14 }, - { eventId: event4._id, views: 189, uniqueViews: 134, anonymousViews: 45, uniqueAnonymousViews: 31, registrations: 28, uniqueRegistrations: 25, engagementRate: 31, eventDate: daysFromNow(5), daysSpread: 10 }, - { eventId: event5._id, views: 94, uniqueViews: 78, anonymousViews: 22, uniqueAnonymousViews: 18, registrations: 15, uniqueRegistrations: 14, engagementRate: 28, eventDate: daysFromNow(21), daysSpread: 5 }, - { eventId: event6._id, views: 67, uniqueViews: 45, anonymousViews: 12, uniqueAnonymousViews: 9, registrations: 8, uniqueRegistrations: 8, engagementRate: 22, eventDate: daysAgo(3), daysSpread: 7 }, + { eventId: event1._id, views: 2340, uniqueViews: 1180, anonymousViews: 870, uniqueAnonymousViews: 540, registrations: 178, uniqueRegistrations: 178, engagementRate: 45, eventDate: daysAgo(14), daysSpread: 30 }, + { eventId: event2._id, views: 680, uniqueViews: 410, anonymousViews: 195, uniqueAnonymousViews: 138, registrations: 62, uniqueRegistrations: 62, engagementRate: 38, eventDate: daysAgo(28), daysSpread: 20 }, + { eventId: event3._id, views: 520, uniqueViews: 320, anonymousViews: 145, uniqueAnonymousViews: 98, registrations: 47, uniqueRegistrations: 47, engagementRate: 35, eventDate: daysAgo(7), daysSpread: 14 }, + { eventId: event4._id, views: 390, uniqueViews: 268, anonymousViews: 92, uniqueAnonymousViews: 65, registrations: 34, uniqueRegistrations: 34, engagementRate: 31, eventDate: daysFromNow(5), daysSpread: 10 }, + { eventId: event5._id, views: 156, uniqueViews: 118, anonymousViews: 38, uniqueAnonymousViews: 30, registrations: 18, uniqueRegistrations: 18, engagementRate: 28, eventDate: daysFromNow(21), daysSpread: 5 }, + { eventId: event6._id, views: 112, uniqueViews: 74, anonymousViews: 22, uniqueAnonymousViews: 16, registrations: 15, uniqueRegistrations: 15, engagementRate: 22, eventDate: daysAgo(3), daysSpread: 7 }, ]; for (const a of analyticsEntries) { const viewHistory = generateViewHistory(a.eventDate, a.views, a.uniqueViews, a.anonymousViews, memberUsers, a.daysSpread); @@ -593,58 +577,65 @@ async function main() { const evId = String(eventDoc._id); const eventStart = eventDoc.start_time || daysAgo(14); const entries = []; - const viewerUsers = memberUsers.slice(0, Math.min(uniqueViewers, memberUsers.length)); - // event_view entries + // Generate synthetic user ObjectIds so unique counts are accurate + const syntheticUserIds = []; + for (let i = 0; i < Math.max(uniqueViewers, formOpens, registrations, checkins); i++) { + if (i < memberUsers.length) { + syntheticUserIds.push(memberUsers[i]._id); + } else { + syntheticUserIds.push(new mongoose.Types.ObjectId()); + } + } + + const sources = ['direct', 'explore', 'org_page', 'direct', 'explore']; + const referrers = ['events-dashboard', 'club-dashboard/RPI-Innovators-Hub', '']; + + // event_view entries (one per unique viewer) for (let i = 0; i < uniqueViewers; i++) { - const u = viewerUsers[i % viewerUsers.length]; - const sources = ['direct', 'explore', 'org_page', 'direct', 'explore']; entries.push({ ...seedMarker, event: 'event_view', - user_id: u._id, + user_id: syntheticUserIds[i], anonymous_id: null, ts: new Date(eventStart.getTime() - Math.random() * 20 * 86400000), properties: { event_id: evId, source: sources[i % sources.length] }, - context: { referrer: i % 3 === 0 ? 'events-dashboard' : i % 3 === 1 ? 'club-dashboard/RPI-Innovators-Hub' : '' }, + context: { referrer: referrers[i % referrers.length] }, }); } - // event_registration_form_open entries + // event_registration_form_open entries (one per unique opener) for (let i = 0; i < formOpens; i++) { - const u = viewerUsers[i % viewerUsers.length]; entries.push({ ...seedMarker, event: 'event_registration_form_open', - user_id: u._id, + user_id: syntheticUserIds[i], anonymous_id: null, ts: new Date(eventStart.getTime() - Math.random() * 15 * 86400000), properties: { event_id: evId }, }); } - // event_registration entries + // event_registration entries (one per unique registrant) for (let i = 0; i < registrations; i++) { - const u = viewerUsers[i % viewerUsers.length]; entries.push({ ...seedMarker, event: 'event_registration', - user_id: u._id, + user_id: syntheticUserIds[i], anonymous_id: null, ts: new Date(eventStart.getTime() - Math.random() * 10 * 86400000), properties: { event_id: evId }, }); } - // event_checkin entries + // event_checkin entries (one per unique check-in) for (let i = 0; i < checkins; i++) { - const u = viewerUsers[i % viewerUsers.length]; entries.push({ ...seedMarker, event: 'event_checkin', - user_id: u._id, + user_id: syntheticUserIds[i], anonymous_id: null, - ts: new Date(eventStart.getTime() + i * 5 * 60000), + ts: new Date(eventStart.getTime() + i * 2 * 60000), properties: { event_id: evId }, }); } @@ -654,18 +645,18 @@ async function main() { } } - // Showcase: 15 unique viewers → 12 form opens → 12 registrations → 10 check-ins - await seedPlatformAnalytics(event1, 15, 12, 12, 10); - // Tech Talk: 12 viewers → 10 form opens → 8 registrations → 7 check-ins - await seedPlatformAnalytics(event2, 12, 10, 8, 7); - // Networking Mixer: 10 viewers → 8 form opens → 7 registrations → 7 check-ins - await seedPlatformAnalytics(event3, 10, 8, 7, 7); - // HackRPI: 8 viewers → 6 form opens → 5 registrations → 0 check-ins (upcoming) - await seedPlatformAnalytics(event4, 8, 6, 5, 0); - // Annual Gala: 5 viewers → 4 form opens → 3 registrations → 0 check-ins (upcoming) - await seedPlatformAnalytics(event5, 5, 4, 3, 0); - // Study Session: 6 viewers → 0 form opens → 5 registrations → 5 check-ins - await seedPlatformAnalytics(event6, 6, 0, 5, 5); + // Showcase: 312 unique viewers → 245 form opens → 178 registrations → 142 check-ins + await seedPlatformAnalytics(event1, 312, 245, 178, 142); + // Tech Talk: 156 viewers → 98 form opens → 62 registrations → 54 check-ins + await seedPlatformAnalytics(event2, 156, 98, 62, 54); + // Networking Mixer: 128 viewers → 82 form opens → 47 registrations → 43 check-ins + await seedPlatformAnalytics(event3, 128, 82, 47, 43); + // HackRPI: 94 viewers → 58 form opens → 34 registrations → 0 check-ins (upcoming) + await seedPlatformAnalytics(event4, 94, 58, 34, 0); + // Annual Gala: 52 viewers → 28 form opens → 18 registrations → 0 check-ins (upcoming) + await seedPlatformAnalytics(event5, 52, 28, 18, 0); + // Study Session: 38 viewers → 0 form opens → 15 registrations → 13 check-ins + await seedPlatformAnalytics(event6, 38, 0, 15, 13); console.log('Created platform analytics events (funnel data)'); @@ -696,7 +687,8 @@ async function main() { 'Exploring new technologies and frameworks', 'The potential to create something impactful', ]; - for (let i = 0; i < 12; i++) { + const formResponseCount = 178; + for (let i = 0; i < formResponseCount; i++) { const u = memberUsers[i % memberUsers.length]; await FormResponse.create({ ...seedMarker, @@ -713,7 +705,7 @@ async function main() { submittedAt: new Date(daysAgo(14).getTime() - (30 - i) * 86400000), }); } - console.log('Created registration form + 12 responses'); + console.log(`Created registration form + ${formResponseCount} responses`); // ─── Event Agenda (for showcase event) ─── const showcaseAgenda = await EventAgenda.create({ From 95cdccba820ac7b60bd2063ecf301786aebcb047 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 7 May 2026 06:13:11 +0000 Subject: [PATCH 7/7] Make showcase upcoming, add exponential registration curve, amplify funnel drop-off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Spring Innovation Showcase is now 8 days in the future (upcoming event) - Registration dates follow an exponential curve with day-to-day noise: slow trickle early on, compounding toward the event (power-law t^2.2 with ±40% variance, occasional spikes and dips) - Funnel views increased to 847 unique viewers vs 312 form opens (63% drop-off) vs 178 registrations — clear narrowing at each stage - All analytics timestamps capped to now (future-dated events no longer produce future timestamps that get filtered by the 90d query window) - Event jobs/volunteers/agenda times updated to match new event date Co-authored-by: James Liu --- scripts/seed-event-demo.js | 245 +++++++++++++++++++++++-------------- 1 file changed, 150 insertions(+), 95 deletions(-) diff --git a/scripts/seed-event-demo.js b/scripts/seed-event-demo.js index dc1e232d..3e73ccda 100644 --- a/scripts/seed-event-demo.js +++ b/scripts/seed-event-demo.js @@ -332,9 +332,67 @@ async function main() { const daysFromNow = (d) => new Date(now.getTime() + d * 86400000); const daysAgo = (d) => new Date(now.getTime() - d * 86400000); + // Helper: generate N attendees with exponential registration curve + noise + // Registrations compound as the event date approaches, with day-to-day variance + function generateAttendees(users, count, eventDate, createdDate, checkedInPct) { + const attendees = []; + const spreadMs = eventDate.getTime() - createdDate.getTime(); + const cutoff = Math.min(eventDate.getTime(), now.getTime()); + const spreadDays = Math.max(1, Math.floor((cutoff - createdDate.getTime()) / 86400000)); + + // Build exponential distribution: weight each day so later days get more registrations + const dayWeights = []; + for (let d = 0; d < spreadDays; d++) { + const t = d / spreadDays; + // Exponential ramp: slow start, accelerating toward event + const base = Math.pow(t, 2.2) * 3 + 0.15; + // Add noise: ±40% variance, occasional spikes and dips + const noise = 0.6 + Math.random() * 0.8; + const spike = Math.random() < 0.08 ? 1.8 + Math.random() : 1.0; + const dip = Math.random() < 0.06 ? 0.3 : 1.0; + dayWeights.push(base * noise * spike * dip); + } + const totalWeight = dayWeights.reduce((a, b) => a + b, 0); + + // Assign registration counts per day + const regsPerDay = dayWeights.map(w => Math.max(0, Math.round((w / totalWeight) * count))); + // Adjust to hit exact count + let assigned = regsPerDay.reduce((a, b) => a + b, 0); + while (assigned < count) { regsPerDay[regsPerDay.length - 1]++; assigned++; } + while (assigned > count) { + const idx = regsPerDay.findIndex(v => v > 0); + if (idx >= 0) { regsPerDay[idx]--; assigned--; } + } + + let idx = 0; + for (let d = 0; d < spreadDays; d++) { + const dayStart = new Date(createdDate.getTime() + d * 86400000); + for (let r = 0; r < regsPerDay[d]; r++) { + const u = users[idx % users.length]; + const hourOffset = Math.random() * 86400000; + const regTime = new Date(dayStart.getTime() + hourOffset); + const isCheckedIn = idx < Math.floor(count * checkedInPct); + attendees.push({ + userId: u._id, + registeredAt: regTime, + guestCount: idx % 11 === 0 ? 1 : 0, + checkedIn: isCheckedIn, + checkedInAt: isCheckedIn ? new Date(eventDate.getTime() + (idx * 2 + 5) * 60000) : null, + }); + idx++; + } + } + + // Sort by registration date + attendees.sort((a, b) => a.registeredAt - b.registeredAt); + return attendees; + } + // ─── EVENTS ─── - // Event 1: Past - Spring Innovation Showcase (flagship event, lots of data) + // Event 1: UPCOMING - Spring Innovation Showcase (flagship, 8 days out) + const event1Created = daysAgo(28); + const event1Start = daysFromNow(8); const event1 = await Event.create({ ...seedMarker, name: 'Spring Innovation Showcase 2026', @@ -342,8 +400,8 @@ async function main() { type: 'showcase', hostingId: org._id, hostingType: 'Org', - start_time: daysAgo(14), - end_time: new Date(daysAgo(14).getTime() + 5 * 3600000), + start_time: event1Start, + end_time: new Date(event1Start.getTime() + 5 * 3600000), location: 'EMPAC Concert Hall', classroom_id: empacRoom._id, status: 'approved', @@ -355,33 +413,18 @@ async function main() { checkInEnabled: true, checkInToken: 'showcase2026', isDeleted: false, - createdAt: daysAgo(45), + createdAt: event1Created, createdBy: adminUser._id, contact: 'innovators@rpi.edu', attendees: [], }); - // Helper: generate N attendees with realistic registration spread - function generateAttendees(users, count, eventDate, spreadDays, checkedInPct) { - const attendees = []; - for (let i = 0; i < count; i++) { - const u = users[i % users.length]; - const isCheckedIn = i < Math.floor(count * checkedInPct); - attendees.push({ - userId: u._id, - registeredAt: new Date(eventDate.getTime() - (spreadDays - (i / count) * spreadDays) * 86400000), - guestCount: i % 7 === 0 ? 1 : 0, - checkedIn: isCheckedIn, - checkedInAt: isCheckedIn ? new Date(eventDate.getTime() + (i * 2 + 5) * 60000) : null, - }); - } - return attendees; - } - - const event1Attendees = generateAttendees(memberUsers, 178, daysAgo(14), 30, 0.80); + const event1Attendees = generateAttendees(memberUsers, 178, event1Start, event1Created, 0); await Event.updateOne({ _id: event1._id }, { $set: { attendees: event1Attendees, registrationCount: 178 } }); // Event 2: Past - Tech Talk: Building Scalable Systems + const event2Created = daysAgo(45); + const event2Start = daysAgo(14); const event2 = await Event.create({ ...seedMarker, name: 'Tech Talk: Building Scalable Systems', @@ -389,8 +432,8 @@ async function main() { type: 'talk', hostingId: org._id, hostingType: 'Org', - start_time: daysAgo(28), - end_time: new Date(daysAgo(28).getTime() + 2 * 3600000), + start_time: event2Start, + end_time: new Date(event2Start.getTime() + 2 * 3600000), location: 'McNeil Room, Rensselaer Union', classroom_id: unionRoom._id, status: 'approved', @@ -400,13 +443,15 @@ async function main() { rsvpEnabled: true, checkInEnabled: true, isDeleted: false, - createdAt: daysAgo(60), + createdAt: event2Created, createdBy: memberUsers[1]._id, - attendees: generateAttendees(memberUsers, 62, daysAgo(28), 20, 0.87), + attendees: generateAttendees(memberUsers, 62, event2Start, event2Created, 0.87), registrationCount: 62, }); // Event 3: Past - Startup Networking Mixer + const event3Created = daysAgo(21); + const event3Start = daysAgo(5); const event3 = await Event.create({ ...seedMarker, name: 'Startup Networking Mixer', @@ -414,8 +459,8 @@ async function main() { type: 'social', hostingId: org._id, hostingType: 'Org', - start_time: daysAgo(7), - end_time: new Date(daysAgo(7).getTime() + 3 * 3600000), + start_time: event3Start, + end_time: new Date(event3Start.getTime() + 3 * 3600000), location: 'McNeil Room, Rensselaer Union', classroom_id: unionRoom._id, status: 'approved', @@ -425,13 +470,15 @@ async function main() { rsvpEnabled: true, checkInEnabled: true, isDeleted: false, - createdAt: daysAgo(21), + createdAt: event3Created, createdBy: adminUser._id, - attendees: generateAttendees(memberUsers, 47, daysAgo(7), 14, 0.91), + attendees: generateAttendees(memberUsers, 47, event3Start, event3Created, 0.91), registrationCount: 47, }); // Event 4: Upcoming - HackRPI Kickoff Workshop + const event4Created = daysAgo(14); + const event4Start = daysFromNow(12); const event4 = await Event.create({ ...seedMarker, name: 'HackRPI Kickoff: Intro to Full-Stack Development', @@ -439,8 +486,8 @@ async function main() { type: 'workshop', hostingId: org._id, hostingType: 'Org', - start_time: daysFromNow(5), - end_time: new Date(daysFromNow(5).getTime() + 3 * 3600000), + start_time: event4Start, + end_time: new Date(event4Start.getTime() + 3 * 3600000), location: 'EMPAC Concert Hall', classroom_id: empacRoom._id, status: 'approved', @@ -452,13 +499,15 @@ async function main() { maxAttendees: 120, checkInEnabled: false, isDeleted: false, - createdAt: daysAgo(10), + createdAt: event4Created, createdBy: memberUsers[2]._id, - attendees: generateAttendees(memberUsers, 34, daysFromNow(5), 10, 0), + attendees: generateAttendees(memberUsers, 34, event4Start, event4Created, 0), registrationCount: 34, }); // Event 5: Upcoming - Annual Gala + const event5Created = daysAgo(7); + const event5Start = daysFromNow(28); const event5 = await Event.create({ ...seedMarker, name: 'Innovators Hub Annual Gala & Awards', @@ -466,8 +515,8 @@ async function main() { type: 'gala', hostingId: org._id, hostingType: 'Org', - start_time: daysFromNow(21), - end_time: new Date(daysFromNow(21).getTime() + 4 * 3600000), + start_time: event5Start, + end_time: new Date(event5Start.getTime() + 4 * 3600000), location: 'EMPAC Concert Hall', classroom_id: empacRoom._id, status: 'approved', @@ -478,13 +527,15 @@ async function main() { rsvpRequired: true, maxAttendees: 200, isDeleted: false, - createdAt: daysAgo(3), + createdAt: event5Created, createdBy: adminUser._id, - attendees: generateAttendees(memberUsers, 18, daysFromNow(21), 5, 0), + attendees: generateAttendees(memberUsers, 18, event5Start, event5Created, 0), registrationCount: 18, }); // Event 6: Past - weekly study session (shows variety) + const event6Created = daysAgo(10); + const event6Start = daysAgo(2); const event6 = await Event.create({ ...seedMarker, name: 'Weekly Code Review & Study Session', @@ -492,8 +543,8 @@ async function main() { type: 'study', hostingId: org._id, hostingType: 'Org', - start_time: daysAgo(3), - end_time: new Date(daysAgo(3).getTime() + 2 * 3600000), + start_time: event6Start, + end_time: new Date(event6Start.getTime() + 2 * 3600000), location: 'McNeil Room, Rensselaer Union', classroom_id: unionRoom._id, status: 'approved', @@ -501,9 +552,9 @@ async function main() { expectedAttendance: 20, going: memberUsers.slice(5, 10).map(u => u._id), isDeleted: false, - createdAt: daysAgo(10), + createdAt: event6Created, createdBy: memberUsers[0]._id, - attendees: generateAttendees(memberUsers, 15, daysAgo(3), 7, 0.87), + attendees: generateAttendees(memberUsers, 15, event6Start, event6Created, 0.87), registrationCount: 15, isStudySession: true, }); @@ -542,12 +593,12 @@ async function main() { } const analyticsEntries = [ - { eventId: event1._id, views: 2340, uniqueViews: 1180, anonymousViews: 870, uniqueAnonymousViews: 540, registrations: 178, uniqueRegistrations: 178, engagementRate: 45, eventDate: daysAgo(14), daysSpread: 30 }, - { eventId: event2._id, views: 680, uniqueViews: 410, anonymousViews: 195, uniqueAnonymousViews: 138, registrations: 62, uniqueRegistrations: 62, engagementRate: 38, eventDate: daysAgo(28), daysSpread: 20 }, - { eventId: event3._id, views: 520, uniqueViews: 320, anonymousViews: 145, uniqueAnonymousViews: 98, registrations: 47, uniqueRegistrations: 47, engagementRate: 35, eventDate: daysAgo(7), daysSpread: 14 }, - { eventId: event4._id, views: 390, uniqueViews: 268, anonymousViews: 92, uniqueAnonymousViews: 65, registrations: 34, uniqueRegistrations: 34, engagementRate: 31, eventDate: daysFromNow(5), daysSpread: 10 }, - { eventId: event5._id, views: 156, uniqueViews: 118, anonymousViews: 38, uniqueAnonymousViews: 30, registrations: 18, uniqueRegistrations: 18, engagementRate: 28, eventDate: daysFromNow(21), daysSpread: 5 }, - { eventId: event6._id, views: 112, uniqueViews: 74, anonymousViews: 22, uniqueAnonymousViews: 16, registrations: 15, uniqueRegistrations: 15, engagementRate: 22, eventDate: daysAgo(3), daysSpread: 7 }, + { eventId: event1._id, views: 3850, uniqueViews: 1920, anonymousViews: 1430, uniqueAnonymousViews: 890, registrations: 178, uniqueRegistrations: 178, engagementRate: 45, eventDate: event1Start, daysSpread: 28 }, + { eventId: event2._id, views: 980, uniqueViews: 580, anonymousViews: 310, uniqueAnonymousViews: 215, registrations: 62, uniqueRegistrations: 62, engagementRate: 38, eventDate: event2Start, daysSpread: 30 }, + { eventId: event3._id, views: 720, uniqueViews: 430, anonymousViews: 210, uniqueAnonymousViews: 145, registrations: 47, uniqueRegistrations: 47, engagementRate: 35, eventDate: event3Start, daysSpread: 16 }, + { eventId: event4._id, views: 540, uniqueViews: 365, anonymousViews: 128, uniqueAnonymousViews: 90, registrations: 34, uniqueRegistrations: 34, engagementRate: 31, eventDate: event4Start, daysSpread: 14 }, + { eventId: event5._id, views: 210, uniqueViews: 158, anonymousViews: 52, uniqueAnonymousViews: 40, registrations: 18, uniqueRegistrations: 18, engagementRate: 28, eventDate: event5Start, daysSpread: 7 }, + { eventId: event6._id, views: 145, uniqueViews: 96, anonymousViews: 34, uniqueAnonymousViews: 24, registrations: 15, uniqueRegistrations: 15, engagementRate: 22, eventDate: event6Start, daysSpread: 8 }, ]; for (const a of analyticsEntries) { const viewHistory = generateViewHistory(a.eventDate, a.views, a.uniqueViews, a.anonymousViews, memberUsers, a.daysSpread); @@ -591,14 +642,18 @@ async function main() { const sources = ['direct', 'explore', 'org_page', 'direct', 'explore']; const referrers = ['events-dashboard', 'club-dashboard/RPI-Innovators-Hub', '']; - // event_view entries (one per unique viewer) + // Cap timestamps to now (analytics events shouldn't be in the future) + const tsNow = now.getTime(); + const cap = (ts) => new Date(Math.min(ts, tsNow - 60000)); + + // event_view entries (one per unique viewer) — spread over 25 days leading up to now for (let i = 0; i < uniqueViewers; i++) { entries.push({ ...seedMarker, event: 'event_view', user_id: syntheticUserIds[i], anonymous_id: null, - ts: new Date(eventStart.getTime() - Math.random() * 20 * 86400000), + ts: cap(eventStart.getTime() - Math.random() * 25 * 86400000), properties: { event_id: evId, source: sources[i % sources.length] }, context: { referrer: referrers[i % referrers.length] }, }); @@ -611,7 +666,7 @@ async function main() { event: 'event_registration_form_open', user_id: syntheticUserIds[i], anonymous_id: null, - ts: new Date(eventStart.getTime() - Math.random() * 15 * 86400000), + ts: cap(eventStart.getTime() - Math.random() * 20 * 86400000), properties: { event_id: evId }, }); } @@ -623,19 +678,19 @@ async function main() { event: 'event_registration', user_id: syntheticUserIds[i], anonymous_id: null, - ts: new Date(eventStart.getTime() - Math.random() * 10 * 86400000), + ts: cap(eventStart.getTime() - Math.random() * 15 * 86400000), properties: { event_id: evId }, }); } - // event_checkin entries (one per unique check-in) + // event_checkin entries (one per unique check-in) — only for past events for (let i = 0; i < checkins; i++) { entries.push({ ...seedMarker, event: 'event_checkin', user_id: syntheticUserIds[i], anonymous_id: null, - ts: new Date(eventStart.getTime() + i * 2 * 60000), + ts: cap(eventStart.getTime() + i * 2 * 60000), properties: { event_id: evId }, }); } @@ -645,18 +700,18 @@ async function main() { } } - // Showcase: 312 unique viewers → 245 form opens → 178 registrations → 142 check-ins - await seedPlatformAnalytics(event1, 312, 245, 178, 142); - // Tech Talk: 156 viewers → 98 form opens → 62 registrations → 54 check-ins - await seedPlatformAnalytics(event2, 156, 98, 62, 54); - // Networking Mixer: 128 viewers → 82 form opens → 47 registrations → 43 check-ins - await seedPlatformAnalytics(event3, 128, 82, 47, 43); - // HackRPI: 94 viewers → 58 form opens → 34 registrations → 0 check-ins (upcoming) - await seedPlatformAnalytics(event4, 94, 58, 34, 0); - // Annual Gala: 52 viewers → 28 form opens → 18 registrations → 0 check-ins (upcoming) - await seedPlatformAnalytics(event5, 52, 28, 18, 0); - // Study Session: 38 viewers → 0 form opens → 15 registrations → 13 check-ins - await seedPlatformAnalytics(event6, 38, 0, 15, 13); + // Showcase (upcoming): 847 viewers → 312 form opens → 178 registrations → 0 check-ins + await seedPlatformAnalytics(event1, 847, 312, 178, 0); + // Tech Talk (past): 385 viewers → 128 form opens → 62 registrations → 54 check-ins + await seedPlatformAnalytics(event2, 385, 128, 62, 54); + // Networking Mixer (past): 296 viewers → 105 form opens → 47 registrations → 43 check-ins + await seedPlatformAnalytics(event3, 296, 105, 47, 43); + // HackRPI (upcoming): 218 viewers → 72 form opens → 34 registrations → 0 check-ins + await seedPlatformAnalytics(event4, 218, 72, 34, 0); + // Annual Gala (upcoming): 94 viewers → 35 form opens → 18 registrations → 0 check-ins + await seedPlatformAnalytics(event5, 94, 35, 18, 0); + // Study Session (past): 58 viewers → 0 form opens → 15 registrations → 13 check-ins + await seedPlatformAnalytics(event6, 58, 0, 15, 13); console.log('Created platform analytics events (funnel data)'); @@ -702,7 +757,7 @@ async function main() { { label: 'Dietary Restrictions', value: i % 4 === 0 ? 'Vegetarian' : '' }, { label: 'What excites you most about innovation?', value: excitementAnswers[i % excitementAnswers.length] }, ], - submittedAt: new Date(daysAgo(14).getTime() - (30 - i) * 86400000), + submittedAt: new Date(event1Start.getTime() - (28 - (i / formResponseCount) * 28) * 86400000), }); } console.log(`Created registration form + ${formResponseCount} responses`); @@ -713,13 +768,13 @@ async function main() { eventId: event1._id, orgId: org._id, items: [ - { id: 'a1', title: 'Registration & Welcome Coffee', type: 'Setup', startTime: daysAgo(14), endTime: new Date(daysAgo(14).getTime() + 30 * 60000), location: 'Main Lobby', isPublic: true, order: 0 }, - { id: 'a2', title: 'Opening Remarks', description: 'Welcome by Jordan Rivera, President', type: 'Speaker', startTime: new Date(daysAgo(14).getTime() + 30 * 60000), endTime: new Date(daysAgo(14).getTime() + 45 * 60000), location: 'Main Stage', isPublic: true, order: 1 }, - { id: 'a3', title: 'Keynote: The Future of AI in Education', description: 'Dr. Sarah Mitchell, Google DeepMind', type: 'Speaker', startTime: new Date(daysAgo(14).getTime() + 45 * 60000), endTime: new Date(daysAgo(14).getTime() + 105 * 60000), location: 'Main Stage', isPublic: true, order: 2 }, - { id: 'a4', title: 'Coffee Break & Demo Setup', type: 'Break', startTime: new Date(daysAgo(14).getTime() + 105 * 60000), endTime: new Date(daysAgo(14).getTime() + 120 * 60000), isPublic: true, order: 3 }, - { id: 'a5', title: 'Student Startup Pitches (Round 1)', description: '5 teams, 5 minutes each + Q&A', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 120 * 60000), endTime: new Date(daysAgo(14).getTime() + 180 * 60000), location: 'Main Stage', isPublic: true, order: 4 }, - { id: 'a6', title: 'Interactive Demo Fair', description: 'Hands-on demos from 12 student projects', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 180 * 60000), endTime: new Date(daysAgo(14).getTime() + 240 * 60000), location: 'Exhibition Hall', isPublic: true, order: 5 }, - { id: 'a7', title: 'Networking Reception & Awards', type: 'Activity', startTime: new Date(daysAgo(14).getTime() + 240 * 60000), endTime: new Date(daysAgo(14).getTime() + 300 * 60000), location: 'Lobby', isPublic: true, order: 6 }, + { id: 'a1', title: 'Registration & Welcome Coffee', type: 'Setup', startTime: event1Start, endTime: new Date(event1Start.getTime() + 30 * 60000), location: 'Main Lobby', isPublic: true, order: 0 }, + { id: 'a2', title: 'Opening Remarks', description: 'Welcome by Jordan Rivera, President', type: 'Speaker', startTime: new Date(event1Start.getTime() + 30 * 60000), endTime: new Date(event1Start.getTime() + 45 * 60000), location: 'Main Stage', isPublic: true, order: 1 }, + { id: 'a3', title: 'Keynote: The Future of AI in Education', description: 'Dr. Sarah Mitchell, Google DeepMind', type: 'Speaker', startTime: new Date(event1Start.getTime() + 45 * 60000), endTime: new Date(event1Start.getTime() + 105 * 60000), location: 'Main Stage', isPublic: true, order: 2 }, + { id: 'a4', title: 'Coffee Break & Demo Setup', type: 'Break', startTime: new Date(event1Start.getTime() + 105 * 60000), endTime: new Date(event1Start.getTime() + 120 * 60000), isPublic: true, order: 3 }, + { id: 'a5', title: 'Student Startup Pitches (Round 1)', description: '5 teams, 5 minutes each + Q&A', type: 'Activity', startTime: new Date(event1Start.getTime() + 120 * 60000), endTime: new Date(event1Start.getTime() + 180 * 60000), location: 'Main Stage', isPublic: true, order: 4 }, + { id: 'a6', title: 'Interactive Demo Fair', description: 'Hands-on demos from 12 student projects', type: 'Activity', startTime: new Date(event1Start.getTime() + 180 * 60000), endTime: new Date(event1Start.getTime() + 240 * 60000), location: 'Exhibition Hall', isPublic: true, order: 5 }, + { id: 'a7', title: 'Networking Reception & Awards', type: 'Activity', startTime: new Date(event1Start.getTime() + 240 * 60000), endTime: new Date(event1Start.getTime() + 300 * 60000), location: 'Lobby', isPublic: true, order: 6 }, ], publicNotes: 'All attendees welcome. Refreshments provided.', internalNotes: 'AV setup must be complete by 8:30am. Catering arrives at 8:00am.', @@ -736,11 +791,11 @@ async function main() { name: 'Stage Manager', description: 'Coordinate speaker transitions, manage timing, and oversee AV cues', requiredCount: 2, - shiftStart: daysAgo(14), - shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + shiftStart: event1Start, + shiftEnd: new Date(event1Start.getTime() + 5 * 3600000), assignments: [ - { memberId: memberUsers[0]._id, status: 'confirmed', assignedAt: daysAgo(20), confirmedAt: daysAgo(18) }, - { memberId: memberUsers[1]._id, status: 'confirmed', assignedAt: daysAgo(20), confirmedAt: daysAgo(17) }, + { memberId: memberUsers[0]._id, status: 'confirmed', assignedAt: daysAgo(10), confirmedAt: daysAgo(8) }, + { memberId: memberUsers[1]._id, status: 'confirmed', assignedAt: daysAgo(10), confirmedAt: daysAgo(7) }, ], }); @@ -752,10 +807,10 @@ async function main() { name: 'AV Technician', description: 'Set up projectors, microphones, and manage livestream', requiredCount: 2, - shiftStart: new Date(daysAgo(14).getTime() - 1 * 3600000), - shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + shiftStart: new Date(event1Start.getTime() - 1 * 3600000), + shiftEnd: new Date(event1Start.getTime() + 5 * 3600000), assignments: [ - { memberId: memberUsers[6]._id, status: 'confirmed', assignedAt: daysAgo(19), confirmedAt: daysAgo(16) }, + { memberId: memberUsers[6]._id, status: 'confirmed', assignedAt: daysAgo(9), confirmedAt: daysAgo(6) }, ], }); @@ -767,23 +822,23 @@ async function main() { name: 'Registration Desk', description: 'Welcome attendees, manage check-in process, hand out name badges', requiredCount: 3, - shiftStart: daysAgo(14), - shiftEnd: new Date(daysAgo(14).getTime() + 2 * 3600000), + shiftStart: event1Start, + shiftEnd: new Date(event1Start.getTime() + 2 * 3600000), assignments: [ - { memberId: memberUsers[3]._id, status: 'confirmed', assignedAt: daysAgo(18), confirmedAt: daysAgo(15) }, - { memberId: memberUsers[4]._id, status: 'confirmed', assignedAt: daysAgo(18), confirmedAt: daysAgo(14) }, - { memberId: memberUsers[7]._id, status: 'assigned', assignedAt: daysAgo(16) }, + { memberId: memberUsers[3]._id, status: 'confirmed', assignedAt: daysAgo(8), confirmedAt: daysAgo(5) }, + { memberId: memberUsers[4]._id, status: 'confirmed', assignedAt: daysAgo(8), confirmedAt: daysAgo(4) }, + { memberId: memberUsers[7]._id, status: 'assigned', assignedAt: daysAgo(6) }, ], }); console.log('Created event jobs (roles + assignments)'); // ─── Volunteer Signups ─── const volunteerData = [ - { memberId: memberUsers[0]._id, roleId: jobStageManager._id, checkedIn: true, checkedOut: true }, - { memberId: memberUsers[1]._id, roleId: jobStageManager._id, checkedIn: true, checkedOut: true }, - { memberId: memberUsers[6]._id, roleId: jobAV._id, checkedIn: true, checkedOut: true }, - { memberId: memberUsers[3]._id, roleId: jobGreeter._id, checkedIn: true, checkedOut: false }, - { memberId: memberUsers[4]._id, roleId: jobGreeter._id, checkedIn: true, checkedOut: false }, + { memberId: memberUsers[0]._id, roleId: jobStageManager._id, checkedIn: false, checkedOut: false }, + { memberId: memberUsers[1]._id, roleId: jobStageManager._id, checkedIn: false, checkedOut: false }, + { memberId: memberUsers[6]._id, roleId: jobAV._id, checkedIn: false, checkedOut: false }, + { memberId: memberUsers[3]._id, roleId: jobGreeter._id, checkedIn: false, checkedOut: false }, + { memberId: memberUsers[4]._id, roleId: jobGreeter._id, checkedIn: false, checkedOut: false }, ]; for (const v of volunteerData) { await VolunteerSignup.create({ @@ -791,13 +846,13 @@ async function main() { eventId: event1._id, memberId: v.memberId, roleId: v.roleId, - shiftStart: daysAgo(14), - shiftEnd: new Date(daysAgo(14).getTime() + 5 * 3600000), + shiftStart: event1Start, + shiftEnd: new Date(event1Start.getTime() + 5 * 3600000), status: 'approved', checkedIn: v.checkedIn, - checkedInAt: v.checkedIn ? daysAgo(14) : null, + checkedInAt: null, checkedOut: v.checkedOut, - checkedOutAt: v.checkedOut ? new Date(daysAgo(14).getTime() + 5 * 3600000) : null, + checkedOutAt: null, }); } console.log('Created volunteer signups');