data-primals-engine 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 - 2026, data-primals-engine (https://npm.io/package/data-primals-engine)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # Data Primals Engine
2
+
3
+ **data-primals-engine** est un moteur backend **Node.js** puissant et flexible, conçu pour accélérer le développement d'applications complexes basées sur les données. Construit sur **Express.js** et **MongoDB**, il offre une architecture modulaire, un système de modélisation de données dynamique, des workflows d'automatisation, une gestion avancée des utilisateurs, et bien plus encore.
4
+
5
+ > Que vous construisiez un CRM, une boutique e-commerce, un CMS ou une application SaaS complexe, **data-primals-engine** vous fournit les fondations robustes pour vous concentrer sur ce qui rend votre application unique.
6
+
7
+ ---
8
+
9
+ ## 🚀 Caractéristiques Principales
10
+
11
+ - **Modélisation de données dynamique** : Créez des schémas de données via JSON sans migration de code.
12
+ - **API RESTful puissante** : Opérations CRUD avancées, recherche et filtrage inclus.
13
+ - **Architecture modulaire** : Ajoutez vos propres modules dynamiquement.
14
+ - **Workflows d'automatisation** : Créez des processus métier automatisés.
15
+ - **Authentification & autorisation** : Fournisseurs d'utilisateurs extensibles, rôles et permissions granulaires.
16
+ - **📃 Packs de démarrage** : CRM, e-commerce, site vitrine, etc.
17
+ - **🧠 Intégration IA** : Support de OpenAI, Google Gemini via LangChain.
18
+ - **i18n complet** : Multilingue avec traduction des interfaces et validations.
19
+ - **📄 Documentation automatique** : Swagger disponible par défaut sur `/api-docs`.
20
+
21
+ ---
22
+
23
+ ## 🚀 Prérequisa
24
+
25
+ - Node.js ≥ 18
26
+ - MongoDB installé ou accessible
27
+ - NPM ou Yarn pour la gestion des dépendances
28
+
29
+ ---
30
+
31
+ ## ⚡ Installation et Lancement Rapide
32
+
33
+ ```bash
34
+ git clone https://votre-repository/data-primals-engine.git
35
+ cd data-primals-engine
36
+ npm install
37
+ ```
38
+
39
+ Créez un fichier `.env` à la racine :
40
+ ```env
41
+ MONGO_DB_URL=mongodb://127.0.0.1:27017
42
+ ```
43
+
44
+ Démarrer le serveur :
45
+ ```bash
46
+ # Mode développement
47
+ tnpm run devserver
48
+
49
+ # Mode production
50
+ npm run server
51
+ ```
52
+
53
+ Par défaut, l'application tourne sur le port **7633**.
54
+
55
+ ---
56
+
57
+ ## 📊 Concepts Clés
58
+
59
+ ### 1. 📂 Modèles
60
+ Définis en JSON, les modèles structurent vos données.
61
+
62
+ ```json
63
+ {
64
+ "name": "product",
65
+ "description": "Fiche produit e-commerce",
66
+ "fields": [
67
+ { "name": "name", "type": "string", "required": true },
68
+ { "name": "price", "type": "number", "required": true },
69
+ { "name": "stock", "type": "number", "default": 0 },
70
+ { "name": "category", "type": "relation", "relation": "taxonomy" }
71
+ ]
72
+ }
73
+ ```
74
+
75
+ ### 2. 📊 Modules
76
+ Fonctionnalités activables via configuration :
77
+ - `mongodb`, `data`, `user`, `workflow`, `file`, `assistant`, `swagger`
78
+
79
+ ### 3. 🏘️ Packs de Démarrage
80
+ - **E-commerce** : Produits, commandes, KPIs...
81
+ - **CRM** : Contacts, opportunités, interactions
82
+ - **Site web / blog** : Pages, articles, multi-langue
83
+
84
+ ### 4. ⚒ Workflows
85
+ Automatisez vos processus avec :
86
+ - **Triggers** : DataAdded, DataUpdated, Scheduled, Manual
87
+ - **Actions** : CreateData, UpdateData, SendEmail, ApiCall
88
+
89
+ Exemple :
90
+ > Lors d'une nouvelle commande : Envoyer un mail au client, mettre à jour le stock, notifier la logistique.
91
+
92
+ ---
93
+
94
+ ## 🔧 API : Exemple avec `curl`
95
+
96
+ ### Créer un produit
97
+ ```bash
98
+ curl -X POST http://localhost:7633/api/data \
99
+ -H "Content-Type: application/json" \
100
+ -H "_user: demouser" \
101
+ -H "Authorization: Bearer demotoken" \
102
+ -d '{
103
+ "model": "product",
104
+ "data": {
105
+ "name": "Widget Extraordinaire",
106
+ "price": 99.99,
107
+ "stock": 150,
108
+ "published": true
109
+ }
110
+ }'
111
+ ```
112
+
113
+ ### Rechercher des produits
114
+ ```bash
115
+ curl -X POST http://localhost:7633/api/data/search \
116
+ -H "Content-Type: application/json" \
117
+ -H "_user: demouser" \
118
+ -H "Authorization: Bearer demotoken" \
119
+ -d '{
120
+ "model": "product",
121
+ "filter": {
122
+ "price": {
123
+ "$gt": 50
124
+ },
125
+ "published": true
126
+ },
127
+ "sort": "price:ASC",
128
+ "limit": 10,
129
+ "depth": 1
130
+ }'
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 📁 Structure du Projet
136
+ ```
137
+ data-primals-engine/
138
+ ├── src/
139
+ │ ├── engine.js
140
+ │ ├── modules/
141
+ │ ├── providers/
142
+ │ ├── constants.js
143
+ │ ├── defaultModels.js
144
+ ├── config/
145
+ │ ├── models/
146
+ │ └── packs/
147
+ └── server.js
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 🌟 Comment Contribuer ?
153
+
154
+ 1. Forkez le repo
155
+ 2. Créez une branche : `git checkout -b feature/nom`
156
+ 3. Commitez : `git commit -m "Ajout de ..."`
157
+ 4. Push : `git push origin feature/nom`
158
+ 5. Ouvrez une pull request
159
+
160
+ Merci de laisser une ⭐ au repo si vous aimez le projet !
161
+
162
+ ---
163
+
164
+ ## 📚 Licence
165
+ Distribué sous licence **MIT**. Voir le fichier `LICENSE`.
166
+
167
+ ---
168
+
169
+ ## 🔼 Retour en haut
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "data-primals-engine",
3
+ "version": "1.0.0",
4
+ "description": "datas-primals-engine is the package responsible from handling large amount of data in a practical and performant way. It can handle large amount of data in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation.",
5
+ "main": "src/engine.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "devserver": "cross-env NODE_ENV=development node server.js",
9
+ "server": "cross-env NODE_ENV=production node server.js",
10
+ "migrate:create": "node src/migrate.js create",
11
+ "migrate:up": "node src/migrate.js up",
12
+ "migrate:down": "node src/migrate.js down",
13
+ "migrate:revert": "node src/migrate.js revert",
14
+ "migrate:status": "node src/migrate.js status"
15
+ },
16
+ "exports": {
17
+ ".": "./src/index.js",
18
+ "./modules/*": "./src/modules/*.js",
19
+ "./*": "./src/*.js"
20
+ },
21
+ "dependencies": {
22
+ "@langchain/core": "^0.3.62",
23
+ "@langchain/google-genai": "^0.2.14",
24
+ "@langchain/openai": "^0.5.18",
25
+ "archiver": "^7.0.1",
26
+ "aws-sdk": "^2.1692.0",
27
+ "body-parser": "^1.20.3",
28
+ "chalk": "^5.4.1",
29
+ "check-disk-space": "^3.4.0",
30
+ "compression": "^1.8.0",
31
+ "connect-mongo": "^5.1.0",
32
+ "cookie-parser": "^1.4.7",
33
+ "csv-parse": "^5.6.0",
34
+ "csv-parser": "^3.2.0",
35
+ "express": "^4.21.2",
36
+ "express-csrf-double-submit-cookie": "^1.2.1",
37
+ "express-session": "^1.18.1",
38
+ "express-sitemap-xml": "^3.1.0",
39
+ "juice": "^11.0.1",
40
+ "mathjs": "^14.4.0",
41
+ "mongodb": "^6.12.0",
42
+ "node-schedule": "^2.1.1",
43
+ "nodemailer": "^6.10.0",
44
+ "rate-limiter-flexible": "^5.0.5",
45
+ "swagger-ui-express": "^5.0.1",
46
+ "sirv": "^3.0.1",
47
+ "tar": "^7.4.3",
48
+ "tar-fs": "^3.0.8",
49
+ "uniqid": "^5.4.0",
50
+ "yaml": "^2.8.0"
51
+ },
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.17.0",
54
+ "cross-env": "^7.0.3",
55
+ "eslint": "^9.17.0",
56
+ "globals": "^15.14.0",
57
+ "process": "^0.11.10"
58
+ },
59
+ "keywords": [
60
+ "mongodb",
61
+ "data",
62
+ "primals",
63
+ "automation",
64
+ "aws",
65
+ "bucket", "S3"
66
+ ],
67
+ "author": "anonympins",
68
+ "license": "MIT",
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ }
72
+ }
package/server.js ADDED
@@ -0,0 +1,85 @@
1
+
2
+
3
+ // ===============
4
+
5
+ //set ES modules to be loaded by the script
6
+ import process from "node:process";
7
+ import {Config} from "../data-primals-engine/src/config.js";
8
+ import {Engine} from "../data-primals-engine/src/engine.js";
9
+ import {BenchmarkTool, GameObject, Logger} from "../data-primals-engine/src/gameObject.js";
10
+ import {
11
+ createModel, DATAS_API_TOKEN,
12
+ getModels, getResource,
13
+ searchData,
14
+ validateModelStructure
15
+ } from "../data-primals-engine/src/modules/data.js";
16
+ import {availableLangs, install, langs, maxBytesPerSecondThrottleFile} from "../data-primals-engine/src/constants.js";
17
+ import fs from "node:fs";
18
+ import {
19
+ event_on, getFileExtension,
20
+ shuffle,
21
+ uuidv4
22
+ } from "../data-primals-engine/src/core.js";
23
+ import {datasCollection, filesCollection, modelsCollection, packsCollection} from "../data-primals-engine/src/modules/mongodb.js";
24
+ import {translations} from "../data-primals-engine/src/i18n.js";
25
+ import {getFieldValueHash} from "../data-primals-engine/src/data.js";
26
+
27
+ import swaggerUi from 'swagger-ui-express';
28
+
29
+ import YAML from 'yaml';
30
+ import util from "node:util";
31
+ import path from "node:path";
32
+ import { getAllPacks} from "../data-primals-engine/src/packs.js";
33
+ import {middlewareAuthenticator, userInitiator} from "../data-primals-engine/src/modules/user.js";
34
+ import {defaultModels} from "../data-primals-engine/src/defaultModels.js";
35
+ Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"])
36
+ Config.Set("middlewares", []);
37
+
38
+ const bench = GameObject.Create("Benchmark");
39
+ const timer = bench.addComponent(BenchmarkTool);
40
+ timer.start();
41
+
42
+ const swaggerDocument = YAML.parse(fs.readFileSync(process.cwd()+'/swagger-en.yml', 'utf8'));
43
+
44
+ const engine = Engine.Create();
45
+
46
+ const models = defaultModels;
47
+
48
+ const port = process.env.PORT || 7633;
49
+ engine.start(port, async (r) => {
50
+ const logger = engine.getComponent(Logger);
51
+ logger.info("Loading and validating models...");
52
+ const ms = Object.values(models);
53
+
54
+ let dbModels = await getModels();
55
+
56
+ for(let i = 0; i < ms.length; ++i){
57
+ const model = ms[i];
58
+ validateModelStructure(model);
59
+ // Création des modèles
60
+ if( !dbModels.find(m =>m.name === model.name) )
61
+ {
62
+ model.locked = true;
63
+ const r = await createModel(model);
64
+ dbModels.push({...model, _id: r.insertedId });
65
+ }
66
+ else
67
+ logger.info('Model loaded (' + model.name + ')', {});
68
+ }
69
+
70
+ engine.use('/doc', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
71
+
72
+ if( install )
73
+ await installAllPacks();
74
+
75
+ timer.stop();
76
+ });
77
+
78
+ const installAllPacks = async () => {
79
+
80
+ const packs = await getAllPacks();
81
+
82
+ console.log(util.inspect(packs, false, 20, true));
83
+ await packsCollection.deleteMany({ _user: { $exists : false }});
84
+ await packsCollection.insertMany(packs);
85
+ }
package/src/config.js ADDED
@@ -0,0 +1,16 @@
1
+
2
+
3
+
4
+ const config = [];
5
+
6
+ export const Config = {
7
+ Get: (name, defaultValue) => {
8
+ if( config[name] === undefined ){
9
+ return defaultValue;
10
+ }
11
+ return config[name];
12
+ },
13
+ Set: (name, value) => {
14
+ config[name] = value;
15
+ }
16
+ }
@@ -0,0 +1,210 @@
1
+ export const dbName = "engine";
2
+
3
+ export const cookiesSecret = 'hoaivuymzovyoznllmafivpzaovphlejvalwjvelfhqochakfesv';
4
+
5
+
6
+ export const langs = {
7
+ fr: "Français",
8
+ en: "English",
9
+ ar: "عربي",
10
+ fa: "فارسی",
11
+ it: "Italiano",
12
+ es: "Español",
13
+ el: "Ελληνικά",
14
+ de: "Deutsch",
15
+ cs: "Čeština",
16
+ sv: "Svenska",
17
+ pt: "Português",
18
+ ja: "日本語",
19
+ zh: "简体中文",
20
+ ru: "Русский",
21
+ };
22
+
23
+ export const availableLangs = [
24
+ "en",
25
+ "fa",
26
+ "ar",
27
+ "fr",
28
+ "it",
29
+ "es",
30
+ "pt",
31
+ "de",
32
+ "el",
33
+ "ru",
34
+ "cs",
35
+ "sv",
36
+ ];
37
+
38
+ export const awsDefaultConfig = {
39
+ bucketName : 'bucket-primals',
40
+ region: 'eu-north-1',
41
+ }
42
+
43
+ // 10000 tiny users
44
+ // 1000 modern users
45
+ // 100 mega utilisateurs potentiality
46
+ // 250 000 entrées par utilisateur
47
+ export const maxModelsPerUser = 1000;
48
+ export const maxTotalDataPerUser = 500000;
49
+ export const maxDataPerModelPerUser = 10000;
50
+ export const maxStringLength = 4096;
51
+ export const maxPasswordLength = 100000;
52
+ export const maxRichTextLength = 100000;
53
+ export const maxExportCount = 50000;
54
+ export const maxMagnetsDataPerModel = 100;
55
+ export const maxMagnetsModels = 20;
56
+ export const defaultMaxRequestData = 500;
57
+ export const maxRequestData = 2500;
58
+ export const maxPostData = 500;
59
+ export const maxRelationsPerData = 1500;
60
+ export const install = true;
61
+ export const maxFilterDepth = 8;
62
+ export const elementsPerPage = 30;
63
+
64
+
65
+ export const maxAlertsPerUser = 15;
66
+ export const storageSafetyMargin = 0.95;
67
+ export const kilobytes = 1024;
68
+ export const megabytes = 1024*1024;
69
+
70
+ export const maxBytesPerSecondThrottleFile = 800*kilobytes; // 800ko/s
71
+ export const maxBytesPerSecondThrottleData = 200*kilobytes; // 200Ko/s
72
+
73
+ export const searchRequestTimeout = 15000;
74
+
75
+ export const maxModelsInCache = 100000;
76
+ export const maxModelNameLength = 150;
77
+
78
+ export const maxDataSize = '20mb';
79
+ export const maxFileSize = 20 * 1024 * 1024; // 20 Mo
80
+
81
+ export const mainFieldsTypes = ['string_t', 'string', 'url', 'enum', 'email', 'phone', 'date', 'datetime'];
82
+
83
+ export const maxExecutionsByStep = 5;
84
+ export const maxWorkflowSteps = 15;
85
+
86
+ export const maxPrivateFileSize = 20 * megabytes; // Taille max par fichier privé (20 Mo)
87
+ export const maxTotalPrivateFilesSizeFree = 125 * megabytes; // Stockage total pour les comptes gratuits (250 Mo)
88
+ export const maxTotalPrivateFilesSizeStandard = 250 * megabytes; // Stockage total pour les comptes standard (250 Mo)
89
+ export const maxTotalPrivateFilesSizePremium = 20000 * kilobytes * kilobytes; //
90
+
91
+ export const plans = {
92
+ free:{
93
+ maxModelsPerUser,
94
+ maxTotalDataPerUser,
95
+ maxDataPerModelPerUser,
96
+ requestLimitPerHour: 7200,
97
+ },
98
+ premium: {
99
+ requestLimitPerHour: 250000,
100
+ }
101
+ }
102
+ //
103
+ export const optionsSanitizer = {
104
+ allowedTags: [
105
+ "img",
106
+ "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
107
+ "h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
108
+ "dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
109
+ "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
110
+ "em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
111
+ "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
112
+ "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr"
113
+ ],
114
+ disallowedTagsMode: 'discard',
115
+ allowedAttributes: {
116
+ a: [ 'href', 'name', 'target' ],
117
+ code: ['class'],
118
+ img: [ 'src', "alt","width","height","style" ]
119
+ },
120
+ // Lots of these won't come up by default because we don't allow them
121
+ selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
122
+ // URL schemes we permit
123
+ allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel' ],
124
+ allowedSchemesByTag: {},
125
+ allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
126
+ allowProtocolRelative: true,
127
+ enforceHtmlBoundary: false
128
+ }
129
+
130
+
131
+ export const metaModels = {};
132
+
133
+ metaModels['common'] = { load: ['contact', 'location', 'request'] };
134
+ metaModels['personal'] = { load: ['budget', 'imageGallery'] };
135
+ metaModels['users'] = { load: ['permission', 'role', 'user', 'token'], 'require': ['i18n', 'common'] };
136
+ metaModels['i18n'] = { load: ['translation','lang']};
137
+ metaModels['website'] = { load: ['webpage', 'content', 'taxonomy', 'contact', 'resource'], 'require': ['i18n'] };
138
+ metaModels['messaging'] = { load: ['alert','ticket', 'message', 'channel'], 'require': ['i18n'] };
139
+ metaModels['eshopping'] = { load: [
140
+ 'order', 'currency', 'product', 'productVariant', 'discount', 'cart', 'cartItem',
141
+ 'brand', 'return', 'review', 'stock', 'returnItem', 'userSubscription',
142
+ 'warehouse', 'shipment', 'campaign', 'stockAlert', 'invoice'],
143
+ 'require': ['i18n', 'users', 'messaging'] };
144
+ metaModels['workflow'] = { load: ['env', 'workflow', 'workflowRun', 'workflowAction', 'workflowStep', 'workflowTrigger']};
145
+ metaModels['erp'] = { load: [ 'accountingExercise', 'accountingLineItem', 'accountingEntry', 'employee', 'dashboard', 'kpi'] };
146
+
147
+ export const profiles = {
148
+ 'personal': ['contact', 'location', 'imageGallery', 'budget', 'currency', 'taxonomy'], // budget,
149
+ 'developer': ['alert','request','webpage', 'content', 'taxonomy', 'resource', 'translation', 'contact', 'location', 'channel', 'lang', 'token', 'message', 'ticket', 'user', 'permission', 'role'],
150
+ 'company': ['alert','request','location', 'campaign', 'order', 'currency', 'product', 'cart', 'cartItem', 'invoice', 'messaging', 'user', 'role', 'permission', 'token','translation', 'lang', 'webpage', 'content', 'taxonomy', 'contact', 'resource', 'accountingExercise', 'accountingLineItem', 'accountingEntry', 'employee', 'kpi', 'dashboard'],
151
+ 'engineer': ['alert','request','dashboard', 'kpi', 'user', 'role', 'token', 'permission', 'workflow', 'workflowRun', 'workflowStep', "channel", "message", 'workflowAction', 'workflowTrigger']
152
+ }
153
+
154
+ // Dans ConditionBuilder.jsx ou un fichier de constantes partagé
155
+ export const mongoOperators = [
156
+ { value: '$eq', label: '==', inputType: 'text' }, // Égal (pour string, number, boolean...)
157
+ { value: '$ne', label: '!=', inputType: 'text' }, // Différent
158
+ { value: '$gt', label: '>', inputType: 'number' }, // Supérieur (nombre, date)
159
+ { value: '$gte', label: '>=', inputType: 'number' }, // Supérieur ou égal à
160
+ { value: '$lt', label: '<', inputType: 'number' }, // Inférieur à
161
+ { value: '$lte', label: '<=', inputType: 'number' }, // Inférieur ou égal à
162
+ { value: '$regex', label: 'contient (regex)', inputType: 'text' }, // Correspondance Regex (pour string)
163
+ { value: '$in', label: 'est dans (liste)', inputType: 'csv' }, // Dans un tableau (valeurs séparées par des virgules)
164
+ { value: '$nin', label: "n'est pas dans (liste)", inputType: 'csv' }, // Pas dans un tableau
165
+ { value: '$exists', label: 'existe', inputType: 'boolean' }, // Le champ existe (true/false)
166
+ // { value: '$not', label: 'NON (condition)', inputType: 'condition' }, // Pourrait encapsuler une autre condition
167
+ // Ajoutez d'autres opérateurs MongoDB pertinents si besoin ($size, $type, $elemMatch...)
168
+ ];
169
+
170
+ export const allowedFields = ['locked', 'hiddenable', 'anonymized', 'condition', 'color', 'index', 'type', 'required', 'hint', 'default', 'validate', 'unique', 'name', 'placeholder', 'asMain'];
171
+
172
+
173
+
174
+ export const MONGO_OPERATORS = {
175
+ ADD: { mongo: '$add', label: '+', type: 'numeric', multi: true },
176
+ SUBTRACT: { mongo: '$subtract', label: '-', type: 'numeric', multi: false },
177
+ MULTIPLY: { mongo: '$multiply', label: '*', type: 'numeric', multi: true },
178
+ DIVIDE: { mongo: '$divide', label: '/', type: 'numeric', multi: false },
179
+ MODULO: { mongo: '$mod', label: '%', type: 'numeric', multi: false },
180
+ POW: { mongo: '$pow', label: 'pow', type: 'numeric', multi: false },
181
+ SQRT: { mongo: '$sqrt', label: '√', type: 'numeric', multi: false, unary: true },
182
+ ABS: { mongo: '$abs', label: 'abs', type: 'numeric', multi: false, unary: true },
183
+ CEIL: { mongo: '$ceil', label: 'ceil', type: 'numeric', multi: false, unary: true },
184
+ FLOOR: { mongo: '$floor', label: 'floor', type: 'numeric', multi: false, unary: true },
185
+ COS: { mongo: '$cos', label: 'cos', type: 'numeric', multi: false, unary: true },
186
+ ACOS: { mongo: '$acos', label: 'acos', type: 'numeric', multi: false, unary: true },
187
+ SIN: { mongo: '$sin', label: 'sin', type: 'numeric', multi: false, unary: true },
188
+ ASIN: { mongo: '$asin', label: 'asin', type: 'numeric', multi: false, unary: true },
189
+ TAN: { mongo: '$tan', label: 'tan', type: 'numeric', multi: false, unary: true },
190
+ ATAN: { mongo: '$atan', label: 'atan', type: 'numeric', multi: false, unary: true },
191
+ LN: { mongo: '$ln', label: 'ln', type: 'numeric', multi: false, unary: true },
192
+ LOG: { mongo: '$log10', label: 'log', type: 'numeric', multi: false, unary: true },
193
+
194
+ CONCAT: { mongo: '$concat', label: 'concat', type: 'string', multi: true },
195
+
196
+ YEAR: { mongo: '$year', label: 'year', type: 'date', unary: true },
197
+ MONTH: { mongo: '$month', label: 'month', type: 'date', unary: true },
198
+ WEEK: { mongo: '$week', label: 'week', type: 'date', unary: true },
199
+ DAY_OF_MONTH: { mongo: '$dayOfMonth', label: 'day', type: 'date', unary: true },
200
+ HOUR: { mongo: '$hour', label: 'hour', type: 'date', unary: true },
201
+ MINUTE: { mongo: '$minute', label: 'min', type: 'date', unary: true },
202
+ SECOND: { mongo: '$second', label: 'sec', type: 'date', unary: true },
203
+ };
204
+
205
+ export const OPERAND_TYPES = {
206
+ FIELD: 'field',
207
+ CONSTANT: 'constant',
208
+ PREVIOUS_STEP: 'previousStep',
209
+ };
210
+