From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 17 Apr 2024 12:27:21 -0400 Subject: restoring eslint - updates not complete yet --- src/server/authentication/AuthenticationManager.ts | 2 +- src/server/authentication/DashUserModel.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/server/authentication') diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts index 9c1525df0..b5d1dba28 100644 --- a/src/server/authentication/AuthenticationManager.ts +++ b/src/server/authentication/AuthenticationManager.ts @@ -6,7 +6,7 @@ import './Passport'; import * as async from 'async'; import * as nodemailer from 'nodemailer'; import * as c from 'crypto'; -import { emptyFunction, Utils } from '../../Utils'; +import { emptyFunction, Utils } from '../../ClientUtils'; import { MailOptions } from 'nodemailer/lib/stream-transport'; import { check, validationResult } from 'express-validator'; diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts index dbb7a79ed..3bc21ecb6 100644 --- a/src/server/authentication/DashUserModel.ts +++ b/src/server/authentication/DashUserModel.ts @@ -1,9 +1,8 @@ -//@ts-ignore import * as bcrypt from 'bcrypt-nodejs'; -//@ts-ignore import * as mongoose from 'mongoose'; import { Utils } from '../../Utils'; +type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => void) => void; export type DashUserModel = mongoose.Document & { email: String; password: string; @@ -26,8 +25,6 @@ export type DashUserModel = mongoose.Document & { comparePassword: comparePasswordFunction; }; -type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => void) => void; - export type AuthToken = { accessToken: string; kind: string; @@ -75,16 +72,19 @@ userSchema.pre('save', function save(next) { bcrypt.hash( user.password, salt, - () => void {}, + () => {}, (err: mongoose.Error, hash: string) => { if (err) { return next(err); } user.password = hash; next(); + return undefined; } ); + return undefined; }); + return undefined; }); const comparePassword: comparePasswordFunction = function (this: DashUserModel, candidatePassword, cb) { -- cgit v1.2.3-70-g09d2 From ec859c33f69d586f287aecdceeca38c4e77cb0ab Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 19 Apr 2024 12:11:16 -0400 Subject: lint errors --- src/Utils.ts | 6 +- src/client/views/MainView.tsx | 3 +- src/server/authentication/AuthenticationManager.ts | 109 +++++++++++---------- 3 files changed, 61 insertions(+), 57 deletions(-) (limited to 'src/server/authentication') diff --git a/src/Utils.ts b/src/Utils.ts index 0455fd19a..c87ef052c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -43,15 +43,15 @@ export namespace Utils { export function TraceConsoleLog() { ['log', 'warn'].forEach(method => { const old = (console as any)[method]; - (console as any)[method] = function () { + (console as any)[method] = function (...args: any[]) { let stack = new Error('').stack?.split(/\n/); // Chrome includes a single "Error" line, FF doesn't. if (stack && stack[0].indexOf('Error') === 0) { stack = stack.slice(1); } const message = (stack?.[1] || 'Stack undefined!').trim(); - const args = ([] as any[]).slice.apply(arguments).concat([message]); - return old.apply(console, args); + const newArgs = args.slice().concat([message]); + return old.apply(console, newArgs); }; }); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 13945cacf..b0156846f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,7 +8,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; -import { emptyFunction } from '../../Utils'; +import { Utils, emptyFunction } from '../../Utils'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { DocCast, StrCast } from '../../fields/Types'; @@ -163,6 +163,7 @@ export class MainView extends ObservableReactComponent<{}> { mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); componentDidMount() { + // Utils.TraceConsoleLog(); reaction( // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection () => SelectionManager.Views.slice(), diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts index b5d1dba28..0cc1553c0 100644 --- a/src/server/authentication/AuthenticationManager.ts +++ b/src/server/authentication/AuthenticationManager.ts @@ -1,21 +1,21 @@ -import { default as User, DashUserModel, initializeGuest } from './DashUserModel'; -import { Request, Response, NextFunction } from 'express'; -import * as passport from 'passport'; -import { IVerifyOptions } from 'passport-local'; -import './Passport'; import * as async from 'async'; -import * as nodemailer from 'nodemailer'; import * as c from 'crypto'; -import { emptyFunction, Utils } from '../../ClientUtils'; -import { MailOptions } from 'nodemailer/lib/stream-transport'; +import { NextFunction, Request, Response } from 'express'; import { check, validationResult } from 'express-validator'; +import * as nodemailer from 'nodemailer'; +import { MailOptions } from 'nodemailer/lib/stream-transport'; +import * as passport from 'passport'; +import { Utils } from '../../Utils'; +import User, { DashUserModel, initializeGuest } from './DashUserModel'; +import './Passport'; +// import { IVerifyOptions } from 'passport-local'; /** * GET /signup * Directs user to the signup page * modeled by signup.pug in views */ -export let getSignup = (req: Request, res: Response) => { +export const getSignup = (req: Request, res: Response) => { if (req.user) { return res.redirect('/home'); } @@ -23,13 +23,23 @@ export let getSignup = (req: Request, res: Response) => { title: 'Sign Up', user: req.user, }); + return undefined; +}; + +const tryRedirectToTarget = (req: Request, res: Response) => { + const target = (req.session as any)?.target; + if (req.session && target) { + res.redirect(target); + } else { + res.redirect('/home'); + } }; /** * POST /signup * Create a new local account. */ -export let postSignup = (req: Request, res: Response, next: NextFunction) => { +export const postSignup = (req: Request, res: Response, next: NextFunction) => { const email = req.body.email as String; check('email', 'Email is not valid').isEmail().run(req); check('password', 'Password must be at least 4 characters long').isLength({ min: 4 }).run(req); @@ -42,7 +52,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { return res.redirect('/signup'); } - const password = req.body.password; + const { password } = req.body; const model = { email, @@ -65,35 +75,29 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => { req.logIn(user, err => { if (err) return next(err); tryRedirectToTarget(req, res); + return undefined; }); }) .catch((err: any) => next(err)); + return undefined; }) .catch((err: any) => next(err)); + return undefined; }; - -const tryRedirectToTarget = (req: Request, res: Response) => { - const target = (req.session as any)?.target; - if (req.session && target) { - res.redirect(target); - } else { - res.redirect('/home'); - } -}; - /** * GET /login * Login page. */ -export let getLogin = (req: Request, res: Response) => { +export const getLogin = (req: Request, res: Response) => { if (req.user) { - //req.session.target = undefined; + // req.session.target = undefined; return res.redirect('/home'); } res.render('login.pug', { title: 'Log In', user: req.user, }); + return undefined; }; /** @@ -101,7 +105,7 @@ export let getLogin = (req: Request, res: Response) => { * Sign in using email and password. * On failure, redirect to signup page */ -export let postLogin = (req: Request, res: Response, next: NextFunction) => { +export const postLogin = (req: Request, res: Response, next: NextFunction) => { if (req.body.email === '') { User.findOne({ email: 'guest' }) .then((user: any) => !user && initializeGuest()) @@ -119,23 +123,21 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { return res.redirect('/signup'); } - const callback = (err: Error, user: DashUserModel, _info: IVerifyOptions) => { + const callback = (err: Error, user: DashUserModel /* , _info: IVerifyOptions */) => { if (err) { next(err); - return; - } - if (!user) { + } else if (!user) { return res.redirect('/signup'); - } - req.logIn(user, err => { - if (err) { - next(err); - return; - } - tryRedirectToTarget(req, res); - }); + } else + req.logIn(user, loginErr => { + if (loginErr) { + next(loginErr); + } else tryRedirectToTarget(req, res); + }); + return undefined; }; setTimeout(() => passport.authenticate('local', callback)(req, res, next), 500); + return undefined; }; /** @@ -143,31 +145,29 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => { * Invokes the logout function on the request * and destroys the user's current session. */ -export let getLogout = (req: Request, res: Response) => { +export const getLogout = (req: Request, res: Response) => { req.logout(err => { if (err) console.log(err); else res.redirect('/login'); }); }; -export let getForgot = function (req: Request, res: Response) { +export const getForgot = function (req: Request, res: Response) { res.render('forgot.pug', { title: 'Recover Password', user: req.user, }); }; -export let postForgot = function (req: Request, res: Response, next: NextFunction) { - const email = req.body.email; +export const postForgot = function (req: Request, res: Response, next: NextFunction) { + const { email } = req.body; async.waterfall( [ function (done: any) { - c.randomBytes(20, function (err: any, buffer: Buffer) { + c.randomBytes(20, (err: any, buffer: Buffer) => { if (err) { done(null); - return; - } - done(null, buffer.toString('hex')); + } else done(null, buffer.toString('hex')); }); }, function (token: string, done: any) { @@ -204,20 +204,21 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio '\n\n' + 'If you did not request this, please ignore this email and your password will remain unchanged.\n', } as MailOptions; - smtpTransport.sendMail(mailOptions, function (err: Error | null) { + smtpTransport.sendMail(mailOptions, (err: Error | null) => { // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.'); done(null, err, 'done'); }); }, ], - function (err) { + err => { if (err) return next(err); res.redirect('/forgotPassword'); + return undefined; } ); }; -export let getReset = function (req: Request, res: Response) { +export const getReset = function (req: Request, res: Response) { User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }) .then((user: any) => { if (!user) return res.redirect('/forgotPassword'); @@ -225,11 +226,12 @@ export let getReset = function (req: Request, res: Response) { title: 'Reset Password', user: req.user, }); + return undefined; }) - .catch((err: any) => res.redirect('/forgotPassword')); + .catch(() => res.redirect('/forgotPassword')); }; -export let postReset = function (req: Request, res: Response) { +export const postReset = function (req: Request, res: Response) { async.waterfall( [ function (done: any) { @@ -251,10 +253,11 @@ export let postReset = function (req: Request, res: Response) { () => (req as any).logIn(user), (err: any) => err ) - .catch((err: any) => res.redirect('/login')); + .catch(() => res.redirect('/login')); done(null, user); + return undefined; }) - .catch((err: any) => res.redirect('back')); + .catch(() => res.redirect('back')); }, function (user: DashUserModel, done: any) { const smtpTransport = nodemailer.createTransport({ @@ -268,13 +271,13 @@ export let postReset = function (req: Request, res: Response) { to: user.email, from: 'browndashptc@gmail.com', subject: 'Your password has been changed', - text: 'Hello,\n\n' + 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n', + text: 'Hello,\n\nThis is a confirmation that the password for your account ' + user.email + ' has just been changed.\n', } as MailOptions; smtpTransport.sendMail(mailOptions, err => done(null, err)); }, ], - function (err) { + () => { res.redirect('/login'); } ); -- cgit v1.2.3-70-g09d2 From 9e809f8748d1812bb03ec6471aa6f97467f8f75a Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 23 Apr 2024 16:20:08 -0400 Subject: fixes for rich text menu updates and setting parameters on text doc vs within in RTF. Lots of lint cleanup. --- package-lock.json | 2 +- package.json | 2 +- src/client/DocServer.ts | 15 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DocumentManager.ts | 36 ++- src/client/util/request-image-size.ts | 26 +- src/client/views/ScriptingRepl.tsx | 191 +++++++------- src/client/views/StyleProvider.tsx | 108 +++++--- src/client/views/collections/CollectionMenu.tsx | 73 ++++-- .../collectionSchema/CollectionSchemaView.tsx | 181 +++++++++---- src/client/views/nodes/DocumentContentsView.tsx | 2 - .../nodes/formattedText/FormattedTextBox.scss | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 53 ++-- .../formattedText/ProsemirrorExampleTransfer.ts | 14 +- .../views/nodes/formattedText/RichTextMenu.tsx | 56 ++-- src/client/views/nodes/formattedText/nodes_rts.ts | 19 +- src/client/views/webcam/DashWebRTCVideo.scss | 82 ------ src/client/views/webcam/DashWebRTCVideo.tsx | 76 ------ src/client/views/webcam/WebCamLogic.js | 292 --------------------- src/fields/Doc.ts | 3 + src/fields/util.ts | 6 +- src/server/ActionUtilities.ts | 79 +++--- src/server/ApiManagers/ApiManager.ts | 4 +- src/server/ApiManagers/DeleteManager.ts | 14 +- src/server/ApiManagers/DownloadManager.ts | 6 +- src/server/ApiManagers/MongoStore.js | 21 +- src/server/ApiManagers/SearchManager.ts | 9 +- src/server/ApiManagers/SessionManager.ts | 26 +- src/server/ApiManagers/UtilManager.ts | 31 +-- .../Session/agents/applied_session_agent.ts | 23 +- src/server/DashSession/Session/agents/monitor.ts | 48 ++-- .../Session/agents/process_message_router.ts | 12 +- .../Session/agents/promisified_ipc_manager.ts | 51 ++-- .../DashSession/Session/agents/server_worker.ts | 46 ++-- src/server/DashSession/Session/utilities/repl.ts | 76 +++--- .../Session/utilities/session_config.ts | 104 ++++---- .../DashSession/Session/utilities/utilities.ts | 43 ++- src/server/DashStats.ts | 5 +- src/server/DashUploadUtils.ts | 8 +- src/server/GarbageCollector.ts | 70 +++-- src/server/MemoryDatabase.ts | 37 +-- src/server/PdfTypes.ts | 20 +- src/server/ProcessFactory.ts | 54 ++-- src/server/RouteSubscriber.ts | 5 +- src/server/SharedMediaTypes.ts | 31 +-- src/server/apis/google/CredentialsLoader.ts | 24 +- src/server/apis/google/GoogleApiServerUtils.ts | 8 +- src/server/apis/google/SharedTypes.ts | 13 +- src/server/apis/youtube/youtubeApiSample.d.ts | 2 +- src/server/authentication/DashUserModel.ts | 8 +- src/server/authentication/Passport.ts | 25 +- src/server/database.ts | 1 + src/server/index.ts | 48 ++-- src/server/updateProtos.ts | 6 +- src/server/websocket.ts | 40 +-- 55 files changed, 965 insertions(+), 1274 deletions(-) delete mode 100644 src/client/views/webcam/DashWebRTCVideo.scss delete mode 100644 src/client/views/webcam/DashWebRTCVideo.tsx delete mode 100644 src/client/views/webcam/WebCamLogic.js (limited to 'src/server/authentication') diff --git a/package-lock.json b/package-lock.json index c616bb6ea..03b788ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,6 +86,7 @@ "D": "^1.0.0", "d3": "^7.8.5", "depcheck": "^1.4.7", + "dotenv": "^16.3.1", "eslint-webpack-plugin": "^4.1.0", "exif": "^0.6.0", "exifr": "^7.1.3", @@ -273,7 +274,6 @@ "@types/youtube": "0.0.50", "chai": "^5.0.0", "cross-env": "^7.0.3", - "dotenv": "^16.3.1", "eslint": "^8.55.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-node": "^4.1.0", diff --git a/package.json b/package.json index a3ba6bdd9..9be1e093d 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "@types/youtube": "0.0.50", "chai": "^5.0.0", "cross-env": "^7.0.3", - "dotenv": "^16.3.1", "eslint": "^8.55.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-node": "^4.1.0", @@ -169,6 +168,7 @@ "D": "^1.0.0", "d3": "^7.8.5", "depcheck": "^1.4.7", + "dotenv": "^16.3.1", "eslint-webpack-plugin": "^4.1.0", "exif": "^0.6.0", "exifr": "^7.1.3", diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index b833d3287..cf7a61d24 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -8,7 +8,7 @@ import { FieldLoader } from '../fields/FieldLoader'; import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols'; import { ObjectField, SetObjGetRefField, SetObjGetRefFields } from '../fields/ObjectField'; import { RefField } from '../fields/RefField'; -import { GestureContent, Message, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message'; +import { GestureContent, Message, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from '../server/Message'; import { SerializationHelper } from './util/SerializationHelper'; /** @@ -63,7 +63,7 @@ export namespace DocServer { return foundDocId ? (_cache[foundDocId] as Doc) : undefined; } - export let _socket: Socket; + let _socket: Socket; // this client's distinct GUID created at initialization let USER_ID: string; // indicates whether or not a document is currently being udpated, and, if so, its id @@ -317,7 +317,7 @@ export namespace DocServer { // ii) which are already in the process of being fetched // iii) which already exist in the cache // eslint-disable-next-line no-restricted-syntax - for (const id of ids.filter(id => id)) { + for (const id of ids.filter(filterid => filterid)) { const cached = _cache[id]; if (cached === undefined) { defaultPromises.push({ @@ -362,6 +362,7 @@ export namespace DocServer { for (const field of serializedFields) { processed++; if (processed % 150 === 0) { + // eslint-disable-next-line no-loop-func runInAction(() => { FieldLoader.ServerLoadStatus.retrieved = processed; }); @@ -375,6 +376,7 @@ export namespace DocServer { // deserialize // adds to a list of promises that will be awaited asynchronously promises.push( + // eslint-disable-next-line no-loop-func (_cache[field.id] = SerializationHelper.Deserialize(field).then(deserialized => { // overwrite or delete any promises (that we inserted as flags // to indicate that the field was in the process of being fetched). Now everything @@ -447,7 +449,10 @@ export namespace DocServer { } // WRITE A NEW DOCUMENT TO THE SERVER - export let CacheNeedsUpdate = false; + let _cacheNeedsUpdate = false; + export function CacheNeedsUpdate() { + return _cacheNeedsUpdate; + } /** * A wrapper around the function local variable _createField. @@ -456,7 +461,7 @@ export namespace DocServer { * @param field the [RefField] to be serialized and sent to the server to be stored in the database */ export function CreateField(field: RefField) { - CacheNeedsUpdate = true; + _cacheNeedsUpdate = true; _CreateField(field); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6dba8027d..acbd0c0b9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -946,7 +946,7 @@ pie title Minerals in my tap water // eslint-disable-next-line no-new new LinkManager(); - DocServer.CacheNeedsUpdate && setTimeout(UPDATE_SERVER_CACHE, 2500); + DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500); setInterval(UPDATE_SERVER_CACHE, 120000); return doc; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 9a7786125..cca92816f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -178,11 +178,13 @@ export class DocumentManager { return containerDocContext; } + static _howl: Howl; static playAudioAnno(doc: Doc) { const anno = Cast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations'], listSpec(AudioField), null)?.lastElement(); if (anno) { + this._howl?.stop(); if (anno instanceof AudioField) { - new Howl({ + this._howl = new Howl({ src: [anno.url.href], format: ['mp3'], autoplay: true, @@ -200,13 +202,13 @@ export class DocumentManager { static _overlayViews = new ObservableSet(); public static LinkCommonAncestor(linkDoc: Doc) { - const anchor = (which: number) => { + const getAnchor = (which: number) => { const anch = DocCast(linkDoc['link_anchor_' + which]); const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch; return DocumentManager.Instance.getDocumentView(anchor); }; - const anchor1 = anchor(1); - const anchor2 = anchor(2); + const anchor1 = getAnchor(1); + const anchor2 = getAnchor(2); return anchor1 ?.docViewPath() .reverse() @@ -275,9 +277,13 @@ export class DocumentManager { if (rootContextView) { const target = await this.focusViewsInPath(rootContextView, options, childViewIterator); - this.restoreDocView(target.viewSpec, target.docView, options, target.contextView ?? target.docView, targetDoc); - finished?.(target.focused); - } else finished?.(false); + if (target) { + this.restoreDocView(target.viewSpec, target.docView, options, target.contextView ?? target.docView, targetDoc); + finished?.(target.focused); + return; + } + } + finished?.(false); }; focusViewsInPath = async ( @@ -289,12 +295,15 @@ export class DocumentManager { let focused = false; let docView = docViewIn; const options = optionsIn; - while (true) { + const maxFocusLength = 100; // want to keep focusing until we get to target, but avoid an infinite loop + for (let i = 0; i < maxFocusLength; i++) { if (docView.Document.layout_fieldKey === 'layout_icon') { - // eslint-disable-next-line no-await-in-loop - await new Promise(res => { + // eslint-disable-next-line no-loop-func + const prom = new Promise(res => { docView.iconify(res); }); + // eslint-disable-next-line no-await-in-loop + await prom; options.didMove = true; } const nextFocus = docView._props.focus(docView.Document, options); // focus the view within its container @@ -305,6 +314,7 @@ export class DocumentManager { contextView = options.anchorDoc?.layout_unrendered && !childDocView.Document.layout_unrendered ? childDocView : docView; docView = childDocView; } + return undefined; }; @action @@ -347,9 +357,9 @@ export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZ const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; options.toggleTarget = undefined; DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { - const cv = DocumentManager.Instance.getDocumentView(containingDoc); - const dv = DocumentManager.Instance.getDocumentView(doc, cv); - dv && Doc.linkFollowHighlight(dv.Document); + const cvFound = DocumentManager.Instance.getDocumentView(containingDoc); + const dvFound = DocumentManager.Instance.getDocumentView(doc, cvFound); + dvFound && Doc.linkFollowHighlight(dvFound.Document); }); } }; diff --git a/src/client/util/request-image-size.ts b/src/client/util/request-image-size.ts index 57e8516ac..0f98a2710 100644 --- a/src/client/util/request-image-size.ts +++ b/src/client/util/request-image-size.ts @@ -14,19 +14,17 @@ const imageSize = require('image-size'); const HttpError = require('standard-http-error'); module.exports = function requestImageSize(options: any) { - let opts = { + let opts: any = { encoding: null, }; if (options && typeof options === 'object') { opts = Object.assign(options, opts); } else if (options && typeof options === 'string') { - opts = Object.assign( - { - uri: options, - }, - opts - ); + opts = { + uri: options, + ...opts, + }; } else { return Promise.reject(new Error('You should provide an URI string or a "request" options object.')); } @@ -38,7 +36,8 @@ module.exports = function requestImageSize(options: any) { req.on('response', (res: any) => { if (res.statusCode >= 400) { - return reject(new HttpError(res.statusCode, res.statusMessage)); + reject(new HttpError(res.statusCode, res.statusMessage)); + return; } let buffer = Buffer.from([]); @@ -51,20 +50,23 @@ module.exports = function requestImageSize(options: any) { size = imageSize(buffer); if (size) { resolve(size); - return req.abort(); + req.abort(); } - } catch (err) {} + } catch (err) { + /* empty */ + } }); res.on('error', reject); res.on('end', () => { if (!size) { - return reject(new Error('Image has no size')); + reject(new Error('Image has no size')); + return; } size.downloaded = buffer.length; - return resolve(size); + resolve(size); }); }); diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index acf0ecff4..ba2e22b3b 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -1,3 +1,6 @@ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -12,6 +15,36 @@ import { OverlayView } from './OverlayView'; import './ScriptingRepl.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; +interface replValueProps { + scrollToBottom: () => void; + value: any; + name?: string; +} +@observer +export class ScriptingValueDisplay extends ObservableReactComponent { + constructor(props: any) { + super(props); + makeObservable(this); + } + + render() { + const val = this._props.name ? this._props.value[this._props.name] : this._props.value; + const title = (name: string) => ( + <> + {this._props.name ? {this._props.name} : : <> } + {name} + + ); + if (typeof val === 'object') { + // eslint-disable-next-line no-use-before-define + return ; + } + if (typeof val === 'function') { + return
{title('[Function]')}
; + } + return
{title(String(val))}
; + } +} interface ReplProps { scrollToBottom: () => void; value: { [key: string]: any }; @@ -37,7 +70,7 @@ export class ScriptingObjectDisplay extends ObservableReactComponent const name = (proto && proto.constructor && proto.constructor.name) || String(val); const title = ( <> - {this.props.name ? {this._props.name} : : <>} + {this.props.name ? {this._props.name} : : null} {name} ); @@ -50,53 +83,23 @@ export class ScriptingObjectDisplay extends ObservableReactComponent {title} (+{Object.keys(val).length}) ); - } else { - return ( -
-
- - - - {title} -
-
- {Object.keys(val).map(key => ( - - ))} -
-
- ); } - } -} - -interface replValueProps { - scrollToBottom: () => void; - value: any; - name?: string; -} -@observer -export class ScriptingValueDisplay extends ObservableReactComponent { - constructor(props: any) { - super(props); - makeObservable(this); - } - - render() { - const val = this._props.name ? this._props.value[this._props.name] : this._props.value; - const title = (name: string) => ( - <> - {this._props.name ? {this._props.name} : : <> } - {name} - + return ( +
+
+ + + + {title} +
+
+ {Object.keys(val).map(key => ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ))} +
+
); - if (typeof val === 'object') { - return ; - } else if (typeof val === 'function') { - const name = '[Function]'; - return
{title('[Function]')}
; - } - return
{title(String(val))}
; } } @@ -119,47 +122,45 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { private args: any = {}; - getTransformer = (): Transformer => { - return { - transformer: context => { - const knownVars: { [name: string]: number } = {}; - const usedDocuments: number[] = []; - ScriptingGlobals.getGlobals().forEach((global: any) => (knownVars[global] = 1)); - return root => { - function visit(node: ts.Node) { - let skip = false; - if (ts.isIdentifier(node)) { - if (ts.isParameter(node.parent)) { - skip = true; - knownVars[node.text] = 1; - } + getTransformer = (): Transformer => ({ + transformer: context => { + const knownVars: { [name: string]: number } = {}; + const usedDocuments: number[] = []; + ScriptingGlobals.getGlobals().forEach((global: any) => { + knownVars[global] = 1; + }); + return root => { + function visit(nodeIn: ts.Node) { + if (ts.isIdentifier(nodeIn)) { + if (ts.isParameter(nodeIn.parent)) { + knownVars[nodeIn.text] = 1; } - node = ts.visitEachChild(node, visit, context); + } + const node = ts.visitEachChild(nodeIn, visit, context); - if (ts.isIdentifier(node)) { - const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node; - const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node; - if (ts.isParameter(node.parent)) { - // delete knownVars[node.text]; - } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) { - const match = node.text.match(/d([0-9]+)/); - if (match) { - const m = parseInt(match[1]); - usedDocuments.push(m); - } else { - return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('args'), node); - // ts.createPropertyAccess(ts.createIdentifier('args'), node); - } + if (ts.isIdentifier(node)) { + const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node; + const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node; + if (ts.isParameter(node.parent)) { + // delete knownVars[node.text]; + } else if (isntPropAccess && isntPropAssign && !(node.text in knownVars) && !(node.text in globalThis)) { + const match = node.text.match(/d([0-9]+)/); + if (match) { + const m = parseInt(match[1]); + usedDocuments.push(m); + } else { + return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('args'), node); + // ts.createPropertyAccess(ts.createIdentifier('args'), node); } } - - return node; } - return ts.visitNode(root, visit); - }; - }, - }; - }; + + return node; + } + return ts.visitNode(root, visit); + }; + }, + }); @action onKeyDown = (e: React.KeyboardEvent) => { @@ -168,14 +169,16 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { case 'Enter': { e.stopPropagation(); const docGlobals: { [name: string]: any } = {}; - DocumentManager.Instance.DocumentViews.forEach((dv, i) => (docGlobals[`d${i}`] = dv.Document)); + DocumentManager.Instance.DocumentViews.forEach((dv, i) => { + docGlobals[`d${i}`] = dv.Document; + }); const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals); const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: 'any' }, transformer: this.getTransformer(), globals }); if (!script.compiled) { this.commands.push({ command: this.commandString, result: script.errors }); return; } - const result = undoable(() => script.run({ args: this.args }, e => this.commands.push({ command: this.commandString, result: e.toString() })), 'run:' + this.commandString)(); + const result = undoable(() => script.run({ args: this.args }, () => this.commands.push({ command: this.commandString, result: e.toString() })), 'run:' + this.commandString)(); if (result.success) { this.commands.push({ command: this.commandString, result: result.result }); this.commandsHistory.push(this.commandString); @@ -260,18 +263,16 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { return (
- {this.commands.map(({ command, result }, i) => { - return ( -
-
- {command ||
} -
-
- {} -
+ {this.commands.map(({ command, result }, i) => ( +
+
+ {command ||
} +
+
+
- ); - })} +
+ ))}
, props: Opt layoutDoc?._layout_isSvg && !props?.LayoutTemplateString; + const { + fieldKey: fieldKeyProp, + styleProvider, + pointerEvents, + isGroupActive, + isDocumentActive, + containerViewPath, + childFilters, + hideCaptions, + // eslint-disable-next-line camelcase + layout_showTitle, + childFiltersByRanges, + renderDepth, + docViewPath, + DocumentView, + LayoutTemplateString, + disableBrushing, + NativeDimScaling, + PanelWidth, + PanelHeight, + } = props || {}; // extract props that are not shared between fieldView and documentView props. + const fieldKey = fieldKeyProp ? fieldKeyProp + '_' : isCaption ? 'caption_' : ''; + const isInk = () => layoutDoc?._layout_isSvg && !LayoutTemplateString; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); - const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight); - const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)); - const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); - const layoutShowTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle); + const titleHeight = () => styleProvider?.(doc, props, StyleProp.TitleHeight); + const backgroundCol = () => styleProvider?.(doc, props, StyleProp.BackgroundColor + ':nonTransparent' + (isNonTransparentLevel + 1)); + const color = () => styleProvider?.(doc, props, StyleProp.Color); + const opacity = () => styleProvider?.(doc, props, StyleProp.Opacity); + const layoutShowTitle = () => styleProvider?.(doc, props, StyleProp.ShowTitle); // prettier-ignore switch (property.split(':')[0]) { case StyleProp.TreeViewIcon: { @@ -137,7 +160,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt dv.IsSelected).length; const highlightIndex = Doc.GetBrushHighlightStatus(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0); const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? "black" : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex]; @@ -152,26 +175,27 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt - +
@@ -251,13 +275,13 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY)); + : Cast((renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY)); break; // if (doc._type_collection !== CollectionViewType.Freeform && doc._type_collection !== CollectionViewType.Time) return "rgb(62,62,62)"; default: docColor = docColor || (Colors.WHITE); } - if (isNonTransparent && isNonTransparentLevel < 9 && (!docColor || docColor === 'transparent') && doc?.embedContainer && props?.styleProvider) { - return props.styleProvider(DocCast(doc.embedContainer), props, StyleProp.BackgroundColor+":nonTransparent"+(isNonTransparentLevel+1)); + if (isNonTransparent && isNonTransparentLevel < 9 && (!docColor || docColor === 'transparent') && doc?.embedContainer && styleProvider) { + return styleProvider(DocCast(doc.embedContainer), props, StyleProp.BackgroundColor+":nonTransparent"+(isNonTransparentLevel+1)); } return (docColor && !doc) ? DashColor(docColor).fade(0.5).toString() : docColor; } @@ -271,7 +295,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt, props: Opt doc?.pointerEvents !== 'none' ? null : ( @@ -312,7 +336,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt ); const paint = () => !doc?.onPaint ? null : ( -
togglePaintView(e, doc, props)}> +
togglePaintView(e, doc, props)}>
); @@ -321,7 +345,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || props?.childFiltersByRanges().length + : childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f) && f !== ClientUtils.noDragDocsFilter).length || childFiltersByRanges?.().length ? 'orange' // 'inheritsFilter' : undefined; return !showFilterIcon ? null : ( @@ -353,7 +377,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) .map(dv => ({ text: StrCast(dv?.Document.title), @@ -365,9 +389,9 @@ export function DefaultStyleProvider(doc: Opt, props: Opt { - const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, AudioAnnoState.stopped); - const audioAnnosCount = (doc: Doc) => StrListCast(doc[fieldKey + 'audioAnnotations']).length; - if (!doc || props?.renderDepth === -1 || !audioAnnosCount(doc)) return null; + const audioAnnoState = (audioDoc: Doc) => StrCast(audioDoc.audioAnnoState, AudioAnnoState.stopped); + const audioAnnosCount = (audioDoc: Doc) => StrListCast(audioDoc[fieldKey + 'audioAnnotations']).length; + if (!doc || renderDepth === -1 || !audioAnnosCount(doc)) return null; const audioIconColors: { [key: string]: string } = { playing: 'green', stopped: 'blue' }; return ( {StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}
}> diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 5a509128d..6dba9e155 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1,3 +1,9 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable react/no-unused-class-component-methods */ +/* eslint-disable react/sort-comp */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { Toggle, ToggleType, Type } from 'browndash-components'; @@ -312,8 +318,6 @@ export class CollectionViewBaseChrome extends React.Component(); @@ -345,11 +351,13 @@ export class CollectionViewBaseChrome extends React.Component { const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : DocCast(this.document.proto); - target._type_collection = e.target.selectedOptions[0].value; + target._type_collection = (e.target as any).selectedOptions[0].value; }; commandChanged = (e: React.ChangeEvent) => { - runInAction(() => (this._currentKey = e.target.selectedOptions[0].value)); + runInAction(() => { + this._currentKey = (e.target as any).selectedOptions[0].value; + }); }; @action closeViewSpecs = () => { @@ -367,7 +375,7 @@ export class CollectionViewBaseChrome extends React.Component c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || [])); e.stopPropagation(); @@ -420,11 +428,11 @@ export class CollectionViewBaseChrome extends React.Component drop document to apply or drag to create button
} placement="bottom">
- e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + return ( + e.stopPropagation()} + onChange={action(e => { + this._newFieldDefault = e.target.value; + })} + /> + ); case ColumnType.Boolean: return ( <> - e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} /> + e.stopPropagation()} + onChange={action(e => { + this._newFieldDefault = e.target.checked; + })} + /> {this._newFieldDefault ? 'true' : 'false'} ); case ColumnType.String: - return e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + return ( + e.stopPropagation()} + onChange={action(e => { + this._newFieldDefault = e.target.value; + })} + /> + ); + default: + return undefined; } } onSearchKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'Enter': - this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))(); + this._menuKeys.length > 0 && this._menuValue.length > 0 + ? this.setKey(this._menuKeys[0]) + : action(() => { + this._makeNewField = true; + })(); break; case 'Escape': this.closeColumnMenu(); break; + default: } }; @@ -568,7 +621,9 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeColumnMenu = () => (this._columnMenuIndex = undefined); + closeColumnMenu = () => { + this._columnMenuIndex = undefined; + }; @action openFilterMenu = (index: number) => { @@ -577,7 +632,9 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - closeFilterMenu = () => (this._filterColumnIndex = undefined); + closeFilterMenu = () => { + this._filterColumnIndex = undefined; + }; openContextMenu = (x: number, y: number, index: number) => { this.closeColumnMenu(); @@ -607,7 +664,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); }; - getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field); + getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] === field); removeFieldFilters = (field: string) => { this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove')); @@ -619,11 +676,14 @@ export class CollectionSchemaView extends CollectionSubView() { case 'Escape': this.closeFilterMenu(); break; + default: } }; @action - updateFilterSearch = (e: React.ChangeEvent) => (this._filterSearchValue = e.target.value); + updateFilterSearch = (e: React.ChangeEvent) => { + this._filterSearchValue = e.target.value; + }; @computed get newFieldMenu() { return ( @@ -632,7 +692,7 @@ export class CollectionSchemaView extends CollectionSubView() { { this._newFieldType = ColumnType.Number; this._newFieldDefault = 0; @@ -644,7 +704,7 @@ export class CollectionSchemaView extends CollectionSubView() { { this._newFieldType = ColumnType.Boolean; this._newFieldDefault = false; @@ -656,7 +716,7 @@ export class CollectionSchemaView extends CollectionSubView() { { this._newFieldType = ColumnType.String; this._newFieldDefault = ''; @@ -668,7 +728,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this._newFieldWarning}
{ + onPointerDown={action(() => { if (this.documentKeys.includes(this._menuValue)) { this._newFieldWarning = 'Field already exists'; } else if (this._menuValue.length === 0) { @@ -733,7 +793,7 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get renderColumnMenu() { - const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); + const x = this._columnMenuIndex! === -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); return (
e.stopPropagation()} /> @@ -817,7 +877,7 @@ export class CollectionSchemaView extends CollectionSubView() { : [...this.childDocs].sort((docA, docB) => { const aStr = Field.toString(docA[field] as FieldType); const bStr = Field.toString(docB[field] as FieldType); - var out = 0; + let out = 0; if (aStr < bStr) out = -1; if (aStr > bStr) out = 1; if (desc) out *= -1; @@ -835,7 +895,7 @@ export class CollectionSchemaView extends CollectionSubView() { render() { return (
this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}> -
+
this.openColumnMenu(-1, true)} icon="plus" />} + toggle={ this.openColumnMenu(-1, true)} icon="plus" />} trigger={PopupTrigger.CLICK} type={Type.TERT} isOpen={this._columnMenuIndex !== -1 ? false : undefined} @@ -860,6 +920,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this.columnKeys.map((key, index) => ( {this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu} {this._filterColumnIndex !== undefined && this.renderFilterMenu} - (this._tableContentRef = ref)} /> + { + // eslint-disable-next-line no-use-before-define + { + this._tableContentRef = ref; + }} + /> + } {this.layoutDoc.chromeHidden ? null : (
(value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')} placeholder={"Type text to create note or ':' to create specific type"} - contents={'+ New Node'} + contents="+ New Node" menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} />
)}
- {this.previewWidth > 0 &&
} + {this.previewWidth > 0 &&
} {this.previewWidth > 0 && ( -
(this._previewRef = ref)}> +
{ + this._previewRef = ref; + }}> {Array.from(this._selectedDocs).lastElement() && ( {this.props.childDocs().docs.map((doc: Doc, index: number) => (
- + { + // eslint-disable-next-line no-use-before-define + + }
))}
@@ -977,6 +1055,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent ); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 832e18b68..cc4b5b67f 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -21,7 +21,6 @@ import { CollectionSchemaView } from '../collections/collectionSchema/Collection import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox'; import { PresElementBox } from './trails/PresElementBox'; import { SearchBox } from '../search/SearchBox'; -import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo'; import { AudioBox } from './AudioBox'; import { ComparisonBox } from './ComparisonBox'; import { DataVizBox } from './DataVizBox/DataVizBox'; @@ -248,7 +247,6 @@ export class DocumentContentsView extends ObservableReactComponent lists when inside a prosemirror span } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 99a2f4ab9..ba37c3265 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -65,6 +65,7 @@ import { FootnoteView } from './FootnoteView'; import './FormattedTextBox.scss'; import { findLinkMark, FormattedTextBoxComment } from './FormattedTextBoxComment'; import { buildKeymap, updateBullets } from './ProsemirrorExampleTransfer'; +// eslint-disable-next-line import/extensions import { removeMarkWithAttrs } from './prosemirrorPatches'; import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu'; import { RichTextRules } from './RichTextRules'; @@ -291,7 +292,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - this._editorView?.state && RichTextMenu.Instance?.setHighlight(color); + this._editorView?.state && RichTextMenu.Instance?.setFontField(color, 'fontHighlight'); return undefined; }, 'highlght text'); AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true); @@ -637,8 +638,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !isClick && batch.end(), + (moveEv, movement, isClick) => !isClick && batch.end(), () => { this.toggleSidebar(); batch.end(); @@ -1301,7 +1302,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._props.rootSelected?.(), action(selected => { - // selected && setTimeout(() => this.prepareForTyping()); + this.prepareForTyping(); if (FormattedTextBox._globalHighlights.has('Bold Text')) { // eslint-disable-next-line operator-assignment this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed @@ -1498,8 +1499,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + this._editorView?.dispatch(tx.deleteSelection().addStoredMark(mark)); + }); this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data } else { const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined; @@ -1526,17 +1530,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (!this._editorView) return; - const docDefaultMarks = [ - ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []), - ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []), - ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []), - ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { fontFamily: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) })] : []), - ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) })] : []), - ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []), - ...[schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })], - ]; - this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks)); + if (this._editorView) { + const { text, paragraph } = schema.nodes; + const selNode = this._editorView.state.selection.$anchor.node(); + if (this._editorView.state.selection.from === 1 && this._editorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) { + const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })]; + this._editorView.state.selection.empty && this._editorView.state.selection.from === 1 && this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor)); + } + } }; componentWillUnmount() { @@ -1601,10 +1602,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink - for (let target = e.target as any; target && !target.dataset?.targethrefs; target = target.parentElement); - while (target && !target.dataset?.targethrefs) target = target.parentElement; - FormattedTextBoxComment.update(this, this.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true'); + let clickTarget = e.target as any; // hrefs are stored on the dataset of the node that wraps the hyerlink + for (let { target } = e as any; target && !target.dataset?.targethrefs; target = target.parentElement); + while (clickTarget && !clickTarget.dataset?.targethrefs) clickTarget = clickTarget.parentElement; + FormattedTextBoxComment.update(this, this.EditorView!, undefined, clickTarget?.dataset?.targethrefs, clickTarget?.dataset.linkdoc, clickTarget?.dataset.nopreview === 'true'); } }; @action @@ -1724,9 +1725,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - tr.addStoredMark(m); - return tr; + const tr = stordMarks?.reduce((tr2, m) => { + tr2.addStoredMark(m); + return tr2; }, this._editorView.state.tr); tr && this._editorView.dispatch(tr); } @@ -2038,7 +2039,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent>(schema: S, props: any): KeyMa } const canEdit = (state: any) => { - switch (GetEffectiveAcl(props.TemplateDataDocument)) { + const permissions = GetEffectiveAcl(props.TemplateDataDocument ?? props.Document[DocData]); + switch (permissions) { case AclAugment: { const prevNode = state.selection.$cursor.nodeBefore; - const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks[prevNode.marks.length - 1].attrs.userid; + const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid; if (prevUser !== ClientUtils.CurrentUserEmail()) { return false; } @@ -278,7 +279,7 @@ export function buildKeymap>(schema: S, props: any): KeyMa dispatch(updateBullets(tx, schema)); if (view.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) { // gets rid of an extra paragraph when joining two list items together. - joinBackward(view.state, (tx: Transaction) => view.dispatch(tx)); + joinBackward(view.state, (tx2: Transaction) => view.dispatch(tx2)); } }) ) { @@ -344,7 +345,7 @@ export function buildKeymap>(schema: S, props: any): KeyMa !splitBlockKeepMarks(state, (tx3: Transaction) => { const tonode = tx3.selection.$to.node(); if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) { - const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks); + const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks).setStoredMarks(marks || []); dispatch(tx4); } @@ -365,7 +366,8 @@ export function buildKeymap>(schema: S, props: any): KeyMa // Command to create a blank space bind('Space', () => { - if (props.TemplateDataDocument && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(props.TemplateDataDocument))) return true; + const editDoc = props.TemplateDataDocument ?? props.Document[DocData]; + if (editDoc && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(editDoc))) return true; return false; }); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 6108383c2..6c12b9991 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -10,7 +10,6 @@ import { EditorView } from 'prosemirror-view'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; -import { numberRange } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -147,12 +146,13 @@ export class RichTextMenu extends AntimodeMenu { const { activeHighlights } = active; const refDoc = SelectionManager.Views.lastElement()?.layoutDoc ?? Doc.UserDoc(); const refField = (pfx => (pfx ? pfx + '_' : ''))(SelectionManager.Views.lastElement()?.LayoutFieldKey); + const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt)); this._activeListType = this.getActiveListStyle(); this._activeAlignment = this.getActiveAlignment(); - this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(refDoc[refField + 'fontFamily'], 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various'; - this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(refDoc[refField + 'fontSize'], '10px')) : activeSizes[0]; - this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(refDoc[refField + 'fontColor'], 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...'; + this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various'; + this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0]; + this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...'; this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...'; // update link in current selection @@ -161,12 +161,7 @@ export class RichTextMenu extends AntimodeMenu { setMark = (mark: Mark, state: EditorState, dispatch: any, dontToggle: boolean = false) => { if (mark) { - const liFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.list_item); - const liTo = numberRange(state.selection.$to.depth + 1).find(i => state.selection.$to.node(i)?.type === state.schema.nodes.list_item); - const olFirst = numberRange(state.selection.$from.depth + 1).find(i => state.selection.$from.node(i)?.type === state.schema.nodes.ordered_list); - const nodeOl = (liFirst && liTo && state.selection.$from.node(liFirst) !== state.selection.$to.node(liTo) && olFirst) || (!liFirst && !liTo && olFirst); - const fromRange = numberRange(state.selection.from).reverse(); - const newPos = nodeOl ? fromRange.find(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) ?? state.selection.from : state.selection.from; + const newPos = state.selection.$anchor.node()?.type === schema.nodes.ordered_list ? state.selection.from : state.selection.from; const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined); if (node?.type === schema.nodes.ordered_list || node?.type === schema.nodes.list_item) { const hasMark = node.marks.some(m => m.type === mark.type); @@ -174,18 +169,16 @@ export class RichTextMenu extends AntimodeMenu { const addAnyway = node.marks.filter(m => m.type === mark.type && Object.keys(m.attrs).some(akey => m.attrs[akey] !== mark.attrs[akey])); const markup = state.tr.setNodeMarkup(newPos, node.type, node.attrs, hasMark && !addAnyway ? otherMarks : [...otherMarks, mark]); dispatch(updateBullets(markup, state.schema)); - } else { - const state = this.view?.state; - if (state) { - const { tr } = state; - if (dontToggle) { - tr.addMark(state.selection.from, state.selection.to, mark); - dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise - } else { - toggleMark(mark.type, mark.attrs)(state, dispatch); - } + } else if (state) { + const { tr } = state; + if (dontToggle) { + tr.addMark(state.selection.from, state.selection.to, mark); + dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise + } else { + toggleMark(mark.type, mark.attrs)(state, dispatch); } } + this.updateMenu(this.view, undefined, undefined, this.layoutDoc); } }; @@ -242,7 +235,7 @@ export class RichTextMenu extends AntimodeMenu { m.type === state.schema.marks.pFontFamily && activeFamilies.add(m.attrs.fontFamily); m.type === state.schema.marks.pFontColor && activeColors.add(m.attrs.fontColor); m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize); - m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHigh)); + m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHighlight)); }); } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) { SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); @@ -359,18 +352,21 @@ export class RichTextMenu extends AntimodeMenu { setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => { if (this.view) { - if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === value))) { + const { text, paragraph } = this.view.state.schema.nodes; + const selNode = this.view.state.selection.$anchor.node(); + if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) { this.TextView.dataDoc[this.TextView.fieldKey + `_${fontField}`] = value; this.view.focus(); - } else { - const attrs: { [key: string]: string } = {}; - attrs[fontField] = value; - const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs); - this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); - this.view.focus(); } - } else Doc.UserDoc()[fontField] = value; - this.updateMenu(this.view, undefined, this.props, this.layoutDoc); + const attrs: { [key: string]: string } = {}; + attrs[fontField] = value; + const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs); + this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); + this.view.focus(); + } else { + Doc.UserDoc()[fontField] = value; + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); + } }; // TODO: remove doesn't work diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 184487b7d..5bf942218 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -3,6 +3,7 @@ import { listItem, orderedList } from 'prosemirror-schema-list'; import { ParagraphNodeSpec, toParagraphDOM, getParagraphNodeAttrs } from './ParagraphNodeSpec'; import { DocServer } from '../../../DocServer'; import { Doc, Field, FieldType } from '../../../../fields/Doc'; +import { schema } from './schema_rts'; const blockquoteDOM: DOMOutputSpec = ['blockquote', 0]; const hrDOM: DOMOutputSpec = ['hr']; @@ -353,7 +354,7 @@ export const nodes: { [index: string]: NodeSpec } = { }, { style: 'list-style-type=disc', - getAttrs(dom: any) { + getAttrs() { return { mapStyle: 'bullet' }; }, }, @@ -373,10 +374,10 @@ export const nodes: { [index: string]: NodeSpec } = { ], toDOM(node: Node) { const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : ''; - const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight); - const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize); - const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family); - const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color); + const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontHighlight)?.attrs.fontHighlight); + const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontSize)?.attrs.fontSize); + const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontFamily)?.attrs.fontFamily); + const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontColor)?.attrs.fontColor); const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : ''; if (node.attrs.mapStyle === 'bullet') { return [ @@ -421,10 +422,10 @@ export const nodes: { [index: string]: NodeSpec } = { }, ], toDOM(node: Node) { - const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight); - const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize); - const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family); - const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color); + const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontHighlight)?.attrs.fontHighlight); + const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontSize)?.attrs.fontSize); + const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontFamily)?.attrs.fontFamily); + const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type === schema.marks.pFontColor)?.attrs.fontColor); const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : ''; return [ 'li', diff --git a/src/client/views/webcam/DashWebRTCVideo.scss b/src/client/views/webcam/DashWebRTCVideo.scss deleted file mode 100644 index 5744ebbcd..000000000 --- a/src/client/views/webcam/DashWebRTCVideo.scss +++ /dev/null @@ -1,82 +0,0 @@ -@import '../global/globalCssVariables.module.scss'; - -.webcam-cont { - background: whitesmoke; - color: grey; - border-radius: 15px; - box-shadow: #9c9396 0.2vw 0.2vw 0.4vw; - border: solid #bbbbbbbb 5px; - pointer-events: all; - display: flex; - flex-direction: column; - overflow: hidden; - - .webcam-header { - height: 50px; - text-align: center; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 16px; - width: 100%; - margin-top: 20px; - } - - .videoContainer { - position: relative; - width: calc(100% - 20px); - height: 100%; - /* border: 10px solid red; */ - margin-left: 10px; - } - - .buttonContainer { - display: flex; - width: calc(100% - 20px); - height: 50px; - justify-content: center; - text-align: center; - /* border: 1px solid black; */ - margin-left: 10px; - margin-top: 0; - margin-bottom: 15px; - } - - #roomName { - outline: none; - border-radius: inherit; - border: 1px solid #bbbbbbbb; - margin: 10px; - padding: 10px; - } - - .side { - width: 25%; - height: 20%; - position: absolute; - /* top: 65%; */ - z-index: 2; - right: 0px; - bottom: 18px; - } - - .main { - position: absolute; - width: 100%; - height: 100%; - /* top: 20%; */ - align-self: center; - } - - .videoButtons { - border-radius: 50%; - height: 30px; - width: 30px; - display: flex; - justify-content: center; - align-items: center; - justify-self: center; - align-self: center; - margin: 5px; - border: 1px solid black; - } -} diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx deleted file mode 100644 index 4e984f3d6..000000000 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { IconLookup } from '@fortawesome/fontawesome-svg-core'; -import { faPhoneSlash, faSync } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc } from '../../../fields/Doc'; -import { InkTool } from '../../../fields/InkField'; -import { SnappingManager } from '../../util/SnappingManager'; -import '../../views/nodes/WebBox.scss'; -import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import './DashWebRTCVideo.scss'; -import { hangup, initialize, refreshVideos } from './WebCamLogic'; - -/** - * This models the component that will be rendered, that can be used as a doc that will reflect the video cams. - */ -@observer -export class DashWebRTCVideo extends React.Component { - private roomText: HTMLInputElement | undefined; - @observable remoteVideoAdded: boolean = false; - - @action - changeUILook = () => (this.remoteVideoAdded = true); - - /** - * Function that submits the title entered by user on enter press. - */ - private onEnterKeyDown = (e: React.KeyboardEvent) => { - if (e.keyCode === 13) { - const submittedTitle = this.roomText!.value; - this.roomText!.value = ''; - this.roomText!.blur(); - initialize(submittedTitle, this.changeUILook); - } - }; - - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(DashWebRTCVideo, fieldKey); - } - - onClickRefresh = () => refreshVideos(); - - onClickHangUp = () => hangup(); - - render() { - const content = ( -
-
DashWebRTC
- (this.roomText = e!)} onKeyDown={this.onEnterKeyDown} /> -
- - -
-
-
- -
-
- -
-
-
- ); - - const frozen = !this.props.isSelected() || SnappingManager.IsResizing; - const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !SnappingManager.IsResizing ? '-interactive' : ''); - - return ( - <> -
{content}
- {!frozen ? null :
} - - ); - } -} diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js deleted file mode 100644 index 5f6202bc8..000000000 --- a/src/client/views/webcam/WebCamLogic.js +++ /dev/null @@ -1,292 +0,0 @@ -'use strict'; -import io from "socket.io-client"; - -var socket; -var isChannelReady = false; -var isInitiator = false; -var isStarted = false; -var localStream; -var pc; -var remoteStream; -var turnReady; -var room; - -export function initialize(roomName, handlerUI) { - - var pcConfig = { - 'iceServers': [{ - 'urls': 'stun:stun.l.google.com:19302' - }] - }; - - // Set up audio and video regardless of what devices are present. - var sdpConstraints = { - offerToReceiveAudio: true, - offerToReceiveVideo: true - }; - - ///////////////////////////////////////////// - - room = roomName; - - socket = io.connect(`${window.location.protocol}//${window.location.hostname}:4321`); - - if (room !== '') { - socket.emit('create or join', room); - console.log('Attempted to create or join room', room); - } - - socket.on('created', function (room) { - console.log('Created room ' + room); - isInitiator = true; - }); - - socket.on('full', function (room) { - console.log('Room ' + room + ' is full'); - }); - - socket.on('join', function (room) { - console.log('Another peer made a request to join room ' + room); - console.log('This peer is the initiator of room ' + room + '!'); - isChannelReady = true; - }); - - socket.on('joined', function (room) { - console.log('joined: ' + room); - isChannelReady = true; - }); - - socket.on('log', function (array) { - console.log.apply(console, array); - }); - - //////////////////////////////////////////////// - - - // This client receives a message - socket.on('message', function (message) { - console.log('Client received message:', message); - if (message === 'got user media') { - maybeStart(); - } else if (message.type === 'offer') { - if (!isInitiator && !isStarted) { - maybeStart(); - } - pc.setRemoteDescription(new RTCSessionDescription(message)); - doAnswer(); - } else if (message.type === 'answer' && isStarted) { - pc.setRemoteDescription(new RTCSessionDescription(message)); - } else if (message.type === 'candidate' && isStarted) { - var candidate = new RTCIceCandidate({ - sdpMLineIndex: message.label, - candidate: message.candidate - }); - pc.addIceCandidate(candidate); - } else if (message === 'bye' && isStarted) { - handleRemoteHangup(); - } - }); - - //////////////////////////////////////////////////// - - var localVideo = document.querySelector('#localVideo'); - var remoteVideo = document.querySelector('#remoteVideo'); - - const gotStream = (stream) => { - console.log('Adding local stream.'); - localStream = stream; - localVideo.srcObject = stream; - sendMessage('got user media'); - if (isInitiator) { - maybeStart(); - } - } - - - navigator.mediaDevices.getUserMedia({ - audio: true, - video: true - }) - .then(gotStream) - .catch(function (e) { - alert('getUserMedia() error: ' + e.name); - }); - - - - var constraints = { - video: true - }; - - console.log('Getting user media with constraints', constraints); - - const requestTurn = (turnURL) => { - var turnExists = false; - for (var i in pcConfig.iceServers) { - if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') { - turnExists = true; - turnReady = true; - break; - } - } - if (!turnExists) { - console.log('Getting TURN server from ', turnURL); - // No TURN server. Get one from computeengineondemand.appspot.com: - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4 && xhr.status === 200) { - var turnServer = JSON.parse(xhr.responseText); - console.log('Got TURN server: ', turnServer); - pcConfig.iceServers.push({ - 'urls': 'turn:' + turnServer.username + '@' + turnServer.turn, - 'credential': turnServer.password - }); - turnReady = true; - } - }; - xhr.open('GET', turnURL, true); - xhr.send(); - } - } - - - - - if (location.hostname !== 'localhost') { - requestTurn( - `${window.location.origin}/corsProxy/${encodeURIComponent("https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913")}` - ); - } - - const maybeStart = () => { - console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady); - if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) { - console.log('>>>>>> creating peer connection'); - createPeerConnection(); - pc.addStream(localStream); - isStarted = true; - console.log('isInitiator', isInitiator); - if (isInitiator) { - doCall(); - } - } - }; - - window.onbeforeunload = function () { - sendMessage('bye'); - }; - - ///////////////////////////////////////////////////////// - - const createPeerConnection = () => { - try { - pc = new RTCPeerConnection(null); - pc.onicecandidate = handleIceCandidate; - pc.onaddstream = handleRemoteStreamAdded; - pc.onremovestream = handleRemoteStreamRemoved; - console.log('Created RTCPeerConnnection'); - } catch (e) { - console.log('Failed to create PeerConnection, exception: ' + e.message); - alert('Cannot create RTCPeerConnection object.'); - return; - } - } - - const handleIceCandidate = (event) => { - console.log('icecandidate event: ', event); - if (event.candidate) { - sendMessage({ - type: 'candidate', - label: event.candidate.sdpMLineIndex, - id: event.candidate.sdpMid, - candidate: event.candidate.candidate - }); - } else { - console.log('End of candidates.'); - } - } - - const handleCreateOfferError = (event) => { - console.log('createOffer() error: ', event); - } - - const doCall = () => { - console.log('Sending offer to peer'); - pc.createOffer(setLocalAndSendMessage, handleCreateOfferError); - } - - const doAnswer = () => { - console.log('Sending answer to peer.'); - pc.createAnswer().then( - setLocalAndSendMessage, - onCreateSessionDescriptionError - ); - } - - const setLocalAndSendMessage = (sessionDescription) => { - pc.setLocalDescription(sessionDescription); - console.log('setLocalAndSendMessage sending message', sessionDescription); - sendMessage(sessionDescription); - } - - const onCreateSessionDescriptionError = (error) => { - trace('Failed to create session description: ' + error.toString()); - } - - - - const handleRemoteStreamAdded = (event) => { - console.log('Remote stream added.'); - remoteStream = event.stream; - remoteVideo.srcObject = remoteStream; - handlerUI(); - - }; - - const handleRemoteStreamRemoved = (event) => { - console.log('Remote stream removed. Event: ', event); - } -} - -export function hangup() { - console.log('Hanging up.'); - stop(); - sendMessage('bye'); - if (localStream) { - localStream.getTracks().forEach(track => track.stop()); - } -} - -function stop() { - isStarted = false; - if (pc) { - pc.close(); - } - pc = null; -} - -function handleRemoteHangup() { - console.log('Session terminated.'); - stop(); - isInitiator = false; - if (localStream) { - localStream.getTracks().forEach(track => track.stop()); - } -} - -function sendMessage(message) { - console.log('Client sending message: ', message); - socket.emit('message', message, room); -}; - -export function refreshVideos() { - var localVideo = document.querySelector('#localVideo'); - var remoteVideo = document.querySelector('#remoteVideo'); - if (localVideo) { - localVideo.srcObject = localStream; - } - if (remoteVideo) { - remoteVideo.srcObject = remoteStream; - } - -} \ No newline at end of file diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4512d5c5b..5028e1f8f 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -116,7 +116,9 @@ export type FieldResult = Opt | FieldWaiting * If no default value is given, and the returned value is not undefined, it can be safely modified. */ export function DocListCastAsync(field: FieldResult): Promise; +// eslint-disable-next-line no-redeclare export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; +// eslint-disable-next-line no-redeclare export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { const list = Cast(field, listSpec(Doc)); return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); @@ -416,6 +418,7 @@ export class Doc extends RefField { } } +// eslint-disable-next-line no-redeclare export namespace Doc { export function SetContainer(doc: Doc, container: Doc) { if (container !== Doc.MyRecentlyClosed) { diff --git a/src/fields/util.ts b/src/fields/util.ts index d7268f31a..72b0ef721 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -285,10 +285,10 @@ export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) * sets a callback function to be called whenever a value is assigned to the specified field key. * For example, this is used to "publish" documents with titles that start with '@' * @param prop - * @param setter + * @param propSetter */ -export function SetPropSetterCb(prop: string, setter: ((target: any, value: any) => void) | undefined) { - _propSetterCB.set(prop, setter); +export function SetPropSetterCb(prop: string, propSetter: ((target: any, value: any) => void) | undefined) { + _propSetterCB.set(prop, propSetter); } // diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts index 6f5b9272a..520ebb42e 100644 --- a/src/server/ActionUtilities.ts +++ b/src/server/ActionUtilities.ts @@ -16,29 +16,30 @@ export function pathFromRoot(relative?: string) { return path.resolve(projectRoot, relative); } -export async function fileDescriptorFromStream(path: string) { - const logStream = createWriteStream(path); - return new Promise(resolve => logStream.on('open', resolve)); +export async function fileDescriptorFromStream(filePath: string) { + const logStream = createWriteStream(filePath); + return new Promise(resolve => { + logStream.on('open', resolve); + }); } -export const command_line = (command: string, fromDirectory?: string) => { - return new Promise((resolve, reject) => { +export const commandLine = (command: string, fromDirectory?: string) => + new Promise((resolve, reject) => { const options: ExecOptions = {}; if (fromDirectory) { options.cwd = fromDirectory ? path.resolve(projectRoot, fromDirectory) : projectRoot; } exec(command, options, (err, stdout) => (err ? reject(err) : resolve(stdout))); }); -}; -export const read_text_file = (relativePath: string) => { +export const readTextFile = (relativePath: string) => { const target = path.resolve(__dirname, relativePath); return new Promise((resolve, reject) => { readFile(target, (err, data) => (err ? reject(err) : resolve(data.toString()))); }); }; -export const write_text_file = (relativePath: string, contents: any) => { +export const writeTextFile = (relativePath: string, contents: any) => { const target = path.resolve(__dirname, relativePath); return new Promise((resolve, reject) => { writeFile(target, contents, err => (err ? reject(err) : resolve())); @@ -55,39 +56,38 @@ export interface LogData { color?: Color; } +function logHelper(content: string, color: Color | string) { + if (typeof color === 'string') { + console.log(color, content); + } else { + console.log(color(content)); + } +} + let current = Math.ceil(Math.random() * 20); export async function logExecution({ startMessage, endMessage, action, color }: LogData): Promise { - let result: T | undefined = undefined, - error: Error | null = null; + let result: T | undefined; + let error: Error | null = null; const resolvedColor = color || `\x1b[${31 + (++current % 6)}m%s\x1b[0m`; - log_helper(`${startMessage}...`, resolvedColor); + logHelper(`${startMessage}...`, resolvedColor); try { result = await action(); } catch (e: any) { error = e; } finally { - log_helper(typeof endMessage === 'string' ? endMessage : endMessage({ result, error }), resolvedColor); + logHelper(typeof endMessage === 'string' ? endMessage : endMessage({ result, error }), resolvedColor); } return result; } - -function log_helper(content: string, color: Color | string) { - if (typeof color === 'string') { - console.log(color, content); - } else { - console.log(color(content)); - } -} - export function logPort(listener: string, port: number) { console.log(`${listener} listening on port ${yellow(String(port))}`); } export function msToTime(duration: number) { - const milliseconds = Math.floor((duration % 1000) / 100), - seconds = Math.floor((duration / 1000) % 60), - minutes = Math.floor((duration / (1000 * 60)) % 60), - hours = Math.floor((duration / (1000 * 60 * 60)) % 24); + const milliseconds = Math.floor((duration % 1000) / 100); + const seconds = Math.floor((duration / 1000) % 60); + const minutes = Math.floor((duration / (1000 * 60)) % 60); + const hours = Math.floor((duration / (1000 * 60 * 60)) % 24); const hoursS = hours < 10 ? '0' + hours : hours; const minutesS = minutes < 10 ? '0' + minutes : minutes; @@ -96,21 +96,32 @@ export function msToTime(duration: number) { return hoursS + ':' + minutesS + ':' + secondsS + '.' + milliseconds; } -export const createIfNotExists = async (path: string) => { - if (await new Promise(resolve => exists(path, resolve))) { +export const createIfNotExists = async (filePath: string) => { + if ( + await new Promise(resolve => { + exists(filePath, resolve); + }) + ) { return true; } - return new Promise(resolve => mkdir(path, error => resolve(error === null))); + return new Promise(resolve => { + mkdir(filePath, error => resolve(error === null)); + }); }; export async function Prune(rootDirectory: string): Promise { // const error = await new Promise(resolve => rimraf(rootDirectory).then(resolve)); - await new Promise(resolve => rimraf(rootDirectory).then(() => resolve())); + await new Promise(resolve => { + rimraf(rootDirectory).then(() => resolve()); + }); // return error === null; return true; } -export const Destroy = (mediaPath: string) => new Promise(resolve => unlink(mediaPath, error => resolve(error === null))); +export const Destroy = (mediaPath: string) => + new Promise(resolve => { + unlink(mediaPath, error => resolve(error === null)); + }); export namespace Email { const smtpTransport = nodemailer.createTransport({ @@ -137,9 +148,9 @@ export namespace Email { const failures: DispatchFailure[] = []; await Promise.all( to.map(async recipient => { - let error: Error | null; const resolved = attachments ? ('length' in attachments ? attachments : [attachments]) : undefined; - if ((error = await Email.dispatch({ to: recipient, subject, content, attachments: resolved })) !== null) { + const error = await Email.dispatch({ to: recipient, subject, content, attachments: resolved }); + if (error !== null) { failures.push({ recipient, error, @@ -158,6 +169,8 @@ export namespace Email { text: `Hello ${to.split('@')[0]},\n\n${content}`, attachments, } as MailOptions; - return new Promise(resolve => smtpTransport.sendMail(mailOptions, resolve)); + return new Promise(resolve => { + smtpTransport.sendMail(mailOptions, resolve); + }); } } diff --git a/src/server/ApiManagers/ApiManager.ts b/src/server/ApiManagers/ApiManager.ts index 27e9de065..f55495b2e 100644 --- a/src/server/ApiManagers/ApiManager.ts +++ b/src/server/ApiManagers/ApiManager.ts @@ -1,4 +1,4 @@ -import { RouteInitializer } from "../RouteManager"; +import { RouteInitializer } from '../RouteManager'; export type Registration = (initializer: RouteInitializer) => void; @@ -8,4 +8,4 @@ export default abstract class ApiManager { public register(register: Registration) { this.initialize(register); } -} \ No newline at end of file +} diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts index 9a9b807ae..9ad334c1b 100644 --- a/src/server/ApiManagers/DeleteManager.ts +++ b/src/server/ApiManagers/DeleteManager.ts @@ -1,12 +1,12 @@ -import ApiManager, { Registration } from './ApiManager'; -import { Method, _permissionDenied } from '../RouteManager'; -import { WebSocket } from '../websocket'; -import { Database } from '../database'; +import { mkdirSync } from 'fs'; import { rimraf } from 'rimraf'; -import { filesDirectory } from '..'; +import { filesDirectory } from '../SocketData'; import { DashUploadUtils } from '../DashUploadUtils'; -import { mkdirSync } from 'fs'; +import { Method } from '../RouteManager'; import RouteSubscriber from '../RouteSubscriber'; +import { Database } from '../database'; +import { WebSocket } from '../websocket'; +import ApiManager, { Registration } from './ApiManager'; export default class DeleteManager extends ApiManager { protected initialize(register: Registration): void { @@ -24,9 +24,11 @@ export default class DeleteManager extends ApiManager { switch (target) { case 'all': all = true; + // eslint-disable-next-line no-fallthrough case 'database': await WebSocket.doDelete(false); if (!all) break; + // eslint-disable-next-line no-fallthrough case 'files': rimraf.sync(filesDirectory); mkdirSync(filesDirectory); diff --git a/src/server/ApiManagers/DownloadManager.ts b/src/server/ApiManagers/DownloadManager.ts index b105c825c..5ee21fb44 100644 --- a/src/server/ApiManagers/DownloadManager.ts +++ b/src/server/ApiManagers/DownloadManager.ts @@ -153,7 +153,7 @@ async function writeHierarchyRecursive(file: Archiver.Archiver, hierarchy: Hiera } } -async function getDocs(id: string) { +async function getDocs(docId: string) { const files = new Set(); const docs: { [id: string]: any } = {}; const fn = (doc: any): string[] => { @@ -209,8 +209,8 @@ async function getDocs(id: string) { } return ids; }; - await Database.Instance.visit([id], fn); - return { id, docs, files }; + await Database.Instance.visit([docId], fn); + return { id: docId, docs, files }; } export default class DownloadManager extends ApiManager { diff --git a/src/server/ApiManagers/MongoStore.js b/src/server/ApiManagers/MongoStore.js index 28515fee4..5d91c2805 100644 --- a/src/server/ApiManagers/MongoStore.js +++ b/src/server/ApiManagers/MongoStore.js @@ -1,10 +1,9 @@ -'use strict'; -var __createBinding = +const __createBinding = (this && this.__createBinding) || (Object.create ? function (o, m, k, k2) { if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); + let desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, @@ -19,25 +18,25 @@ var __createBinding = if (k2 === undefined) k2 = k; o[k2] = m[k]; }); -var __setModuleDefault = +const __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? function (o, v) { Object.defineProperty(o, 'default', { enumerable: true, value: v }); } : function (o, v) { - o['default'] = v; + o.default = v; }); -var __importStar = +const __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + const result = {}; + if (mod != null) for (const k in mod) if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; -var __importDefault = +const __importDefault = (this && this.__importDefault) || function (mod) { return mod && mod.__esModule ? mod : { default: mod }; @@ -246,7 +245,7 @@ class MongoStore extends session.Store { */ set(sid, session, callback = noop) { (async () => { - var _a; + let _a; try { debug(`MongoStore#set=${sid}`); // Removing the lastModified prop from the session object before update @@ -306,7 +305,7 @@ class MongoStore extends session.Store { } touch(sid, session, callback = noop) { (async () => { - var _a; + let _a; try { debug(`MongoStore#touch=${sid}`); const updateFields = {}; diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts index 1b1db5809..f43ed6ac9 100644 --- a/src/server/ApiManagers/SearchManager.ts +++ b/src/server/ApiManagers/SearchManager.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-use-before-define */ import { exec } from 'child_process'; import { cyan, green, red, yellow } from 'colors'; import { logExecution } from '../ActionUtilities'; @@ -17,8 +18,10 @@ export class SearchManager extends ApiManager { switch (action) { case 'start': case 'stop': - const status = req.params.action === 'start'; - SolrManager.SetRunning(status); + { + const status = req.params.action === 'start'; + SolrManager.SetRunning(status); + } break; case 'update': await SolrManager.update(); @@ -95,7 +98,7 @@ export namespace SolrManager { if (doc.__type !== 'Doc') { return; } - const fields = doc.fields; + const { fields } = doc; if (!fields) { return; } diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts index c3139896f..bebe50a62 100644 --- a/src/server/ApiManagers/SessionManager.ts +++ b/src/server/ApiManagers/SessionManager.ts @@ -9,20 +9,18 @@ const permissionError = 'You are not authorized!'; export default class SessionManager extends ApiManager { private secureSubscriber = (root: string, ...params: string[]) => new RouteSubscriber(root).add('session_key', ...params); - private authorizedAction = (handler: SecureHandler) => { - return (core: AuthorizedCore) => { - const { - req: { params }, - res, - } = core; - if (!process.env.MONITORED) { - return res.send('This command only makes sense in the context of a monitored session.'); - } - if (params.session_key !== process.env.session_key) { - return _permissionDenied(res, permissionError); - } - return handler(core); - }; + private authorizedAction = (handler: SecureHandler) => (core: AuthorizedCore) => { + const { + req: { params }, + res, + } = core; + if (!process.env.MONITORED) { + return res.send('This command only makes sense in the context of a monitored session.'); + } + if (params.session_key !== process.env.session_key) { + return _permissionDenied(res, permissionError); + } + return handler(core); }; protected initialize(register: Registration): void { diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index e657866ce..8ad421a30 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -1,6 +1,7 @@ -import ApiManager, { Registration } from "./ApiManager"; -import { Method } from "../RouteManager"; import { exec } from 'child_process'; +import ApiManager, { Registration } from './ApiManager'; +import { Method } from '../RouteManager'; + // import { IBM_Recommender } from "../../client/apis/IBM_Recommender"; // import { Recommender } from "../Recommender"; @@ -8,9 +9,7 @@ import { exec } from 'child_process'; // recommender.testModel(); export default class UtilManager extends ApiManager { - protected initialize(register: Registration): void { - // register({ // method: Method.POST, // subscription: "/IBMAnalysis", @@ -33,26 +32,25 @@ export default class UtilManager extends ApiManager { register({ method: Method.GET, - subscription: "/pull", - secureHandler: async ({ res }) => { - return new Promise(resolve => { + subscription: '/pull', + secureHandler: async ({ res }) => + new Promise(resolve => { exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', err => { if (err) { res.send(err.message); return; } - res.redirect("/"); + res.redirect('/'); resolve(); }); - }); - } + }), }); register({ method: Method.GET, - subscription: "/version", - secureHandler: ({ res }) => { - return new Promise(resolve => { + subscription: '/version', + secureHandler: ({ res }) => + new Promise(resolve => { exec('"C:\\Program Files\\Git\\bin\\git.exe" rev-parse HEAD', (err, stdout) => { if (err) { res.send(err.message); @@ -61,10 +59,7 @@ export default class UtilManager extends ApiManager { res.send(stdout); }); resolve(); - }); - } + }), }); - } - -} \ No newline at end of file +} diff --git a/src/server/DashSession/Session/agents/applied_session_agent.ts b/src/server/DashSession/Session/agents/applied_session_agent.ts index 2037e93e5..c42ba95cc 100644 --- a/src/server/DashSession/Session/agents/applied_session_agent.ts +++ b/src/server/DashSession/Session/agents/applied_session_agent.ts @@ -1,13 +1,13 @@ -import * as _cluster from "cluster"; -import { Monitor } from "./monitor"; -import { ServerWorker } from "./server_worker"; +import * as _cluster from 'cluster'; +import { Monitor } from './monitor'; +import { ServerWorker } from './server_worker'; + const cluster = _cluster as any; const isMaster = cluster.isPrimary; export type ExitHandler = (reason: Error | boolean) => void | Promise; export abstract class AppliedSessionAgent { - // the following two methods allow the developer to create a custom // session and use the built in customization options for each thread protected abstract initializeMonitor(monitor: Monitor): Promise; @@ -18,15 +18,15 @@ export abstract class AppliedSessionAgent { public killSession = (reason: string, graceful = true, errorCode = 0) => { const target = cluster.default.isPrimary ? this.sessionMonitor : this.serverWorker; target.killSession(reason, graceful, errorCode); - } + }; private sessionMonitorRef: Monitor | undefined; public get sessionMonitor(): Monitor { if (!cluster.default.isPrimary) { - this.serverWorker.emit("kill", { + this.serverWorker.emit('kill', { graceful: false, - reason: "Cannot access the session monitor directly from the server worker thread.", - errorCode: 1 + reason: 'Cannot access the session monitor directly from the server worker thread.', + errorCode: 1, }); throw new Error(); } @@ -36,7 +36,7 @@ export abstract class AppliedSessionAgent { private serverWorkerRef: ServerWorker | undefined; public get serverWorker(): ServerWorker { if (isMaster) { - throw new Error("Cannot access the server worker directly from the session monitor thread"); + throw new Error('Cannot access the server worker directly from the session monitor thread'); } return this.serverWorkerRef!; } @@ -52,8 +52,7 @@ export abstract class AppliedSessionAgent { this.serverWorkerRef = await this.initializeServerWorker(); } } else { - throw new Error("Cannot launch a session thread more than once per process."); + throw new Error('Cannot launch a session thread more than once per process.'); } } - -} \ No newline at end of file +} diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts index a6fde4356..6cdad46c2 100644 --- a/src/server/DashSession/Session/agents/monitor.ts +++ b/src/server/DashSession/Session/agents/monitor.ts @@ -1,21 +1,19 @@ -import { ExitHandler } from './applied_session_agent'; -import { Configuration, configurationSchema, defaultConfig, Identifiers, colorMapping } from '../utilities/session_config'; -import Repl, { ReplAction } from '../utilities/repl'; +import { ExecOptions, exec } from 'child_process'; import * as _cluster from 'cluster'; import { Worker } from 'cluster'; -import { manage, MessageHandler, ErrorLike } from './promisified_ipc_manager'; -import { red, cyan, white, yellow, blue } from 'colors'; -import { exec, ExecOptions } from 'child_process'; -import { validate, ValidationError } from 'jsonschema'; -import { Utilities } from '../utilities/utilities'; +import { blue, cyan, red, white, yellow } from 'colors'; import { readFileSync } from 'fs'; +import { ValidationError, validate } from 'jsonschema'; +import Repl, { ReplAction } from '../utilities/repl'; +import { Configuration, Identifiers, colorMapping, configurationSchema, defaultConfig } from '../utilities/session_config'; +import { Utilities } from '../utilities/utilities'; +import { ExitHandler } from './applied_session_agent'; import IPCMessageReceiver from './process_message_router'; +import { ErrorLike, MessageHandler, manage } from './promisified_ipc_manager'; import { ServerWorker } from './server_worker'; + const cluster = _cluster as any; -const isWorker = cluster.isWorker; -const setupMaster = cluster.setupPrimary; -const on = cluster.on; -const fork = cluster.fork; +const { isWorker, setupMaster, on, fork } = cluster; /** * Validates and reads the configuration file, accordingly builds a child process factory @@ -41,9 +39,8 @@ export class Monitor extends IPCMessageReceiver { } else if (++Monitor.count > 1) { console.error(red('cannot create more than one monitor.')); process.exit(1); - } else { - return new Monitor(); } + return new Monitor(); } private constructor() { @@ -128,25 +125,25 @@ export class Monitor extends IPCMessageReceiver { this.repl.registerCommand(basename, argPatterns, action); }; - public exec = (command: string, options?: ExecOptions) => { - return new Promise(resolve => { + public exec = (command: string, options?: ExecOptions) => + new Promise(resolve => { exec(command, { ...options, encoding: 'utf8' }, (error, stdout, stderr) => { if (error) { this.execLog(red(`unable to execute ${white(command)}`)); error.message.split('\n').forEach(line => line.length && this.execLog(red(`(error) ${line}`))); } else { - let outLines: string[], errorLines: string[]; - if ((outLines = stdout.split('\n').filter(line => line.length)).length) { + const outLines = stdout.split('\n').filter(line => line.length); + if (outLines.length) { outLines.forEach(line => line.length && this.execLog(cyan(`(stdout) ${line}`))); } - if ((errorLines = stderr.split('\n').filter(line => line.length)).length) { + const errorLines = stderr.split('\n').filter(line => line.length); + if (errorLines.length) { errorLines.forEach(line => line.length && this.execLog(yellow(`(stderr) ${line}`))); } } resolve(); }); }); - }; /** * Generates a blue UTC string associated with the time @@ -226,12 +223,10 @@ export class Monitor extends IPCMessageReceiver { const newPollingIntervalSeconds = Math.floor(Number(args[1])); if (newPollingIntervalSeconds < 0) { this.mainLog(red('the polling interval must be a non-negative integer')); - } else { - if (newPollingIntervalSeconds !== this.config.polling.intervalSeconds) { - this.config.polling.intervalSeconds = newPollingIntervalSeconds; - if (args[2] === 'true') { - Monitor.IPCManager.emit('updatePollingInterval', { newPollingIntervalSeconds }); - } + } else if (newPollingIntervalSeconds !== this.config.polling.intervalSeconds) { + this.config.polling.intervalSeconds = newPollingIntervalSeconds; + if (args[2] === 'true') { + Monitor.IPCManager.emit('updatePollingInterval', { newPollingIntervalSeconds }); } } }); @@ -297,6 +292,7 @@ export class Monitor extends IPCMessageReceiver { }; } +// eslint-disable-next-line no-redeclare export namespace Monitor { export enum IntrinsicEvents { KeyGenerated = 'key_generated', diff --git a/src/server/DashSession/Session/agents/process_message_router.ts b/src/server/DashSession/Session/agents/process_message_router.ts index 0745ea455..3e2b7d8d0 100644 --- a/src/server/DashSession/Session/agents/process_message_router.ts +++ b/src/server/DashSession/Session/agents/process_message_router.ts @@ -1,7 +1,6 @@ -import { MessageHandler, PromisifiedIPCManager, HandlerMap } from "./promisified_ipc_manager"; +import { MessageHandler, PromisifiedIPCManager, HandlerMap } from './promisified_ipc_manager'; export default abstract class IPCMessageReceiver { - protected static IPCManager: PromisifiedIPCManager; protected handlers: HandlerMap = {}; @@ -18,7 +17,7 @@ export default abstract class IPCMessageReceiver { } else { handlers.push(handler); } - } + }; /** * Unregister a given listener at this message. @@ -31,11 +30,10 @@ export default abstract class IPCMessageReceiver { handlers.splice(index, 1); } } - } + }; - /** + /** * Unregister all listeners at this message. */ public clearMessageListeners = (...names: string[]) => names.map(name => delete this.handlers[name]); - -} \ No newline at end of file +} diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts index 76e218977..99b4d4de3 100644 --- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts +++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts @@ -1,13 +1,14 @@ -import { Utilities } from '../utilities/utilities'; import { ChildProcess } from 'child_process'; +import { Utilities } from '../utilities/utilities'; /** - * Convenience constructor - * @param target the process / worker to which to attach the specialized listeners + * Specifies a general message format for this API */ -export function manage(target: IPCTarget, handlers?: HandlerMap) { - return new PromisifiedIPCManager(target, handlers); -} +export type Message = { + name: string; + args?: T; +}; +export type MessageHandler = (args: T) => any | Promise; /** * Captures the logic to execute upon receiving a message @@ -22,15 +23,10 @@ export type HandlerMap = { [name: string]: MessageHandler[] }; */ export type IPCTarget = NodeJS.Process | ChildProcess; -/** - * Specifies a general message format for this API - */ -export type Message = { - name: string; - args?: T; -}; -export type MessageHandler = (args: T) => any | Promise; - +interface Metadata { + isResponse: boolean; + id: string; +} /** * When a message is emitted, it is embedded with private metadata * to facilitate the resolution of promises, etc. @@ -38,10 +34,6 @@ export type MessageHandler = (args: T) => any | Promise; interface InternalMessage extends Message { metadata: Metadata; } -interface Metadata { - isResponse: boolean; - id: string; -} /** * Allows for the transmission of the error's key features over IPC. @@ -95,7 +87,7 @@ export class PromisifiedIPCManager { } return new Promise>(resolve => { const messageId = Utilities.guid(); - type InternalMessageHandler = (message: any /* MessageListener*/) => any | Promise; + type InternalMessageHandler = (message: any /* MessageListener */) => any | Promise; const responseHandler: InternalMessageHandler = ({ metadata: { id, isResponse }, args }) => { if (isResponse && id === messageId) { this.target.removeListener('message', responseHandler); @@ -118,8 +110,8 @@ export class PromisifiedIPCManager { * completion response for each of the pending messages, allowing their * promises in the caller to resolve. */ - public destroy = () => { - return new Promise(async resolve => { + public destroy = () => + new Promise(async resolve => { if (this.callerIsTarget) { this.destroyHelper(); } else { @@ -127,7 +119,6 @@ export class PromisifiedIPCManager { } resolve(); }); - }; /** * Dispatches the dummy responses and sets the isDestroyed flag to true. @@ -168,12 +159,20 @@ export class PromisifiedIPCManager { error = e; } if (!this.isDestroyed && this.target.send) { - const metadata = { id, isResponse: true }; + const metadataRes = { id, isResponse: true }; const response: Response = { results, error }; - const message = { name, args: response, metadata }; + const messageRes = { name, args: response, metadata: metadataRes }; delete this.pendingMessages[id]; - this.target.send(message); + this.target.send(messageRes); } } }; } + +/** + * Convenience constructor + * @param target the process / worker to which to attach the specialized listeners + */ +export function manage(target: IPCTarget, handlers?: HandlerMap) { + return new PromisifiedIPCManager(target, handlers); +} diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts index d8b3ee80b..85e1b31d6 100644 --- a/src/server/DashSession/Session/agents/server_worker.ts +++ b/src/server/DashSession/Session/agents/server_worker.ts @@ -1,10 +1,10 @@ -import cluster from "cluster"; -import { green, red, white, yellow } from "colors"; -import { get } from "request-promise"; -import { ExitHandler } from "./applied_session_agent"; -import { Monitor } from "./monitor"; -import IPCMessageReceiver from "./process_message_router"; -import { ErrorLike, manage } from "./promisified_ipc_manager"; +import cluster from 'cluster'; +import { green, red, white, yellow } from 'colors'; +import { get } from 'request-promise'; +import { ExitHandler } from './applied_session_agent'; +import { Monitor } from './monitor'; +import IPCMessageReceiver from './process_message_router'; +import { ErrorLike, manage } from './promisified_ipc_manager'; /** * Effectively, each worker repairs the connection to the server by reintroducing a consistent state @@ -23,18 +23,17 @@ export class ServerWorker extends IPCMessageReceiver { private isInitialized = false; public static Create(work: Function) { if (cluster.isPrimary) { - console.error(red("cannot create a worker on the monitor process.")); + console.error(red('cannot create a worker on the monitor process.')); process.exit(1); } else if (++ServerWorker.count > 1) { - ServerWorker.IPCManager.emit("kill", { - reason: "cannot create more than one worker on a given worker process.", + ServerWorker.IPCManager.emit('kill', { + reason: 'cannot create more than one worker on a given worker process.', graceful: false, - errorCode: 1 + errorCode: 1, }); process.exit(1); - } else { - return new ServerWorker(work); } + return new ServerWorker(work); } /** @@ -48,7 +47,7 @@ export class ServerWorker extends IPCMessageReceiver { * server worker (child process). This will also kill * this process (child process). */ - public killSession = (reason: string, graceful = true, errorCode = 0) => this.emit("kill", { reason, graceful, errorCode }); + public killSession = (reason: string, graceful = true, errorCode = 0) => this.emit('kill', { reason, graceful, errorCode }); /** * A convenience wrapper to tell the session monitor (parent process) @@ -60,7 +59,7 @@ export class ServerWorker extends IPCMessageReceiver { super(); this.configureInternalHandlers(); ServerWorker.IPCManager = manage(process, this.handlers); - this.lifecycleNotification(green(`initializing process... ${white(`[${process.execPath} ${process.execArgv.join(" ")}]`)}`)); + this.lifecycleNotification(green(`initializing process... ${white(`[${process.execPath} ${process.execArgv.join(' ')}]`)}`)); const { pollingRoute, serverPort, pollingIntervalSeconds, pollingFailureTolerance } = process.env; this.serverPort = Number(serverPort); @@ -78,8 +77,10 @@ export class ServerWorker extends IPCMessageReceiver { */ protected configureInternalHandlers = () => { // updates the local values of variables to the those sent from master - this.on("updatePollingInterval", ({ newPollingIntervalSeconds }) => this.pollingIntervalSeconds = newPollingIntervalSeconds); - this.on("manualExit", async ({ isSessionEnd }) => { + this.on('updatePollingInterval', ({ newPollingIntervalSeconds }) => { + this.pollingIntervalSeconds = newPollingIntervalSeconds; + }); + this.on('manualExit', async ({ isSessionEnd }) => { await ServerWorker.IPCManager.destroy(); await this.executeExitHandlers(isSessionEnd); process.exit(0); @@ -91,7 +92,7 @@ export class ServerWorker extends IPCMessageReceiver { const appropriateError = reason instanceof Error ? reason : new Error(`unhandled rejection: ${reason}`); this.proactiveUnplannedExit(appropriateError); }); - } + }; /** * Execute the list of functions registered to be called @@ -102,7 +103,7 @@ export class ServerWorker extends IPCMessageReceiver { /** * Notify master thread (which will log update in the console) of initialization via IPC. */ - public lifecycleNotification = (event: string) => this.emit("lifecycle", { event }); + public lifecycleNotification = (event: string) => this.emit('lifecycle', { event }); /** * Called whenever the process has a reason to terminate, either through an uncaught exception @@ -120,11 +121,11 @@ export class ServerWorker extends IPCMessageReceiver { this.lifecycleNotification(red(error.message)); await ServerWorker.IPCManager.destroy(); process.exit(1); - } + }; /** * This monitors the health of the server by submitting a get request to whatever port / route specified - * by the configuration every n seconds, where n is also given by the configuration. + * by the configuration every n seconds, where n is also given by the configuration. */ private pollServer = async (): Promise => { await new Promise(resolve => { @@ -156,6 +157,5 @@ export class ServerWorker extends IPCMessageReceiver { }); // controlled, asynchronous infinite recursion achieves a persistent poll that does not submit a new request until the previous has completed this.pollServer(); - } - + }; } diff --git a/src/server/DashSession/Session/utilities/repl.ts b/src/server/DashSession/Session/utilities/repl.ts index 643141286..5d9f15e4c 100644 --- a/src/server/DashSession/Session/utilities/repl.ts +++ b/src/server/DashSession/Session/utilities/repl.ts @@ -1,5 +1,5 @@ -import { createInterface, Interface } from "readline"; -import { red, green, white } from "colors"; +import { createInterface, Interface } from 'readline'; +import { red, green, white } from 'colors'; export interface Configuration { identifier: () => string | string; @@ -32,76 +32,82 @@ export default class Repl { this.interface = createInterface(process.stdin, process.stdout).on('line', this.considerInput); } - private resolvedIdentifier = () => typeof this.identifier === "string" ? this.identifier : this.identifier(); + private resolvedIdentifier = () => (typeof this.identifier === 'string' ? this.identifier : this.identifier()); private usage = (command: string, validCommand: boolean) => { if (validCommand) { const formatted = white(command); - const patterns = green(this.commandMap.get(command)!.map(({ argPatterns }) => `${formatted} ${argPatterns.join(" ")}`).join('\n')); + const patterns = green( + this.commandMap + .get(command)! + .map(({ argPatterns }) => `${formatted} ${argPatterns.join(' ')}`) + .join('\n') + ); return `${this.resolvedIdentifier()}\nthe given arguments do not match any registered patterns for ${formatted}\nthe list of valid argument patterns is given by:\n${patterns}`; - } else { - const resolved = this.keys; - if (resolved) { - return resolved; - } - const members: string[] = []; - const keys = this.commandMap.keys(); - let next: IteratorResult; - while (!(next = keys.next()).done) { - members.push(next.value); - } - return `${this.resolvedIdentifier()} commands: { ${members.sort().join(", ")} }`; } - } + const resolved = this.keys; + if (resolved) { + return resolved; + } + const members: string[] = []; + const keys = this.commandMap.keys(); + let next: IteratorResult; + // eslint-disable-next-line no-cond-assign + while (!(next = keys.next()).done) { + members.push(next.value); + } + return `${this.resolvedIdentifier()} commands: { ${members.sort().join(', ')} }`; + }; private success = (command: string) => `${this.resolvedIdentifier()} completed local execution of ${white(command)}`; public registerCommand = (basename: string, argPatterns: (RegExp | string)[], action: ReplAction) => { const existing = this.commandMap.get(basename); - const converted = argPatterns.map(input => input instanceof RegExp ? input : new RegExp(input)); + const converted = argPatterns.map(input => (input instanceof RegExp ? input : new RegExp(input))); const registration = { argPatterns: converted, action }; if (existing) { existing.push(registration); } else { this.commandMap.set(basename, [registration]); } - } + }; private invalid = (command: string, validCommand: boolean) => { - console.log(red(typeof this.onInvalid === "string" ? this.onInvalid : this.onInvalid(command, validCommand))); + console.log(red(typeof this.onInvalid === 'string' ? this.onInvalid : this.onInvalid(command, validCommand))); this.busy = false; - } + }; private valid = (command: string) => { - console.log(green(typeof this.onValid === "string" ? this.onValid : this.onValid(command))); + console.log(green(typeof this.onValid === 'string' ? this.onValid : this.onValid(command))); this.busy = false; - } + }; - private considerInput = async (line: string) => { + private considerInput = async (lineIn: string) => { if (this.busy) { - console.log(red("Busy")); + console.log(red('Busy')); return; } this.busy = true; - line = line.trim(); + let line = lineIn.trim(); if (this.isCaseSensitive) { line = line.toLowerCase(); } const [command, ...args] = line.split(/\s+/g); if (!command) { - return this.invalid(command, false); + this.invalid(command, false); + return; } const registered = this.commandMap.get(command); if (registered) { const { length } = args; const candidates = registered.filter(({ argPatterns: { length: count } }) => count === length); - for (const { argPatterns, action } of candidates) { + candidates.forEach(({ argPatterns, action }: { argPatterns: any; action: any }) => { const parsed: string[] = []; let matched = true; if (length) { for (let i = 0; i < length; i++) { - let matches: RegExpExecArray | null; - if ((matches = argPatterns[i].exec(args[i])) === null) { + const matches = argPatterns[i].exec(args[i]); + if (matches === null) { matched = false; break; } @@ -110,19 +116,17 @@ export default class Repl { } if (!length || matched) { const result = action(parsed); - const resolve = () => this.valid(`${command} ${parsed.join(" ")}`); + const resolve = () => this.valid(`${command} ${parsed.join(' ')}`); if (result instanceof Promise) { result.then(resolve); } else { resolve(); } - return; } - } + }); this.invalid(command, true); } else { this.invalid(command, false); } - } - -} \ No newline at end of file + }; +} diff --git a/src/server/DashSession/Session/utilities/session_config.ts b/src/server/DashSession/Session/utilities/session_config.ts index 266759929..b42c1a3c7 100644 --- a/src/server/DashSession/Session/utilities/session_config.ts +++ b/src/server/DashSession/Session/utilities/session_config.ts @@ -1,85 +1,85 @@ -import { Schema } from "jsonschema"; -import { yellow, red, cyan, green, blue, magenta, Color, grey, gray, white, black } from "colors"; +import { Schema } from 'jsonschema'; +import { yellow, red, cyan, green, blue, magenta, Color, grey, gray, white, black } from 'colors'; const colorPattern = /black|red|green|yellow|blue|magenta|cyan|white|gray|grey/; const identifierProperties: Schema = { - type: "object", + type: 'object', properties: { text: { - type: "string", - minLength: 1 + type: 'string', + minLength: 1, }, color: { - type: "string", - pattern: colorPattern - } - } + type: 'string', + pattern: colorPattern, + }, + }, }; const portProperties: Schema = { - type: "number", + type: 'number', minimum: 443, - maximum: 65535 + maximum: 65535, }; export const configurationSchema: Schema = { - id: "/configuration", - type: "object", + id: '/configuration', + type: 'object', properties: { - showServerOutput: { type: "boolean" }, + showServerOutput: { type: 'boolean' }, ports: { - type: "object", + type: 'object', properties: { server: portProperties, - socket: portProperties + socket: portProperties, }, - required: ["server"], - additionalProperties: true + required: ['server'], + additionalProperties: true, }, identifiers: { - type: "object", + type: 'object', properties: { master: identifierProperties, worker: identifierProperties, - exec: identifierProperties - } + exec: identifierProperties, + }, }, polling: { - type: "object", + type: 'object', additionalProperties: false, properties: { intervalSeconds: { - type: "number", + type: 'number', minimum: 1, - maximum: 86400 + maximum: 86400, }, route: { - type: "string", - pattern: /\/[a-zA-Z]*/g + type: 'string', + pattern: /\/[a-zA-Z]*/g, }, failureTolerance: { - type: "number", + type: 'number', minimum: 0, - } - } + }, + }, }, - } + }, }; -type ColorLabel = "yellow" | "red" | "cyan" | "green" | "blue" | "magenta" | "grey" | "gray" | "white" | "black"; +type ColorLabel = 'yellow' | 'red' | 'cyan' | 'green' | 'blue' | 'magenta' | 'grey' | 'gray' | 'white' | 'black'; export const colorMapping: Map = new Map([ - ["yellow", yellow], - ["red", red], - ["cyan", cyan], - ["green", green], - ["blue", blue], - ["magenta", magenta], - ["grey", grey], - ["gray", gray], - ["white", white], - ["black", black] + ['yellow', yellow], + ['red', red], + ['cyan', cyan], + ['green', green], + ['blue', blue], + ['magenta', magenta], + ['grey', grey], + ['gray', gray], + ['white', white], + ['black', black], ]); interface Identifier { @@ -108,22 +108,22 @@ export const defaultConfig: Configuration = { showServerOutput: false, identifiers: { master: { - text: "__monitor__", - color: "yellow" + text: '__monitor__', + color: 'yellow', }, worker: { - text: "__server__", - color: "magenta" + text: '__server__', + color: 'magenta', }, exec: { - text: "__exec__", - color: "green" - } + text: '__exec__', + color: 'green', + }, }, ports: { server: 1050 }, polling: { - route: "/", + route: '/', intervalSeconds: 30, - failureTolerance: 0 - } -}; \ No newline at end of file + failureTolerance: 0, + }, +}; diff --git a/src/server/DashSession/Session/utilities/utilities.ts b/src/server/DashSession/Session/utilities/utilities.ts index eb8de9d7e..a2ba29c67 100644 --- a/src/server/DashSession/Session/utilities/utilities.ts +++ b/src/server/DashSession/Session/utilities/utilities.ts @@ -1,31 +1,16 @@ -import { v4 } from "uuid"; +import { v4 } from 'uuid'; export namespace Utilities { - export function guid() { return v4(); } - /** - * At any arbitrary layer of nesting within the configuration objects, any single value that - * is not specified by the configuration is given the default counterpart. If, within an object, - * one peer is given by configuration and two are not, the one is preserved while the two are given - * the default value. - * @returns the composition of all of the assigned objects, much like Object.assign(), but with more - * granularity in the overwriting of nested objects - */ - export function preciseAssign(target: any, ...sources: any[]): any { - for (const source of sources) { - preciseAssignHelper(target, source); - } - return target; - } - export function preciseAssignHelper(target: any, source: any) { - Array.from(new Set([...Object.keys(target), ...Object.keys(source)])).map(property => { - let targetValue: any, sourceValue: any; - if (sourceValue = source[property]) { - if (typeof sourceValue === "object" && typeof (targetValue = target[property]) === "object") { + Array.from(new Set([...Object.keys(target), ...Object.keys(source)])).forEach(property => { + const targetValue = target[property]; + const sourceValue = source[property]; + if (sourceValue) { + if (typeof sourceValue === 'object' && typeof targetValue === 'object') { preciseAssignHelper(targetValue, sourceValue); } else { target[property] = sourceValue; @@ -34,4 +19,18 @@ export namespace Utilities { }); } -} \ No newline at end of file + /** + * At any arbitrary layer of nesting within the configuration objects, any single value that + * is not specified by the configuration is given the default counterpart. If, within an object, + * one peer is given by configuration and two are not, the one is preserved while the two are given + * the default value. + * @returns the composition of all of the assigned objects, much like Object.assign(), but with more + * granularity in the overwriting of nested objects + */ + export function preciseAssign(target: any, ...sources: any[]): any { + sources.forEach(source => { + preciseAssignHelper(target, source); + }); + return target; + } +} diff --git a/src/server/DashStats.ts b/src/server/DashStats.ts index 485ab9f99..808d2c6f2 100644 --- a/src/server/DashStats.ts +++ b/src/server/DashStats.ts @@ -1,7 +1,6 @@ import { cyan, magenta } from 'colors'; import { Response } from 'express'; import * as fs from 'fs'; -import SocketIO from 'socket.io'; import { socketMap, timeMap, userOperations } from './SocketData'; /** @@ -242,7 +241,7 @@ export namespace DashStats { * @param username the username in the format of "username@domain.com logged in" * @param socket the websocket associated with the current connection */ - export function logUserLogin(username: string | undefined, socket: SocketIO.Socket) { + export function logUserLogin(username: string | undefined) { if (!(username === undefined)) { const currentDate = new Date(); console.log(magenta(`User ${username.split(' ')[0]} logged in at: ${currentDate.toISOString()}`)); @@ -267,7 +266,7 @@ export namespace DashStats { * @param username the username in the format of "username@domain.com logged in" * @param socket the websocket associated with the current connection. */ - export function logUserLogout(username: string | undefined, socket: SocketIO.Socket) { + export function logUserLogout(username: string | undefined) { if (!(username === undefined)) { const currentDate = new Date(); diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 3d8325da9..08cea1de5 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -186,8 +186,8 @@ export namespace DashUploadUtils { const image = await request.get(source, { encoding: null }); const { /* data, */ error } = await new Promise<{ data: any; error: any }>(resolve => { // eslint-disable-next-line no-new - new ExifImage({ image }, (error, data) => { - const reason = (error as any)?.code; + new ExifImage({ image }, (exifError, data) => { + const reason = (exifError as any)?.code; resolve({ data, error: reason }); }); }); @@ -506,8 +506,8 @@ export namespace DashUploadUtils { if (fExists(fileKey, Directory.pdfs) && fExists(textFilename, Directory.text)) { fs.unlink(file.filepath, () => {}); return new Promise(res => { - const textFilename = `${fileKey.substring(0, fileKey.length - 4)}.txt`; - const readStream = createReadStream(serverPathToFile(Directory.text, textFilename)); + const pdfTextFilename = `${fileKey.substring(0, fileKey.length - 4)}.txt`; + const readStream = createReadStream(serverPathToFile(Directory.text, pdfTextFilename)); let rawText = ''; readStream .on('data', chunk => { diff --git a/src/server/GarbageCollector.ts b/src/server/GarbageCollector.ts index 423c719c2..041f65592 100644 --- a/src/server/GarbageCollector.ts +++ b/src/server/GarbageCollector.ts @@ -1,11 +1,15 @@ +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-continue */ +/* eslint-disable no-cond-assign */ +/* eslint-disable no-restricted-syntax */ import * as fs from 'fs'; import * as path from 'path'; import { Database } from './database'; import { Search } from './Search'; - function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { for (const key in doc) { + // eslint-disable-next-line no-prototype-builtins if (!doc.hasOwnProperty(key)) { continue; } @@ -13,22 +17,22 @@ function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { if (field === undefined || field === null) { continue; } - if (field.__type === "proxy" || field.__type === "prefetch_proxy") { + if (field.__type === 'proxy' || field.__type === 'prefetch_proxy') { ids.push(field.fieldId); - } else if (field.__type === "list") { + } else if (field.__type === 'list') { addDoc(field.fields, ids, files); - } else if (typeof field === "string") { - const re = /"(?:dataD|d)ocumentId"\s*:\s*"([\w\-]*)"/g; + } else if (typeof field === 'string') { + const re = /"(?:dataD|d)ocumentId"\s*:\s*"([\w-]*)"/g; let match: string[] | null; while ((match = re.exec(field)) !== null) { ids.push(match[1]); } - } else if (field.__type === "RichTextField") { + } else if (field.__type === 'RichTextField') { const re = /"href"\s*:\s*"(.*?)"/g; let match: string[] | null; while ((match = re.exec(field.Data)) !== null) { const urlString = match[1]; - const split = new URL(urlString).pathname.split("doc/"); + const split = new URL(urlString).pathname.split('doc/'); if (split.length > 1) { ids.push(split[split.length - 1]); } @@ -36,7 +40,7 @@ function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { const re2 = /"src"\s*:\s*"(.*?)"/g; while ((match = re2.exec(field.Data)) !== null) { const urlString = match[1]; - const pathname = new URL(urlString).pathname; + const { pathname } = new URL(urlString); const ext = path.extname(pathname); const fileName = path.basename(pathname, ext); let exts = files[fileName]; @@ -45,9 +49,9 @@ function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { } exts.push(ext); } - } else if (["audio", "image", "video", "pdf", "web", "map"].includes(field.__type)) { + } else if (['audio', 'image', 'video', 'pdf', 'web', 'map'].includes(field.__type)) { const url = new URL(field.url); - const pathname = url.pathname; + const { pathname } = url; const ext = path.extname(pathname); const fileName = path.basename(pathname, ext); let exts = files[fileName]; @@ -60,12 +64,12 @@ function addDoc(doc: any, ids: string[], files: { [name: string]: string[] }) { } async function GarbageCollect(full: boolean = true) { - console.log("start GC"); + console.log('start GC'); const start = Date.now(); // await new Promise(res => setTimeout(res, 3000)); const cursor = await Database.Instance.query({}, { userDocumentId: 1 }, 'users'); const users = await cursor.toArray(); - const ids: string[] = [...users.map((user:any) => user.userDocumentId), ...users.map((user:any) => user.sharingDocumentId), ...users.map((user:any) => user.linkDatabaseId)]; + const ids: string[] = [...users.map((user: any) => user.userDocumentId), ...users.map((user: any) => user.sharingDocumentId), ...users.map((user: any) => user.linkDatabaseId)]; const visited = new Set(); const files: { [name: string]: string[] } = {}; @@ -76,9 +80,11 @@ async function GarbageCollect(full: boolean = true) { if (!fetchIds.length) { continue; } - const docs = await new Promise<{ [key: string]: any }[]>(res => Database.Instance.getDocuments(fetchIds, res)); + const docs = await new Promise<{ [key: string]: any }[]>(res => { + Database.Instance.getDocuments(fetchIds, res); + }); for (const doc of docs) { - const id = doc.id; + const { id } = doc; if (doc === undefined) { console.log(`Couldn't find field with Id ${id}`); continue; @@ -95,19 +101,27 @@ async function GarbageCollect(full: boolean = true) { const notToDelete = Array.from(visited); const toDeleteCursor = await Database.Instance.query({ _id: { $nin: notToDelete } }, { _id: 1 }); - const toDelete: string[] = (await toDeleteCursor.toArray()).map((doc:any) => doc._id); + const toDelete: string[] = (await toDeleteCursor.toArray()).map((doc: any) => doc._id); toDeleteCursor.close(); if (!full) { - await Database.Instance.updateMany({ _id: { $nin: notToDelete } }, { $set: { "deleted": true } }); - await Database.Instance.updateMany({ _id: { $in: notToDelete } }, { $unset: { "deleted": true } }); - console.log(await Search.updateDocuments( - notToDelete.map(id => ({ - id, deleted: { set: null } - })) - .concat(toDelete.map(id => ({ - id, deleted: { set: true } - }))))); - console.log("Done with partial GC"); + await Database.Instance.updateMany({ _id: { $nin: notToDelete } }, { $set: { deleted: true } }); + await Database.Instance.updateMany({ _id: { $in: notToDelete } }, { $unset: { deleted: true } }); + console.log( + await Search.updateDocuments( + notToDelete + .map(id => ({ + id, + deleted: { set: null }, + })) + .concat( + toDelete.map(id => ({ + id, + deleted: { set: true }, + })) + ) + ) + ); + console.log('Done with partial GC'); console.log(`Took ${(Date.now() - start) / 1000} seconds`); } else { let i = 0; @@ -123,15 +137,15 @@ async function GarbageCollect(full: boolean = true) { console.log(`${deleted} documents deleted`); await Search.deleteDocuments(toDelete); - console.log("Cleared search documents"); + console.log('Cleared search documents'); - const folder = "./src/server/public/files/"; + const folder = './src/server/public/files/'; fs.readdir(folder, (_, fileList) => { const filesToDelete = fileList.filter(file => { const ext = path.extname(file); let base = path.basename(file, ext); const existsInDb = (base in files || (base = base.substring(0, base.length - 2)) in files) && files[base].includes(ext); - return file !== ".gitignore" && !existsInDb; + return file !== '.gitignore' && !existsInDb; }); console.log(`Deleting ${filesToDelete.length} files`); filesToDelete.forEach(file => { diff --git a/src/server/MemoryDatabase.ts b/src/server/MemoryDatabase.ts index b74332bf5..1432d91c4 100644 --- a/src/server/MemoryDatabase.ts +++ b/src/server/MemoryDatabase.ts @@ -3,16 +3,15 @@ import { DocumentsCollection, IDatabase } from './IDatabase'; import { Transferable } from './Message'; export class MemoryDatabase implements IDatabase { - private db: { [collectionName: string]: { [id: string]: any } } = {}; private getCollection(collectionName: string) { const collection = this.db[collectionName]; if (collection) { return collection; - } else { - return this.db[collectionName] = {}; } + this.db[collectionName] = {}; + return {}; } public getCollectionNames() { @@ -21,15 +20,15 @@ export class MemoryDatabase implements IDatabase { public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, _upsert?: boolean, collectionName = DocumentsCollection): Promise { const collection = this.getCollection(collectionName); - const set = "$set"; + const set = '$set'; if (set in value) { let currentVal = collection[id] ?? (collection[id] = {}); const val = value[set]; for (const key in val) { - const keys = key.split("."); + const keys = key.split('.'); for (let i = 0; i < keys.length - 1; i++) { const k = keys[i]; - if (typeof currentVal[k] === "object") { + if (typeof currentVal[k] === 'object') { currentVal = currentVal[k]; } else { currentVal[k] = {}; @@ -45,7 +44,7 @@ export class MemoryDatabase implements IDatabase { return Promise.resolve(undefined); } - public updateMany(query: any, update: any, collectionName = DocumentsCollection): Promise { + public updateMany(/* query: any, update: any, collectionName = DocumentsCollection */): Promise { throw new Error("Can't updateMany a MemoryDatabase"); } @@ -54,7 +53,9 @@ export class MemoryDatabase implements IDatabase { } public delete(query: any, collectionName?: string): Promise; + // eslint-disable-next-line no-dupe-class-members public delete(id: string, collectionName?: string): Promise; + // eslint-disable-next-line no-dupe-class-members public delete(id: any, collectionName = DocumentsCollection): Promise { const i = id.id ?? id; delete this.getCollection(collectionName)[i]; @@ -75,7 +76,7 @@ export class MemoryDatabase implements IDatabase { } public insert(value: any, collectionName = DocumentsCollection): Promise { - const id = value.id; + const { id } = value; this.getCollection(collectionName)[id] = value; return Promise.resolve(); } @@ -93,14 +94,18 @@ export class MemoryDatabase implements IDatabase { const count = Math.min(ids.length, 1000); const index = ids.length - count; const fetchIds = ids.splice(index, count).filter(id => !visited.has(id)); - if (!fetchIds.length) { - continue; - } - const docs = await new Promise<{ [key: string]: any }[]>(res => this.getDocuments(fetchIds, res, collectionName)); - for (const doc of docs) { - const id = doc.id; - visited.add(id); - ids.push(...(await fn(doc))); + if (fetchIds.length) { + // eslint-disable-next-line no-await-in-loop + const docs = await new Promise<{ [key: string]: any }[]>(res => { + this.getDocuments(fetchIds, res, collectionName); + }); + // eslint-disable-next-line no-restricted-syntax + for (const doc of docs) { + const { id } = doc; + visited.add(id); + // eslint-disable-next-line no-await-in-loop + ids.push(...(await fn(doc))); + } } } } diff --git a/src/server/PdfTypes.ts b/src/server/PdfTypes.ts index e87f08e1d..fb435a677 100644 --- a/src/server/PdfTypes.ts +++ b/src/server/PdfTypes.ts @@ -1,21 +1,19 @@ -export interface ParsedPDF { - numpages: number; - numrender: number; - info: PDFInfo; - metadata: PDFMetadata; - version: string; //https://mozilla.github.io/pdf.js/getting_started/ - text: string; -} - export interface PDFInfo { PDFFormatVersion: string; IsAcroFormPresent: boolean; IsXFAPresent: boolean; [key: string]: any; } - export interface PDFMetadata { parse(): void; get(name: string): string; has(name: string): boolean; -} \ No newline at end of file +} +export interface ParsedPDF { + numpages: number; + numrender: number; + info: PDFInfo; + metadata: PDFMetadata; + version: string; // https://mozilla.github.io/pdf.js/getting_started/ + text: string; +} diff --git a/src/server/ProcessFactory.ts b/src/server/ProcessFactory.ts index f69eda4c3..3791b0e1e 100644 --- a/src/server/ProcessFactory.ts +++ b/src/server/ProcessFactory.ts @@ -1,44 +1,42 @@ -import { ChildProcess, spawn, StdioOptions } from "child_process"; -import { existsSync, mkdirSync } from "fs"; -import { Stream } from "stream"; +import { ChildProcess, spawn, StdioOptions } from 'child_process'; +import { existsSync, mkdirSync } from 'fs'; +import { rimraf } from 'rimraf'; +import { Stream } from 'stream'; import { fileDescriptorFromStream, pathFromRoot } from './ActionUtilities'; -import { rimraf } from "rimraf"; - -export namespace ProcessFactory { - - export type Sink = "pipe" | "ipc" | "ignore" | "inherit" | Stream | number | null | undefined; - - export async function createWorker(command: string, args?: readonly string[], stdio?: StdioOptions | "logfile", detached = true): Promise { - if (stdio === "logfile") { - const log_fd = await Logger.create(command, args); - stdio = ["ignore", log_fd, log_fd]; - } - const child = spawn(command, args ?? [], { detached, stdio }); - child.unref(); - return child; - } - -} export namespace Logger { - - const logPath = pathFromRoot("./logs"); + const logPath = pathFromRoot('./logs'); export async function initialize() { if (existsSync(logPath)) { if (!process.env.SPAWNED) { - await new Promise(resolve => rimraf(logPath).then(resolve)); + await new Promise(resolve => { + rimraf(logPath).then(resolve); + }); } } mkdirSync(logPath); } - export async function create(command: string, args?: readonly string[]): Promise { - return fileDescriptorFromStream(generate_log_path(command, args)); + function generateLogPath(command: string, args?: readonly string[]) { + return pathFromRoot(`./logs/${command}-${args?.length}-${new Date().toUTCString()}.log`); } - function generate_log_path(command: string, args?: readonly string[]) { - return pathFromRoot(`./logs/${command}-${args?.length}-${new Date().toUTCString()}.log`); + export async function create(command: string, args?: readonly string[]): Promise { + return fileDescriptorFromStream(generateLogPath(command, args)); } +} +export namespace ProcessFactory { + export type Sink = 'pipe' | 'ipc' | 'ignore' | 'inherit' | Stream | number | null | undefined; -} \ No newline at end of file + export async function createWorker(command: string, args?: readonly string[], stdio?: StdioOptions | 'logfile', detached = true): Promise { + if (stdio === 'logfile') { + const logFd = await Logger.create(command, args); + // eslint-disable-next-line no-param-reassign + stdio = ['ignore', logFd, logFd]; + } + const child = spawn(command, args ?? [], { detached, stdio }); + child.unref(); + return child; + } +} diff --git a/src/server/RouteSubscriber.ts b/src/server/RouteSubscriber.ts index a1cf7c1c4..b923805a8 100644 --- a/src/server/RouteSubscriber.ts +++ b/src/server/RouteSubscriber.ts @@ -18,9 +18,8 @@ export default class RouteSubscriber { public get build() { let output = this._root; if (this.requestParameters.length) { - output = `${output}/:${this.requestParameters.join("/:")}`; + output = `${output}/:${this.requestParameters.join('/:')}`; } return output; } - -} \ No newline at end of file +} diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts index 7db1c2dae..0a961f570 100644 --- a/src/server/SharedMediaTypes.ts +++ b/src/server/SharedMediaTypes.ts @@ -22,24 +22,17 @@ export namespace Upload { return 'duration' in uploadResponse; } + export interface AccessPathInfo { + [suffix: string]: { client: string; server: string }; + } export interface FileInformation { accessPaths: AccessPathInfo; rawText?: string; duration?: number; } - - export type FileResponse = { source: File; result: T | Error }; - - export type ImageInformation = FileInformation & InspectionResults; - - export type VideoInformation = FileInformation & VideoResults; - - export interface AccessPathInfo { - [suffix: string]: { client: string; server: string }; - } - - export interface VideoResults { - duration: number; + export interface EnrichedExifData { + data: ExifData & ExifData['gps']; + error?: string; } export interface InspectionResults { source: string; @@ -51,9 +44,13 @@ export namespace Upload { nativeHeight: number; filename?: string; } - - export interface EnrichedExifData { - data: ExifData & ExifData['gps']; - error?: string; + export interface VideoResults { + duration: number; } + + export type FileResponse = { source: File; result: T | Error }; + + export type ImageInformation = FileInformation & InspectionResults; + + export type VideoInformation = FileInformation & VideoResults; } diff --git a/src/server/apis/google/CredentialsLoader.ts b/src/server/apis/google/CredentialsLoader.ts index ef1f9a91e..46dc00b8a 100644 --- a/src/server/apis/google/CredentialsLoader.ts +++ b/src/server/apis/google/CredentialsLoader.ts @@ -1,10 +1,9 @@ -import { readFile, readFileSync } from "fs"; -import { pathFromRoot } from "../../ActionUtilities"; -import { SecureContextOptions } from "tls"; -import { blue, red } from "colors"; +import { readFile, readFileSync } from 'fs'; +import { SecureContextOptions } from 'tls'; +import { blue, red } from 'colors'; +import { pathFromRoot } from '../../ActionUtilities'; export namespace GoogleCredentialsLoader { - export interface InstalledCredentials { client_id: string; project_id: string; @@ -28,18 +27,16 @@ export namespace GoogleCredentialsLoader { }); }); } - } export namespace SSL { - export let Credentials: SecureContextOptions = {}; export let Loaded = false; const suffixes = { - privateKey: ".key", - certificate: ".crt", - caBundle: "-ca.crt" + privateKey: '.key', + certificate: '.crt', + caBundle: '-ca.crt', }; export async function loadCredentials() { @@ -57,11 +54,10 @@ export namespace SSL { } export function exit() { - console.log(red("Running this server in release mode requires the following SSL credentials in the project root:")); - const serverName = process.env.serverName ? process.env.serverName : "{process.env.serverName}"; + console.log(red('Running this server in release mode requires the following SSL credentials in the project root:')); + const serverName = process.env.serverName ? process.env.serverName : '{process.env.serverName}'; Object.values(suffixes).forEach(suffix => console.log(blue(`${serverName}${suffix}`))); - console.log(red("Please ensure these files exist and restart, or run this in development mode.")); + console.log(red('Please ensure these files exist and restart, or run this in development mode.')); process.exit(0); } - } diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts index 55e5fd7c0..d3acc968b 100644 --- a/src/server/apis/google/GoogleApiServerUtils.ts +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -57,12 +57,12 @@ export namespace GoogleApiServerUtils { * global, intentionally unauthenticated worker OAuth2 client instance. */ export function processProjectCredentials(): void { - const { client_secret, client_id, redirect_uris } = GoogleCredentialsLoader.ProjectCredentials; + const { client_secret: clientSecret, client_id: clientId, redirect_uris: redirectUris } = GoogleCredentialsLoader.ProjectCredentials; // initialize the global authorization client oAuthOptions = { - clientId: client_id, - clientSecret: client_secret, - redirectUri: redirect_uris[0], + clientId, + clientSecret, + redirectUri: redirectUris[0], }; worker = generateClient(); } diff --git a/src/server/apis/google/SharedTypes.ts b/src/server/apis/google/SharedTypes.ts index 9ad6130b6..f5e0e2e2b 100644 --- a/src/server/apis/google/SharedTypes.ts +++ b/src/server/apis/google/SharedTypes.ts @@ -1,9 +1,3 @@ -export interface NewMediaItemResult { - uploadToken: string; - status: { code: number, message: string }; - mediaItem: MediaItem; -} - export interface MediaItem { id: string; description: string; @@ -17,5 +11,10 @@ export interface MediaItem { }; filename: string; } +export interface NewMediaItemResult { + uploadToken: string; + status: { code: number; message: string }; + mediaItem: MediaItem; +} -export type MediaItemCreationResult = { newMediaItemResults: NewMediaItemResult[] }; \ No newline at end of file +export type MediaItemCreationResult = { newMediaItemResults: NewMediaItemResult[] }; diff --git a/src/server/apis/youtube/youtubeApiSample.d.ts b/src/server/apis/youtube/youtubeApiSample.d.ts index 427f54608..97c3f0b54 100644 --- a/src/server/apis/youtube/youtubeApiSample.d.ts +++ b/src/server/apis/youtube/youtubeApiSample.d.ts @@ -1,2 +1,2 @@ declare const YoutubeApi: any; -export = YoutubeApi; \ No newline at end of file +export = YoutubeApi; diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts index 3bc21ecb6..a288bfeab 100644 --- a/src/server/authentication/DashUserModel.ts +++ b/src/server/authentication/DashUserModel.ts @@ -73,9 +73,9 @@ userSchema.pre('save', function save(next) { user.password, salt, () => {}, - (err: mongoose.Error, hash: string) => { - if (err) { - return next(err); + (cryptErr: mongoose.Error, hash: string) => { + if (cryptErr) { + return next(cryptErr); } user.password = hash; next(); @@ -97,7 +97,7 @@ const comparePassword: comparePasswordFunction = function (this: DashUserModel, userSchema.methods.comparePassword = comparePassword; -const User = mongoose.model('User', userSchema); +const User: any = mongoose.model('User', userSchema); export function initializeGuest() { new User({ email: 'guest', diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts index a9cf6698b..a5222e531 100644 --- a/src/server/authentication/Passport.ts +++ b/src/server/authentication/Passport.ts @@ -1,6 +1,6 @@ import * as passport from 'passport'; import * as passportLocal from 'passport-local'; -import { DashUserModel, default as User } from './DashUserModel'; +import User, { DashUserModel } from './DashUserModel'; const LocalStrategy = passportLocal.Strategy; @@ -11,22 +11,25 @@ passport.serializeUser((req, user, done) => { passport.deserializeUser((id, done) => { User.findById(id) .exec() - .then(user => done(undefined, user)); + .then((user: any) => done(undefined, user)); }); // AUTHENTICATE JUST WITH EMAIL AND PASSWORD passport.use( new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => { User.findOne({ email: email.toLowerCase() }) - .then(user => { - if (!user) return done(undefined, false, { message: 'Invalid email or password' }); // invalid email - (user as any as DashUserModel).comparePassword(password, (error: Error, isMatch: boolean) => { - if (error) return done(error); - if (!isMatch) return done(undefined, false, { message: 'Invalid email or password' }); // invalid password - // valid authentication HERE - return done(undefined, user); - }); + .then((user: any) => { + if (!user) { + done(undefined, false, { message: 'Invalid email or password' }); // invalid email + } else { + (user as any as DashUserModel).comparePassword(password, (error: Error, isMatch: boolean) => { + if (error) return done(error); + if (!isMatch) return done(undefined, false, { message: 'Invalid email or password' }); // invalid password + // valid authentication HERE + return done(undefined, user); + }); + } }) - .catch(error => done(error)); + .catch((error: any) => done(error)); }) ); diff --git a/src/server/database.ts b/src/server/database.ts index 3a28dc87e..ff5635b2c 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -58,6 +58,7 @@ export namespace Database { } } + // eslint-disable-next-line @typescript-eslint/no-shadow export class Database implements IDatabase { private MongoClient = mongodb.MongoClient; private currentWrites: { [id: string]: Promise } = {}; diff --git a/src/server/index.ts b/src/server/index.ts index 5a86f36d9..4374a72b7 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,15 +1,14 @@ import { yellow } from 'colors'; +// eslint-disable-next-line import/no-extraneous-dependencies import * as dotenv from 'dotenv'; import * as mobileDetect from 'mobile-detect'; import * as path from 'path'; -import * as qs from 'query-string'; import { logExecution } from './ActionUtilities'; import { AdminPrivileges, resolvedPorts } from './SocketData'; import DataVizManager from './ApiManagers/DataVizManager'; import DeleteManager from './ApiManagers/DeleteManager'; import DownloadManager from './ApiManagers/DownloadManager'; import GeneralGoogleManager from './ApiManagers/GeneralGoogleManager'; -//import GooglePhotosManager from './ApiManagers/GooglePhotosManager'; import { SearchManager } from './ApiManagers/SearchManager'; import SessionManager from './ApiManagers/SessionManager'; import UploadManager from './ApiManagers/UploadManager'; @@ -26,8 +25,11 @@ import { Logger } from './ProcessFactory'; import RouteManager, { Method, PublicHandler } from './RouteManager'; import RouteSubscriber from './RouteSubscriber'; import initializeServer from './server_Initialization'; +// import GooglePhotosManager from './ApiManagers/GooglePhotosManager'; + dotenv.config(); export const onWindows = process.platform === 'win32'; +// eslint-disable-next-line import/no-mutable-exports export let sessionAgent: AppliedSessionAgent; /** @@ -60,8 +62,18 @@ async function preliminaryFunctions() { * that will manage the registration of new routes * with the server */ -function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) { - const managers = [new SessionManager(), new UserManager(), new UploadManager(), new DownloadManager(), new SearchManager(), new DeleteManager(), new UtilManager(), new GeneralGoogleManager(), /* new GooglePhotosManager(),*/ new DataVizManager()]; +function routeSetter({ addSupervisedRoute, logRegistrationOutcome }: RouteManager) { + const managers = [ + new SessionManager(), + new UserManager(), + new UploadManager(), + new DownloadManager(), + new SearchManager(), + new DeleteManager(), + new UtilManager(), + new GeneralGoogleManager(), + /* new GooglePhotosManager(), */ new DataVizManager(), + ]; // initialize API Managers console.log(yellow('\nregistering server routes...')); @@ -102,6 +114,7 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: }); const serve: PublicHandler = ({ req, res }) => { + // eslint-disable-next-line new-cap const detector = new mobileDetect(req.headers['user-agent'] || ''); const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html'; res.sendFile(path.join(__dirname, '../../deploy/' + filename)); @@ -116,9 +129,8 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: secureHandler: ({ res, isRelease }) => { const { PASSWORD } = process.env; if (!(isRelease && PASSWORD)) { - return res.redirect('/home'); - } - res.render('admin.pug', { title: 'Enter Administrator Password' }); + res.redirect('/home'); + } else res.render('admin.pug', { title: 'Enter Administrator Password' }); }, }); @@ -128,18 +140,19 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: secureHandler: async ({ req, res, isRelease, user: { id } }) => { const { PASSWORD } = process.env; if (!(isRelease && PASSWORD)) { - return res.redirect('/home'); - } - const { password } = req.body; - const { previous_target } = req.params; - let redirect: string; - if (password === PASSWORD) { - AdminPrivileges.set(id, true); - redirect = `/${previous_target.replace(':', '/')}`; + res.redirect('/home'); } else { - redirect = `/admin/${previous_target}`; + const { password } = req.body; + const { previous_target: previousTarget } = req.params; + let redirect: string; + if (password === PASSWORD) { + AdminPrivileges.set(id, true); + redirect = `/${previousTarget.replace(':', '/')}`; + } else { + redirect = `/admin/${previousTarget}`; + } + res.redirect(redirect); } - res.redirect(redirect); }, }); @@ -149,7 +162,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: secureHandler: serve, publicHandler: ({ req, res, ...remaining }) => { const { originalUrl: target } = req; - const sharing = qs.parse(qs.extract(req.originalUrl), { sort: false }).sharing === 'true'; const docAccess = target.startsWith('/doc/'); // since this is the public handler, there's no meaning of '/home' to speak of // since there's no user logged in, so the only viable operation diff --git a/src/server/updateProtos.ts b/src/server/updateProtos.ts index 2f3772a77..72a44ebf4 100644 --- a/src/server/updateProtos.ts +++ b/src/server/updateProtos.ts @@ -6,15 +6,15 @@ const protos = ['text', 'image', 'web', 'collection', 'kvp', 'video', 'audio', ' await Promise.all( protos.map( protoId => - new Promise(res => + new Promise(res => { Database.Instance.update( protoId, { $set: { 'fields.isBaseProto': true }, }, res - ) - ) + ); + }) ) ); diff --git a/src/server/websocket.ts b/src/server/websocket.ts index cb16bce72..cece8a1b7 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -79,7 +79,7 @@ export namespace WebSocket { timeMap[userEmail] = Date.now(); socketMap.set(socket, userEmail + ' at ' + datetime); userOperations.set(userEmail, 0); - DashStats.logUserLogin(userEmail, socket); + DashStats.logUserLogin(userEmail); } function getField([id, callback]: [string, (result?: Transferable) => void]) { @@ -417,38 +417,12 @@ export namespace WebSocket { socket.in(room).emit('message', message); }); - socket.on('create or join', room => { - console.log('Received request to create or join room ' + room); - - const clientsInRoom = socket.rooms.has(room); - const numClients = clientsInRoom ? Object.keys(room.sockets).length : 0; - console.log('Room ' + room + ' now has ' + numClients + ' client(s)'); - - if (numClients === 0) { - socket.join(room); - console.log('Client ID ' + socket.id + ' created room ' + room); - socket.emit('created', room, socket.id); - } else if (numClients === 1) { - console.log('Client ID ' + socket.id + ' joined room ' + room); - socket.in(room).emit('join', room); - socket.join(room); - socket.emit('joined', room, socket.id); - socket.in(room).emit('ready'); - } else { - // max two clients - socket.emit('full', room); - } - }); - socket.on('ipaddr', () => { - const ifaces = networkInterfaces(); - for (const dev in ifaces) { - ifaces[dev]?.forEach(details => { - if (details.family === 'IPv4' && details.address !== '127.0.0.1') { - socket.emit('ipaddr', details.address); - } - }); - } + networkInterfaces().keys?.forEach(dev => { + if (dev.family === 'IPv4' && dev.address !== '127.0.0.1') { + socket.emit('ipaddr', dev.address); + } + }); }); socket.on('bye', () => { @@ -459,7 +433,7 @@ export namespace WebSocket { const currentUser = socketMap.get(socket); if (!(currentUser === undefined)) { const currentUsername = currentUser.split(' ')[0]; - DashStats.logUserLogout(currentUsername, socket); + DashStats.logUserLogout(currentUsername); delete timeMap[currentUsername]; } }); -- cgit v1.2.3-70-g09d2 From 6de58d7ecbfd14beb7389c6ff56e764b5c00ba25 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 30 Apr 2024 01:10:20 -0400 Subject: changed acl- and some other field- to acl_ and field_ style --- report.20231129.000028.55430.0.001.json | 1343 -------------------- src/client/documents/DocumentTypes.ts | 1 - src/client/documents/Documents.ts | 137 +- src/client/util/CurrentUserUtils.ts | 4 +- src/client/util/ServerStats.tsx | 2 +- src/client/util/SharingManager.tsx | 22 +- src/client/views/DashboardView.tsx | 2 +- src/client/views/FilterPanel.tsx | 2 +- src/client/views/PropertiesView.tsx | 16 +- .../views/collections/CollectionCarouselView.tsx | 2 +- src/client/views/collections/CollectionMenu.tsx | 12 +- src/client/views/collections/CollectionView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 3 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 5 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 2 +- src/fields/Doc.ts | 12 +- src/fields/util.ts | 22 +- src/server/authentication/DashUserModel.ts | 4 +- src/server/authentication/Passport.ts | 4 +- 22 files changed, 127 insertions(+), 1480 deletions(-) delete mode 100644 report.20231129.000028.55430.0.001.json (limited to 'src/server/authentication') diff --git a/report.20231129.000028.55430.0.001.json b/report.20231129.000028.55430.0.001.json deleted file mode 100644 index a29d550ae..000000000 --- a/report.20231129.000028.55430.0.001.json +++ /dev/null @@ -1,1343 +0,0 @@ - -{ - "header": { - "reportVersion": 1, - "event": "Allocation failed - JavaScript heap out of memory", - "trigger": "FatalError", - "filename": "report.20231129.000028.55430.0.001.json", - "dumpEventTime": "2023-11-29T00:00:28Z", - "dumpEventTimeStamp": "1701234028056", - "processId": 55430, - "cwd": "/Users/sarah/Desktop/dash/Dash-Web", - "commandLine": [ - "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node", - "--max-old-space-size=2048", - "/Users/sarah/Desktop/dash/Dash-Web/node_modules/ts-node-dev/lib/wrap.js", - "/Users/sarah/Desktop/dash/Dash-Web/node_modules/fork-ts-checker-webpack-plugin/lib/service.js" - ], - "nodejsVersion": "v12.16.0", - "wordSize": 64, - "arch": "x64", - "platform": "darwin", - "componentVersions": { - "node": "12.16.0", - "v8": "7.8.279.23-node.31", - "uv": "1.34.0", - "zlib": "1.2.11", - "brotli": "1.0.7", - "ares": "1.15.0", - "modules": "72", - "nghttp2": "1.40.0", - "napi": "5", - "llhttp": "2.0.4", - "http_parser": "2.9.3", - "openssl": "1.1.1d", - "cldr": "35.1", - "icu": "64.2", - "tz": "2019c", - "unicode": "12.1" - }, - "release": { - "name": "node", - "lts": "Erbium", - "headersUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0-headers.tar.gz", - "sourceUrl": "https://nodejs.org/download/release/v12.16.0/node-v12.16.0.tar.gz" - }, - "osName": "Darwin", - "osRelease": "22.6.0", - "osVersion": "Darwin Kernel Version 22.6.0: Wed Jul 5 22:21:56 PDT 2023; root:xnu-8796.141.3~6/RELEASE_X86_64", - "osMachine": "x86_64", - "cpus": [ - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 19491620, - "nice": 0, - "sys": 10530730, - "idle": 128029940, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1041080, - "nice": 0, - "sys": 832510, - "idle": 156711500, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 17230140, - "nice": 0, - "sys": 7308720, - "idle": 133550270, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1101080, - "nice": 0, - "sys": 813690, - "idle": 156678120, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 14246350, - "nice": 0, - "sys": 5654800, - "idle": 138314740, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1142930, - "nice": 0, - "sys": 790770, - "idle": 156668310, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 12228700, - "nice": 0, - "sys": 4533960, - "idle": 141551990, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1165420, - "nice": 0, - "sys": 773220, - "idle": 156669740, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 9805920, - "nice": 0, - "sys": 3386750, - "idle": 145216650, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1177080, - "nice": 0, - "sys": 759980, - "idle": 156674610, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 8806950, - "nice": 0, - "sys": 2889820, - "idle": 146774530, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1166250, - "nice": 0, - "sys": 737790, - "idle": 156711830, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 7865920, - "nice": 0, - "sys": 2438950, - "idle": 148205800, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1155370, - "nice": 0, - "sys": 719580, - "idle": 156743860, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 7478040, - "nice": 0, - "sys": 2248690, - "idle": 148810130, - "irq": 0 - }, - { - "model": "Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz", - "speed": 2300, - "user": 1131070, - "nice": 0, - "sys": 695000, - "idle": 156797450, - "irq": 0 - } - ], - "networkInterfaces": [ - { - "name": "lo0", - "internal": true, - "mac": "00:00:00:00:00:00", - "address": "127.0.0.1", - "netmask": "255.0.0.0", - "family": "IPv4" - }, - { - "name": "lo0", - "internal": true, - "mac": "00:00:00:00:00:00", - "address": "::1", - "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - "family": "IPv6", - "scopeid": 0 - }, - { - "name": "lo0", - "internal": true, - "mac": "00:00:00:00:00:00", - "address": "fe80::1", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 1 - }, - { - "name": "en5", - "internal": false, - "mac": "ac:de:48:00:11:22", - "address": "fe80::aede:48ff:fe00:1122", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 4 - }, - { - "name": "en0", - "internal": false, - "mac": "88:66:5a:29:28:77", - "address": "fe80::1467:9dad:c3d1:73a7", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 6 - }, - { - "name": "en0", - "internal": false, - "mac": "88:66:5a:29:28:77", - "address": "10.38.21.14", - "netmask": "255.255.192.0", - "family": "IPv4" - }, - { - "name": "en0", - "internal": false, - "mac": "88:66:5a:29:28:77", - "address": "fde6:e673:d766:4b72:1481:e813:77a2:da34", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 0 - }, - { - "name": "en0", - "internal": false, - "mac": "88:66:5a:29:28:77", - "address": "2620:6e:6000:3100:148e:201a:1a33:145d", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 0 - }, - { - "name": "en0", - "internal": false, - "mac": "88:66:5a:29:28:77", - "address": "2620:6e:6000:3100:5c7f:8d3f:76ba:694b", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 0 - }, - { - "name": "awdl0", - "internal": false, - "mac": "12:df:77:23:4c:4e", - "address": "fe80::10df:77ff:fe23:4c4e", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 9 - }, - { - "name": "llw0", - "internal": false, - "mac": "12:df:77:23:4c:4e", - "address": "fe80::10df:77ff:fe23:4c4e", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 12 - }, - { - "name": "utun0", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::6616:29f1:d83d:2f8d", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 14 - }, - { - "name": "utun1", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::b4dc:ec9a:5d3d:18ab", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 15 - }, - { - "name": "utun2", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::ce81:b1c:bd2c:69e", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 16 - }, - { - "name": "utun3", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::bd9f:d9d8:700d:2011", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 17 - }, - { - "name": "utun4", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::9a49:1de7:e220:2bb", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 18 - }, - { - "name": "utun5", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::93fe:38cf:1393:3191", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 19 - }, - { - "name": "utun6", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::781d:651c:69a7:3d42", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 20 - }, - { - "name": "utun7", - "internal": false, - "mac": "00:00:00:00:00:00", - "address": "fe80::b39:d094:2a6d:e0c5", - "netmask": "ffff:ffff:ffff:ffff::", - "family": "IPv6", - "scopeid": 21 - } - ], - "host": "sarahs-mbp-2.devices.brown.edu" - }, - "javascriptStack": { - "message": "No stack.", - "stack": [ - "Unavailable." - ] - }, - "nativeStack": [ - { - "pc": "0x000000010015c8ca", - "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__1::basic_string, std::__1::allocator> const&, v8::Local) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100080f3e", - "symbol": "node::OnFatalError(char const*, char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100185467", - "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100185403", - "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x000000010030b5f5", - "symbol": "v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x000000010030ccc4", - "symbol": "v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100309b37", - "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100307afd", - "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x00000001003132ba", - "symbol": "v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100313341", - "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x00000001002e065b", - "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100618a18", - "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - }, - { - "pc": "0x0000000100950c19", - "symbol": "Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit [/Users/sarah/.nvm/versions/node/v12.16.0/bin/node]" - } - ], - "javascriptHeap": { - "totalMemory": 2154991616, - "totalCommittedMemory": 2153815368, - "usedMemory": 2136222480, - "availableMemory": 51343040, - "memoryLimit": 2197815296, - "heapSpaces": { - "read_only_space": { - "memorySize": 262144, - "committedMemory": 33088, - "capacity": 32808, - "used": 32808, - "available": 0 - }, - "new_space": { - "memorySize": 8388608, - "committedMemory": 8388528, - "capacity": 4189824, - "used": 69544, - "available": 4120280 - }, - "old_space": { - "memorySize": 1946566656, - "committedMemory": 1946346224, - "capacity": 1939183704, - "used": 1938974448, - "available": 209256 - }, - "code_space": { - "memorySize": 14323712, - "committedMemory": 13779552, - "capacity": 12481792, - "used": 12481792, - "available": 0 - }, - "map_space": { - "memorySize": 1576960, - "committedMemory": 1394440, - "capacity": 1280720, - "used": 1280720, - "available": 0 - }, - "large_object_space": { - "memorySize": 183824384, - "committedMemory": 183824384, - "capacity": 183380384, - "used": 183380384, - "available": 0 - }, - "code_large_object_space": { - "memorySize": 49152, - "committedMemory": 49152, - "capacity": 2784, - "used": 2784, - "available": 0 - }, - "new_large_object_space": { - "memorySize": 0, - "committedMemory": 0, - "capacity": 4189824, - "used": 0, - "available": 4189824 - } - } - }, - "resourceUsage": { - "userCpuSeconds": 306.691, - "kernelCpuSeconds": 15.3869, - "cpuConsumptionPercent": 11.2772, - "maxRss": 2231285841920, - "pageFaults": { - "IORequired": 110, - "IONotRequired": 2487718 - }, - "fsActivity": { - "reads": 0, - "writes": 0 - } - }, - "libuv": [ - ], - "environmentVariables": { - "npm_config_save_dev": "", - "npm_config_legacy_bundling": "", - "npm_config_dry_run": "", - "npm_package_dependencies_request": "^2.88.2", - "npm_package_dependencies_express_flash": "0.0.2", - "npm_package_dependencies__fortawesome_fontawesome_svg_core": "^6.3.0", - "NVM_INC": "/Users/sarah/.nvm/versions/node/v12.16.0/include/node", - "npm_config_viewer": "man", - "npm_config_only": "", - "npm_config_commit_hooks": "true", - "npm_config_browser": "", - "npm_package_gitHead": "5754c1c056ec62e17b5d7badddc4a8fc3637e09f", - "npm_package_dependencies_webpack_dev_middleware": "^5.3.1", - "npm_package_dependencies_webpack_cli": "^4.10.0", - "npm_package_devDependencies_prettier": "^2.7.1", - "npm_package_devDependencies_awesome_typescript_loader": "^5.2.1", - "npm_package_devDependencies__types_archiver": "^3.1.1", - "npm_config_also": "", - "npm_package_dependencies_react_jsx_parser": "^1.29.0", - "npm_package_dependencies_mongoose": "^5.13.14", - "npm_package_dependencies_connect_flash": "^0.1.1", - "npm_package_browser_child_process": "false", - "npm_config_sign_git_commit": "", - "npm_config_rollback": "true", - "npm_package_dependencies_material_ui": "^0.20.2", - "npm_package_devDependencies__types_sharp": "^0.23.1", - "npm_package_devDependencies__types_passport_local": "^1.0.34", - "npm_package_devDependencies__types_dotenv": "^6.1.1", - "npm_package_devDependencies__types_cookie_parser": "^1.4.2", - "TERM_PROGRAM": "Apple_Terminal", - "NODE": "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node", - "npm_config_usage": "", - "npm_config_audit": "true", - "npm_package_dependencies_reveal_js": "^4.3.0", - "npm_package_dependencies_process": "^0.11.10", - "npm_package_dependencies_pdfjs": "^2.4.7", - "npm_package_dependencies_html_to_image": "^0.1.3", - "npm_package_devDependencies_file_loader": "^3.0.1", - "npm_package_devDependencies__types_express_flash": "0.0.0", - "npm_package_scripts_monitor": "cross-env MONITORED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node src/server/index.ts", - "INIT_CWD": "/Users/sarah/Desktop/dash/Dash-Web", - "npm_package_dependencies_rehype_raw": "^6.1.1", - "npm_package_dependencies_react_audio_waveform": "0.0.5", - "npm_package_dependencies_path_browserify": "^1.0.1", - "npm_package_dependencies_nodemailer": "^5.1.1", - "npm_package_dependencies_axios": "^0.19.2", - "npm_package_devDependencies_typescript": "^4.7.4", - "NVM_CD_FLAGS": "-q", - "npm_config_globalignorefile": "/Users/sarah/.nvm/versions/node/v12.16.0/etc/npmignore", - "npm_package_dependencies_react_grid_layout": "^1.3.4", - "npm_package_dependencies_prosemirror_find_replace": "^0.9.0", - "npm_package_dependencies_normalize_css": "^8.0.1", - "npm_package_devDependencies_mocha": "^5.2.0", - "npm_package_devDependencies__types_express_session": "^1.17.5", - "SHELL": "/bin/zsh", - "TERM": "xterm-256color", - "npm_config_shell": "/bin/zsh", - "npm_config_maxsockets": "50", - "npm_config_init_author_url": "", - "npm_package_dependencies_prosemirror_dev_tools": "^3.1.0", - "npm_package_dependencies_p_limit": "^2.2.0", - "npm_package_dependencies_bson": "^4.6.1", - "npm_package_dependencies__types_dom_speech_recognition": "0.0.1", - "npm_package_dependencies__emotion_styled": "^11.11.0", - "npm_package_devDependencies_style_loader": "^0.23.1", - "npm_package_devDependencies__types_react_datepicker": "^3.1.8", - "npm_config_shrinkwrap": "true", - "npm_config_parseable": "", - "npm_config_metrics_registry": "https://registry.npmjs.org/", - "npm_package_dependencies_xregexp": "^4.4.1", - "npm_package_dependencies_shelljs": "^0.8.5", - "npm_package_dependencies_bezier_curve": "^1.0.0", - "npm_package_dependencies__mui_icons_material": "^5.11.16", - "npm_package_devDependencies_tslint": "^5.20.1", - "npm_package_devDependencies__types_react_transition_group": "^4.4.5", - "npm_package_scripts_tsc": "tsc", - "TMPDIR": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/T/", - "npm_config_timing": "", - "npm_config_init_license": "ISC", - "npm_package_dependencies_socket_io": "^2.5.0", - "npm_package_dependencies_probe_image_size": "^4.0.0", - "npm_package_dependencies_canvas": "^2.9.3", - "npm_package_dependencies__hig_theme_data": "^2.23.1", - "npm_package_devDependencies__types_react_select": "^3.1.2", - "npm_package_devDependencies__types_prosemirror_model": "^1.16.1", - "CONDA_SHLVL": "1", - "npm_config_if_present": "", - "npm_package_dependencies_typescript_collections": "^1.3.3", - "npm_package_dependencies_rimraf": "^3.0.0", - "npm_package_dependencies_react_autosuggest": "^9.4.3", - "npm_package_dependencies_flexlayout_react": "^0.3.11", - "npm_package_dependencies_find_in_files": "^0.5.0", - "npm_package_devDependencies__types_chai": "^4.3.0", - "CONDA_PROMPT_MODIFIER": "(base) ", - "TERM_PROGRAM_VERSION": "447", - "npm_package_dependencies_prosemirror_inputrules": "^1.1.3", - "npm_package_dependencies_bcrypt_nodejs": "0.0.3", - "npm_package_dependencies_async": "^2.6.2", - "npm_config_sign_git_tag": "", - "npm_config_init_author_email": "", - "npm_config_cache_max": "Infinity", - "npm_package_dependencies_uuid": "^3.4.0", - "npm_package_dependencies_supercluster": "^7.1.4", - "npm_package_dependencies_remark_gfm": "^3.0.1", - "npm_package_dependencies_connect_mongo": "^2.0.3", - "npm_package_dependencies_browser_assert": "^1.2.1", - "npm_package_devDependencies_sass_loader": "^7.3.1", - "npm_package_scripts_start_release_debug": "cross-env RELEASE=true USE_AZURE=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --inspect -- src/server/index.ts", - "npm_config_preid": "", - "npm_config_long": "", - "npm_config_local_address": "", - "npm_config_git_tag_version": "true", - "npm_config_cert": "", - "npm_package_dependencies_js_datepicker": "^4.6.6", - "npm_package_devDependencies__types_webpack_hot_middleware": "^2.25.6", - "npm_package_devDependencies__types_mongodb": "^3.6.20", - "npm_package_devDependencies__types_mocha": "^5.2.6", - "TERM_SESSION_ID": "4AACD142-8780-44F4-A4FA-BB53E1BF6081", - "npm_config_registry": "https://registry.npmjs.org/", - "npm_config_noproxy": "", - "npm_config_fetch_retries": "2", - "npm_package_dependencies_react_compound_slider": "^2.5.0", - "npm_package_dependencies_prosemirror_history": "^1.2.0", - "npm_package_devDependencies__types_react_color": "^2.17.6", - "npm_package_devDependencies__types_google_maps_react": "^2.0.5", - "npm_package_devDependencies__types_color": "^3.0.3", - "npm_package_dependencies_react_dom": "^18.2.0", - "npm_package_dependencies_passport_local": "^1.0.0", - "npm_package_dependencies__octokit_core": "^4.0.4", - "npm_package_devDependencies__types_async": "^2.4.1", - "npm_package_scripts_debug": "cross-env NODE_OPTIONS=--max_old_space_size=8192 ts-node-dev --transpile-only --inspect -- src/server/index.ts", - "npm_package_scripts_oldstart": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug -- src/server/index.ts", - "npm_config_versions": "", - "npm_config_message": "%s", - "npm_config_key": "", - "npm_package_readmeFilename": "README.md", - "npm_package_dependencies_react_refresh_typescript": "^2.0.7", - "npm_package_dependencies_react_dropzone": "^14.2.3", - "npm_package_dependencies_image_size": "^0.7.5", - "npm_package_dependencies_html_to_text": "^5.1.1", - "npm_package_dependencies_express_validator": "^5.3.1", - "npm_package_devDependencies_eslint_plugin_jsx_a11y": "^6.6.0", - "npm_package_node_child_process": "empty", - "npm_package_dependencies_react_resizable_rotatable_draggable": "^0.2.0", - "npm_package_dependencies_got": "^12.0.1", - "npm_package_dependencies__types_d3_color": "^2.0.3", - "npm_package_devDependencies_webpack": "^5.69.1", - "npm_package_devDependencies__types_nodemailer": "^4.6.6", - "npm_package_description": "Install Node.js, then, from the project directory, run", - "NVM_DIR": "/Users/sarah/.nvm", - "USER": "sarah", - "npm_package_dependencies__types_d3_scale": "^3.3.2", - "npm_package_devDependencies_dotenv": "^8.6.0", - "npm_package_devDependencies__types_react": "^18.0.15", - "npm_package_devDependencies__types_prosemirror_transform": "^1.1.5", - "npm_package_devDependencies__types_prosemirror_history": "^1.0.3", - "npm_package_dependencies_readline": "^1.3.0", - "npm_package_dependencies__types_supercluster": "^7.1.0", - "npm_package_dependencies__azure_storage_blob": "^12.14.0", - "npm_config_globalconfig": "/Users/sarah/.nvm/versions/node/v12.16.0/etc/npmrc", - "npm_package_dependencies_depcheck": "^0.9.2", - "npm_package_dependencies__types_web": "0.0.53", - "CONDA_EXE": "/Users/sarah/miniconda3/bin/conda", - "npm_config_prefer_online": "", - "npm_config_logs_max": "10", - "npm_config_always_auth": "", - "npm_package_dependencies_react_icons": "^4.3.1", - "npm_package_dependencies_passport_google_oauth20": "^2.0.0", - "npm_package_devDependencies_webpack_dev_server": "^3.11.3", - "npm_package_devDependencies__types_brotli": "^1.3.1", - "npm_package_dependencies_url_loader": "^1.1.2", - "npm_package_dependencies_stream_browserify": "^3.0.0", - "npm_package_dependencies_prosemirror_transform": "^1.3.4", - "npm_package_dependencies_lodash": "^4.17.21", - "npm_package_dependencies_i": "^0.3.7", - "npm_package_devDependencies_tslint_loader": "^3.6.0", - "SSH_AUTH_SOCK": "/private/tmp/com.apple.launchd.dBaRxB6a53/Listeners", - "npm_package_dependencies_words_to_numbers": "^1.5.1", - "npm_package_dependencies_valid_url": "^1.0.9", - "npm_package_dependencies_styled_components": "^4.4.1", - "npm_package_dependencies_csv_parser": "^3.0.0", - "npm_package_dependencies_class_transformer": "^0.2.0", - "npm_package_devDependencies_eslint": "^8.36.0", - "npm_package_devDependencies__types_prosemirror_inputrules": "^1.0.4", - "npm_package_devDependencies__types_express": "^4.17.13", - "__CF_USER_TEXT_ENCODING": "0x1F5:0x0:0x0", - "npm_execpath": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/bin/npm-cli.js", - "npm_config_global_style": "", - "npm_config_cache_lock_retries": "10", - "npm_package_dependencies_wikijs": "^6.3.3", - "npm_package_dependencies_bluebird": "^3.7.2", - "npm_package_devDependencies__types_react_typist": "^2.0.3", - "npm_config_update_notifier": "true", - "npm_config_cafile": "", - "npm_package_dependencies_util": "^0.12.4", - "npm_package_dependencies_raw_loader": "^1.0.0", - "npm_package_dependencies_https_browserify": "^1.0.0", - "npm_package_dependencies_brotli": "^1.3.3", - "npm_package_dependencies__mui_material": "^5.13.1", - "npm_package_dependencies__fortawesome_react_fontawesome": "^0.2.0", - "npm_package_devDependencies__types_passport_google_oauth20": "^2.0.11", - "npm_package_dependencies_cors": "^2.8.5", - "npm_package_dependencies_bezier_js": "^4.1.1", - "npm_package_dependencies__fortawesome_free_brands_svg_icons": "^6.3.0", - "npm_config_heading": "npm", - "npm_config_audit_level": "low", - "npm_package_dependencies_chrome": "^0.1.0", - "npm_package_dependencies__react_three_fiber": "^6.2.3", - "npm_package_devDependencies_eslint_plugin_prettier": "^4.2.1", - "npm_package_devDependencies_copy_webpack_plugin": "^4.6.0", - "npm_package_devDependencies__types_react_measure": "^2.0.8", - "npm_package_devDependencies__types_react_dom": "^18.0.6", - "npm_package_devDependencies__types_mobile_detect": "^1.3.4", - "_CE_CONDA": "", - "npm_config_searchlimit": "20", - "npm_config_read_only": "", - "npm_config_offline": "", - "npm_config_fetch_retry_mintimeout": "10000", - "npm_package_dependencies_react_typist": "^2.0.5", - "npm_package_dependencies_mobx_react_devtools": "^6.1.1", - "npm_package_dependencies_md5_file": "^5.0.0", - "npm_package_dependencies_forever_agent": "^0.6.1", - "npm_package_devDependencies__types_xregexp": "^4.4.0", - "npm_package_devDependencies__types_typescript": "^2.0.0", - "npm_package_devDependencies__types_request": "^2.48.8", - "npm_package_devDependencies__types_prosemirror_commands": "^1.0.4", - "npm_config_json": "", - "npm_config_access": "", - "npm_config_argv": "{\"remain\":[],\"cooked\":[\"start\"],\"original\":[\"start\"]}", - "npm_package_dependencies__fortawesome_free_solid_svg_icons": "^6.3.0", - "npm_package_devDependencies__types_socket_io": "^2.1.13", - "PATH": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/sarah/Desktop/dash/Dash-Web/node_modules/.bin:/Users/sarah/.nvm/versions/node/v12.16.0/bin:/Users/sarah/miniconda3/bin:/Users/sarah/miniconda3/condabin:/Users/sarah/.elan/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin", - "npm_config_allow_same_version": "", - "npm_package_dependencies_webrtc_adapter": "^7.7.1", - "npm_package_dependencies_react_reveal": "^1.2.2", - "npm_package_dependencies_prosemirror_schema_list": "^1.1.6", - "npm_package_dependencies__material_ui_core": "^4.12.3", - "npm_package_devDependencies__types_rimraf": "^2.0.5", - "npm_package_devDependencies__types_connect_flash": "0.0.34", - "npm_config_https_proxy": "", - "npm_config_engine_strict": "", - "npm_config_description": "true", - "npm_package_dependencies_pug": "^2.0.4", - "npm_package_dependencies_prosemirror_keymap": "^1.1.5", - "npm_package_dependencies_pdfjs_dist": "^2.14.305", - "npm_package_dependencies_mobile_detect": "^1.4.5", - "npm_package_dependencies_image_size_stream": "^1.1.0", - "npm_package_dependencies_golden_layout": "^1.5.9", - "npm_package_dependencies_child_process": "^1.0.2", - "npm_package_dependencies__types_d3_axis": "^2.1.3", - "_": "/Users/sarah/Desktop/dash/Dash-Web/node_modules/.bin/cross-env", - "LaunchInstanceID": "16109DB5-CDB2-454A-9613-8F6F534702AC", - "npm_config_userconfig": "/Users/sarah/.npmrc", - "npm_config_init_module": "/Users/sarah/.npm-init.js", - "npm_package_dependencies__react_google_maps_api": "^2.7.0", - "CONDA_PREFIX": "/Users/sarah/miniconda3", - "__CFBundleIdentifier": "com.apple.Terminal", - "npm_config_cidr": "", - "npm_package_dependencies_puppeteer": "^3.3.0", - "npm_package_dependencies_prosemirror_view": "^1.26.5", - "npm_package_dependencies_mongodb": "^3.7.3", - "npm_package_dependencies_google_auth_library": "^4.2.4", - "npm_package_dependencies_bootstrap": "^4.6.1", - "npm_package_devDependencies_eslint_config_airbnb": "^19.0.4", - "PWD": "/Users/sarah/desktop/dash/dash-web", - "npm_config_user": "501", - "npm_config_node_version": "12.16.0", - "npm_package_dependencies_node_sass": "^4.14.1", - "npm_package_dependencies_howler": "^2.2.3", - "npm_package_dependencies_expressjs": "^1.0.1", - "npm_package_dependencies_core_js": "^3.28.0", - "npm_package_dependencies_browndash_components": "^0.1.36", - "npm_package_devDependencies_eslint_plugin_react_hooks": "^4.6.0", - "npm_package_devDependencies__types_lodash": "^4.14.179", - "JAVA_HOME": "/Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home", - "npm_lifecycle_event": "start", - "npm_package_dependencies_react_table": "^6.11.5", - "npm_package_dependencies_react_loading": "^2.0.3", - "npm_package_dependencies_mobx": "^5.15.7", - "npm_package_dependencies_babel": "^6.23.0", - "npm_package_devDependencies_jsdom": "^15.2.1", - "npm_package_devDependencies_chai": "^4.3.6", - "npm_config_save": "true", - "npm_config_ignore_prepublish": "", - "npm_config_editor": "vi", - "npm_config_auth_type": "legacy", - "npm_package_dependencies_npm": "^6.14.18", - "npm_package_dependencies_node_stream_zip": "^1.15.0", - "npm_package_dependencies_image_data_uri": "^2.0.1", - "npm_package_scripts_start_release": "cross-env RELEASE=true USE_AZURE=false NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts", - "npm_package_name": "dash", - "LANG": "en_US.UTF-8", - "npm_config_tag": "latest", - "npm_config_script_shell": "", - "npm_package_dependencies_query_string": "^6.14.1", - "npm_package_dependencies_mobx_utils": "^5.6.2", - "npm_package_dependencies_file_saver": "^2.0.5", - "npm_package_dependencies_body_parser": "^1.19.2", - "npm_package_dependencies__types_reveal": "^3.3.33", - "npm_package_devDependencies_eslint_plugin_import": "^2.26.0", - "npm_package_devDependencies__types_prosemirror_view": "^1.23.1", - "npm_config_progress": "true", - "npm_config_global": "", - "npm_config_before": "", - "npm_package_dependencies_xoauth2": "^1.2.0", - "npm_package_dependencies_standard_http_error": "^2.0.1", - "npm_package_dependencies_react_loader_spinner": "^5.3.4", - "npm_package_dependencies_http_browserify": "^1.7.0", - "npm_package_dependencies__types_d3_selection": "^2.0.1", - "npm_package_dependencies__hig_flyout": "^1.3.1", - "npm_package_devDependencies_fork_ts_checker_webpack_plugin": "^1.6.0", - "npm_package_scripts_build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 webpack --env production", - "npm_package_scripts_start": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts", - "npm_config_searchstaleness": "900", - "npm_config_optional": "true", - "npm_config_ham_it_up": "", - "npm_package_dependencies_sharp": "^0.23.4", - "npm_package_dependencies_rc_switch": "^1.9.2", - "npm_package_dependencies_googlephotos": "^0.2.5", - "npm_package_dependencies_exifr": "^7.1.3", - "npm_package_dependencies__types_google_maps": "^3.2.3", - "npm_package_dependencies__types_bezier_js": "^4.1.0", - "npm_package_dependencies__ffmpeg_core": "0.10.0", - "npm_package_devDependencies_ts_loader": "^5.3.3", - "npm_package_devDependencies__types_bcrypt_nodejs": "0.0.30", - "XPC_FLAGS": "0x0", - "npm_config_save_prod": "", - "npm_config_force": "", - "npm_config_bin_links": "true", - "npm_package_devDependencies__types_youtube": "0.0.39", - "npm_config_searchopts": "", - "npm_package_dependencies_react_beautiful_dnd": "^13.1.0", - "npm_package_dependencies_jszip": "^3.7.1", - "npm_package_dependencies_csv_stringify": "^6.3.0", - "npm_package_devDependencies__types_react_icons": "^3.0.0", - "npm_config_node_gyp": "/Users/sarah/.nvm/versions/node/v12.16.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js", - "npm_config_depth": "Infinity", - "npm_package_dependencies_google_maps_react": "^2.0.6", - "npm_package_dependencies_express_session": "^1.17.2", - "npm_package_devDependencies_eslint_plugin_node": "^11.1.0", - "npm_package_devDependencies_eslint_config_prettier": "^8.5.0", - "npm_package_main": "index.js", - "npm_config_sso_poll_frequency": "500", - "npm_config_rebuild_bundle": "true", - "npm_package_dependencies_chart_js": "^3.8.0", - "npm_package_dependencies__emotion_react": "^11.11.0", - "npm_package_devDependencies__types_prosemirror_menu": "^1.0.6", - "npm_package_devDependencies__types_prosemirror_keymap": "^1.0.4", - "npm_package_devDependencies__types_pdfjs_dist": "^2.10.378", - "npm_package_devDependencies__types_exif": "^0.6.3", - "npm_package_version": "1.0.0", - "_CE_M": "", - "XPC_SERVICE_NAME": "0", - "npm_config_unicode": "true", - "npm_package_dependencies_typescript_language_server": "^0.4.0", - "npm_package_dependencies_prosemirror_model": "^1.18.1", - "npm_package_dependencies__ffmpeg_ffmpeg": "0.10.0", - "SHLVL": "2", - "HOME": "/Users/sarah", - "npm_config_fetch_retry_maxtimeout": "60000", - "npm_package_dependencies_request_promise": "^4.2.6", - "npm_package_dependencies_react_markdown": "^8.0.3", - "npm_package_dependencies__hig_theme_context": "^2.1.3", - "npm_package_devDependencies__types_react_autosuggest": "^9.3.14", - "npm_package_devDependencies__types_mongoose": "^5.11.97", - "npm_package_devDependencies__types_d3": "^7.4.0", - "npm_package_devDependencies__types_animejs": "^2.0.2", - "npm_package_scripts_test": "mocha -r ts-node/register test/**/*.ts", - "npm_config_tag_version_prefix": "v", - "npm_config_strict_ssl": "true", - "npm_config_sso_type": "oauth", - "npm_config_scripts_prepend_node_path": "warn-only", - "npm_config_save_prefix": "^", - "npm_config_loglevel": "notice", - "npm_config_ca": "", - "npm_package_dependencies_three": "^0.127.0", - "npm_package_dependencies_openai": "^3.2.1", - "npm_package_dependencies_mobx_react": "^5.4.4", - "npm_package_dependencies_google_translate_api_browser": "^3.0.1", - "npm_package_dependencies_cookie_parser": "^1.4.6", - "npm_package_dependencies_adm_zip": "^0.4.16", - "npm_package_devDependencies_eslint_config_node": "^4.1.0", - "npm_config_save_exact": "", - "npm_config_group": "20", - "npm_config_fetch_retry_factor": "10", - "npm_config_dev": "", - "npm_package_devDependencies_webpack_hot_middleware": "^2.25.1", - "npm_package_devDependencies_cross_env": "^5.2.1", - "npm_config_version": "", - "npm_config_prefer_offline": "", - "npm_config_cache_lock_stale": "60000", - "npm_package_devDependencies__types_prosemirror_state": "^1.2.8", - "npm_package_devDependencies__types_body_parser": "^1.19.2", - "npm_config_otp": "", - "npm_config_cache_min": "10", - "npm_package_dependencies_react_color": "^2.19.3", - "npm_package_dependencies_d3": "^7.6.1", - "npm_package_devDependencies_ts_node": "^10.9.1", - "npm_package_devDependencies__types_react_grid_layout": "^1.3.2", - "npm_config_searchexclude": "", - "npm_config_cache": "/Users/sarah/.npm", - "npm_package_dependencies_tough_cookie": "^4.0.0", - "npm_package_dependencies_googleapis": "^40.0.0", - "npm_package_devDependencies__types_valid_url": "^1.0.3", - "npm_package_devDependencies__types_passport": "^1.0.9", - "npm_package_devDependencies__types_adm_zip": "^0.4.34", - "CONDA_PYTHON_EXE": "/Users/sarah/miniconda3/bin/python", - "LOGNAME": "sarah", - "npm_lifecycle_script": "cross-env NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev --debug --transpile-only -- src/server/index.ts", - "npm_config_color": "true", - "npm_package_dependencies_solr_node": "^1.2.1", - "npm_package_dependencies_react_transition_group": "^4.4.2", - "npm_package_dependencies_iink_js": "^1.5.4", - "npm_package_dependencies_html_webpack_plugin": "^5.5.0", - "npm_config_proxy": "", - "npm_config_package_lock": "true", - "npm_package_dependencies_prosemirror_state": "^1.4.1", - "npm_package_dependencies_nodemon": "^1.19.4", - "npm_package_dependencies_function_plot": "^1.22.8", - "npm_package_dependencies_equation_editor_react": "github:bobzel/equation-editor-react#useLocally", - "npm_package_devDependencies__types_socket_io_parser": "^3.0.0", - "CLASSPATH": "/Users/sarah/Downloads/cs15/*:.", - "npm_config_package_lock_only": "", - "npm_config_fund": "true", - "npm_package_dependencies_react": "^18.2.0", - "npm_package_dependencies_bingmaps_react": "^1.2.10", - "npm_package_devDependencies_scss_loader": "0.0.1", - "npm_package_devDependencies__types_cookie_session": "^2.0.44", - "npm_config_save_optional": "", - "npm_package_dependencies_textarea_caret": "^3.1.0", - "npm_package_dependencies_react_measure": "^2.5.2", - "npm_package_dependencies_exif": "^0.6.0", - "NVM_BIN": "/Users/sarah/.nvm/versions/node/v12.16.0/bin", - "CONDA_DEFAULT_ENV": "base", - "npm_config_ignore_scripts": "", - "npm_config_user_agent": "npm/6.14.7 node/v12.16.0 darwin x64", - "npm_package_dependencies_react_resizable": "^1.11.1", - "npm_package_dependencies_prosemirror_commands": "^1.2.1", - "npm_package_dependencies_memorystream": "^0.3.1", - "npm_package_dependencies_formidable": "1.2.1", - "npm_package_devDependencies__types_uuid": "^3.4.10", - "npm_config_cache_lock_wait": "10000", - "npm_package_dependencies_socket_io_client": "^2.5.0", - "npm_package_dependencies_recharts": "^2.1.12", - "npm_package_dependencies_react_chartjs_2": "^4.3.0", - "npm_package_dependencies_fluent_ffmpeg": "^2.1.2", - "npm_package_dependencies__types_cors": "^2.8.12", - "npm_package_devDependencies__types_node": "^10.17.60", - "npm_package_devDependencies__types_file_saver": "^2.0.5", - "npm_config_production": "", - "npm_package_dependencies_jsonschema": "^1.4.0", - "npm_package_dependencies_ffmpeg": "0.0.4", - "npm_package_dependencies_cookie_session": "^2.0.0", - "npm_package_dependencies_color": "^3.2.1", - "npm_package_devDependencies__types_webpack": "^4.41.32", - "npm_package_devDependencies__types_request_promise": "^4.1.48", - "npm_package_devDependencies__types_prosemirror_schema_list": "^1.0.3", - "npm_config_send_metrics": "", - "npm_config_save_bundle": "", - "npm_package_dependencies_web_request": "^1.0.7", - "npm_package_dependencies_react_datepicker": "^3.8.0", - "npm_package_dependencies_express": "^4.17.3", - "npm_package_dependencies_D": "^1.0.0", - "npm_package_dependencies__types_formidable": "1.0.31", - "npm_package_devDependencies__types_rc_switch": "^1.9.2", - "npm_package_devDependencies__types_prosemirror_dev_tools": "^2.1.0", - "npm_package_devDependencies__types_jquery": "^3.5.14", - "npm_config_umask": "0022", - "npm_config_node_options": "", - "npm_config_init_version": "1.0.0", - "npm_package_dependencies_https": "^1.0.0", - "npm_package_dependencies_array_batcher": "^1.2.3", - "npm_package_dependencies__fortawesome_free_regular_svg_icons": "^6.3.0", - "npm_package_devDependencies__types_shelljs": "^0.8.11", - "npm_package_devDependencies__types_libxmljs": "^0.18.7", - "npm_package_devDependencies__types_express_validator": "^3.0.0", - "npm_package_devDependencies__types_bluebird": "^3.5.36", - "npm_config_init_author_name": "", - "npm_config_git": "git", - "npm_config_scope": "", - "npm_package_dependencies_react_select": "^3.2.0", - "npm_package_dependencies_pdf_parse": "^1.1.1", - "npm_package_dependencies_colors": "^1.4.0", - "npm_package_dependencies_archiver": "^3.1.1", - "npm_package_devDependencies_css_loader": "^2.1.1", - "npm_package_devDependencies__types_socket_io_client": "^1.4.36", - "SECURITYSESSIONID": "186a6", - "npm_config_unsafe_perm": "true", - "npm_config_tmp": "/var/folders/yk/p_39q8jn673c5p8_66mcxm7r0000gn/T", - "npm_config_onload_script": "", - "npm_package_dependencies_serializr": "^1.5.4", - "npm_package_dependencies_fit_curve": "^0.1.7", - "npm_package_dependencies__webscopeio_react_textarea_autocomplete": "^4.9.1", - "npm_package_dependencies__types_three": "^0.126.2", - "npm_package_devDependencies_ts_node_dev": "^2.0.0", - "npm_node_execpath": "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node", - "npm_config_prefix": "/Users/sarah/.nvm/versions/node/v12.16.0", - "npm_config_link": "", - "npm_config_format_package_lock": "true", - "npm_package_dependencies_passport": "^0.4.0", - "npm_package_devDependencies_eslint_plugin_react": "^7.30.1", - "npm_package_devDependencies__types_react_table": "^6.8.9", - "npm_package_devDependencies__types_react_reconciler": "^0.26.4", - "NODE_OPTIONS": "--max_old_space_size=4096", - "TS_NODE_DEV": "true", - "_CLIENT_OPENAI_KEY": "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", - "VIPSHOME": "/usr/local/Cellar/vips/8.8.1", - "TYPESCRIPT_PATH": "/Users/sarah/Desktop/dash/Dash-Web/node_modules/typescript/lib/typescript.js", - "TSCONFIG": "/Users/sarah/Desktop/dash/Dash-Web/tsconfig.json", - "COMPILER_OPTIONS": "{}", - "TSLINT": "true", - "CONTEXT": "/Users/sarah/Desktop/dash/Dash-Web", - "TSLINTAUTOFIX": "false", - "ESLINT": "false", - "ESLINT_OPTIONS": "{}", - "WATCH": "", - "WORK_DIVISION": "1", - "MEMORY_LIMIT": "2048", - "CHECK_SYNTACTIC_ERRORS": "false", - "USE_INCREMENTAL_API": "true", - "VUE": "false" - }, - "userLimits": { - "core_file_size_blocks": { - "soft": 0, - "hard": "unlimited" - }, - "data_seg_size_kbytes": { - "soft": "unlimited", - "hard": "unlimited" - }, - "file_size_blocks": { - "soft": "unlimited", - "hard": "unlimited" - }, - "max_locked_memory_bytes": { - "soft": "unlimited", - "hard": "unlimited" - }, - "max_memory_size_kbytes": { - "soft": "unlimited", - "hard": "unlimited" - }, - "open_files": { - "soft": 1048575, - "hard": "unlimited" - }, - "stack_size_bytes": { - "soft": 8388608, - "hard": 67104768 - }, - "cpu_time_seconds": { - "soft": "unlimited", - "hard": "unlimited" - }, - "max_user_processes": { - "soft": 2784, - "hard": 4176 - }, - "virtual_memory_kbytes": { - "soft": "unlimited", - "hard": "unlimited" - } - }, - "sharedObjects": [ - "/Users/sarah/.nvm/versions/node/v12.16.0/bin/node", - "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", - "/usr/lib/libobjc.A.dylib", - "/System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal", - "/usr/lib/liboah.dylib", - "/usr/lib/libfakelink.dylib", - "/usr/lib/libicucore.A.dylib", - "/usr/lib/libSystem.B.dylib", - "/System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking", - "/usr/lib/libc++abi.dylib", - "/usr/lib/libc++.1.dylib", - "/usr/lib/system/libcache.dylib", - "/usr/lib/system/libcommonCrypto.dylib", - "/usr/lib/system/libcompiler_rt.dylib", - "/usr/lib/system/libcopyfile.dylib", - "/usr/lib/system/libcorecrypto.dylib", - "/usr/lib/system/libdispatch.dylib", - "/usr/lib/system/libdyld.dylib", - "/usr/lib/system/libkeymgr.dylib", - "/usr/lib/system/libmacho.dylib", - "/usr/lib/system/libquarantine.dylib", - "/usr/lib/system/libremovefile.dylib", - "/usr/lib/system/libsystem_asl.dylib", - "/usr/lib/system/libsystem_blocks.dylib", - "/usr/lib/system/libsystem_c.dylib", - "/usr/lib/system/libsystem_collections.dylib", - "/usr/lib/system/libsystem_configuration.dylib", - "/usr/lib/system/libsystem_containermanager.dylib", - "/usr/lib/system/libsystem_coreservices.dylib", - "/usr/lib/system/libsystem_darwin.dylib", - "/usr/lib/system/libsystem_dnssd.dylib", - "/usr/lib/system/libsystem_featureflags.dylib", - "/usr/lib/system/libsystem_info.dylib", - "/usr/lib/system/libsystem_m.dylib", - "/usr/lib/system/libsystem_malloc.dylib", - "/usr/lib/system/libsystem_networkextension.dylib", - "/usr/lib/system/libsystem_notify.dylib", - "/usr/lib/system/libsystem_sandbox.dylib", - "/usr/lib/system/libsystem_secinit.dylib", - "/usr/lib/system/libsystem_kernel.dylib", - "/usr/lib/system/libsystem_platform.dylib", - "/usr/lib/system/libsystem_pthread.dylib", - "/usr/lib/system/libsystem_symptoms.dylib", - "/usr/lib/system/libsystem_trace.dylib", - "/usr/lib/system/libunwind.dylib", - "/usr/lib/system/libxpc.dylib", - "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", - "/usr/lib/libDiagnosticMessagesClient.dylib", - "/usr/lib/libenergytrace.dylib", - "/usr/lib/libbsm.0.dylib", - "/usr/lib/libz.1.dylib", - "/usr/lib/system/libkxld.dylib", - "/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices", - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList", - "/System/Library/Frameworks/Security.framework/Versions/A/Security", - "/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration", - "/usr/lib/libapple_nghttp2.dylib", - "/usr/lib/libcompression.dylib", - "/usr/lib/libnetwork.dylib", - "/usr/lib/libsqlite3.dylib", - "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation", - "/System/Library/Frameworks/Network.framework/Versions/A/Network", - "/usr/lib/libCoreEntitlements.dylib", - "/System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity", - "/System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer", - "/usr/lib/libMobileGestalt.dylib", - "/System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression", - "/usr/lib/libcoretls.dylib", - "/usr/lib/libcoretls_cfhelpers.dylib", - "/usr/lib/libpam.2.dylib", - "/usr/lib/libxar.1.dylib", - "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout", - "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration", - "/usr/lib/libarchive.2.dylib", - "/usr/lib/libxml2.2.dylib", - "/usr/lib/liblangid.dylib", - "/System/Library/Frameworks/Combine.framework/Versions/A/Combine", - "/usr/lib/swift/libswiftCore.dylib", - "/usr/lib/swift/libswiftCoreFoundation.dylib", - "/usr/lib/swift/libswiftDarwin.dylib", - "/usr/lib/swift/libswiftDispatch.dylib", - "/usr/lib/swift/libswiftIOKit.dylib", - "/usr/lib/swift/libswiftObjectiveC.dylib", - "/usr/lib/swift/libswiftXPC.dylib", - "/usr/lib/swift/libswift_Concurrency.dylib", - "/usr/lib/swift/libswift_StringProcessing.dylib", - "/usr/lib/swift/libswiftos.dylib", - "/System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo", - "/System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer", - "/System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface", - "/usr/lib/libpcap.A.dylib", - "/usr/lib/libdns_services.dylib", - "/usr/lib/liblzma.5.dylib", - "/usr/lib/libbz2.1.0.dylib", - "/usr/lib/libiconv.2.dylib", - "/usr/lib/libcharset.1.dylib", - "/usr/lib/swift/libswift_RegexParser.dylib", - "/usr/lib/libheimdal-asn1.dylib", - "/usr/lib/libCheckFix.dylib", - "/System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC", - "/System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP", - "/System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate", - "/usr/lib/libmecab.dylib", - "/usr/lib/libCRFSuite.dylib", - "/usr/lib/libgermantok.dylib", - "/usr/lib/libThaiTokenizer.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib", - "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib", - "/System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL", - "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory", - "/System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory", - "/System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS", - "/System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation", - "/usr/lib/libutil.dylib", - "/System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary", - "/System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore", - "/usr/lib/libapp_launch_measurement.dylib", - "/System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity", - "/usr/lib/libmis.dylib", - "/System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices", - "/System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper", - "/System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics", - "/System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce", - "/System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling", - "/usr/lib/libxslt.1.dylib", - "/usr/lib/libcmph.dylib", - "/System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji", - "/System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData", - "/System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon", - "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement", - "/usr/lib/libTLE.dylib", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices", - "/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics", - "/System/Library/Frameworks/CoreText.framework/Versions/A/CoreText", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO", - "/System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis", - "/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight", - "/System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib", - "/System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices", - "/System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator", - "/System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient", - "/System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay", - "/System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia", - "/System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator", - "/System/Library/Frameworks/Metal.framework/Versions/A/Metal", - "/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders", - "/System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport", - "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", - "/System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox", - "/System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard", - "/System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG", - "/usr/lib/libexpat.1.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib", - "/usr/lib/libate.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib", - "/System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib", - "/System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler", - "/System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment", - "/System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay", - "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libllvm-flatbuffers.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib", - "/System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/31001/Libraries/libGPUCompilerUtils.dylib", - "/System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore", - "/usr/lib/libspindump.dylib", - "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio", - "/System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation", - "/System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime", - "/System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport", - "/System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata", - "/System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore", - "/System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk", - "/usr/lib/libAudioStatistics.dylib", - "/System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy", - "/usr/lib/libSMC.dylib", - "/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI", - "/usr/lib/libAudioToolboxUtility.dylib", - "/System/Library/PrivateFrameworks/OSAServicesClient.framework/Versions/A/OSAServicesClient", - "/usr/lib/libperfcheck.dylib", - "/System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit", - "/System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib", - "/usr/lib/libRosetta.dylib", - "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray", - "/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions", - "/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools", - "/System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary", - "/usr/lib/libIOReport.dylib", - "/System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage", - "/System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing", - "/System/Library/Frameworks/OpenCL.framework/Versions/A/OpenCL", - "/System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer", - "/System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices", - "/System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers", - "/System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib", - "/System/Library/PrivateFrameworks/FontServices.framework/libhvf.dylib", - "/System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib", - "/usr/lib/swift/libswiftMetal.dylib", - "/usr/lib/swift/libswiftsimd.dylib", - "/System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport", - "/System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA", - "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI", - "/usr/lib/libcups.2.dylib", - "/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos", - "/System/Library/Frameworks/GSS.framework/Versions/A/GSS", - "/usr/lib/libresolv.9.dylib", - "/System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal", - "/System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib", - "/System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth", - "/System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio", - "/System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities", - "/System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox", - "/System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession", - "/System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth", - "/System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience", - "/System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib", - "/System/Library/PrivateFrameworks/AudioResourceArbitration.framework/Versions/A/AudioResourceArbitration", - "/System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog", - "/System/Library/Frameworks/CoreData.framework/Versions/A/CoreData", - "/System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth", - "/System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit", - "/System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils", - "/System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras", - "/System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211", - "/System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag", - "/Users/sarah/Desktop/dash/Dash-Web/node_modules/fsevents/build/Release/fse.node" - ] -} \ No newline at end of file diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 4e3496608..b4ad9c17d 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -33,7 +33,6 @@ export enum DocumentType { PRES = 'presentation', PRESELEMENT = 'preselement', COMPARISON = 'comparison', - GROUP = 'group', PUSHPIN = 'pushpin', MAPROUTE = 'maproute', CALENDAR = 'calendar', diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index cf397e85a..ddb6f65b6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -246,8 +246,8 @@ export class DocumentOptions { _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', false); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)', false); - 'acl-Guest'?: STRt = new StrInfo("permissions granted to users logged in as 'guest' (either view, or private)"); // public permissions - '_acl-Guest'?: string; // public permissions + acl_Guest?: STRt = new StrInfo("permissions granted to users logged in as 'guest' (either view, or private)"); // public permissions + _acl_Guest?: string; // public permissions type?: DTYPEt = new DTypeInfo('type of document', true); type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection @@ -535,6 +535,7 @@ export namespace Docs { { layout: { view: FormattedTextBox, dataField: 'text' }, options: { + acl: '', _height: 35, _xMargin: 10, _yMargin: 10, @@ -550,21 +551,21 @@ export namespace Docs { DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, - options: { _width: 400 }, + options: { acl: '', _width: 400 }, }, ], [ DocumentType.IMG, { layout: { view: ImageBox, dataField: defaultDataKey }, - options: { freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, + options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, }, ], [ DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, + options: { acl: '', _height: 300, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, }, ], [ @@ -572,6 +573,7 @@ export namespace Docs { { layout: { view: CollectionView, dataField: defaultDataKey }, options: { + acl: '', _layout_fitWidth: true, freeform: '', _freeform_panX: 0, @@ -588,42 +590,42 @@ export namespace Docs { DocumentType.KVP, { layout: { view: KeyValueBox, dataField: defaultDataKey }, - options: { _layout_fitWidth: true, _height: 150 }, + options: { acl: '', _layout_fitWidth: true, _height: 150 }, }, ], [ DocumentType.VID, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, + options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, }, ], [ DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, + options: { acl: '', _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, }, ], [ DocumentType.REC, { layout: { view: VideoBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, + options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, }, ], [ DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _layout_curPage: 1, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, + options: { acl: '', _layout_curPage: 1, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, }, ], [ DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { map: '', _height: 600, _width: 800, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, + options: { acl: '', map: '', _height: 600, _width: 800, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, }, ], [ @@ -631,6 +633,7 @@ export namespace Docs { { layout: { view: LinkBox, dataField: 'link' }, options: { + acl: '', childDontRegisterViews: true, layout_hideLinkAnchors: true, _height: 1, @@ -647,88 +650,77 @@ export namespace Docs { { data: new List(), layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { title: 'Global Script Database' }, + options: { acl: '', title: 'Global Script Database' }, }, ], [ DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { systemIcon: 'BsFileEarmarkCodeFill' }, + options: { acl: '', systemIcon: 'BsFileEarmarkCodeFill' }, }, ], [ DocumentType.LABEL, { layout: { view: LabelBox, dataField: 'title' }, - options: { _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, + options: { acl: '', _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, }, ], [ DocumentType.EQUATION, { layout: { view: EquationBox, dataField: 'text' }, - options: { - fontSize: '14px', - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - layout_hideDecorationTitle: true, - systemIcon: 'BsCalculatorFill', - }, // systemIcon: 'BsSuperscript' + BsSubscript + options: { acl: '', fontSize: '14px', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, // systemIcon: 'BsSuperscript' + BsSubscript }, ], [ DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - }, + options: { acl: '', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true }, }, ], [ DocumentType.BUTTON, { layout: { view: LabelBox, dataField: 'title' }, - options: { layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, + options: { acl: '', layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, }, ], [ DocumentType.PRES, { layout: { view: PresBox, dataField: defaultDataKey }, - options: { defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, + options: { acl: '', defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, }, ], [ DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: 'icon' }, - options: { defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, + options: { acl: '', defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, }, ], [ DocumentType.WEBCAM, { layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { systemIcon: 'BsFillCameraVideoFill' }, + options: { acl: '', systemIcon: 'BsFillCameraVideoFill' }, }, ], [ DocumentType.PRESELEMENT, { layout: { view: PresElementBox, dataField: defaultDataKey }, - options: { title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, + options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, }, ], [ DocumentType.CONFIG, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: { config: '', layout_hideLinkButton: true, layout_unrendered: true }, + options: { acl: '', config: '', layout_hideLinkButton: true, layout_unrendered: true }, }, ], [ @@ -737,6 +729,7 @@ export namespace Docs { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: 'stroke' }, options: { + acl: '', systemIcon: 'BsFillPencilFill', // layout_nativeDimEditable: true, layout_reflowVertical: true, @@ -751,7 +744,7 @@ export namespace Docs { DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, + options: { acl: '', layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, }, ], [ @@ -759,35 +752,48 @@ export namespace Docs { { data: '', layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { backgroundColor: 'gray', dropAction: dropActionType.move, waitForDoubleClickToClick: 'always', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsLayoutSplit' }, + options: { + acl: '', + backgroundColor: 'gray', + dropAction: dropActionType.move, + waitForDoubleClickToClick: 'always', + layout_reflowHorizontal: true, + layout_reflowVertical: true, + layout_nativeDimEditable: true, + systemIcon: 'BsLayoutSplit', + }, }, ], [ DocumentType.GROUPDB, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { title: 'Global Group Database' }, - }, - ], - [ - DocumentType.GROUP, - { - layout: { view: EmptyBox, dataField: defaultDataKey }, - options: {}, + options: { acl: '', title: 'Global Group Database' }, }, ], [ DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { dataViz_title: '', dataViz_line: '', dataViz_pie: '', dataViz_histogram: '', dataViz: 'table', _layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true }, + options: { + acl: '', + dataViz_title: '', + dataViz_line: '', + dataViz_pie: '', + dataViz_histogram: '', + dataViz: 'table', + _layout_fitWidth: true, + layout_reflowHorizontal: true, + layout_reflowVertical: true, + layout_nativeDimEditable: true, + }, }, ], [ DocumentType.LOADING, { layout: { view: LoadingBox, dataField: '' }, - options: { _layout_fitWidth: true, _fitHeight: true, layout_nativeDimEditable: true }, + options: { acl: '', _layout_fitWidth: true, _fitHeight: true, layout_nativeDimEditable: true }, }, ], [ @@ -795,41 +801,28 @@ export namespace Docs { { data: '', layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 }, - options: { - _height: 100, - mass1: '', - mass2: '', - layout_nativeDimEditable: true, - position: '', - acceleration: '', - pendulum: '', - spring: '', - wedge: '', - simulation: '', - review: '', - systemIcon: 'BsShareFill', - }, + options: { acl: '', _height: 100, mass1: '', mass2: '', layout_nativeDimEditable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '', systemIcon: 'BsShareFill' }, }, ], [ DocumentType.PUSHPIN, { layout: { view: MapPushpinBox, dataField: defaultDataKey }, - options: {}, + options: { acl: '' }, }, ], [ DocumentType.MAPROUTE, { layout: { view: CollectionView, dataField: defaultDataKey }, - options: {}, + options: { acl: '' }, }, ], [ DocumentType.CALENDAR, { layout: { view: CalendarBox, dataField: defaultDataKey }, - options: {}, + options: { acl: '' }, }, ], ]); @@ -926,7 +919,7 @@ export namespace Docs { type, isBaseProto: true, _width: 300, - 'acl-Guest': SharingPermissions.View, + acl_Guest: SharingPermissions.View, ...(template.options || {}), layout: layout.view?.LayoutString(layout.dataField), data: template.data, @@ -971,8 +964,8 @@ export namespace Docs { const viewKeys = ['x', 'y', 'isSystem']; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); - // dataProps['acl-Override'] = SharingPermissions.Unset; - dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); + // dataProps.acl_Override = SharingPermissions.Unset; + dataProps.acl_Guest = options.acl_Guest ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); dataProps.isSystem = viewProps.isSystem; dataProps.isDataDoc = true; dataProps.author = ClientUtils.CurrentUserEmail(); @@ -996,7 +989,7 @@ export namespace Docs { if (!noView) { const viewFirstProps: { [id: string]: any } = { author: ClientUtils.CurrentUserEmail() }; - viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); + viewFirstProps.acl_Guest = options._acl_Guest ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View); let viewDoc: Doc; // determines whether viewDoc should be created using placeholder Doc or default if (placeholderDoc) { @@ -1004,7 +997,7 @@ export namespace Docs { placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined; viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true); Array.from(Object.keys(placeholderDoc)) - .filter(key => key.startsWith('acl')) + .filter(key => key.startsWith('acl_')) .forEach(key => { dataDoc[key] = viewDoc[key] = placeholderDoc[key]; }); @@ -1157,8 +1150,8 @@ export namespace Docs { I.rotation = 0; I.defaultDoubleClick = 'ignore'; I.author_date = new DateField(); - I['acl-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; - // I['acl-Override'] = SharingPermissions.Unset; + I.acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; + // I.acl_Override = SharingPermissions.Unset; I[Initializing] = false; return ink; @@ -1335,7 +1328,7 @@ export namespace Docs { const doc = DockDocument( configs.map(c => c.doc), JSON.stringify(layoutConfig), - ClientUtils.CurrentUserEmail() === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options }, + ClientUtils.CurrentUserEmail() === 'guest' ? options : { acl_Guest: SharingPermissions.View, ...options }, id ); configs.forEach(c => { @@ -1539,8 +1532,8 @@ export namespace DocUtils { source, target, { - 'acl-Guest': SharingPermissions.Augment, - '_acl-Guest': SharingPermissions.Augment, + acl_Guest: SharingPermissions.Augment, + _acl_Guest: SharingPermissions.Augment, title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index eae5b2d6a..00279e7e1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -836,7 +836,7 @@ pie title Minerals in my tap water linkDocs.author = ClientUtils.CurrentUserEmail(); linkDocs.isSystem = true; linkDocs.data = new List([]); - linkDocs["acl-Guest"] = SharingPermissions.Augment; + linkDocs.acl_Guest = SharingPermissions.Augment; doc.myLinkDatabase = new PrefetchProxy(linkDocs); } } @@ -857,7 +857,7 @@ pie title Minerals in my tap water // childContextMenuScripts: new List([addToDashboards!,]), // childContextMenuLabels: new List(["Add to Dashboards",]), // childContextMenuIcons: new List(["user-plus",]), - "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment, + acl_Guest: SharingPermissions.Augment, _acl_Guest: SharingPermissions.Augment, childDragAction: dropActionType.embed, isSystem: true, childContentPointerEvents: "none", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true, // NOTE: treeView_HideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar _layout_showTitle: "title", treeView_HideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx index 891561245..57363663d 100644 --- a/src/client/util/ServerStats.tsx +++ b/src/client/util/ServerStats.tsx @@ -14,7 +14,7 @@ export class ServerStats extends React.Component<{}> { @observable _stats: { [key: string]: any } | undefined = undefined; // private get linkVisible() { - // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false; + // return this.targetDoc ? this.targetDoc['acl_' + PublicKey] !== SharingPermissions.None : false; // } constructor(props: {}) { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 5b4ac5aff..19a7948e6 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -90,7 +90,7 @@ export class SharingManager extends React.Component<{}> { @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not // private get linkVisible() { - // return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false; + // return this.targetDoc ? this.targetDoc['acl_' + PublicKey] !== SharingPermissions.None : false; // } constructor(props: {}) { @@ -172,10 +172,10 @@ export class SharingManager extends React.Component<{}> { // the list of users shared with const userListContents = users - // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email)) + // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl_${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email)) .filter(({ user }) => docs[0]?.author !== user.email) .map(({ user, linkDatabase, sharingDoc, userColor }) => { - const userKey = `acl-${normalizeEmail(user.email)}`; + const userKey = `acl_${normalizeEmail(user.email)}`; const uniform = docs.every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]); // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; let permissions = targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(targetDoc[userKey]); @@ -204,7 +204,7 @@ export class SharingManager extends React.Component<{}> { const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author); // the owner of the doc and the current user are placed at the top of the user list. - const userKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}`; + const userKey = `acl_${normalizeEmail(ClientUtils.CurrentUserEmail())}`; const curUserPermission = StrCast(targetDoc[userKey]); // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name userListContents.unshift( @@ -230,10 +230,10 @@ export class SharingManager extends React.Component<{}> { ); // the list of groups shared with - const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true)); + const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl_${normalizeEmail(StrCast(title))}`) : true)); groupListMap.unshift({ title: 'Guest' }); // , { title: "ALL" }); const groupListContents = groupListMap.map(group => { - const groupKey = `acl-${StrCast(group.title)}`; + const groupKey = `acl_${StrCast(group.title)}`; const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]); const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; @@ -442,7 +442,7 @@ export class SharingManager extends React.Component<{}> { setInternalSharing = undoable((recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => { const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; - const acl = `acl-${normalizeEmail(user.email)}`; + const acl = `acl_${normalizeEmail(user.email)}`; const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); @@ -459,7 +459,7 @@ export class SharingManager extends React.Component<{}> { */ setInternalGroupSharing = undoable((group: Doc | { title: string }, permission: string, targetDoc?: Doc) => { const target = targetDoc || this.targetDoc!; - const acl = `acl-${normalizeEmail(StrCast(group.title))}`; + const acl = `acl_${normalizeEmail(StrCast(group.title))}`; const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { @@ -578,7 +578,7 @@ export class SharingManager extends React.Component<{}> { } else { docs.forEach(doc => { if (GetEffectiveAcl(doc) === AclAdmin) { - distributeAcls(`acl-${shareWith}`, permission, doc, undefined); + distributeAcls(`acl_${shareWith}`, permission, doc, undefined); } }); } @@ -618,7 +618,7 @@ export class SharingManager extends React.Component<{}> { removeGroup = (group: Doc) => { if (group.docsShared) { DocListCast(group.docsShared).forEach(doc => { - const acl = `acl-${StrCast(group.title)}`; + const acl = `acl_${StrCast(group.title)}`; distributeAcls(acl, SharingPermissions.None, doc); const members: string[] = JSON.parse(StrCast(group.members)); @@ -634,7 +634,7 @@ export class SharingManager extends React.Component<{}> { // if (!targetDoc) { // return; // } - // targetDoc["acl-" + PublicKey] = permission; + // targetDoc['acl_' + PublicKey] = permission; // }s /** diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 25415a4f0..c4855d2e7 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -165,7 +165,7 @@ export class DashboardView extends ObservableReactComponent<{}> { : this.getDashboards(this.selectedDashboardGroup).map(dashboard => { const href = ImageCast(dashboard.thumb)?.url?.href; const shared = Object.keys(dashboard[DocAcl]) - .filter(key => key !== `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}` && !['acl-Me', 'acl-Guest'].includes(key)) + .filter(key => key !== `acl_${normalizeEmail(ClientUtils.CurrentUserEmail())}` && !['acl_Me', 'acl_Guest'].includes(key)) .some(key => dashboard[DocAcl][key] !== AclPrivate); return (
{
- +
{/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */} {/*
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5e1c80f0c..f4ded8367 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -231,7 +231,7 @@ export class PropertiesView extends ObservableReactComponent key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl')), noviceReqFields); + return this.editableFields(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl_')), noviceReqFields); } @undoBatch @@ -473,7 +473,7 @@ export class PropertiesView extends ObservableReactComponent { let userOnDoc = true; if (seldoc) { - if (Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) { + if (Doc.GetT(seldoc, 'acl_' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl_' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) { userOnDoc = false; } } @@ -485,7 +485,7 @@ export class PropertiesView extends ObservableReactComponent { - const userKey = `acl-${normalizeEmail(userEmail)}`; + const userKey = `acl_${normalizeEmail(userEmail)}`; const aclField = Doc.GetT(this.layoutDocAcls ? target : Doc.GetProto(target), userKey, 'string', true); const permission = StrCast(aclField); individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user @@ -494,7 +494,7 @@ export class PropertiesView extends ObservableReactComponent { if (group.title !== 'Guest' && this.selectedDoc) { - const groupKey = 'acl-' + normalizeEmail(StrCast(group.title)); + const groupKey = 'acl_' + normalizeEmail(StrCast(group.title)); if (this.selectedDoc[groupKey] !== '' && this.selectedDoc[groupKey] !== undefined) { let permission; if (this.layoutDocAcls) { @@ -529,7 +529,7 @@ export class PropertiesView extends ObservableReactComponent @@ -1733,8 +1733,8 @@ export class PropertiesView extends ObservableReactComponent 0; - const type = [DocumentType.AUDIO, DocumentType.VID].includes(DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType) - ? (DocCast(PresBox.Instance.activeItem?.annotationOn)?.type as any as DocumentType) + const type = [DocumentType.AUDIO, DocumentType.VID].find(dt => dt === DocCast(PresBox.Instance.activeItem?.annotationOn)?.type) + ? DocCast(PresBox.Instance.activeItem?.annotationOn)?.type : PresBox.targetRenderedDoc(PresBox.Instance.activeItem)?.type; return (
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 51352d3e2..fda320077 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -52,7 +52,7 @@ export class CollectionCarouselView extends CollectionSubView() { }; captionStyleProvider = (doc: Doc | undefined, captionProps: Opt, property: string): any => { // first look for properties on the document in the carousel, then fallback to properties on the container - const childValue = doc?.['caption-' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined; + const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined; return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property); }; panelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 6dba9e155..f945a7aa4 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -53,7 +53,7 @@ export class CollectionMenu extends AntimodeMenu { makeObservable(this); CollectionMenu.Instance = this; this._canFade = false; // don't let the inking menu fade away - this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true); + this.Pinned = Cast(Doc.UserDoc().menuCollections_pinned, 'boolean', true); this.jumpTo(300, 300); } @@ -71,7 +71,7 @@ export class CollectionMenu extends AntimodeMenu { @action toggleMenuPin = () => { - Doc.UserDoc()['menuCollections-pinned'] = this.Pinned = !this.Pinned; + Doc.UserDoc().menuCollections_pinned = this.Pinned = !this.Pinned; if (!this.Pinned && this._left < 0) { this.jumpTo(300, 300); } @@ -280,8 +280,8 @@ export class CollectionViewBaseChrome extends React.Component { this.target._childFilters = undefined; this.target._searchFilterDocs = undefined; @@ -289,8 +289,8 @@ export class CollectionViewBaseChrome extends React.Component { const activeDash = Doc.ActiveDashboard; if (activeDash) { - button['target-childFilters'] = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as any as ObjectField) : undefined; - button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; + button.target_childFilters = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as any as ObjectField) : undefined; + button.target_searchFilterDocs = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; } }, }; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a0d84ab28..d6cbe0dab 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -70,7 +70,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this._props.renderDepth ? this.ScreenToLocalBoxXf() : this.ScreenToLocalBoxXf().scale(this._props.PanelWidth() / this.bodyPanelWidth())); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 986c1e357..c1a889539 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1664,7 +1664,7 @@ export class CollectionFreeFormView extends CollectionSubView { @observable _hover = false; docView = () => this._props.docView; render() { - const destinationIcon = Doc.toIcon(this._props.destinationDoc) as any as IconProp; + const destinationIcon = Doc.toIcon(this._props.destinationDoc); const title = StrCast(this._props.destinationDoc.title).length > 18 ? StrCast(this._props.destinationDoc.title).substr(0, 14) + '...' : this._props.destinationDoc.title; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 6a86af6a7..7b1847ae4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -160,7 +160,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice(); - findexed[timecode] = vals[val] as any as string; + findexed[timecode] = vals[val] || ''; d[`${val}_indexed`] = new List(findexed); }); } @@ -169,7 +169,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice(); - findexed[timecode] = vals[val] as any as number; + findexed[timecode] = vals[val] || 0; d[`${val}_indexed`] = new List(findexed); }); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index db28d0a48..6d3d4be06 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -6,11 +6,10 @@ import { observer } from 'mobx-react'; import { extname } from 'path'; import * as React from 'react'; import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; -import { Doc, Opt } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; -import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; @@ -341,7 +340,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl @computed get paths() { const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc - const alts = this.dataDoc[this.fieldKey + '_alternates'] as any as List; // retrieve alternate documents that may be rendered as alternate images + const alts = DocListCast(this.dataDoc[this.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images const defaultUrl = new URL(ClientUtils.prepend('/assets/unknown-file-icon-hi.png')); const altpaths = alts diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index f96dd2b76..a9aa017a1 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -101,7 +101,7 @@ export class KeyValuePair extends ObservableReactComponent { pair[0].replace(/^_/, '') === this._props.keyName)?.[1].description ?? ''}> -
+
{'('.repeat(parenCount)} {this._props.keyName} {')'.repeat(parenCount)} diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ed9635bdd..4fe7ec993 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1607,7 +1607,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any as DocumentType) ? null : ( + {[DocumentType.AUDIO, DocumentType.VID].find(dt => dt === targetType) ? null : ( <>
Slide Duration
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c9aaa5158..9083d6ca3 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -156,9 +156,9 @@ export const ReverseHierarchyMap: Map { - key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl); + key.startsWith('acl_') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl); }); if (Object.keys(permissions).length || doc[DocAcl]?.length) { runInAction(() => { @@ -382,7 +382,7 @@ export class Doc extends RefField { this[UpdatingFromServer] = true; this[fKey] = value; this[UpdatingFromServer] = false; - if (fKey.startsWith('acl')) { + if (fKey.startsWith('acl_')) { updateCachedAcls(this); } if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) { @@ -390,7 +390,7 @@ export class Doc extends RefField { } }; const writeMode = DocServer.getFieldWriteMode(fKey); - if (fKey.startsWith('acl') || writeMode !== DocServer.WriteMode.Playground) { + if (fKey.startsWith('acl_') || writeMode !== DocServer.WriteMode.Playground) { delete this[CachedUpdates][fKey]; // eslint-disable-next-line no-await-in-loop await fn(); @@ -832,7 +832,7 @@ export namespace Doc { newLayoutDoc.rootDocument = targetDoc; newLayoutDoc.embedContainer = targetDoc; newLayoutDoc.resolvedDataDoc = dataDoc; - newLayoutDoc['acl-Guest'] = SharingPermissions.Edit; + newLayoutDoc.acl_Guest = SharingPermissions.Edit; if (dataDoc[templateField] === undefined && (templateLayoutDoc[templateField] as any)?.length) { dataDoc[templateField] = ObjectField.MakeCopy(templateLayoutDoc[templateField] as List); // ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"])`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc }); @@ -970,7 +970,7 @@ export namespace Doc { delegate.proto = doc; delegate.author = ClientUtils.CurrentUserEmail(); Object.keys(doc) - .filter(key => key.startsWith('acl')) + .filter(key => key.startsWith('acl_')) .forEach(key => { delegate[key] = doc[key]; }); diff --git a/src/fields/util.ts b/src/fields/util.ts index 72b0ef721..9361430cb 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -107,7 +107,7 @@ const _setterImpl = action((target: any, prop: string | symbol | number, valueIn if (value === undefined) (target as Doc|ObjectField)[FieldChanged]?.(undefined, { $unset: { ['fields.' + prop]: '' } }); else (target as Doc|ObjectField)[FieldChanged]?.(undefined, { $set: { ['fields.' + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) :value}}); - if (prop === 'author' || prop.toString().startsWith('acl')) updateCachedAcls(target); + if (prop === 'author' || prop.toString().startsWith('acl_')) updateCachedAcls(target); } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } @@ -184,7 +184,7 @@ export function SetCachedGroups(groups: string[]) { } function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[DocAcl]; - if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; + if (targetAcls?.acl_Me === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin; const userChecked = user || ClientUtils.CurrentUserEmail(); // if the current user is the author of the document / the current user is a member of the admin group if (targetAcls && Object.keys(targetAcls).length) { @@ -210,7 +210,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { /** * Recursively distributes the access right for a user across the children of a document and its annotations. - * @param key the key storing the access right (e.g. acl-groupname) + * @param key the key storing the access right (e.g. acl_groupname) * @param acl the access right being stored (e.g. "Can Edit") * @param target the document on which this access right is being set * @param visited list of Doc's already distributed to. @@ -219,7 +219,7 @@ function getEffectiveAcl(target: any, user?: string): symbol { */ // eslint-disable-next-line default-param-last export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited: Doc[] = [], allowUpgrade?: boolean, layoutOnly = false) { - const selfKey = `acl-${normalizeEmail(ClientUtils.CurrentUserEmail())}`; + const selfKey = `acl_${normalizeEmail(ClientUtils.CurrentUserEmail())}`; if (!target || visited.includes(target) || key === selfKey) return; visited.push(target); @@ -268,15 +268,15 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc * Copies parent's acl fields to the child */ export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) { - [...Object.keys(parent), ...(ClientUtils.CurrentUserEmail() !== parent.author ? ['acl-Owner'] : [])] - .filter(key => key.startsWith('acl')) + [...Object.keys(parent), ...(ClientUtils.CurrentUserEmail() !== parent.author ? ['acl_Owner'] : [])] + .filter(key => key.startsWith('acl_')) .forEach(key => { - // if the default acl mode is private, then don't inherit the acl-guest permission, but set it to private. - // const permission: string = key === 'acl-guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key]; - const parAcl = ReverseHierarchyMap.get(StrCast(key === 'acl-Owner' ? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Edit) : parent[key]))?.acl; + // if the default acl mode is private, then don't inherit the acl_guest permission, but set it to private. + // const permission: string = key === 'acl_Guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key]; + const parAcl = ReverseHierarchyMap.get(StrCast(key === 'acl_Owner' ? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Edit) : parent[key]))?.acl; if (parAcl) { const sharePermission = HierarchyMapping.get(parAcl)?.name; - sharePermission && distributeAcls(key === 'acl-Owner' ? `acl-${normalizeEmail(StrCast(parent.author))}` : key, sharePermission, child, undefined, false, layoutOnly); + sharePermission && distributeAcls(key === 'acl_Owner' ? `acl_${normalizeEmail(StrCast(parent.author))}` : key, sharePermission, child, undefined, false, layoutOnly); } }); } @@ -303,7 +303,7 @@ export function setter(target: any, inProp: string | symbol | number, value: any const effectiveAcl = inProp === 'constructor' || typeof inProp === 'symbol' ? AclAdmin : GetPropAcl(target, prop); if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true; // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't - if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; + if (typeof prop === 'string' && prop.startsWith('acl_') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true; if (typeof prop === 'string' && prop !== '__id' && prop !== '__fieldTuples' && prop.startsWith('_')) { if (!prop.startsWith('__')) prop = prop.substring(1); diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts index a288bfeab..bfa6d7bdb 100644 --- a/src/server/authentication/DashUserModel.ts +++ b/src/server/authentication/DashUserModel.ts @@ -61,7 +61,7 @@ const userSchema = new mongoose.Schema( * Password hash middleware. */ userSchema.pre('save', function save(next) { - const user = this as any as DashUserModel; + const user = this; if (!user.isModified('password')) { return next(); } @@ -70,7 +70,7 @@ userSchema.pre('save', function save(next) { return next(err); } bcrypt.hash( - user.password, + user.password ?? '', salt, () => {}, (cryptErr: mongoose.Error, hash: string) => { diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts index a5222e531..ca9e3058e 100644 --- a/src/server/authentication/Passport.ts +++ b/src/server/authentication/Passport.ts @@ -18,11 +18,11 @@ passport.deserializeUser((id, done) => { passport.use( new LocalStrategy({ usernameField: 'email', passReqToCallback: true }, (req, email, password, done) => { User.findOne({ email: email.toLowerCase() }) - .then((user: any) => { + .then((user: DashUserModel) => { if (!user) { done(undefined, false, { message: 'Invalid email or password' }); // invalid email } else { - (user as any as DashUserModel).comparePassword(password, (error: Error, isMatch: boolean) => { + user.comparePassword(password, (error: Error, isMatch: boolean) => { if (error) return done(error); if (!isMatch) return done(undefined, false, { message: 'Invalid email or password' }); // invalid password // valid authentication HERE -- cgit v1.2.3-70-g09d2