
- Complete NestJS TypeScript implementation with WebSocket support - Direct messaging (DM) and group chat functionality - End-to-end encryption with AES encryption and key pairs - Media file support (images, videos, audio, documents) up to 100MB - Push notifications with Firebase Cloud Messaging integration - Mention alerts and real-time typing indicators - User authentication with JWT and Passport - SQLite database with TypeORM entities and relationships - Comprehensive API documentation with Swagger/OpenAPI - File upload handling with secure access control - Online/offline status tracking and presence management - Message editing, deletion, and reply functionality - Notification management with automatic cleanup - Health check endpoint for monitoring - CORS configuration for cross-origin requests - Environment-based configuration management - Structured for Flutter SDK integration Features implemented: ✅ Real-time messaging with Socket.IO ✅ User registration and authentication ✅ Direct messages and group chats ✅ Media file uploads and management ✅ End-to-end encryption ✅ Push notifications ✅ Mention alerts ✅ Typing indicators ✅ Message read receipts ✅ Online status tracking ✅ File access control ✅ Comprehensive API documentation Ready for Flutter SDK development and production deployment.
170 lines
4.2 KiB
JavaScript
170 lines
4.2 KiB
JavaScript
/**
|
|
* Catharsis
|
|
* A parser for Google Closure Compiler type expressions, powered by PEG.js.
|
|
*
|
|
* @author Jeff Williams <jeffrey.l.williams@gmail.com>
|
|
* @license MIT
|
|
*/
|
|
|
|
const describe = require('./lib/describe');
|
|
const { parse } = require('./lib/parser');
|
|
const stringify = require('./lib/stringify');
|
|
|
|
const typeExpressionCache = {
|
|
normal: new Map(),
|
|
jsdoc: new Map()
|
|
};
|
|
|
|
const parsedTypeCache = {
|
|
normal: new Map(),
|
|
htmlSafe: new Map()
|
|
};
|
|
|
|
const descriptionCache = {
|
|
normal: new Map()
|
|
};
|
|
|
|
function getTypeExpressionCache({useCache, jsdoc}) {
|
|
if (useCache === false) {
|
|
return null;
|
|
} else if (jsdoc === true) {
|
|
return typeExpressionCache.jsdoc;
|
|
} else {
|
|
return typeExpressionCache.normal;
|
|
}
|
|
}
|
|
|
|
function getParsedTypeCache({useCache, links, htmlSafe}) {
|
|
if (useCache === false || links !== null || links !== undefined) {
|
|
return null;
|
|
} else if (htmlSafe === true) {
|
|
return parsedTypeCache.htmlSafe;
|
|
} else {
|
|
return parsedTypeCache.normal;
|
|
}
|
|
}
|
|
|
|
function getDescriptionCache({useCache, links}) {
|
|
if (useCache === false || links !== null || links !== undefined) {
|
|
return null;
|
|
} else {
|
|
return descriptionCache.normal;
|
|
}
|
|
}
|
|
|
|
// can't return the original if any of the following are true:
|
|
// 1. restringification was requested
|
|
// 2. htmlSafe option was requested
|
|
// 3. links option was provided
|
|
// 4. typeExpression property is missing
|
|
function canReturnOriginalExpression(parsedType, {restringify, htmlSafe, links}) {
|
|
return restringify !== true && htmlSafe !== true &&
|
|
(links === null || links === undefined) &&
|
|
Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression');
|
|
}
|
|
|
|
// Add non-enumerable properties to a result object, then freeze it.
|
|
function prepareFrozenObject(obj, expr, {jsdoc}) {
|
|
Object.defineProperty(obj, 'jsdoc', {
|
|
value: jsdoc === true ? jsdoc : false
|
|
});
|
|
|
|
if (expr) {
|
|
Object.defineProperty(obj, 'typeExpression', {
|
|
value: expr
|
|
});
|
|
}
|
|
|
|
return Object.freeze(obj);
|
|
}
|
|
|
|
function cachedParse(expr, options) {
|
|
const cache = getTypeExpressionCache(options);
|
|
let parsedType = cache ? cache.get(expr) : null;
|
|
|
|
if (parsedType) {
|
|
return parsedType;
|
|
} else {
|
|
parsedType = parse(expr, options);
|
|
parsedType = prepareFrozenObject(parsedType, expr, options);
|
|
|
|
if (cache) {
|
|
cache.set(expr, parsedType);
|
|
}
|
|
|
|
return parsedType;
|
|
}
|
|
}
|
|
|
|
function cachedStringify(parsedType, options) {
|
|
const cache = getParsedTypeCache(options);
|
|
let stringified;
|
|
|
|
if (canReturnOriginalExpression(parsedType, options)) {
|
|
return parsedType.typeExpression;
|
|
} else if (cache) {
|
|
stringified = cache.get(parsedType);
|
|
if (!stringified) {
|
|
stringified = stringify(parsedType, options);
|
|
cache.set(parsedType, stringified);
|
|
}
|
|
|
|
return stringified;
|
|
} else {
|
|
return stringify(parsedType, options);
|
|
}
|
|
}
|
|
|
|
function cachedDescribe(parsedType, options) {
|
|
const cache = getDescriptionCache(options);
|
|
let description = cache ? cache.get(parsedType) : null;
|
|
|
|
if (description) {
|
|
return description;
|
|
} else {
|
|
description = describe(parsedType, options);
|
|
description = prepareFrozenObject(description, null, options);
|
|
|
|
if (cache) {
|
|
cache.set(parsedType, description);
|
|
}
|
|
|
|
return description;
|
|
}
|
|
}
|
|
|
|
/* eslint-disable class-methods-use-this */
|
|
class Catharsis {
|
|
constructor() {
|
|
this.Types = require('./lib/types');
|
|
}
|
|
|
|
parse(typeExpr, options = {}) {
|
|
typeExpr = typeExpr.replace(/[\r\n]/g, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
|
|
return cachedParse(typeExpr, options);
|
|
}
|
|
|
|
stringify(parsedType, options) {
|
|
let result;
|
|
|
|
options = options || {};
|
|
|
|
result = cachedStringify(parsedType, options);
|
|
if (options.validate) {
|
|
this.parse(result, options);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
describe(parsedType, options = {}) {
|
|
return cachedDescribe(parsedType, options);
|
|
}
|
|
}
|
|
/* eslint-enable class-methods-use-this */
|
|
|
|
module.exports = new Catharsis();
|