├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INTERCONNECTIBILITY.md ├── LICENSE ├── README.md ├── app ├── config.json ├── facts │ ├── fact.json │ ├── goodbye │ │ ├── goodbye.js │ │ └── index.js │ ├── index.js │ └── native │ │ ├── 8fact.js │ │ └── funs.js └── index.js ├── assets ├── auth.svg ├── carbon-light.svg ├── carbon.svg ├── favicon.ico ├── flat.svg ├── github.svg ├── idea.svg ├── index.css ├── index.js ├── interconnect.png ├── interconnect.svg ├── interconnections.svg ├── intro-art.svg ├── laptop-n-server.svg ├── laptop-shadow.svg ├── laptop.svg ├── linkedin.svg ├── logo-backdrop.svg ├── logo-std.svg ├── logo-type-std.svg ├── logo.svg ├── macrologic.svg ├── map.svg ├── medium.svg ├── micrologic.svg ├── server-shadow.svg ├── server.svg ├── shot1.png ├── shot2.png ├── twitter-type.svg └── twitter.svg ├── index.html ├── index.js ├── nodemon.json ├── package-lock.json ├── package.json ├── panel ├── .editorconfig ├── .gitignore ├── .npmignore ├── README.md ├── angular.json ├── browserslist ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── main.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── components │ │ │ ├── auth │ │ │ │ ├── auth.component.css │ │ │ │ ├── auth.component.html │ │ │ │ ├── auth.component.spec.ts │ │ │ │ └── auth.component.ts │ │ │ ├── editor │ │ │ │ ├── adder │ │ │ │ │ ├── adder.component.css │ │ │ │ │ ├── adder.component.html │ │ │ │ │ ├── adder.component.spec.ts │ │ │ │ │ └── adder.component.ts │ │ │ │ ├── bar │ │ │ │ │ ├── bar.component.css │ │ │ │ │ ├── bar.component.html │ │ │ │ │ ├── bar.component.spec.ts │ │ │ │ │ └── bar.component.ts │ │ │ │ ├── card │ │ │ │ │ ├── card.component.css │ │ │ │ │ ├── card.component.html │ │ │ │ │ ├── card.component.spec.ts │ │ │ │ │ └── card.component.ts │ │ │ │ ├── code │ │ │ │ │ ├── code.component.css │ │ │ │ │ ├── code.component.html │ │ │ │ │ ├── code.component.spec.ts │ │ │ │ │ └── code.component.ts │ │ │ │ ├── editor.component.css │ │ │ │ ├── editor.component.html │ │ │ │ ├── editor.component.spec.ts │ │ │ │ ├── editor.component.ts │ │ │ │ ├── general │ │ │ │ │ ├── general.component.css │ │ │ │ │ ├── general.component.html │ │ │ │ │ ├── general.component.spec.ts │ │ │ │ │ └── general.component.ts │ │ │ │ ├── link │ │ │ │ │ ├── link.component.css │ │ │ │ │ ├── link.component.html │ │ │ │ │ ├── link.component.spec.ts │ │ │ │ │ └── link.component.ts │ │ │ │ ├── overlays │ │ │ │ │ └── editor-misc-overlay │ │ │ │ │ │ ├── editor-misc-overlay.component.css │ │ │ │ │ │ ├── editor-misc-overlay.component.html │ │ │ │ │ │ ├── editor-misc-overlay.component.spec.ts │ │ │ │ │ │ └── editor-misc-overlay.component.ts │ │ │ │ ├── pane │ │ │ │ │ ├── pane.component.css │ │ │ │ │ ├── pane.component.html │ │ │ │ │ ├── pane.component.spec.ts │ │ │ │ │ ├── pane.component.ts │ │ │ │ │ └── selector │ │ │ │ │ │ ├── selector.component.css │ │ │ │ │ │ ├── selector.component.html │ │ │ │ │ │ ├── selector.component.spec.ts │ │ │ │ │ │ └── selector.component.ts │ │ │ │ ├── pin │ │ │ │ │ ├── pin.component.css │ │ │ │ │ ├── pin.component.html │ │ │ │ │ ├── pin.component.spec.ts │ │ │ │ │ └── pin.component.ts │ │ │ │ └── timeline │ │ │ │ │ ├── timeline.component.css │ │ │ │ │ ├── timeline.component.html │ │ │ │ │ ├── timeline.component.spec.ts │ │ │ │ │ └── timeline.component.ts │ │ │ ├── home │ │ │ │ ├── config │ │ │ │ │ ├── config.component.css │ │ │ │ │ ├── config.component.html │ │ │ │ │ ├── config.component.spec.ts │ │ │ │ │ └── config.component.ts │ │ │ │ ├── home.component.css │ │ │ │ ├── home.component.html │ │ │ │ ├── home.component.spec.ts │ │ │ │ ├── home.component.ts │ │ │ │ ├── nodes │ │ │ │ │ ├── nodelist-entry │ │ │ │ │ │ ├── nodelist-entry.component.css │ │ │ │ │ │ ├── nodelist-entry.component.html │ │ │ │ │ │ ├── nodelist-entry.component.spec.ts │ │ │ │ │ │ └── nodelist-entry.component.ts │ │ │ │ │ ├── nodes.component.css │ │ │ │ │ ├── nodes.component.html │ │ │ │ │ ├── nodes.component.spec.ts │ │ │ │ │ └── nodes.component.ts │ │ │ │ ├── packages │ │ │ │ │ ├── packages.component.css │ │ │ │ │ ├── packages.component.html │ │ │ │ │ ├── packages.component.spec.ts │ │ │ │ │ └── packages.component.ts │ │ │ │ ├── services │ │ │ │ │ ├── services.component.css │ │ │ │ │ ├── services.component.html │ │ │ │ │ ├── services.component.spec.ts │ │ │ │ │ └── services.component.ts │ │ │ │ └── vault │ │ │ │ │ ├── vault.component.css │ │ │ │ │ ├── vault.component.html │ │ │ │ │ ├── vault.component.spec.ts │ │ │ │ │ └── vault.component.ts │ │ │ └── shared │ │ │ │ ├── command-palette │ │ │ │ ├── command-palette.component.css │ │ │ │ ├── command-palette.component.html │ │ │ │ ├── command-palette.component.spec.ts │ │ │ │ └── command-palette.component.ts │ │ │ │ ├── hintman │ │ │ │ ├── hintman.component.css │ │ │ │ ├── hintman.component.html │ │ │ │ ├── hintman.component.spec.ts │ │ │ │ └── hintman.component.ts │ │ │ │ └── overlay │ │ │ │ ├── overlay.component.css │ │ │ │ ├── overlay.component.html │ │ │ │ ├── overlay.component.spec.ts │ │ │ │ └── overlay.component.ts │ │ ├── directives │ │ │ ├── autofocus.directive.spec.ts │ │ │ └── autofocus.directive.ts │ │ ├── interceptors │ │ │ └── token.interceptor.ts │ │ ├── models │ │ │ ├── abstract-node.model.ts │ │ │ ├── box.model.ts │ │ │ ├── call.model.ts │ │ │ ├── expr.model.ts │ │ │ ├── link.model.ts │ │ │ ├── node.model.ts │ │ │ ├── pin-list.model.ts │ │ │ ├── pin.model.ts │ │ │ ├── signature.model.ts │ │ │ ├── subgraph.model.ts │ │ │ ├── switch.model.ts │ │ │ └── value.model.ts │ │ ├── services │ │ │ ├── backend.service.spec.ts │ │ │ ├── backend.service.ts │ │ │ ├── clipboard.service.spec.ts │ │ │ ├── clipboard.service.ts │ │ │ ├── editor-model.service.spec.ts │ │ │ ├── editor-model.service.ts │ │ │ ├── editor.service.spec.ts │ │ │ ├── editor.service.ts │ │ │ ├── hint.service.spec.ts │ │ │ ├── hint.service.ts │ │ │ ├── nodelist-state.service.spec.ts │ │ │ ├── nodelist-state.service.ts │ │ │ ├── registry.service.spec.ts │ │ │ ├── registry.service.ts │ │ │ ├── repo.service.spec.ts │ │ │ ├── repo.service.ts │ │ │ ├── tester.service.spec.ts │ │ │ ├── tester.service.ts │ │ │ ├── token.service.spec.ts │ │ │ └── token.service.ts │ │ └── util │ │ │ ├── decompose-code.ts │ │ │ ├── deep-merge.ts │ │ │ ├── elem-box.ts │ │ │ └── subscribable.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── arrow-head-control.svg │ │ ├── arrow-head-white.svg │ │ ├── arrow-head.svg │ │ ├── auth.svg │ │ ├── close-filled.svg │ │ ├── close.svg │ │ ├── collapse.svg │ │ ├── config-animated.svg │ │ ├── config.svg │ │ ├── copy.svg │ │ ├── empty.svg │ │ ├── expand.svg │ │ ├── folder-open.svg │ │ ├── folder.svg │ │ ├── info-filled.svg │ │ ├── info.svg │ │ ├── installing.svg │ │ ├── key-filled.svg │ │ ├── key.svg │ │ ├── logo-backdrop.svg │ │ ├── logo.svg │ │ ├── package.svg │ │ ├── packages.svg │ │ ├── services.svg │ │ ├── timer.svg │ │ ├── update-filled.svg │ │ ├── update.svg │ │ └── watching.svg │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── fav.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ├── platform ├── bind │ ├── common │ │ ├── routes.js │ │ └── test │ │ │ ├── index.js │ │ │ └── routes.js │ ├── express │ │ ├── index.js │ │ ├── init.js │ │ ├── req-handler.js │ │ ├── router.js │ │ └── test │ │ │ ├── index.js │ │ │ ├── req-handler.js │ │ │ └── router.js │ ├── panel │ │ ├── config │ │ │ ├── load.js │ │ │ └── save.js │ │ ├── delete-node.js │ │ ├── index.js │ │ ├── load-node.js │ │ ├── name.js │ │ ├── packages │ │ │ ├── install.js │ │ │ ├── list.js │ │ │ ├── repo.js │ │ │ ├── status.js │ │ │ └── uninstall.js │ │ ├── panel-nodes.js │ │ ├── registry-service.js │ │ ├── save-node.js │ │ ├── services │ │ │ ├── info.js │ │ │ ├── list.js │ │ │ ├── remove.js │ │ │ └── save.js │ │ ├── shell-url.js │ │ ├── test.js │ │ ├── test │ │ │ ├── index.js │ │ │ ├── panel-nodes.js │ │ │ ├── save-node.js │ │ │ └── util │ │ │ │ ├── api.js │ │ │ │ ├── deepClone.js │ │ │ │ └── dummyNodes.js │ │ ├── util │ │ │ ├── authorize.js │ │ │ ├── config.js │ │ │ ├── file-io.js │ │ │ ├── lockFile.js │ │ │ └── purify-recording.js │ │ ├── vault │ │ │ ├── delete.js │ │ │ ├── get.js │ │ │ ├── list.js │ │ │ └── put.js │ │ ├── version.js │ │ └── watch.js │ ├── shell │ │ ├── auth.js │ │ ├── config.js │ │ ├── index.js │ │ ├── proxy.js │ │ ├── run.js │ │ └── token.js │ ├── socket.io │ │ ├── Sockets.js │ │ ├── index.js │ │ └── test │ │ │ ├── Sockets.js │ │ │ ├── index.js │ │ │ └── socket-io.js │ └── utils │ │ ├── index.js │ │ └── iterate.js ├── builder │ ├── builder.js │ ├── composite.js │ ├── composition.js │ ├── errors.js │ ├── from-json.js │ ├── index.js │ ├── recipe.js │ └── test │ │ ├── builder.js │ │ ├── composite.js │ │ ├── composition.js │ │ ├── fact-recipe.json │ │ ├── fact.js │ │ ├── from-json.js │ │ ├── index.js │ │ └── recipe.js ├── conventions │ ├── controls.js │ └── index.js ├── core │ ├── base │ │ ├── control.js │ │ ├── errors.js │ │ ├── index.js │ │ ├── io.js │ │ ├── node.js │ │ ├── pin.js │ │ ├── subscribable.js │ │ └── test │ │ │ ├── control.js │ │ │ ├── index.js │ │ │ ├── io.js │ │ │ ├── node.js │ │ │ └── pin.js │ ├── call.js │ ├── callable.js │ ├── errors.js │ ├── expression.js │ ├── index.js │ ├── node.js │ ├── registry.js │ ├── script │ │ ├── context.js │ │ ├── index.js │ │ └── script.js │ ├── switch.js │ └── test │ │ ├── call.js │ │ ├── callable.js │ │ ├── expression.js │ │ ├── fib.js │ │ ├── index.js │ │ ├── node.js │ │ ├── registry.js │ │ └── switch.js ├── index.js ├── loaders │ ├── index.js │ ├── json.js │ ├── load-external.js │ ├── load-node.js │ ├── module.js │ ├── native.js │ └── service.js ├── recorder │ ├── composite-watcher.js │ ├── composition-scenario.js │ ├── console-watcher.js │ ├── index.js │ ├── json-scenario.js │ ├── recorder.js │ ├── scenario.js │ ├── test │ │ ├── composite-watcher.js │ │ ├── console-watcher.js │ │ ├── index.js │ │ ├── json-scenario.js │ │ ├── recorder.js │ │ ├── watch-composition.js │ │ ├── watch-node.js │ │ ├── watch-pins.js │ │ └── watcher.js │ ├── timed-scenario.js │ ├── watch.js │ └── watcher.js ├── test │ └── index.js ├── tools │ └── native-call.js └── util │ ├── build-from-factory-or-class.js │ ├── color-text.js │ ├── config.js │ ├── hash.js │ ├── index.js │ ├── now.js │ └── test │ ├── config.js │ ├── hash.js │ └── index.js ├── run-test.js ├── run.js ├── test-app ├── config.json ├── index.js └── panel-generated │ ├── config.json │ ├── index.js │ └── path-map.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | **/.DS_Store 4 | /idea 5 | /app/panel-generated 6 | /app/secure/* 7 | *.code-workspace -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | **/.DS_Store 4 | /idea 5 | /app 6 | /assets 7 | *.code-workspace -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Eugene Ghanizadeh Khoub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": "{{ CONNECT_PORT || 4000 }}", 3 | "greet": "hellow", 4 | "panel": { 5 | "expose": true 6 | }, 7 | "firestore": { 8 | "project": "connect-platform-binding-test", 9 | "keyfile": "secure/firestore.json" 10 | }, 11 | "mongo-db": { 12 | "URL": "mongodb://localhost:22000", 13 | "db": "mydb" 14 | }, 15 | "nodes": { 16 | "native": [ ], 17 | "module": [ 18 | "platform/bind/utils", 19 | "platform/bind/panel", 20 | "panel-generated" 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/facts/fact.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "/fact", 3 | "public": true, 4 | "description": "factoriel function", 5 | "in": ["n"], 6 | "out": ["f"], 7 | "nodes": [ 8 | {"tag": "ci", "in": ["n"], "expr": "n > 1"}, 9 | {"tag": "cis", "cases": ["true", "false"]}, 10 | {"tag": "iv", "expr": "1"}, 11 | {"tag": "pn", "in": ["n"], "expr": "n - 1"}, 12 | {"tag": "fpn", "path": "/fact"}, 13 | {"tag": "nn", "in": ["n", "fpn"], "expr": "n * fpn"} 14 | ], 15 | "links": [ 16 | [{"in": "n"}, [{"ci": {"in": "n"}}, {"pn": {"in": "n"}}, {"nn": {"in": "n"}}]], 17 | [{"ci": "result"}, {"cis": "target"}], 18 | [{"cis": {"case": "false"}}, "iv"], 19 | [{"cis": {"case": "true"}}, "pn"], 20 | [{"pn": "result"}, {"fpn": {"in": "n"}}], 21 | [{"fpn": {"out": "f"}}, {"nn": {"in": "fpn"}}], 22 | [{"out": "f"}, [{"iv": "result"}, {"nn": "result"}]] 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /app/facts/goodbye/goodbye.js: -------------------------------------------------------------------------------- 1 | const platform = require('platform'); 2 | 3 | 4 | platform.core.node({ 5 | path: '/goodbye/:dude', 6 | public: true, 7 | inputs: ['dude'], 8 | outputs: ['msg'] 9 | }, (inputs, output) => output('msg', `Goodbye ${inputs.dude}!`)); 10 | -------------------------------------------------------------------------------- /app/facts/goodbye/index.js: -------------------------------------------------------------------------------- 1 | module.exports.platform = { 2 | config : { 3 | nodes: { 4 | native: ["goodbye"], 5 | } 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /app/facts/index.js: -------------------------------------------------------------------------------- 1 | module.exports.platform = { 2 | config : { 3 | nodes: { 4 | native: ["native/8fact", "native/funs"], 5 | json: ["fact"], 6 | module: ["goodbye"], 7 | }, 8 | aliases: { 9 | '/ff' : '/fact', 10 | '/8f' : '/8fact', 11 | } 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /app/facts/native/8fact.js: -------------------------------------------------------------------------------- 1 | const platform = require('platform'); 2 | 3 | 4 | platform.core.node({ 5 | path: '/8fact', 6 | public: true, 7 | outputs: ['8!'], 8 | }, (_, o) => { 9 | platform.call('/fact', {n : 8}, res => { 10 | o('8!', res.data); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /app/facts/native/funs.js: -------------------------------------------------------------------------------- 1 | const platform = require('platform'); 2 | 3 | 4 | platform.core.node({ 5 | path: '/funs', 6 | public: true, 7 | inputs: ['a'], 8 | optionalInputs: ['b', 'c', 'also another really important input'], 9 | outputs: ['ab', 'nob'], 10 | }, (i, o) => { 11 | if (i.b) o('ab', i.a * i.b); 12 | else o('nob', i.a); 13 | }); 14 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const platform = require('platform'); 3 | 4 | 5 | platform 6 | .configure(require('./config')) 7 | .configure({root: __dirname}); 8 | 9 | try { 10 | let panelconf = require('./panel-generated/platform-config'); 11 | platform.configure(panelconf); 12 | 13 | require('./panel-generated/platform-config.script'); 14 | } catch(err) { 15 | console.log(err); 16 | } 17 | 18 | try { 19 | if (process.env.CONNECT_PRODUCTION_MODE && process.env.CONNECT_PRODUCTION_MODE.toLocaleLowerCase() === 'true') { 20 | let prodconf = require('./panel-generated/platform-config.prod'); 21 | platform.configure(prodconf); 22 | } 23 | } catch(err) { 24 | console.log(err); 25 | } 26 | 27 | try { 28 | if (process.env.CONNECT_ENABLE_SOCKET && process.env.CONNECT_ENABLE_SOCKET.toLocaleLowerCase() === 'true') { 29 | console.info('Enabling sockets'); 30 | platform.configure({ 31 | enable_sockets: true 32 | }); 33 | } 34 | } catch(err) { 35 | console.error(err); 36 | } 37 | 38 | platform.config.autoparseFromEnvironmentVars(); 39 | 40 | platform.start() 41 | .then(server => { 42 | console.log(`running on http://${server.address().address}` + 43 | `:${server.address().port}`); 44 | }); 45 | -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/assets/favicon.ico -------------------------------------------------------------------------------- /assets/interconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/assets/interconnect.png -------------------------------------------------------------------------------- /assets/laptop-shadow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | laptop 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /assets/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | medium 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/server-shadow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | server 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /assets/shot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/assets/shot1.png -------------------------------------------------------------------------------- /assets/shot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/assets/shot2.png -------------------------------------------------------------------------------- /assets/twitter-type.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | twitter 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "package.json", 4 | "package-lock.json", 5 | "node_modules/*" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "connect-platform", 3 | "version": "0.3.9", 4 | "description": "A platform to build backened code mostly in a visual manner.", 5 | "keywords": [ 6 | "connect", 7 | "platform", 8 | "connect-platform", 9 | "visual development", 10 | "RAD", 11 | "low code", 12 | "no code" 13 | ], 14 | "homepage": "https://connect-platform.com", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/CONNECT-platform/connect-platform.git" 18 | }, 19 | "main": "platform", 20 | "scripts": { 21 | "start": "NODE_PATH=. DEVELOPMENT=true node run", 22 | "test": "mocha --exit" 23 | }, 24 | "author": "Eugene Ghanizadeh Khoub (https://eugene.coding.blog)", 25 | "contributors": [ 26 | "Eugene Ghanizadeh Khoub (https://eugene.coding.blog)", 27 | "Mustapha Ben Chaaben (https://must.coding.blog)" 28 | ], 29 | "license": "MIT", 30 | "dependencies": { 31 | "axios": "^0.19.2", 32 | "body-parser": "^1.19.0", 33 | "cors": "^2.8.5", 34 | "express": "^4.17.1", 35 | "http-proxy-middleware": "^1.0.3", 36 | "jsonwebtoken": "^8.5.1", 37 | "lockfile": "^1.0.4", 38 | "mkdirp": "^0.5.5", 39 | "object-hash": "^2.0.3", 40 | "redis": "^3.0.2", 41 | "socket.io": "^2.3.0", 42 | "socket.io-redis": "^5.3.0" 43 | }, 44 | "devDependencies": { 45 | "chai": "^4.2.0", 46 | "chai-http": "^4.3.0", 47 | "deep-assign": "^3.0.0", 48 | "mocha": "^7.1.2", 49 | "nodemon": "^1.19.4", 50 | "proxyquire": "^2.1.3", 51 | "sinon": "^9.0.2", 52 | "socket.io-client": "^2.3.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /panel/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /panel/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /panel/.npmignore: -------------------------------------------------------------------------------- 1 | /e2e 2 | /node_modules 3 | /src 4 | .editorconfig 5 | karma.conf.js 6 | main.js 7 | package.json 8 | protractor.conf.js 9 | README.md 10 | tsconfig.json 11 | tslint.json 12 | .DS_Store 13 | .angular-cli.json 14 | -------------------------------------------------------------------------------- /panel/README.md: -------------------------------------------------------------------------------- 1 | # Panel 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /panel/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /panel/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('panel App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /panel/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /panel/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /panel/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /panel/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /panel/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/panel/src/app/app.component.css -------------------------------------------------------------------------------- /panel/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /panel/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | describe('AppComponent', () => { 4 | beforeEach(async(() => { 5 | TestBed.configureTestingModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | }).compileComponents(); 10 | })); 11 | it('should create the app', async(() => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.debugElement.componentInstance; 14 | expect(app).toBeTruthy(); 15 | })); 16 | it(`should have as title 'app'`, async(() => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.debugElement.componentInstance; 19 | expect(app.title).toEqual('app'); 20 | })); 21 | it('should render title in a h1 tag', async(() => { 22 | const fixture = TestBed.createComponent(AppComponent); 23 | fixture.detectChanges(); 24 | const compiled = fixture.debugElement.nativeElement; 25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /panel/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app'; 10 | } 11 | -------------------------------------------------------------------------------- /panel/src/app/components/auth/auth.component.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: 100vh; 6 | } 7 | 8 | .inner { 9 | max-width: 40vw; 10 | text-align: center; 11 | } 12 | 13 | p { 14 | margin: 16px 0; 15 | } 16 | 17 | input { 18 | background: black; 19 | width: 80%; 20 | border-radius: 38px; 21 | height: 20px; 22 | line-height: 20px; 23 | padding: 8px; 24 | color: #00BFA5; 25 | border: none; 26 | outline: none; 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /panel/src/app/components/auth/auth.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 |

Authentication Required

7 |

8 | in order to access this instance, you need to have a proper authorization token. 9 | you will automatically get a token when using a valid URL, or you can enter your token 10 | into the field below. 11 |

12 | 13 |

14 | 17 |
18 | 19 |

Authenticating ...

20 |

21 | We are verifying your access to your panel. You will be redirected to your instance shortly. 22 |

23 | 24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /panel/src/app/components/auth/auth.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AuthComponent } from './auth.component'; 4 | 5 | describe('AuthComponent', () => { 6 | let component: AuthComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AuthComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AuthComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/auth/auth.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | import { TokenService } from '../../services/token.service'; 4 | 5 | 6 | @Component({ 7 | selector: 'auth', 8 | templateUrl: './auth.component.html', 9 | styleUrls: ['./auth.component.css'] 10 | }) 11 | export class AuthComponent implements OnInit { 12 | 13 | private given: string; 14 | 15 | constructor( 16 | public token: TokenService 17 | ) { } 18 | 19 | ngOnInit() { 20 | } 21 | 22 | auth() { 23 | this.token.token = this.given; 24 | this.token.goback(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/adder/adder.component.html: -------------------------------------------------------------------------------- 1 |
4 | + 5 | 6 |
7 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/adder/adder.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AdderComponent } from './adder.component'; 4 | 5 | describe('AdderComponent', () => { 6 | let component: AdderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ AdderComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AdderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/adder/adder.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, EventEmitter, Input, Output } from '@angular/core'; 2 | 3 | 4 | enum AdderStates { 5 | initial, activate, reset, 6 | } 7 | 8 | @Component({ 9 | selector: 'editor-adder', 10 | templateUrl: './adder.component.html', 11 | styleUrls: ['./adder.component.css'] 12 | }) 13 | export class AdderComponent implements OnInit { 14 | 15 | states = AdderStates; 16 | state = AdderStates.initial; 17 | 18 | @Input() disabled: boolean = false; 19 | private _style: string = ''; 20 | @Output() add : EventEmitter = new EventEmitter(); 21 | 22 | constructor() { } 23 | 24 | ngOnInit() { 25 | } 26 | 27 | get style() { 28 | return this._style; 29 | } 30 | 31 | @Input() set style(style: string) { 32 | this._style = style; 33 | } 34 | 35 | activate() { 36 | if (this.disabled || this.state !== AdderStates.initial) return; 37 | 38 | this.state = AdderStates.activate; 39 | setTimeout(() => this.reset(), 300); 40 | } 41 | 42 | reset() { 43 | if (this.disabled || this.state !== AdderStates.activate) return; 44 | 45 | this.state = AdderStates.reset; 46 | this.add.emit(); 47 | setTimeout(() => this.state = AdderStates.initial, 300); 48 | } 49 | 50 | get controlStyle() { 51 | return this.style == 'switch' || this.style == 'control'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/bar/bar.component.css: -------------------------------------------------------------------------------- 1 | .holder { 2 | background-image: linear-gradient(rgba(76, 76, 76, .25), rgba(59, 59, 59, .25)); 3 | backdrop-filter: blur(20px) saturate(200%) brightness(150%); 4 | position: absolute; 5 | top: 0; 6 | height: 100vh; 7 | width: 156px; 8 | z-index: 1000; 9 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 10 | } 11 | 12 | .holder.not-safari { 13 | background-image: linear-gradient(rgba(76, 76, 76, .85), rgba(59, 59, 59, .85)); 14 | } 15 | 16 | .holder.left { 17 | left: 0; 18 | } 19 | 20 | .holder.right { 21 | right: 0; 22 | } 23 | 24 | .inner { 25 | position: absolute; 26 | top: 16px; 27 | bottom: 16px; 28 | left: 16px; 29 | right: 16px; 30 | } 31 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/bar/bar.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/bar/bar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { BarComponent } from './bar.component'; 4 | 5 | describe('BarComponent', () => { 6 | let component: BarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ BarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(BarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/bar/bar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'editor-bar', 5 | templateUrl: './bar.component.html', 6 | styleUrls: ['./bar.component.css'] 7 | }) 8 | export class BarComponent implements OnInit { 9 | 10 | @Input() position: string = 'left'; 11 | 12 | constructor() { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | public get isSafari() { 18 | return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/card/card.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CardComponent } from './card.component'; 4 | 5 | describe('CardComponent', () => { 6 | let component: CardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/code/code.component.css: -------------------------------------------------------------------------------- 1 | .holder { 2 | background: #212121; 3 | border: 1px solid white; 4 | padding: 4px; 5 | border-radius: 13px; 6 | margin: 0; 7 | position: relative; 8 | } 9 | 10 | .holder.shadow { 11 | box-shadow: 0 2px 6px rgba(0,0,0,0.5); 12 | transition: box-shadow .3s; 13 | } 14 | 15 | .holder.shadow:hover:not(.selected) { 16 | box-shadow: 0 6px 18px rgba(0,0,0,0.5); 17 | } 18 | 19 | .holder.selected { 20 | box-shadow: 0 0px 0px 1px #40c4ff, 0 0px 32px #0064b7; 21 | } 22 | 23 | .holder.error { 24 | border-color: #ff0033; 25 | box-shadow: 0 0px 0px 2px #ff0033, 0 0px 24px #ff0033 !important; 26 | } 27 | 28 | .holder.control { 29 | border-color: #FFC400; 30 | } 31 | 32 | .ace-holder { 33 | margin-right: 20px; 34 | } 35 | 36 | ace-editor { 37 | } 38 | 39 | editor-pin { 40 | position: absolute; 41 | right: 6px; 42 | top: 50%; 43 | margin-top: -7px; 44 | } 45 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/code/code.component.html: -------------------------------------------------------------------------------- 1 |
4 |
5 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/code/code.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CodeComponent } from './code.component'; 4 | 5 | describe('CodeComponent', () => { 6 | let component: CodeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CodeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CodeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/code/code.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, EventEmitter, Input, Output, ViewChild } from '@angular/core'; 2 | import { Pin } from '../../../models/pin.model'; 3 | 4 | import { EditorService } from '../../../services/editor.service'; 5 | 6 | 7 | @Component({ 8 | selector: 'editor-code', 9 | templateUrl: './code.component.html', 10 | styleUrls: ['./code.component.css'] 11 | }) 12 | export class CodeComponent implements OnInit { 13 | @ViewChild('ace', { static: true }) ace; 14 | 15 | @Input() selected: boolean = false; 16 | @Input() error: boolean = false; 17 | @Input() readonly: boolean = false; 18 | @Input() code: string = ''; 19 | @Input() shadow: boolean = true; 20 | @Input() pin: Pin; 21 | @Output() codeChange: EventEmitter = new EventEmitter(); 22 | 23 | options: any = { 24 | showGutter: false, 25 | maxLines: 12, 26 | tabSize: 2, 27 | } 28 | 29 | constructor(private editor: EditorService) { } 30 | 31 | ngOnInit() { 32 | if (!this.code) 33 | this.ace.getEditor().focus(); 34 | } 35 | 36 | get _code() { return this.code; } 37 | set _code(code: string) { 38 | this.code = code; 39 | this.codeChange.emit(code); 40 | } 41 | 42 | focused(event) { 43 | this.editor.deselect(); 44 | event.stopPropagation(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EditorComponent } from './editor.component'; 4 | 5 | describe('EditorComponent', () => { 6 | let component: EditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EditorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EditorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/general/general.component.css: -------------------------------------------------------------------------------- 1 | .holder { 2 | } 3 | 4 | .access { 5 | cursor: pointer; 6 | font-size: 16px; 7 | font-weight: bold; 8 | transition: color .3s; 9 | } 10 | 11 | .access.public { 12 | color: #76FF03; 13 | } 14 | 15 | .access.internal { 16 | color: #448aff; 17 | } 18 | 19 | .access.socket { 20 | color: #49c289; 21 | } 22 | 23 | .method { 24 | vertical-align: baseline; 25 | font-weight: 900; 26 | cursor: pointer; 27 | font-size: 13px; 28 | color: #9e9e9e; 29 | margin: 0 4px; 30 | transition: color .3s; 31 | } 32 | 33 | .method:hover { 34 | color: white; 35 | } 36 | 37 | .path { 38 | border: none; 39 | background: none; 40 | outline: none; 41 | font-family: inherit; 42 | color: white; 43 | font-size: 18px; 44 | font-weight: 100; 45 | margin: 8px 0; 46 | } 47 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/general/general.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 | {{ model.public ? "public" : (model.socket ? "socket" : "internal") }} 11 | 12 | 16 | {{model.method}} 17 | 18 |
19 |
20 | 21 |
22 |
23 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/general/general.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { GeneralComponent } from './general.component'; 4 | 5 | describe('GeneralComponent', () => { 6 | let component: GeneralComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ GeneralComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(GeneralComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/general/general.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { EditorModelService } from '../../../services/editor-model.service'; 3 | import { TesterService } from '../../../services/tester.service'; 4 | 5 | 6 | @Component({ 7 | selector: 'editor-general', 8 | templateUrl: './general.component.html', 9 | styleUrls: ['./general.component.css'] 10 | }) 11 | export class GeneralComponent implements OnInit { 12 | 13 | constructor( 14 | private _model: EditorModelService, 15 | private _tester: TesterService, 16 | ) { } 17 | 18 | get model() { 19 | return this._model; 20 | } 21 | 22 | get tester() { 23 | return this._tester; 24 | } 25 | 26 | ngOnInit() { 27 | } 28 | 29 | toggleAccess() { 30 | if (this.tester.active) return; 31 | 32 | if(this.model.public) { 33 | this.model.public = false; 34 | } else { 35 | if(this.model.socket) { 36 | this.model.socket = false; 37 | this.model.public = true; 38 | } else { 39 | this.model.socket = true; 40 | } 41 | } 42 | } 43 | 44 | nextMethod() { 45 | if (this.tester.active) return this.model.method; 46 | 47 | if (this.model.method == 'GET') return 'POST'; 48 | if (this.model.method == 'POST') return 'PUT'; 49 | if (this.model.method == 'PUT') return 'DELETE'; 50 | if (this.model.method == 'DELETE') return 'GET'; 51 | return 'GET'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/link/link.component.css: -------------------------------------------------------------------------------- 1 | .holder { 2 | position: absolute; 3 | z-index: 2000; 4 | height: 0px; 5 | cursor: pointer; 6 | transform-origin: center left; 7 | 8 | top: 120px; 9 | width: 256px; 10 | } 11 | 12 | .holder.in-pane { 13 | z-index: 999; 14 | } 15 | 16 | 17 | .holder:hover { 18 | opacity: 1; 19 | } 20 | 21 | .holder .inner { 22 | position: relative; 23 | } 24 | 25 | .holder .line { 26 | border: 5px dotted #76FF03; 27 | margin-top: -4px; 28 | margin-left: 7px; 29 | margin-right: 16px; 30 | transform: scaleY(.15); 31 | opacity: 0.5; 32 | transition: opacity .15s, box-shadow .15s; 33 | } 34 | 35 | .holder .line.chrome { 36 | border-style: dashed; 37 | } 38 | 39 | .holder:hover .line { 40 | opacity: 1; 41 | } 42 | 43 | .holder.active .line { 44 | border-style: solid; 45 | opacity: 1; 46 | } 47 | 48 | .holder.selected .line { 49 | opacity: 1; 50 | box-shadow: 0 0px 0px 12px rgba(118,255,3,.2), 0 0px 128px #76FF03; 51 | } 52 | 53 | .holder.control .line { 54 | border-color: #FFC400; 55 | } 56 | 57 | .holder.control.selected .line { 58 | box-shadow: 0 0 64px #FFC400; 59 | box-shadow: 0 0px 0px 12px rgba(255,196,0,.2), 0 0px 128px #FFC400; 60 | } 61 | 62 | .holder img { 63 | width: 13px; 64 | height: 13px; 65 | position: absolute; 66 | right: 8px; 67 | top: -2px; 68 | transform: scaleY(.65); 69 | } 70 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/link/link.component.html: -------------------------------------------------------------------------------- 1 |
7 |
8 |
9 | 10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/link/link.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LinkComponent } from './link.component'; 4 | 5 | describe('LinkComponent', () => { 6 | let component: LinkComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LinkComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LinkComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/overlays/editor-misc-overlay/editor-misc-overlay.component.css: -------------------------------------------------------------------------------- 1 | .section { 2 | border-bottom: 1px solid rgba(255, 255, 255, .15); 3 | margin-bottom: 16px; 4 | padding-bottom: 16px; 5 | } 6 | 7 | .section .buttons { 8 | text-align: right; 9 | } 10 | 11 | .file-upload { 12 | cursor: pointer; 13 | display: inline-flex; 14 | vertical-align: middle; 15 | align-items: center; 16 | justify-content: center; 17 | text-align: center; 18 | 19 | width: 192px; 20 | height: 96px; 21 | border-radius: 8px; 22 | border: 2px dashed rgba(255, 255, 255, .5); 23 | position: relative; 24 | overflow: hidden; 25 | 26 | transition: border-color .15s, background-color .15s; 27 | } 28 | 29 | .file-upload span { 30 | opacity: 0.5; 31 | font-size: 14px; 32 | margin: 12px; 33 | } 34 | 35 | .file-upload input { 36 | cursor: pointer; 37 | opacity: 0; 38 | position: absolute; 39 | left: 0; 40 | top: 0; 41 | right: 0; 42 | bottom: 0; 43 | } 44 | 45 | .file-upload:hover { 46 | background: rgba(255, 255, 255, .05); 47 | } 48 | 49 | .file-upload.dragOver { 50 | background: rgba(255, 255, 255, .15); 51 | border-color: #ffffff; 52 | } 53 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/overlays/editor-misc-overlay/editor-misc-overlay.component.html: -------------------------------------------------------------------------------- 1 | 2 | Miscellaneous 3 | 4 |
5 |

Export

6 |

Export this node in JSON format. You can import this JSON format in other CONNECT platform instances.

7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |

Import

15 |

Import from a JSON file of an exported node. This will override the contents of your node.

16 |
17 |
18 | Drop a file here or click to choose it 19 | 23 |
24 |
25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/overlays/editor-misc-overlay/editor-misc-overlay.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EditorMiscOverlayComponent } from './editor-misc-overlay.component'; 4 | 5 | describe('EditorMiscOverlayComponent', () => { 6 | let component: EditorMiscOverlayComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EditorMiscOverlayComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EditorMiscOverlayComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/pane.component.css: -------------------------------------------------------------------------------- 1 | .outer { 2 | position: absolute; 3 | left: 0; 4 | top: 0; 5 | bottom: 0; 6 | right: 0; 7 | overflow: scroll; 8 | z-index: 0; 9 | } 10 | 11 | .outer::-webkit-scrollbar { 12 | width: 0px; 13 | } 14 | 15 | .inner { 16 | width: 1000%; 17 | height: 100%; 18 | } 19 | 20 | 21 | .backdrop { 22 | position: absolute; 23 | left: 0; 24 | top: 0; 25 | right: 0; 26 | bottom: 0; 27 | } 28 | 29 | .empty { 30 | position: fixed; 31 | width: 100vw; 32 | height: 100vh; 33 | display: flex; 34 | align-items: center; 35 | justify-content: center; 36 | z-index: 1000; 37 | } 38 | 39 | .empty>div { 40 | max-width: 30vw; 41 | text-align: center; 42 | } 43 | 44 | .empty>div>p { 45 | opacity: .5; 46 | margin-bottom: 24px; 47 | } 48 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/pane.component.html: -------------------------------------------------------------------------------- 1 |
2 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

17 | New to CONNECT platform? Read our guides and documents to find out 18 | what you can do with it and how. 19 |

20 | Check Out The Guides 23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/pane.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PaneComponent } from './pane.component'; 4 | 5 | describe('PaneComponent', () => { 6 | let component: PaneComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PaneComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PaneComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/pane.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | import { EditorService } from '../../../services/editor.service'; 3 | import { EditorModelService } from '../../../services/editor-model.service'; 4 | import { SelectorComponent } from './selector/selector.component'; 5 | 6 | 7 | @Component({ 8 | selector: 'editor-pane', 9 | templateUrl: './pane.component.html', 10 | styleUrls: ['./pane.component.css'] 11 | }) 12 | export class PaneComponent implements OnInit { 13 | 14 | @ViewChild(SelectorComponent, { static: true }) selector; 15 | 16 | constructor(private _editor: EditorService, 17 | private _model: EditorModelService) { 18 | } 19 | 20 | get editor() { 21 | return this._editor; 22 | } 23 | 24 | get model() { 25 | return this._model; 26 | } 27 | 28 | ngOnInit() { 29 | } 30 | 31 | public scrollEvent(event) { 32 | this.editor.paneScrollEvent(event); 33 | } 34 | 35 | public mouseDownEvent(event) { 36 | this.selector.mouseDownEvent(event); 37 | } 38 | 39 | public mouseMoveEvent(event) { 40 | this.editor.mouseMoveEvent(event); 41 | this.selector.mouseMoveEvent(event); 42 | } 43 | 44 | public mouseUpEvent(event) { 45 | this.editor.unpickEvent(); 46 | this.selector.mouseUpEvent(event); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/selector/selector.component.css: -------------------------------------------------------------------------------- 1 | .selector-label { 2 | position: absolute; 3 | 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | 8 | opacity: 0; 9 | color: #616161; 10 | transform-origin: center; 11 | font-size: 12px; 12 | width: 300px; 13 | height: 60px; 14 | margin-left: -150px; 15 | margin-top: -30px; 16 | transform: translateX(-1000px); 17 | 18 | transition: opacity .3s; 19 | } 20 | 21 | .selector-label.active { 22 | opacity: 1; 23 | } 24 | 25 | .selector-box { 26 | background: #000000; 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | width: 100vw; 31 | height: 100vh; 32 | transform-origin: left top; 33 | opacity: 0; 34 | 35 | transition: opacity .5s; 36 | } 37 | 38 | .selector-box.active { 39 | opacity: .25; 40 | } 41 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/selector/selector.component.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |
8 | Hold <Shift> key to add nodes to your selection.
9 | Hold <Ctrl | CMD> key to remove nodes from your selection. 10 |
11 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pane/selector/selector.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SelectorComponent } from './selector.component'; 4 | 5 | describe('SelectorComponent', () => { 6 | let component: SelectorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SelectorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SelectorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pin/pin.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 14px; 3 | height: 14px; 4 | display: inline-block; 5 | vertical-align: middle; 6 | } 7 | 8 | div { 9 | display: inline-block; 10 | vertical-align: top; 11 | width: 12px; 12 | height: 12px; 13 | border-radius: 24px; 14 | cursor: pointer; 15 | border: 1px solid #ffffff; 16 | transition: box-shadow .3s; 17 | } 18 | 19 | div.control { 20 | border-color: #FFC400; 21 | } 22 | 23 | div:hover { 24 | box-shadow: 0 0 6px white; 25 | } 26 | 27 | div.control:hover { 28 | box-shadow: 0 0 6px #FFC400; 29 | } 30 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pin/pin.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pin/pin.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PinComponent } from './pin.component'; 4 | 5 | describe('PinComponent', () => { 6 | let component: PinComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PinComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PinComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/pin/pin.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ElementRef, Input } from '@angular/core'; 2 | 3 | import { EditorService } from '../../../services/editor.service'; 4 | import { Pin, PinType } from '../../../models/pin.model'; 5 | import { Box } from '../../../models/box.model'; 6 | 7 | 8 | @Component({ 9 | selector: 'editor-pin', 10 | templateUrl: './pin.component.html', 11 | styleUrls: ['./pin.component.css'] 12 | }) 13 | export class PinComponent implements OnInit { 14 | 15 | cooldown = undefined; 16 | @Input() pin: Pin; 17 | @Input() controlStyle: boolean = false; 18 | 19 | constructor(private el: ElementRef, private editor: EditorService) { } 20 | 21 | ngOnInit() { 22 | if (this.pin) setTimeout(() => this.pin.component = this); 23 | } 24 | 25 | public get pos() { 26 | return Box.fromElement(this.el.nativeElement).center; 27 | } 28 | 29 | get control() { 30 | return (this.pin && this.pin.type == PinType.control) || this.controlStyle; 31 | } 32 | 33 | click(event?) { 34 | if (event && this.pin) { 35 | event.preventDefault(); 36 | event.stopPropagation(); 37 | 38 | if (this.pin.node && this.editor.isPicked(this.pin.node)) { 39 | this.editor.unpickEvent(); 40 | this.heatup(); 41 | } 42 | } 43 | 44 | if (!this.cooldown) { 45 | if (this.pin) 46 | this.editor.pickEvent({ 47 | pin: this.pin 48 | }); 49 | 50 | this.heatup(); 51 | } 52 | } 53 | 54 | heatup() { 55 | this.cooldown = setTimeout(() => this.cooldown = undefined, 200); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /panel/src/app/components/editor/timeline/timeline.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TimelineComponent } from './timeline.component'; 4 | 5 | describe('TimelineComponent', () => { 6 | let component: TimelineComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ TimelineComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(TimelineComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/config/config.component.css: -------------------------------------------------------------------------------- 1 | .title { 2 | font-size: 32px; 3 | margin-bottom: 56px; 4 | } 5 | 6 | .ace-holder { 7 | background: black; 8 | padding: 16px; 9 | border-radius: 18px; 10 | border: 1px solid white; 11 | } 12 | 13 | .buttons { 14 | margin-top: 24px; 15 | text-align: right; 16 | } 17 | 18 | .error-details { 19 | margin: 16px; 20 | color: #ff1744; 21 | } 22 | -------------------------------------------------------------------------------- /panel/src/app/components/home/config/config.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConfigComponent } from './config.component'; 4 | 5 | describe('ConfigComponent', () => { 6 | let component: ConfigComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ConfigComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConfigComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/nodes/nodelist-entry/nodelist-entry.component.html: -------------------------------------------------------------------------------- 1 |
5 | {{ prepath }} 6 | {{ postpath }} 7 |
8 | 13 | {{ entry.content.public ? "public" : (entry.content.socket ? "socket" : "internal") }} 14 | 15 | 16 | {{ entry.content.method }} 17 | 18 |
19 |
20 | 21 |
23 |
24 |
25 | {{ prepath }} 26 | {{ postpath }} 27 |
28 | 29 | 30 | 31 |
32 |
33 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /panel/src/app/components/home/nodes/nodelist-entry/nodelist-entry.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NodelistEntryComponent } from './nodelist-entry.component'; 4 | 5 | describe('NodelistEntryComponent', () => { 6 | let component: NodelistEntryComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NodelistEntryComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NodelistEntryComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/nodes/nodes.component.html: -------------------------------------------------------------------------------- 1 |
2 |
NODES
3 | 4 |
5 | 7 |
8 | 9 |
10 |

11 | nodes are logical endpoints of your project. they can be either 12 | public, which means they are accessible via requests 13 | from outside world, or can be internal, which 14 | means they can only be called from other nodes of this project. 15 |

16 |
17 | Check Out The Guides 18 | 19 |
20 | 21 |
22 | 30 |
31 | + 32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /panel/src/app/components/home/nodes/nodes.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NodesComponent } from './nodes.component'; 4 | 5 | describe('NodesComponent', () => { 6 | let component: NodesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NodesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NodesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/packages/packages.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PackagesComponent } from './packages.component'; 4 | 5 | describe('PackagesComponent', () => { 6 | let component: PackagesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PackagesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PackagesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/services/services.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ServicesComponent } from './services.component'; 4 | 5 | describe('ServicesComponent', () => { 6 | let component: ServicesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ServicesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ServicesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/home/vault/vault.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { VaultComponent } from './vault.component'; 4 | 5 | describe('VaultComponent', () => { 6 | let component: VaultComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ VaultComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(VaultComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/command-palette/command-palette.component.css: -------------------------------------------------------------------------------- 1 | .contents { 2 | text-align: center; 3 | display: flex; 4 | flex-direction: row; 5 | align-items: center; 6 | height: calc(100vh - 256px); 7 | margin: 0 auto; 8 | padding: 0 256px; 9 | } 10 | 11 | .grid { 12 | display: flex; 13 | flex-direction: row; 14 | flex-grow: 1; 15 | } 16 | 17 | .col { 18 | flex-grow: 1; 19 | } 20 | 21 | .command { 22 | display: flex; 23 | flex-direction: row; 24 | align-items: center; 25 | text-align: left; 26 | } 27 | 28 | .command .key { 29 | min-width: 16px; 30 | height: 48px; 31 | text-align: center; 32 | border-radius: 5px; 33 | font-size: 24px; 34 | line-height: 44px; 35 | margin: 16px; 36 | padding: 0 16px; 37 | background: #424242; 38 | box-shadow: 0 1px 3px black; 39 | } 40 | 41 | .command.complex .key { 42 | font-size: 18px; 43 | line-height: 48px; 44 | padding: 0 32px; 45 | } 46 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/command-palette/command-palette.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 | 7 |
9 |
{{ cmd }}
10 |
{{ current[cmd].name }}
11 |
12 |
13 |
14 |
15 | 16 |
18 |
{{ cmd }}
19 |
{{ current[cmd].name }}
20 |
21 |
22 |
23 |
24 | 25 |
27 |
{{ cmd }}
28 |
{{ current[cmd].name }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/command-palette/command-palette.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CommandPaletteComponent } from './command-palette.component'; 4 | 5 | describe('CommandPaletteComponent', () => { 6 | let component: CommandPaletteComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CommandPaletteComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CommandPaletteComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/hintman/hintman.component.css: -------------------------------------------------------------------------------- 1 | .message-box { 2 | position: absolute; 3 | z-index: 10000000; 4 | background: #212121; 5 | color: #ffffff; 6 | padding: 4px 12px; 7 | border-radius: 12px; 8 | border: 1px solid #ffffff; 9 | font-size: 12px; 10 | opacity: 0; 11 | max-width: 256px; 12 | 13 | transform: scale(0.5) translateY(64px); 14 | 15 | transition: opacity .15s ease-in-out, transform .15s ease-in-out; 16 | } 17 | 18 | .message-box small { 19 | font-size: 10px; 20 | opacity: 0.5; 21 | } 22 | 23 | .message-box.active { 24 | transform: scale(1) translateY(32px); 25 | opacity: 1; 26 | } 27 | 28 | .message-box .code { 29 | width: 156px; 30 | } 31 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/hintman/hintman.component.html: -------------------------------------------------------------------------------- 1 |
4 | 5 |
7 |
8 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/hintman/hintman.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HintmanComponent } from './hintman.component'; 4 | 5 | describe('HintmanComponent', () => { 6 | let component: HintmanComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HintmanComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HintmanComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/hintman/hintman.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, HostListener } from '@angular/core'; 2 | 3 | import { HintService } from '../../../services/hint.service'; 4 | 5 | @Component({ 6 | selector: 'hintman', 7 | templateUrl: './hintman.component.html', 8 | styleUrls: ['./hintman.component.css'] 9 | }) 10 | export class HintmanComponent implements OnInit { 11 | 12 | public mousex: number; 13 | public mousey: number; 14 | 15 | aceOptions: any = { 16 | showGutter: false, 17 | maxLines: Infinity, 18 | tabSize: 2, 19 | wrap: true, 20 | } 21 | 22 | constructor( 23 | private _hint: HintService 24 | ) { } 25 | 26 | ngOnInit() { 27 | } 28 | 29 | @HostListener('document:mousemove', ['$event']) 30 | mousemove(event) { 31 | this.mousex = event.clientX; 32 | this.mousey = event.clientY; 33 | } 34 | 35 | get hint() { 36 | return this._hint; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/overlay/overlay.component.html: -------------------------------------------------------------------------------- 1 |
3 |
4 |
5 | 6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | -------------------------------------------------------------------------------- /panel/src/app/components/shared/overlay/overlay.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { OverlayComponent } from './overlay.component'; 4 | 5 | describe('OverlayComponent', () => { 6 | let component: OverlayComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ OverlayComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(OverlayComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /panel/src/app/directives/autofocus.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { AutofocusDirective } from './autofocus.directive'; 2 | import { ElementRef } from '@angular/core'; 3 | 4 | class MockElementRef extends ElementRef { 5 | constructor() { super(undefined); } 6 | // nativeElement = {}; 7 | } 8 | 9 | describe('AutofocusDirective', () => { 10 | it('should create an instance', () => { 11 | const directive = new AutofocusDirective(new MockElementRef()); 12 | expect(directive).toBeTruthy(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /panel/src/app/directives/autofocus.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[autofocus]' 5 | }) 6 | export class AutofocusDirective { 7 | private focus: boolean = true; 8 | 9 | constructor(private el: ElementRef) { } 10 | 11 | ngOnInit() { 12 | if (this.focus) { 13 | requestAnimationFrame(() => { 14 | this.el.nativeElement.focus(); 15 | }); 16 | } 17 | } 18 | 19 | @Input() set autofocus(condition: boolean){ 20 | this.focus = condition !== false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /panel/src/app/interceptors/token.interceptor.ts: -------------------------------------------------------------------------------- 1 | 2 | import { share } from 'rxjs/operators'; 3 | import { Injectable } from '@angular/core'; 4 | import { 5 | HttpRequest, 6 | HttpHandler, 7 | HttpEvent, 8 | HttpInterceptor, 9 | HttpErrorResponse, 10 | } from '@angular/common/http'; 11 | import { Observable } from 'rxjs'; 12 | 13 | 14 | import { TokenService } from '../services/token.service'; 15 | 16 | 17 | 18 | @Injectable() 19 | export class TokenInterceptor implements HttpInterceptor { 20 | constructor(private token: TokenService) {} 21 | 22 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 23 | request = request.clone({ 24 | setHeaders: { 25 | 'connect_token': this.token.token 26 | } 27 | }); 28 | 29 | let handle = next.handle(request).pipe(share()); 30 | handle.subscribe(()=>{}, 31 | (error:HttpErrorResponse) => { 32 | if (error.error == 'unauthorized' || error.status == 401) { 33 | this.token.request(); 34 | } 35 | }); 36 | return handle; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /panel/src/app/models/abstract-node.model.ts: -------------------------------------------------------------------------------- 1 | import { Subscribable } from '../util/subscribable'; 2 | 3 | export abstract class AbstractNode extends Subscribable { 4 | private _tag: string; 5 | 6 | public constructor(_tag: string) { 7 | super(); 8 | this._tag = _tag; 9 | } 10 | 11 | public get tag(): string { return this._tag; } 12 | public abstract is(type: string): boolean; 13 | } 14 | -------------------------------------------------------------------------------- /panel/src/app/models/signature.model.ts: -------------------------------------------------------------------------------- 1 | export interface SignatureHints { 2 | node?: string; 3 | inputs?: {[key: string]: string}; 4 | outputs?: {[key: string]: string}; 5 | controlOutputs?: {[key: string]: string}; 6 | } 7 | 8 | export interface Signature { 9 | configs?: Array; 10 | inputs: Array; 11 | optionalInputs?: Array; 12 | outputs: Array; 13 | controlOutputs?: Array; 14 | path: string; 15 | public?: boolean; 16 | socket?: boolean; 17 | method?: string; 18 | key?: string; 19 | hints?: SignatureHints; 20 | } 21 | -------------------------------------------------------------------------------- /panel/src/app/models/subgraph.model.ts: -------------------------------------------------------------------------------- 1 | import { Node, NodeJson } from './node.model'; 2 | import { Link, LinkJson } from './link.model'; 3 | 4 | export interface SubGraphJson { 5 | nodes: NodeJson[]; 6 | links: LinkJson[]; 7 | } 8 | 9 | export interface SubGraph { 10 | nodes: Node[]; 11 | links: Link[]; 12 | } 13 | 14 | function updatePinTag(pinJson, mapping) { 15 | for (let key of Object.keys(pinJson)) { 16 | if (key in mapping && key != mapping[key]) { 17 | pinJson[mapping[key]] = pinJson[key]; 18 | delete pinJson[key]; 19 | } 20 | } 21 | } 22 | 23 | export function updateSubgraphTags(subGraph: SubGraphJson, mapping: {[old: string]: string}) { 24 | for (let node of subGraph.nodes) 25 | if (node.tag in mapping) node.tag = mapping[node.tag]; 26 | 27 | for (let link of subGraph.links) { 28 | updatePinTag(link[0], mapping); 29 | if (typeof link[1] === 'string') { 30 | if (link[1] in mapping) link[1] = mapping[link[1]]; 31 | } 32 | else updatePinTag(link[1], mapping); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /panel/src/app/models/switch.model.ts: -------------------------------------------------------------------------------- 1 | import { Node, NodeJson } from './node.model'; 2 | import { Box } from './box.model'; 3 | 4 | 5 | export interface SwitchJson extends NodeJson { 6 | cases?: string[]; 7 | } 8 | 9 | export class Switch extends Node { 10 | public static _Target = 'target'; 11 | 12 | constructor(tag: string, box: Box) { 13 | super(tag, box); 14 | this.in.add(Switch._Target); 15 | } 16 | 17 | public get target() { return this.in.get(Switch._Target); } 18 | public get cases() { return this.control; } 19 | 20 | protected toJson(): SwitchJson { 21 | return Object.assign(super.toJson(), { 22 | cases : this.cases.items.map(i => i.label), 23 | }); 24 | } 25 | 26 | public static emptySwitch(tag: string, left: number, top: number): Switch { 27 | let sw = new Switch(tag, new Box(left || 256, top || 256, 144, 32)); 28 | sw.cases.add('true'); 29 | sw.cases.add('false'); 30 | return sw; 31 | } 32 | 33 | public static fromJson(json: SwitchJson) { 34 | let sw = new Switch(json.tag, Box.fromJson(json.box)); 35 | for (let c of json.cases) { 36 | sw.cases.add(c); 37 | } 38 | 39 | return sw; 40 | } 41 | 42 | public is(type: string): boolean { 43 | if (type === 'switch') return true; 44 | else return super.is(type); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /panel/src/app/models/value.model.ts: -------------------------------------------------------------------------------- 1 | import { Expr, ExprJson } from './expr.model'; 2 | import { Box } from './box.model'; 3 | 4 | 5 | export enum ValueEvents { 6 | codeChange, 7 | } 8 | 9 | export class Value extends Expr { 10 | public set code(code : string) { 11 | super._setCode(code); 12 | this.publish(ValueEvents.codeChange, code); 13 | } 14 | 15 | public get code() { return this._getCode(); } 16 | 17 | public static emptyValue(tag: string, left: number, top: number): Value { 18 | let value = new Value(tag, new Box(left || 256, top || 256, 172, 32)); 19 | value.code = '//something ...'; 20 | return value; 21 | } 22 | 23 | public static fromJson(json: ExprJson) { 24 | let value = new Value(json.tag, Box.fromJson(json.box)); 25 | value.code = json.expr; 26 | return value; 27 | } 28 | 29 | public is(type: string): boolean { 30 | if (type === 'value') return true; 31 | else return super.is(type); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /panel/src/app/services/backend.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { BackendService } from './backend.service'; 4 | 5 | describe('BackendService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [BackendService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([BackendService], (service: BackendService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/clipboard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ClipboardService } from './clipboard.service'; 4 | 5 | describe('ClipboardService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ClipboardService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ClipboardService], (service: ClipboardService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/clipboard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class ClipboardService { 5 | 6 | constructor() { } 7 | 8 | public copy(text: string, event: any) { 9 | if (event) { 10 | event.preventDefault(); 11 | event.stopPropagation(); 12 | } 13 | 14 | let _ = document.createElement('textarea'); 15 | _.style.position = 'fixed'; _.style.left = '0'; _.style.top = '0'; _.style.opacity = '0'; 16 | _.value = text; 17 | document.body.appendChild(_); 18 | _.focus(); _.select(); 19 | document.execCommand('copy'); 20 | document.body.removeChild(_); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /panel/src/app/services/editor-model.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { EditorModelService } from './editor-model.service'; 4 | 5 | describe('EditorModelService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [EditorModelService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([EditorModelService], (service: EditorModelService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/editor.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { EditorService } from './editor.service'; 4 | 5 | describe('EditorService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [EditorService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([EditorService], (service: EditorService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/hint.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { HintService } from './hint.service'; 4 | 5 | describe('HintService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [HintService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([HintService], (service: HintService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/hint.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | export enum HintType { 4 | _HyperText, 5 | _Code, 6 | } 7 | 8 | export class HintRef { 9 | constructor( 10 | private master: HintService, 11 | private _message: string, 12 | private _type: HintType = HintType._HyperText, 13 | ) { } 14 | 15 | public get message(): string { 16 | return this._message; 17 | } 18 | 19 | public get type(): HintType { 20 | return this._type; 21 | } 22 | 23 | public clear() { 24 | this.master.clear(this); 25 | return this; 26 | } 27 | } 28 | 29 | @Injectable() 30 | export class HintService { 31 | 32 | public types = HintType; 33 | 34 | private refs: Array = []; 35 | private _current: HintRef; 36 | 37 | constructor() { } 38 | 39 | public get current(): HintRef { 40 | return this._current; 41 | } 42 | 43 | public display(message: string, type: HintType = HintType._HyperText) : HintRef { 44 | this._current = new HintRef(this, message, type); 45 | this.refs.push(this._current); 46 | return this._current; 47 | } 48 | 49 | public clear(ref: HintRef) { 50 | this.refs = this.refs.filter(_ref => _ref != ref); 51 | if (this.refs.length > 0) this._current = this.refs[this.refs.length - 1]; 52 | return this; 53 | } 54 | 55 | public get visible(): boolean { 56 | return this.refs.length > 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /panel/src/app/services/nodelist-state.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { NodelistStateService } from './nodelist-state.service'; 4 | 5 | describe('NodelistStateService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [NodelistStateService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([NodelistStateService], (service: NodelistStateService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/nodelist-state.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | 4 | const LSKEY = 'connect-platform-nodelist-state'; 5 | 6 | @Injectable() 7 | export class NodelistStateService { 8 | list = undefined; 9 | 10 | constructor() { } 11 | 12 | fetch() { 13 | if (!this.list) { 14 | let lsvalue = localStorage.getItem(LSKEY); 15 | if (lsvalue) this.list = JSON.parse(lsvalue); 16 | else this.list = []; 17 | } 18 | } 19 | 20 | save() { 21 | localStorage.setItem(LSKEY, JSON.stringify(this.list)); 22 | } 23 | 24 | public savedState(path: string) { 25 | this.fetch(); 26 | return this.list.includes(path); 27 | } 28 | 29 | public updateState(path: string, state: boolean) { 30 | this.fetch(); 31 | if (!state) 32 | this.list = this.list.filter(p => p != path); 33 | else if (!this.list.includes(path)) this.list.push(path); 34 | this.save(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /panel/src/app/services/registry.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { RegistryService } from './registry.service'; 4 | 5 | describe('RegistryService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [RegistryService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([RegistryService], (service: RegistryService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/repo.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { RepoService } from './repo.service'; 4 | 5 | describe('RepoService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [RepoService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([RepoService], (service: RepoService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/repo.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Observer } from 'rxjs'; 3 | 4 | import { BackendService } from './backend.service'; 5 | 6 | 7 | @Injectable() 8 | export class RepoService { 9 | private _repo: any; 10 | 11 | constructor( 12 | private backend: BackendService, 13 | ) { } 14 | 15 | private get repo(): Observable { 16 | return Observable.create((observer: Observer) => { 17 | if (this._repo) { 18 | observer.next(this._repo); 19 | observer.complete(); 20 | } 21 | else { 22 | this.backend.packageRepo().subscribe(response => { 23 | if (response.repo) this._repo = response.repo; 24 | observer.next(this._repo); 25 | observer.complete(); 26 | }); 27 | } 28 | }); 29 | } 30 | 31 | public get display(): Observable { 32 | return Observable.create((observer: Observer) => { 33 | this.repo.subscribe(repo => { 34 | observer.next(repo.packages); 35 | observer.complete(); 36 | }) 37 | }); 38 | } 39 | 40 | public package(name: string): Observable { 41 | return Observable.create((observer: Observer) => { 42 | this.repo.subscribe(repo => { 43 | let candidates = repo.packages.filter(pkg => pkg.name == name); 44 | 45 | if (candidates.length > 0) 46 | observer.next(candidates[0]); 47 | else 48 | observer.next({}); 49 | 50 | observer.complete(); 51 | }); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /panel/src/app/services/tester.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { TesterService } from './tester.service'; 4 | 5 | describe('TesterService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [TesterService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([TesterService], (service: TesterService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/token.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { TokenService } from './token.service'; 4 | 5 | describe('TokenService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [TokenService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([TokenService], (service: TokenService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /panel/src/app/services/token.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | const _ConnectPanelToken: string = '__connect_panel_token'; 5 | 6 | @Injectable() 7 | export class TokenService { 8 | 9 | private _token: string = null; 10 | private _urlBefore: string = '/'; 11 | 12 | constructor(private router: Router) { } 13 | 14 | get token(): string { 15 | if (this._token === null) { 16 | this._token = localStorage.getItem(_ConnectPanelToken) || ''; 17 | } 18 | 19 | return this._token; 20 | } 21 | 22 | set token(_token: string) { 23 | this._token = _token; 24 | localStorage[_ConnectPanelToken] = _token; 25 | } 26 | 27 | reset() { 28 | if (this.token !== '') { 29 | this.token = ''; 30 | this.router.navigate(['auth']); 31 | } 32 | } 33 | 34 | request() {; 35 | let tree = this.router.parseUrl(this.router.url); 36 | if (tree.queryParamMap.has('token')) { 37 | let token = tree.queryParamMap.get('token'); 38 | if (token != this.token) { 39 | this.token = token; 40 | 41 | let _url = this.router.url; 42 | if (_url == '/auth') _url = '/'; 43 | 44 | this.router.navigate(['auth']).then(() => { 45 | setTimeout(() => { 46 | this.router.navigateByUrl(_url); 47 | }, 2000); 48 | }); 49 | return; 50 | } 51 | } 52 | 53 | if (this.router.url != '/auth') { 54 | this._urlBefore = this.router.url; 55 | this.router.navigate(['auth'], { preserveQueryParams: true }); 56 | } 57 | } 58 | 59 | goback() { 60 | this.router.navigateByUrl(this._urlBefore || '/'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /panel/src/app/util/decompose-code.ts: -------------------------------------------------------------------------------- 1 | export function decomposeCode(code, variable) { 2 | let decomp = []; 3 | let split = code.split(/(?=[\s|\.|\"|\'|\`|\||\.|\+|\-|\*|\/|\^|\&|\:|\?|\!])/g) 4 | 5 | let str = null; 6 | 7 | for (let i of split) { 8 | if (str) { 9 | if (i[0] == str) str = null; 10 | else { 11 | decomp.push(i); 12 | continue; 13 | } 14 | } 15 | else { 16 | if (i[0] == '"' || i[0] == "'" || i[0] == '`') { 17 | str = i[0]; 18 | decomp.push(i); 19 | continue; 20 | } 21 | } 22 | 23 | if (i.endsWith(variable) && i[0] != '.') { 24 | decomp.push(i.replace(variable, "")); 25 | decomp.push(null); 26 | continue; 27 | } 28 | 29 | decomp.push(i); 30 | } 31 | 32 | return decomp; 33 | } 34 | 35 | export function recomposeCode(decomp, variable) { 36 | let res = ''; 37 | 38 | for (let i of decomp) { 39 | if (i !== null) { 40 | res += i; 41 | } 42 | else 43 | res += variable; 44 | } 45 | 46 | return res; 47 | } 48 | -------------------------------------------------------------------------------- /panel/src/app/util/deep-merge.ts: -------------------------------------------------------------------------------- 1 | // Utility code blatently copied from https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge 2 | /** 3 | * Simple object check. 4 | * @param item 5 | * @returns {boolean} 6 | */ 7 | export function isObject(item) { 8 | return (item && typeof item === 'object' && !Array.isArray(item)); 9 | } 10 | 11 | /** 12 | * Deep merge two objects. 13 | * @param target 14 | * @param ...sources 15 | */ 16 | export function deepMerge(target, ...sources) { 17 | if (!sources.length) return target; 18 | const source = sources.shift(); 19 | 20 | if (isObject(target) && isObject(source)) { 21 | for (const key in source) { 22 | if (isObject(source[key])) { 23 | if (!target[key]) Object.assign(target, { [key]: {} }); 24 | deepMerge(target[key], source[key]); 25 | } else { 26 | Object.assign(target, { [key]: source[key] }); 27 | } 28 | } 29 | } 30 | 31 | return deepMerge(target, ...sources); 32 | } -------------------------------------------------------------------------------- /panel/src/app/util/elem-box.ts: -------------------------------------------------------------------------------- 1 | function getOffsetSum(elem) { 2 | var top = 0, 3 | left = 0, 4 | bottom = 0, 5 | right = 0 6 | 7 | var width = elem.offsetWidth; 8 | var height = elem.offsetHeight; 9 | 10 | while (elem) { 11 | top += elem.offsetTop; 12 | left += elem.offsetLeft; 13 | elem = elem.offsetParent; 14 | } 15 | 16 | right = left + width; 17 | bottom = top + height; 18 | 19 | return { 20 | top: top, 21 | left: left, 22 | bottom: bottom, 23 | right: right, 24 | } 25 | } 26 | 27 | function getOffsetRect(elem) { 28 | var box = elem.getBoundingClientRect(); 29 | 30 | var body = document.body; 31 | var docElem = document.documentElement; 32 | 33 | var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop; 34 | var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft; 35 | 36 | var clientTop = docElem.clientTop; 37 | var clientLeft = docElem.clientLeft; 38 | 39 | 40 | var top = box.top + scrollTop - clientTop; 41 | var left = box.left + scrollLeft - clientLeft; 42 | var bottom = top + (box.bottom - box.top); 43 | var right = left + (box.right - box.left); 44 | 45 | return { 46 | top: top, 47 | left: left, 48 | bottom: bottom, 49 | right: right, 50 | } 51 | } 52 | 53 | export function elementBox(elem) { 54 | if (elem) { 55 | if (elem.getBoundingClientRect) { 56 | return getOffsetRect(elem); 57 | } else { // old browser 58 | return getOffsetSum(elem); 59 | } 60 | } else 61 | return null; 62 | } 63 | -------------------------------------------------------------------------------- /panel/src/app/util/subscribable.ts: -------------------------------------------------------------------------------- 1 | export class Subscribable { 2 | private subscribers: Object; 3 | 4 | constructor() { 5 | this.subscribers = {} 6 | } 7 | 8 | public subscribe(event, callback): Subscribable { 9 | if (event instanceof Array) { 10 | for (let _event of event) this.subscribe(_event, callback); 11 | } 12 | else { 13 | this.subscribers[event] = this.subscribers[event] || []; 14 | this.subscribers[event].push(callback); 15 | } 16 | return this; 17 | } 18 | 19 | public unsubscribe(event, callback): Subscribable { 20 | if (event instanceof Array) { 21 | for (let _event of event) this.unsubscribe(_event, callback); 22 | } 23 | else { 24 | if (this.subscribers[event]) 25 | this.subscribers[event] = this.subscribers[event].filter(c => c != callback); 26 | } 27 | return this; 28 | } 29 | 30 | public publish(event, data?): Subscribable { 31 | if (this.subscribers[event]) 32 | for (let callback of this.subscribers[event]) 33 | callback(data, this); 34 | return this; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /panel/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/panel/src/assets/.gitkeep -------------------------------------------------------------------------------- /panel/src/assets/arrow-head-control.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arrow-head-control 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/arrow-head-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arrow-head-white 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /panel/src/assets/arrow-head.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arrow-head 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/close-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | close 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | close 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /panel/src/assets/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | folder 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /panel/src/assets/info-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | info 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | info 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/key-filled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | key 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/assets/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | key 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /panel/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /panel/src/fav.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CONNECT-platform/connect-platform/1bced92cb49ab35119137c203ee78f2784b7adb7/panel/src/fav.ico -------------------------------------------------------------------------------- /panel/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /panel/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /** 5 | * Required to support Web Animations `@angular/platform-browser/animations`. 6 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 7 | **/ 8 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 9 | 10 | 11 | 12 | /*************************************************************************************************** 13 | * Zone JS is required by default for Angular itself. 14 | */ 15 | import 'zone.js/dist/zone'; // Included with Angular CLI. 16 | 17 | 18 | 19 | /*************************************************************************************************** 20 | * APPLICATION IMPORTS 21 | */ 22 | -------------------------------------------------------------------------------- /panel/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /panel/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "main.ts", 10 | "polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /panel/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /panel/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /panel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "downlevelIteration": true, 5 | "importHelpers": true, 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es2015", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ], 20 | "module": "esnext", 21 | "baseUrl": "./" 22 | } 23 | } -------------------------------------------------------------------------------- /platform/bind/common/test/index.js: -------------------------------------------------------------------------------- 1 | describe('bind.common', () => { 2 | require('./routes'); 3 | }); 4 | -------------------------------------------------------------------------------- /platform/bind/express/index.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const cors = require('cors'); 3 | 4 | const router = require('./router'); 5 | const init = require('./init'); 6 | 7 | 8 | module.exports = (app, config) => { 9 | if (!app) app = init(); 10 | 11 | if (config.has('instance_auto_sleep')) { 12 | let timeout = config.get('instance_auto_sleep'); 13 | let kill = () => { 14 | console.log('Killing instance due to inactivity'); 15 | process.exit(1); 16 | }; 17 | let timer = setTimeout(kill, timeout); 18 | app.use((req, res, next) => { 19 | clearTimeout(timer); 20 | timer = setTimeout(kill, timeout); 21 | next(); 22 | }); 23 | } 24 | 25 | app.use(bodyParser.json({ 26 | verify(req, res, buf) { 27 | req.raw = buf; 28 | }, 29 | limit: config.get('request_limit', '10mb'), 30 | extended: true, 31 | })); 32 | 33 | if (config.get('cors', true)) 34 | app.use(cors()); 35 | app.use(router(config)); 36 | 37 | if (config.get('interconnectible', true)) 38 | app.get('/api', (req, res) => { 39 | res.status(200).send(router.routes.get().filter(signature => signature.interconnectible !== false)); 40 | }); 41 | 42 | return app; 43 | } 44 | -------------------------------------------------------------------------------- /platform/bind/express/init.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | module.exports = () => express(); 4 | -------------------------------------------------------------------------------- /platform/bind/express/req-handler.js: -------------------------------------------------------------------------------- 1 | const core = require('../../core'); 2 | const conventions = require('../../conventions'); 3 | 4 | 5 | const reqHandler = (factoryOrClass, signature) => { 6 | return (req, res) => { 7 | let params = {}; 8 | 9 | if (signature && signature.inputs) 10 | for (let input of signature.inputs) { 11 | let candidate = req.header(input); 12 | if (candidate || typeof candidate == 'string') 13 | params[input] = candidate; 14 | } 15 | 16 | Object.assign(params, req.body); 17 | Object.assign(params, req.params); 18 | Object.assign(params, req.query); 19 | 20 | core.callable(factoryOrClass, {req, res})(params).then(result => { 21 | if (result.output) { 22 | let r = {}; 23 | r[result.output] = result.data; 24 | res.status(200).json(r); 25 | } 26 | else if (result.control) { 27 | let status = 200; 28 | 29 | //TODO: move these to somewhere nicer. 30 | // 31 | if (result.control == conventions.controls._WrongInput) status = 400; 32 | if (result.control == conventions.controls._Unauthorized) status = 401; 33 | if (result.control == conventions.controls._Forbidden) status = 403; 34 | if (result.control == conventions.controls._NotFound) status = 404; 35 | if (result.control == conventions.controls._InternalError) status = 500; 36 | 37 | res.status(status).json(result.control); 38 | } 39 | }).catch(error => { 40 | res.status(error.status || 500).send(error.message); 41 | }); 42 | }; 43 | }; 44 | 45 | module.exports = reqHandler; 46 | -------------------------------------------------------------------------------- /platform/bind/express/router.js: -------------------------------------------------------------------------------- 1 | const { Router } = require('express'); 2 | const core = require('../../core'); 3 | const reqHandler = require('./req-handler'); 4 | const Routes = require('../common/routes'); 5 | 6 | const { hashSig } = require('../../util/hash'); 7 | 8 | const publicRoutes = new Routes(core.registry, 'public'); 9 | 10 | const buildRouter = () => { 11 | let router = Router(); 12 | 13 | for (let signature of publicRoutes.get()) { 14 | let method = (signature.method) ? (signature.method.toLowerCase()) : 'get'; 15 | let handler = null; 16 | try { 17 | handler = reqHandler(() => core.registry.instance(signature.path, hashSig(signature)), signature); 18 | } catch(err) { 19 | handler = reqHandler(() => core.registry.instance(signature.path, method), signature); 20 | } 21 | 22 | if (method == 'get') router.get(signature.path, handler); 23 | if (method == 'post') router.post(signature.path, handler); 24 | if (method == 'put') router.put(signature.path, handler); 25 | if (method == 'delete') router.delete(signature.path, handler); 26 | } 27 | 28 | return router; 29 | } 30 | 31 | module.exports = buildRouter; 32 | module.exports.routes = publicRoutes; 33 | -------------------------------------------------------------------------------- /platform/bind/express/test/index.js: -------------------------------------------------------------------------------- 1 | describe('bind.express', () => { 2 | require('./req-handler'); 3 | require('./router'); 4 | }); 5 | -------------------------------------------------------------------------------- /platform/bind/panel/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const platform = require('../../'); 5 | const config = require('./util/config'); 6 | 7 | 8 | platform.subscribe(platform.events.bind, (_, platform) => { 9 | if (config.expose) { 10 | let staticFiles = path.join(__dirname, '../../../panel/dist/'); 11 | platform.app.use(config.path, express.static(staticFiles)); 12 | platform.app.use(path.join(config.path, '*'), (req, res) => { 13 | res.sendFile(path.join(staticFiles, 'index.html')); 14 | }); 15 | } 16 | }); 17 | 18 | module.exports.platform = { 19 | config : { 20 | nodes: { 21 | native: [ 22 | "name", 23 | "registry-service", 24 | 25 | "save-node", 26 | "load-node", 27 | "delete-node", 28 | "panel-nodes", 29 | 30 | "test", 31 | "watch", 32 | 33 | "config/load", 34 | "config/save", 35 | 36 | "vault/list", 37 | "vault/put", 38 | "vault/get", 39 | "vault/delete", 40 | 41 | "packages/list", 42 | "packages/install", 43 | "packages/uninstall", 44 | "packages/status", 45 | "packages/repo", 46 | 47 | "services/list", 48 | "services/save", 49 | "services/remove", 50 | "services/info", 51 | 52 | "shell-url", 53 | "version", 54 | ], 55 | }, 56 | }, 57 | } 58 | -------------------------------------------------------------------------------- /platform/bind/panel/load-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../'); 4 | const config = require('./util/config'); 5 | const files = require('./util/file-io'); 6 | const authorize = require('./util/authorize'); 7 | 8 | 9 | platform.core.node({ 10 | path : `${config.path}load/:id`, 11 | public : config.expose, 12 | interconnectible: false, 13 | method : 'GET', 14 | inputs : ['id', 'connect_token'], 15 | outputs : ['node'], 16 | controlOutputs: [ 17 | platform.conventions.controls._Unauthorized, 18 | platform.conventions.controls._NotFound, 19 | ], 20 | }, (inputs, output, control) => { 21 | authorize(inputs.connect_token) 22 | .then(() => { 23 | let nodefile = path.join(config.directory, config.files.nodedir, inputs.id); 24 | files.json.load(nodefile).then(node => { 25 | output('node', node); 26 | }).catch(error => { 27 | control(platform.conventions.controls._NotFound); 28 | }); 29 | }) 30 | .catch(error => { 31 | control(platform.conventions.controls._Unauthorized); 32 | }) 33 | }); 34 | -------------------------------------------------------------------------------- /platform/bind/panel/name.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../'); 2 | const config = require('./util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}name`, 7 | method : 'GET', 8 | public: config.expose, 9 | outputs : ['name'], 10 | interconnectible: false, 11 | }, 12 | (_, output) => { 13 | output('name', platform.config.get('name', '')); 14 | }); 15 | -------------------------------------------------------------------------------- /platform/bind/panel/packages/install.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}packages/install`, 7 | public : config.expose, 8 | method : 'POST', 9 | interconnectible: false, 10 | inputs : ['connect_token', 'name', 'source'], 11 | controlOutputs: [ 12 | 'done', 13 | 'already_installed', 14 | platform.conventions.controls._Unauthorized 15 | ], 16 | }, (inputs, output, control) => { 17 | platform.call(`${config.path}config/load`, 18 | { connect_token: inputs.connect_token }) 19 | .then(result => { 20 | if (result.data) { 21 | let conf = result.data; 22 | 23 | conf.nodes = conf.nodes || {}; 24 | conf.nodes.module = conf.nodes.module || []; 25 | 26 | if (conf.nodes.module.some(pkg => { 27 | if (typeof pkg == 'object' && pkg.name) return pkg.name == inputs.name; 28 | else return pkg == inputs.name; 29 | })) 30 | control('already_installed'); 31 | else { 32 | conf.nodes.module.push({ 33 | name: inputs.name, 34 | source: inputs.source, 35 | }); 36 | 37 | platform.call(`${config.path}config/save`, { 38 | connect_token: inputs.connect_token, 39 | config: conf, 40 | }) 41 | .then(() => control('done')); 42 | } 43 | } 44 | else control(platform.conventions.controls._Unauthorized); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /platform/bind/panel/packages/list.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}packages/list`, 7 | public : config.expose, 8 | method : 'GET', 9 | interconnectible: false, 10 | inputs : ['connect_token'], 11 | outputs : ['list'], 12 | controlOutputs: [ platform.conventions.controls._Unauthorized ], 13 | }, (inputs, output, control) => { 14 | platform.call(`${config.path}config/load`, 15 | { connect_token: inputs.connect_token }) 16 | .then(result => { 17 | if (result.data) { 18 | if (result.data.nodes && result.data.nodes.module) 19 | output('list', result.data.nodes.module); 20 | else 21 | output('list', []); 22 | } 23 | else control(platform.conventions.controls._Unauthorized); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /platform/bind/panel/packages/repo.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | const platform = require('../../../'); 4 | const config = require('../util/config'); 5 | 6 | 7 | const _Gateway = 'https://raw.githubusercontent.com/loreanvictor/connect-repo/master/gateway.json'; 8 | 9 | platform.core.node({ 10 | path : `${config.path}packages/repo`, 11 | public : config.expose, 12 | method : 'GET', 13 | interconnectible: false, 14 | outputs : ['repo'], 15 | }, (inputs, output, control, error) => { 16 | axios.get(_Gateway) 17 | .then(response => { 18 | if (response.data.repository) 19 | axios.get(response.data.repository).then(response => { 20 | output('repo', response.data); 21 | }) 22 | .catch(err => error(err)); 23 | else output('repo', {}); 24 | }) 25 | .catch(err => error(err)); 26 | }); 27 | -------------------------------------------------------------------------------- /platform/bind/panel/packages/status.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | //const external = require('../../../loaders/load-external'); 3 | const config = require('../util/config'); 4 | 5 | 6 | platform.core.node({ 7 | path : `${config.path}packages/status`, 8 | public : config.expose, 9 | method : 'GET', 10 | interconnectible: false, 11 | inputs : ['name'], 12 | outputs : ['status'] 13 | }, (inputs, output, control) => { 14 | try { 15 | let mod = require(inputs.name); 16 | 17 | let provided = []; 18 | if (inputs.name in global.connect_platform_dependencies) provided = global.connect_platform_dependencies[inputs.name]; 19 | 20 | let hints = []; 21 | if (mod.platform && mod.platform.hints) hints = mod.platform.hints; 22 | 23 | output('status', { 24 | installed: true, 25 | provided: provided, 26 | hints: hints, 27 | }); 28 | } catch(_) { 29 | output('status', { 30 | installed: false, 31 | }); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /platform/bind/panel/registry-service.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../'); 2 | const config = require('./util/config'); 3 | 4 | 5 | //TODO: make this an authorized node. 6 | // 7 | 8 | platform.core.node({ 9 | path : `${config.path}registry`, 10 | method : 'GET', 11 | public: config.expose, 12 | interconnectible: false, 13 | outputs : ['registry'], 14 | }, 15 | (_, output) => { 16 | output('registry', platform.core.registry.registrants); 17 | }); 18 | -------------------------------------------------------------------------------- /platform/bind/panel/services/info.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | const authorize = require('../util/authorize'); 4 | 5 | 6 | platform.core.node({ 7 | path : `${config.path}services/info`, 8 | public : config.expose, 9 | method : 'GET', 10 | interconnectible: false, 11 | inputs : ['name', 'connect_token'], 12 | outputs : ['info'], 13 | controlOutputs: [ 14 | platform.conventions.controls._NotFound, 15 | platform.conventions.controls._Unauthorized ], 16 | }, (inputs, output, control) => { 17 | authorize(inputs.connect_token) 18 | .then(() => { 19 | if (inputs.name in global.connect_platform_service_dependencies) 20 | output('info', global.connect_platform_service_dependencies[inputs.name]); 21 | else control(platform.conventions.controls._NotFound); 22 | }) 23 | .catch(error => control(platform.conventions.controls._Unauthorized)); 24 | }); 25 | -------------------------------------------------------------------------------- /platform/bind/panel/services/list.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}services/list`, 7 | public : config.expose, 8 | method : 'GET', 9 | interconnectible: false, 10 | inputs : ['connect_token'], 11 | outputs : ['list'], 12 | controlOutputs: [ platform.conventions.controls._Unauthorized ], 13 | }, (inputs, output, control) => { 14 | platform.call(`${config.path}config/load`, 15 | { connect_token: inputs.connect_token }) 16 | .then(result => { 17 | if (result.data) { 18 | if (result.data.nodes && result.data.nodes.service) 19 | output('list', result.data.nodes.service); 20 | else 21 | output('list', []); 22 | } 23 | else control(platform.conventions.controls._Unauthorized); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /platform/bind/panel/services/remove.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}services/remove`, 7 | public : config.expose, 8 | method : 'POST', 9 | interconnectible: false, 10 | inputs : ['name', 'connect_token'], 11 | outputs : [], 12 | controlOutputs: [ 'done', platform.conventions.controls._Unauthorized ], 13 | }, (inputs, output, control) => { 14 | platform.call(`${config.path}config/load`, 15 | { connect_token: inputs.connect_token }) 16 | .then(result => { 17 | if (result.data) { 18 | let conf = result.data; 19 | 20 | if (conf.nodes && conf.nodes.service) { 21 | conf.nodes.service = conf.nodes.service.filter(service => service.name != inputs.name); 22 | 23 | platform.call(`${config.path}config/save`, { 24 | connect_token: inputs.connect_token, 25 | config: conf, 26 | }) 27 | .then(() => control('done')); 28 | } 29 | else control('done'); 30 | } 31 | else control(platform.conventions.controls._Unauthorized); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /platform/bind/panel/services/save.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../../'); 2 | const config = require('../util/config'); 3 | 4 | 5 | platform.core.node({ 6 | path : `${config.path}services/save`, 7 | public : config.expose, 8 | method : 'POST', 9 | interconnectible: false, 10 | inputs : ['name', 'url', 'connect_token'], 11 | outputs : [], 12 | controlOutputs: [ 'done', platform.conventions.controls._Unauthorized ], 13 | }, (inputs, output, control) => { 14 | platform.call(`${config.path}config/load`, 15 | { connect_token: inputs.connect_token }) 16 | .then(result => { 17 | if (result.data) { 18 | let conf = result.data; 19 | 20 | conf.nodes = conf.nodes || {}; 21 | conf.nodes.service = conf.nodes.service || []; 22 | 23 | if (conf.nodes.service.some(service => service.name == inputs.name)) 24 | conf.nodes.service.forEach(service => { 25 | if (service.name == inputs.name) 26 | service.url = inputs.url; 27 | }); 28 | else conf.nodes.service.push({name: inputs.name, url: inputs.url}); 29 | 30 | platform.call(`${config.path}config/save`, { 31 | connect_token: inputs.connect_token, 32 | config: conf, 33 | }) 34 | .then(() => control('done')); 35 | } 36 | else control(platform.conventions.controls._Unauthorized); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /platform/bind/panel/shell-url.js: -------------------------------------------------------------------------------- 1 | const platform = require('../../'); 2 | 3 | const config = require('./util/config'); 4 | const authorize = require('./util/authorize'); 5 | 6 | const shellauth = require('../shell/auth'); 7 | const shell = require('../shell'); 8 | 9 | 10 | platform.core.node({ 11 | path : `${config.path}shell-url`, 12 | method : 'GET', 13 | public: config.expose, 14 | inputs: ['connect_token'], 15 | outputs : ['url'], 16 | controlOutputs: [ 17 | platform.conventions.controls._Unauthorized, 18 | platform.conventions.controls._InternalError, 19 | platform.conventions.controls._NotFound, 20 | ], 21 | interconnectible: false, 22 | }, 23 | (inputs, output, control) => { 24 | if (shell.info.running) { 25 | authorize(inputs.connect_token) 26 | .then(() => { 27 | shellauth.sign() 28 | .then(token => output('url', shell.info.config.path + `?token=${token}`)) 29 | .catch(error => control('no_shell')); 30 | }) 31 | .catch(error => { 32 | control(platform.conventions.controls._InternalError,); 33 | }); 34 | } 35 | else control(platform.conventions.controls._NotFound); 36 | }); 37 | -------------------------------------------------------------------------------- /platform/bind/panel/test.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../'); 4 | const config = require('./util/config'); 5 | const record = require('../../recorder'); 6 | 7 | const purify = require('./util/purify-recording'); 8 | 9 | 10 | platform.core.node({ 11 | path : `${config.path}test`, 12 | public : config.expose, 13 | method : 'POST', 14 | interconnectible: false, 15 | inputs : ['model', 'inputs', 'timelimit'], 16 | outputs : ['recording', 'error'], 17 | controlOutputs : ['wrong_input'], 18 | }, (inputs, output, control, _, context) => { 19 | 20 | let provided = Object.keys(inputs.inputs); 21 | if (inputs.model.in.some(input => !provided.includes(input))) { 22 | control('wrong_input'); 23 | return; 24 | } 25 | 26 | record(inputs.model, inputs.inputs, platform.config.core, inputs.timelimit, context) 27 | .then(recording => { 28 | output('recording', purify(recording)); 29 | }) 30 | .catch(error => { 31 | console.log(error); 32 | output('error', error); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /platform/bind/panel/test/index.js: -------------------------------------------------------------------------------- 1 | describe('integration panel', () => { 2 | require('./save-node'); 3 | require('./panel-nodes'); 4 | }); 5 | -------------------------------------------------------------------------------- /platform/bind/panel/test/util/api.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const files = require('../../../../bind/panel/util/file-io'); 3 | 4 | module.exports.deleteNodes = function(requester, ids) { 5 | const promises = []; 6 | 7 | for(let k in ids) { 8 | if(ids[k]) { 9 | promises.push(requester.delete(`/panel/delete/${ids[k]}`)); 10 | } 11 | } 12 | 13 | return Promise.all(promises); 14 | } 15 | 16 | module.exports.validateDeleteCalls = function(results, validatePathmap = true) { 17 | for(let k in results) { 18 | results[k].status.should.equal(200); 19 | results[k].should.have.property('body'); 20 | results[k].body.should.equal('deleted'); 21 | } 22 | 23 | if(validatePathmap) { 24 | let pathmapfile = path.join('test-app/panel-generated', 'path-map'); 25 | 26 | return files.json.load(pathmapfile, {}) 27 | .then((pathmap) => { 28 | pathmap.should.eql({}); 29 | 30 | return pathmap; 31 | }); 32 | } 33 | } -------------------------------------------------------------------------------- /platform/bind/panel/test/util/deepClone.js: -------------------------------------------------------------------------------- 1 | module.exports = function(o) { 2 | return JSON.parse(JSON.stringify(o)); 3 | } -------------------------------------------------------------------------------- /platform/bind/panel/util/authorize.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | const config = require('./config'); 4 | 5 | 6 | module.exports = token => new Promise((resolve, reject) => { 7 | if (config.secret) 8 | jwt.verify(token, config.secret, {}, (error, payload) => { 9 | if (error) reject(); 10 | else if (payload.connect_panel_access) resolve(); 11 | else reject(); 12 | }); 13 | else resolve(); 14 | }); 15 | -------------------------------------------------------------------------------- /platform/bind/panel/util/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const platform = require('../../../'); 3 | const config = platform.config.get('panel', {}); 4 | 5 | let directory = null; 6 | let vault = null; 7 | let vaultdir = 'secure/'; 8 | 9 | if (platform.config.has('root')) { 10 | directory = path.join(platform.config.get('root'), config.directory || 'panel-generated/'); 11 | vault = path.join(platform.config.get('root'), vaultdir); 12 | } 13 | 14 | module.exports = { 15 | expose: config.expose || false, 16 | secret: config.secret, 17 | path: config.path || '/panel/', 18 | directory: directory, 19 | files : { 20 | pathmap: 'path-map', 21 | index : 'index.js', 22 | nodedir : 'nodes', 23 | conf : 'config', 24 | platformconf: 'platform-config', 25 | platformprodconf: 'platform-config.prod', 26 | platformconfscript: 'platform-config.script.js', 27 | vaultlisting: 'vault-listing', 28 | }, 29 | vault: vault, 30 | vaultdir: vaultdir, 31 | } 32 | -------------------------------------------------------------------------------- /platform/bind/panel/util/lockFile.js: -------------------------------------------------------------------------------- 1 | const lockFile = require('lockfile'); 2 | 3 | module.exports.lock = function(pathmapfileLock, opt = { retries: 50, stale: 10000, pollPeriod: 50 }) { 4 | return new Promise((resolve, reject) => { 5 | lockFile.lock(pathmapfileLock, opt, function (error) { 6 | if(error) { 7 | reject(error); 8 | return; 9 | } 10 | 11 | resolve(true); 12 | }) 13 | }); 14 | }; 15 | 16 | module.exports.unlock = function(pathmapfileLock) { 17 | return new Promise((resolve, reject) => { 18 | lockFile.unlock(pathmapfileLock, function (error) { 19 | if(error) { 20 | reject(error); 21 | return; 22 | } 23 | 24 | resolve(true); 25 | }) 26 | }); 27 | }; -------------------------------------------------------------------------------- /platform/bind/panel/util/purify-recording.js: -------------------------------------------------------------------------------- 1 | module.exports = recording => { 2 | let cache = []; 3 | return JSON.parse(JSON.stringify(recording, (key, value) => { 4 | if (key == 'subject') return; 5 | if (typeof value === 'object' && value != null) { 6 | if (cache.includes(value)) return "_referenced earlier_"; 7 | else cache.push(value); 8 | } 9 | 10 | return value; 11 | })); 12 | } 13 | -------------------------------------------------------------------------------- /platform/bind/panel/vault/delete.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../../'); 4 | const config = require('../util/config'); 5 | const files = require('../util/file-io'); 6 | 7 | 8 | platform.core.node({ 9 | path : `${config.path}vault/delete/:key`, 10 | public : config.expose, 11 | method : 'DELETE', 12 | interconnectible: false, 13 | inputs : ['key', 'connect_token'], 14 | controlOutputs: [ 'done', 15 | platform.conventions.controls._NotFound, 16 | platform.conventions.controls._Unauthorized ], 17 | }, (inputs, output, control, error) => { 18 | platform.call(`${config.path}vault/list`, 19 | { 20 | connect_token: inputs.connect_token, 21 | }, 22 | result => { 23 | if (result.data) { 24 | let list = result.data; 25 | let listingfile = path.join(config.directory, config.files.vaultlisting); 26 | 27 | if (list.keys.includes(inputs.key)) { 28 | list.keys = list.keys.filter(key => key != inputs.key) 29 | files.json.save(listingfile, list); 30 | 31 | let keyfile = path.join(config.vault, inputs.key); 32 | files.delete(keyfile).catch(() => {}); 33 | 34 | control('done'); 35 | } 36 | else control(platform.conventions.controls._NotFound); 37 | } 38 | else control(platform.conventions.controls._Unauthorized); 39 | } 40 | ); 41 | }); 42 | -------------------------------------------------------------------------------- /platform/bind/panel/vault/get.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../../'); 4 | const config = require('../util/config'); 5 | const files = require('../util/file-io'); 6 | 7 | 8 | platform.core.node({ 9 | path : `${config.path}vault/get`, 10 | public : config.expose, 11 | method : 'GET', 12 | interconnectible: false, 13 | inputs : ['key', 'connect_token'], 14 | outputs: ['content'], 15 | controlOutputs: [ 16 | platform.conventions.controls._NotFound, 17 | platform.conventions.controls._Unauthorized 18 | ], 19 | }, (inputs, output, control, error) => { 20 | platform.call(`${config.path}vault/list`, 21 | { 22 | connect_token: inputs.connect_token, 23 | }, 24 | result => { 25 | if (result.data) { 26 | let list = result.data; 27 | 28 | if (!list.keys.includes(inputs.key)) { 29 | control(platform.conventions._NotFound); 30 | } 31 | else { 32 | let keyfile = path.join(config.vault, inputs.key); 33 | files.load(keyfile).then(content => { 34 | output('content', content); 35 | }) 36 | .catch(err => { 37 | error(err); 38 | }); 39 | } 40 | } 41 | else control(platform.conventions.controls._Unauthorized); 42 | } 43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /platform/bind/panel/vault/list.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../../'); 4 | const config = require('../util/config'); 5 | const files = require('../util/file-io'); 6 | const authorize = require('../util/authorize'); 7 | 8 | 9 | platform.core.node({ 10 | path : `${config.path}vault/list`, 11 | public : config.expose, 12 | method : 'GET', 13 | interconnectible: false, 14 | inputs : ['connect_token'], 15 | outputs : ['list'], 16 | controlOutputs: [ platform.conventions.controls._Unauthorized ], 17 | }, (inputs, output, control) => { 18 | authorize(inputs.connect_token) 19 | .then(() => { 20 | let listingfile = path.join(config.directory, config.files.vaultlisting); 21 | files.json.load(listingfile).then(listing => { 22 | output('list', listing); 23 | }).catch(error => { 24 | output('list', { 25 | keys: [], 26 | directory: config.vaultdir, 27 | }); 28 | }); 29 | }) 30 | .catch(error => { 31 | control(platform.conventions.controls._Unauthorized); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /platform/bind/panel/vault/put.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const platform = require('../../../'); 4 | const config = require('../util/config'); 5 | const files = require('../util/file-io'); 6 | 7 | 8 | platform.core.node({ 9 | path : `${config.path}vault/put`, 10 | public : config.expose, 11 | method : 'PUT', 12 | interconnectible: false, 13 | inputs : ['key', 'content', 'connect_token'], 14 | controlOutputs: [ 'done', platform.conventions.controls._Unauthorized ], 15 | }, (inputs, output, control, error) => { 16 | platform.call(`${config.path}vault/list`, 17 | { 18 | connect_token: inputs.connect_token, 19 | }, 20 | result => { 21 | if (result.data) { 22 | let list = result.data; 23 | let listingfile = path.join(config.directory, config.files.vaultlisting); 24 | 25 | if (!list.keys.includes(inputs.key)) { 26 | list.keys.push(inputs.key); 27 | files.json.save(listingfile, list) 28 | .then(() => {}) 29 | .catch(() => {}); 30 | } 31 | 32 | let keyfile = path.join(config.vault, inputs.key); 33 | files.save(keyfile, inputs.content) 34 | .then(() => { 35 | control('done'); 36 | }) 37 | .catch(err => { 38 | error(err); 39 | }); 40 | } 41 | else control(platform.conventions.controls._Unauthorized); 42 | } 43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /platform/bind/panel/version.js: -------------------------------------------------------------------------------- 1 | const package = require('../../../package.json'); 2 | 3 | const platform = require('../../'); 4 | const config = require('./util/config'); 5 | 6 | 7 | platform.core.node({ 8 | path : `${config.path}version`, 9 | method : 'GET', 10 | public: config.expose, 11 | outputs : ['version'], 12 | interconnectible: false, 13 | }, 14 | (_, output) => { 15 | output('version', package.version); 16 | }); 17 | -------------------------------------------------------------------------------- /platform/bind/shell/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const token = require('./token'); 3 | 4 | const secret = token(); 5 | 6 | const sign = () => new Promise((resolve, reject) => { 7 | jwt.sign({ 8 | connect_shell_access: true 9 | }, 10 | secret, 11 | { 12 | expiresIn: '1h' 13 | }, (err, token) => { 14 | if (err) reject(err); 15 | else resolve(token); 16 | }); 17 | }); 18 | 19 | const verify = token => { 20 | try { 21 | return jwt.verify(token, secret).connect_shell_access; 22 | } 23 | catch(err) { 24 | return false; 25 | } 26 | }; 27 | 28 | module.exports = { 29 | sign: sign, 30 | verify: verify, 31 | } 32 | -------------------------------------------------------------------------------- /platform/bind/shell/config.js: -------------------------------------------------------------------------------- 1 | module.exports = conf => { 2 | const platform = require('../../'); 3 | 4 | let config = conf?conf.get('remote-shell', {}):platform.config.get('remote-shell', {}); 5 | 6 | return { 7 | enabled: config.enabled || false, 8 | path: config.path || '/shell', 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /platform/bind/shell/index.js: -------------------------------------------------------------------------------- 1 | const proxy = require('./proxy'); 2 | const run = require('./run'); 3 | const token = require('./token'); 4 | const config = require('./config'); 5 | 6 | const info = {}; 7 | 8 | module.exports = (app, conf) => { 9 | let _conf = config(conf); 10 | 11 | if (_conf.enabled) { 12 | const credentials = token(); 13 | 14 | run(credentials).on('exit', () => { 15 | info.running = false; 16 | }); 17 | 18 | app.use(proxy(credentials, _conf)); 19 | 20 | info.running = true; 21 | info.config = _conf; 22 | } 23 | else { 24 | info.running = false; 25 | } 26 | } 27 | 28 | module.exports.info = info; 29 | -------------------------------------------------------------------------------- /platform/bind/shell/proxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require('http-proxy-middleware'); 2 | const { URL } = require('url'); 3 | 4 | const auth = require('./auth'); 5 | 6 | 7 | const SHELL_URL = 'http://localhost:7681'; 8 | 9 | module.exports = (credentials, config) => { 10 | let ttypath = config.path; 11 | return proxy([ttypath, '/auth_token.js'], 12 | { 13 | target: SHELL_URL, 14 | ws: true, 15 | changeOrigin: true, 16 | pathRewrite: path => { 17 | if (path.startsWith(ttypath)) return path.substr(ttypath.length); 18 | else return path; 19 | }, 20 | onProxyReq: (proxyReq, req) => { 21 | if (req.originalUrl.startsWith(ttypath)) { 22 | if (req.query.token && auth.verify(req.query.token)) { 23 | proxyReq.setHeader('Authorization', 'Basic ' + new Buffer(credentials).toString('base64')); 24 | } 25 | } 26 | else if(req.originalUrl == '/auth_token.js') { 27 | const referer = new URL(req.get('Referer')); 28 | if (referer.searchParams.has('token') && auth.verify(referer.searchParams.get('token'))) 29 | proxyReq.setHeader('Authorization', 'Basic ' + new Buffer(credentials).toString('base64')); 30 | } 31 | }, 32 | } 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /platform/bind/shell/run.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | module.exports = credentials => { 4 | let proc = exec(`ttyd ` + 5 | `-t cursorStyle="underline" `+ 6 | `-t cursorBlink=true ` + 7 | `-t theme=black ` + 8 | `-c ${credentials} bash`); 9 | 10 | process.on('exit', () => { 11 | proc.kill(); 12 | }); 13 | 14 | return proc; 15 | }; 16 | -------------------------------------------------------------------------------- /platform/bind/shell/token.js: -------------------------------------------------------------------------------- 1 | const block = () => Math.random().toString(36).substring(2); 2 | 3 | module.exports = () => { 4 | return 'u' + block() + ':' + block() + block(); 5 | } 6 | -------------------------------------------------------------------------------- /platform/bind/socket.io/Sockets.js: -------------------------------------------------------------------------------- 1 | const { Subscribable } = require('../../core/base/subscribable'); 2 | 3 | const SocketsEvents = { 4 | removed: 'removed' 5 | } 6 | 7 | 8 | class Sockets extends Subscribable { 9 | constructor() { 10 | super(); 11 | this._list = []; 12 | this._map = {}; 13 | } 14 | 15 | add(socket) { 16 | this._list.push(socket.id); 17 | this._map[socket.id] = { 18 | socket 19 | }; 20 | } 21 | 22 | remove(socket) { 23 | const index = this._list.indexOf(socket.id); 24 | if (index !== -1) this._list.splice(index, 1); 25 | 26 | delete this._map[socket.id]; 27 | 28 | this.publish(SocketsEvents.removed, socket.id); 29 | } 30 | 31 | get(id) { 32 | return this._map[id]; 33 | } 34 | 35 | has(id) { 36 | const index = this._list.indexOf(id); 37 | 38 | return index !== -1; 39 | } 40 | 41 | get map() { 42 | return this._map; 43 | } 44 | 45 | set map(map) { 46 | this._map = map; 47 | } 48 | 49 | get list() { 50 | return this._list; 51 | } 52 | 53 | set list(_list) { 54 | this._list = list; 55 | } 56 | 57 | get events() { 58 | return SocketsEvents; 59 | } 60 | } 61 | 62 | module.exports = { 63 | Sockets, 64 | events: SocketsEvents 65 | } 66 | -------------------------------------------------------------------------------- /platform/bind/socket.io/test/Sockets.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const { Sockets } = require('../Sockets'); 3 | const sinon = require('sinon'); 4 | const expect = require('chai').expect; 5 | 6 | describe('Sockets', () => { 7 | let sockets = null; 8 | const FAKE_SOCKET = { id: 'random', fake: true }; 9 | 10 | beforeEach(() => { 11 | sockets = new Sockets(); 12 | }); 13 | 14 | afterEach(() => { 15 | delete sockets; 16 | }) 17 | 18 | describe('add()', () => { 19 | it('should add socket', () => { 20 | sockets.add(FAKE_SOCKET); 21 | 22 | sockets._list.should.include('random'); 23 | sockets._map.should.have.property('random'); 24 | sockets._map.random.should.eql({ socket: FAKE_SOCKET }); 25 | }); 26 | }); 27 | 28 | describe('has()', () => { 29 | it('should return if socket exists', () => { 30 | sockets.add(FAKE_SOCKET); 31 | 32 | sockets.has(FAKE_SOCKET.id).should.be.true; 33 | }); 34 | }); 35 | 36 | describe('get()', () => { 37 | it('should get socket by id', () => { 38 | sockets.add(FAKE_SOCKET); 39 | 40 | sockets.get(FAKE_SOCKET.id).should.eql({ socket: FAKE_SOCKET }); 41 | }); 42 | }); 43 | 44 | describe('remove()', () => { 45 | it('remove socket', () => { 46 | sockets.add(FAKE_SOCKET); 47 | 48 | sockets.remove(FAKE_SOCKET); 49 | 50 | sockets._list.should.eql([]); 51 | sockets._map.should.not.have.property('random'); 52 | sockets._map.should.eql({ }); 53 | }); 54 | }); 55 | }); -------------------------------------------------------------------------------- /platform/bind/socket.io/test/index.js: -------------------------------------------------------------------------------- 1 | describe('socket.io', () => { 2 | require('./socket-io'); 3 | require('./Sockets'); 4 | }); -------------------------------------------------------------------------------- /platform/bind/utils/index.js: -------------------------------------------------------------------------------- 1 | module.exports.platform = { 2 | config : { 3 | nodes : { 4 | native : ['iterate'] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /platform/builder/builder.js: -------------------------------------------------------------------------------- 1 | const core = require('../core'); 2 | const { Composition } = require('./composition'); 3 | const { Recipe } = require('./recipe'); 4 | const { Composite } = require('./composite'); 5 | 6 | 7 | class Builder { 8 | constructor(config) { 9 | this._config = config || {}; 10 | } 11 | 12 | build(recipe, skipRegistration) { 13 | let factory = () => { 14 | let composition = new Composition(); 15 | recipe.apply(composition); 16 | return new Composite(composition, this._config); 17 | }; 18 | 19 | if (recipe.signature.path && !skipRegistration) { 20 | core.registry.register(recipe.signature, factory); 21 | } 22 | 23 | return factory; 24 | } 25 | } 26 | 27 | module.exports = { 28 | Builder: Builder, 29 | } 30 | -------------------------------------------------------------------------------- /platform/builder/composite.js: -------------------------------------------------------------------------------- 1 | const core = require('../core'); 2 | 3 | const { CompositionEvents } = require('./composition'); 4 | 5 | 6 | class Composite extends core.Node { 7 | constructor(composition, config) { 8 | super(composition.signature); 9 | this._composition = composition; 10 | this._config = config; 11 | } 12 | 13 | get composition() { return this._composition; } 14 | 15 | bind(context) { 16 | this.composition.bind(context); 17 | return super.bind(context);; 18 | } 19 | 20 | run(inputs, output, control) { 21 | for (let [key, pin] of Object.entries(this.composition.outputs)) { 22 | if (pin instanceof core.pins.InputPin) 23 | pin.subscribe(core.events.io.receive, data => { 24 | output(key, data); 25 | }); 26 | else 27 | pin.subscribe(core.events.pin.activate, () => { 28 | control(key); 29 | }); 30 | } 31 | 32 | this.composition.subscribe(CompositionEvents.error, error => this.error(error)); 33 | 34 | this.composition.start(inputs, this._config); 35 | } 36 | 37 | reset() { 38 | this.composition.reset(); 39 | super.reset(); 40 | return this; 41 | } 42 | } 43 | 44 | module.exports = { 45 | Composite: Composite, 46 | } 47 | -------------------------------------------------------------------------------- /platform/builder/errors.js: -------------------------------------------------------------------------------- 1 | class NotFound extends Error { 2 | constructor(item) { 3 | super(`${item} not found.`); 4 | } 5 | } 6 | 7 | module.exports = { 8 | NotFound: NotFound, 9 | } 10 | -------------------------------------------------------------------------------- /platform/builder/index.js: -------------------------------------------------------------------------------- 1 | const { Builder } = require('./builder'); 2 | const fromJSON = require('./from-json'); 3 | 4 | 5 | module.exports = { 6 | Builder: Builder, 7 | fromJSON: fromJSON, 8 | } 9 | -------------------------------------------------------------------------------- /platform/builder/recipe.js: -------------------------------------------------------------------------------- 1 | class Recipe { 2 | constructor(signature) { 3 | this.instructions = []; 4 | this.signature = signature || {}; 5 | } 6 | 7 | add(instruction) { 8 | this.instructions.push(instruction); 9 | return this; 10 | } 11 | 12 | apply(composition) { 13 | composition.meta = this.signature; 14 | for (let instruction of this.instructions) 15 | instruction(composition); 16 | 17 | return this; 18 | } 19 | } 20 | 21 | module.exports = { 22 | Recipe: Recipe, 23 | } 24 | -------------------------------------------------------------------------------- /platform/builder/test/composite.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const core = require('../../core'); 3 | const { Composition } = require('../composition'); 4 | const { Composite } = require('../composite'); 5 | 6 | 7 | describe('Composite', () => { 8 | it('should be a node that runs a composition.', done => { 9 | let comp = new Composition(); 10 | comp.addValue('v', '"Hellow World!"'); 11 | comp.addOutput('x'); 12 | comp.nodes.v.pins.result.connect(comp.outputs.x); 13 | 14 | let c = new Composite(comp); 15 | c.pins.out.x.subscribe(core.events.io.send, x => { 16 | assert.equal(x, 'Hellow World!'); 17 | done(); 18 | }); 19 | 20 | c.checkActivate(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /platform/builder/test/fact-recipe.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": "/test/fact/", 3 | "description": "factoriel function", 4 | "public": false, 5 | "socket": false, 6 | "method": "", 7 | "in": ["n"], 8 | "out": ["f"], 9 | "nodes": [ 10 | {"tag": "ci", "in": ["n"], "expr": "n > 1"}, 11 | {"tag": "cis", "cases": ["true", "false"]}, 12 | {"tag": "iv", "expr": "1"}, 13 | {"tag": "pn", "in": ["n"], "expr": "n - 1"}, 14 | {"tag": "fpn", "path": "/test/fact/"}, 15 | {"tag": "nn", "in": ["n", "fpn"], "expr": "n * fpn"} 16 | ], 17 | "links": [ 18 | [{"in": "n"}, [{"ci": {"in": "n"}}, {"pn": {"in": "n"}}, {"nn": {"in": "n"}}]], 19 | [{"ci": "result"}, {"cis": "target"}], 20 | [{"cis": {"case": "false"}}, "iv"], 21 | [{"cis": {"case": "true"}}, "pn"], 22 | [{"pn": "result"}, {"fpn": {"in": "n"}}], 23 | [{"fpn": {"out": "f"}}, {"nn": {"in": "fpn"}}], 24 | [{"out": "f"}, [{"iv": "result"}, {"nn": "result"}]] 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /platform/builder/test/fact.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const core = require('../../core'); 5 | const { Builder } = require('../builder'); 6 | const fromJSON = require('../from-json'); 7 | const { hashSig } = require('../../util/hash'); 8 | 9 | describe('*** Magnificent Factoriel Composite ***', () => { 10 | it('should calculate 5!', done => { 11 | fs.readFile(path.join(__dirname, 'fact-recipe.json'), 'utf-8', (err, json) => { 12 | new Builder().build(fromJSON(json)); 13 | 14 | let f = core.callable( 15 | () => core.registry.instance( 16 | '/test/fact/', 17 | hashSig(JSON.parse(json)) 18 | ) 19 | ); 20 | f({n : 5}).then(res => { 21 | assert.equal(res.data, 120); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /platform/builder/test/index.js: -------------------------------------------------------------------------------- 1 | describe('builder', ()=> { 2 | require('./composition'); 3 | require('./composite'); 4 | require('./recipe'); 5 | require('./builder'); 6 | require('./from-json'); 7 | 8 | require('./fact'); 9 | }); 10 | -------------------------------------------------------------------------------- /platform/builder/test/recipe.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Composition } = require('../composition'); 3 | const { Recipe } = require('../recipe'); 4 | 5 | 6 | describe('Recipe', () => { 7 | describe('.add()', () => { 8 | it('should add instructions.', () => { 9 | let r = new Recipe(); 10 | r.add(() => {}); 11 | }); 12 | }); 13 | 14 | describe('.apply()', () => { 15 | it('should apply given instructions on given composition.', done => { 16 | let r = new Recipe(); 17 | let c = new Composition(); 18 | 19 | r.add(_c => { 20 | assert.equal(_c, c); 21 | done(); 22 | }); 23 | r.apply(c); 24 | }); 25 | 26 | it('should apply given instructions in given order.', () => { 27 | let r = new Recipe(); 28 | let _ = ""; 29 | 30 | r.add(() => _ += 'a'); 31 | r.add(() => _ += 'b'); 32 | r.add(() => _ += 'c'); 33 | r.apply(new Composition()); 34 | assert.equal(_, 'abc'); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /platform/conventions/controls.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | _WrongInput: 'wrong_input', 3 | _Unauthorized: 'unauthorized', 4 | _Forbidden: 'forbidden', 5 | _NotFound: 'not_found', 6 | _InternalError: 'internal_error', 7 | } 8 | -------------------------------------------------------------------------------- /platform/conventions/index.js: -------------------------------------------------------------------------------- 1 | module.exports.controls = require('./controls'); 2 | -------------------------------------------------------------------------------- /platform/core/base/control.js: -------------------------------------------------------------------------------- 1 | const { Pin, PinEvents } = require('./pin'); 2 | const { IncompatiblePins } = require('./errors'); 3 | 4 | 5 | class ControllerPin extends Pin { 6 | checkConnection(pin, callback) { 7 | if (!(pin instanceof ControlPin)) 8 | throw new IncompatiblePins(this, pin); 9 | 10 | return this; 11 | } 12 | 13 | activate() { 14 | this._activate(); 15 | return this; 16 | } 17 | } 18 | 19 | class ControlPin extends Pin { 20 | constructor(singleActivation) { 21 | super(); 22 | this.__checkActivate = this._checkActivate.bind(this); 23 | 24 | this.subscribe(PinEvents.connect, pin => { 25 | pin.subscribe(PinEvents.activate, this.__checkActivate); 26 | this._checkActivate(); 27 | }); 28 | 29 | this.subscribe(PinEvents.disconnect, pin => { 30 | pin.unsubscribe(PinEvents.activate, this.__checkActivate); 31 | this._checkActivate(); 32 | }); 33 | 34 | this.singleActivation = singleActivation || false; 35 | } 36 | 37 | checkConnection(pin) { 38 | if (!(pin instanceof ControllerPin)) 39 | throw new IncompatiblePins(this, pin); 40 | 41 | return this; 42 | } 43 | 44 | _checkActivate() { 45 | if (this.singleActivation) { 46 | for (let pin of this.connections) 47 | if (pin.activated) { 48 | this._activate(); 49 | return; 50 | } 51 | } 52 | else { 53 | for (let pin of this.connections) { 54 | if (!pin.activated) 55 | return; 56 | } 57 | 58 | this._activate(); 59 | } 60 | } 61 | } 62 | 63 | module.exports = { 64 | ControlPin: ControlPin, 65 | ControllerPin: ControllerPin, 66 | } 67 | -------------------------------------------------------------------------------- /platform/core/base/errors.js: -------------------------------------------------------------------------------- 1 | class PinConnectionError extends Error { 2 | constructor(reason, pin1, pin2) { 3 | super(`cannot connect ${pin1} to ${pin2}: ${reason}`); 4 | 5 | this.reason = reason; 6 | this.pin1 = pin1; 7 | this.pin2 = pin2; 8 | } 9 | } 10 | 11 | class IncompatiblePins extends PinConnectionError { 12 | constructor(pin1, pin2) { 13 | super(`${typeof(pin1)} and ${typeof(pin2)} aren't compatible.`, pin1, pin2); 14 | } 15 | } 16 | 17 | class WrongNodeOutput extends Error { 18 | constructor(node, output) { 19 | super(`${output} is not acceptable for node with signature ${JSON.stringify(node.signature)}.'`); 20 | } 21 | } 22 | 23 | module.exports = { 24 | PinConnectionError: PinConnectionError, 25 | IncompatiblePins: IncompatiblePins, 26 | 27 | WrongNodeOutput: WrongNodeOutput, 28 | } 29 | -------------------------------------------------------------------------------- /platform/core/base/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | errors: require('./errors'), 3 | pin: require('./pin'), 4 | io: require('./io'), 5 | control: require('./control'), 6 | node: require('./node'), 7 | } 8 | -------------------------------------------------------------------------------- /platform/core/base/io.js: -------------------------------------------------------------------------------- 1 | const { Pin } = require('./pin'); 2 | const { IncompatiblePins, PinConnectionError } = require('./errors'); 3 | 4 | 5 | const IOPinEvents = { 6 | receive: 'receive', 7 | send: 'send', 8 | } 9 | 10 | class InputPin extends Pin { 11 | constructor(optional) { 12 | super(); 13 | this._data = undefined; 14 | this._optional = optional || false; 15 | } 16 | 17 | checkConnection(pin) { 18 | if (!(pin instanceof OutputPin)) 19 | throw new IncompatiblePins(this, pin); 20 | 21 | return this; 22 | } 23 | 24 | receive(data) { 25 | this._activate(()=> { 26 | this._data = data; 27 | this.publish(IOPinEvents.receive, data); 28 | }); 29 | 30 | return this; 31 | } 32 | 33 | get data() { return this._data; } 34 | get optional() { return this._optional; } 35 | 36 | reset() { 37 | this._data = undefined; 38 | super.reset(); 39 | return this; 40 | } 41 | } 42 | 43 | class OutputPin extends Pin { 44 | constructor() { 45 | super(); 46 | this._data = undefined; 47 | } 48 | 49 | checkConnection(pin) { 50 | if (!(pin instanceof InputPin)) 51 | throw new IncompatiblePins(this, pin); 52 | 53 | return this; 54 | } 55 | 56 | send(data) { 57 | this._activate(() => { 58 | this._data = data; 59 | this.publish(IOPinEvents.send, data); 60 | for (let pin of this.connections) 61 | pin.receive(data); 62 | }) 63 | 64 | return this; 65 | } 66 | 67 | get data() { return this._data; } 68 | 69 | reset() { 70 | this._data = undefined; 71 | super.reset(); 72 | return this; 73 | } 74 | } 75 | 76 | module.exports = { 77 | InputPin: InputPin, 78 | OutputPin: OutputPin, 79 | IOPinEvents: IOPinEvents, 80 | } 81 | -------------------------------------------------------------------------------- /platform/core/base/pin.js: -------------------------------------------------------------------------------- 1 | const { Subscribable } = require('./subscribable') 2 | 3 | 4 | const PinEvents = { 5 | connect: 'connect', 6 | disconnect: 'disconnect', 7 | activate: 'activate', 8 | reset: 'reset', 9 | } 10 | 11 | class Pin extends Subscribable { 12 | constructor() { 13 | super(); 14 | this._connections = []; 15 | this._activated = false; 16 | } 17 | 18 | connected(pin) { 19 | return this._connections.indexOf(pin) != -1; 20 | } 21 | 22 | checkConnection(pin) { return this; } 23 | 24 | connect(pin) { 25 | if (!this.connected(pin)) { 26 | this.checkConnection(pin); 27 | this._connections.push(pin); 28 | pin.connect(this); 29 | 30 | this.publish(PinEvents.connect, pin); 31 | } 32 | 33 | return this; 34 | } 35 | 36 | disconnect(pin) { 37 | if (this.connected(pin)) { 38 | this._connections = this._connections.filter(p => p != pin); 39 | pin.disconnect(this); 40 | 41 | this.publish(PinEvents.disconnect, pin); 42 | } 43 | 44 | return this; 45 | } 46 | 47 | get connections() { 48 | return this._connections; 49 | } 50 | 51 | get activated() { 52 | return this._activated; 53 | } 54 | 55 | get bound() { 56 | return this._connections.length > 0; 57 | } 58 | 59 | _activate(callback) { 60 | //if (this.activated) 61 | // return; 62 | 63 | this._activated = true; 64 | if (callback) 65 | callback(); 66 | 67 | this.publish(PinEvents.activate); 68 | return this; 69 | } 70 | 71 | reset() { 72 | this._activated = false; 73 | 74 | this.publish(PinEvents.reset); 75 | return this; 76 | } 77 | } 78 | 79 | module.exports = { 80 | Pin: Pin, 81 | PinEvents: PinEvents, 82 | } 83 | -------------------------------------------------------------------------------- /platform/core/base/subscribable.js: -------------------------------------------------------------------------------- 1 | class Subscribable { 2 | constructor() { 3 | this.subscribers = {}; 4 | } 5 | 6 | subscribe(event, subscriber) { 7 | this.subscribers[event] = this.subscribers[event] || []; 8 | 9 | if (this.subscribers[event].indexOf(subscriber) == -1) 10 | this.subscribers[event].push(subscriber); 11 | return this; 12 | } 13 | 14 | unsubscribe(event, subscriber) { 15 | if (event in this.subscribers) 16 | this.subscribers[event] = this.subscribers[event].filter(s => s != subscriber); 17 | 18 | return this; 19 | } 20 | 21 | publish(event, data) { 22 | if (event in this.subscribers) { 23 | for (let subscriber of this.subscribers[event]) 24 | subscriber(data, this); 25 | } 26 | 27 | return this; 28 | } 29 | } 30 | 31 | module.exports = { Subscribable: Subscribable }; 32 | -------------------------------------------------------------------------------- /platform/core/base/test/index.js: -------------------------------------------------------------------------------- 1 | describe('core.base', ()=> { 2 | require('./pin'); 3 | require('./io'); 4 | require('./control'); 5 | require('./node'); 6 | }); 7 | -------------------------------------------------------------------------------- /platform/core/base/test/pin.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Pin, PinEvents } = require('../pin'); 3 | 4 | 5 | describe('Pin', ()=> { 6 | 7 | describe('.connect() and .disconnect()', ()=> 8 | it('should connect and disconnect two pins.', ()=> { 9 | let a = new Pin(); 10 | let b = new Pin(); 11 | 12 | a.connect(b); 13 | 14 | assert(a.connected(b)); 15 | assert(b.connected(a)); 16 | 17 | b.disconnect(a); 18 | 19 | assert(!a.connected(b)); 20 | assert(!b.connected(a)); 21 | })); 22 | 23 | describe('.reset()', ()=> { 24 | it('should reset pin\'s state.', ()=> { 25 | let a = new Pin(); 26 | 27 | a._activate(); 28 | assert(a.activated); 29 | 30 | a.reset(); 31 | assert(!a.activated); 32 | }); 33 | 34 | it('should be subscribable.', done => { 35 | let a = new Pin(); 36 | a.subscribe(PinEvents.reset, ()=>{done()}); 37 | a.reset(); 38 | }); 39 | }); 40 | 41 | describe('.bound', () => { 42 | it('should be true if the pin has any connections.', () => { 43 | let a = new Pin(); 44 | let b = new Pin(); 45 | 46 | a.connect(b); 47 | 48 | assert(a.bound); 49 | }); 50 | 51 | it('should be false if the pin does not have any connections.', () => { 52 | let a = new Pin(); 53 | assert(!a.bound); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /platform/core/call.js: -------------------------------------------------------------------------------- 1 | const base = require('./base'); 2 | const registry = require('./registry'); 3 | const { hashSig } = require('../util/hash'); 4 | 5 | class Call extends base.node.Node { 6 | constructor(path, key) { 7 | if(key === undefined) { 8 | key = hashSig({ path }); 9 | } 10 | 11 | if(! registry.registered(path, key)) key = registry.resolveDefaultKey(path); 12 | 13 | super(registry.signature(path, key)); 14 | this._key = key; 15 | this._path = path; 16 | } 17 | 18 | run(inputs, output, control, error) { 19 | registry.instance(this._path, this._key) 20 | .bind(this.context) 21 | .run(inputs, output, control, error); 22 | } 23 | 24 | get path() { 25 | return this._path; 26 | } 27 | } 28 | 29 | module.exports = { 30 | Call: Call, 31 | } 32 | -------------------------------------------------------------------------------- /platform/core/errors.js: -------------------------------------------------------------------------------- 1 | class InputMissing extends Error { 2 | constructor(missing, given) { 3 | super(`input ${missing} is missing from {${Object.keys(given)}}`); 4 | this.status = 400; 5 | } 6 | } 7 | 8 | class UnregisteredPath extends Error { 9 | constructor(path) { 10 | super(`${path} is not registered with registry.`); 11 | this.status = 404; 12 | } 13 | } 14 | 15 | module.exports = { 16 | InputMissing: InputMissing, 17 | UnregisteredPath: UnregisteredPath, 18 | } 19 | -------------------------------------------------------------------------------- /platform/core/expression.js: -------------------------------------------------------------------------------- 1 | const base = require('./base'); 2 | const script = require('./script'); 3 | 4 | 5 | const _Result = 'result'; 6 | 7 | class Expression extends base.node.Node { 8 | constructor(expression, inputs) { 9 | super({ inputs: inputs || [], outputs: [_Result]}); 10 | 11 | this._expr = expression; 12 | 13 | try { 14 | this._script = script(this._expr); 15 | } catch(error) { 16 | if (error instanceof SyntaxError) 17 | this._compileError = new Error(`

${error.message}

18 |

line ${error.stack.split('\n')[0].split(':')[1]}


${error.stack.split('\n')[1]}`); 19 | else 20 | this._compileError = error; 21 | } 22 | 23 | this.pins.result = this.pins.out[_Result]; 24 | 25 | //this._sync = true; 26 | } 27 | 28 | run(inputs, output, _, error) { 29 | if (this._compileError) { 30 | error(this._compileError); 31 | } 32 | else { 33 | let context = Object.assign({ console, error, require, context: this.context, setTimeout, setInterval }, inputs); 34 | let res = this._script.evaluate(context, inputs); 35 | if (typeof res === 'function') { 36 | if (res.length == 0) output(_Result, res()); 37 | else if (res.length == 1) res(val => output(_Result, val)); 38 | else output(_Result, res); 39 | } 40 | else output(_Result, res); 41 | } 42 | } 43 | } 44 | 45 | module.exports = { 46 | Expression: Expression, 47 | } 48 | -------------------------------------------------------------------------------- /platform/core/index.js: -------------------------------------------------------------------------------- 1 | const { PinEvents } = require('./base/pin'); 2 | const { InputPin, OutputPin, IOPinEvents } = require('./base/io'); 3 | const { ControlPin, ControllerPin } = require('./base/control'); 4 | const { Node, NodeEvents } = require('./base/node'); 5 | const { Expression } = require('./expression'); 6 | const { Switch } = require('./switch'); 7 | const { Call } = require('./call'); 8 | 9 | const node = require('./node'); 10 | const callable = require('./callable'); 11 | const registry = require('./registry'); 12 | 13 | module.exports = { 14 | events: { 15 | pin: PinEvents, 16 | io: IOPinEvents, 17 | node: NodeEvents, 18 | }, 19 | 20 | pins: { 21 | InputPin: InputPin, 22 | OutputPin: OutputPin, 23 | ControlPin: ControlPin, 24 | ControllerPin: ControllerPin, 25 | }, 26 | 27 | Node: Node, 28 | Expression: Expression, 29 | Switch: Switch, 30 | Call: Call, 31 | 32 | node: node, 33 | callable: callable, 34 | registry: registry, 35 | } 36 | -------------------------------------------------------------------------------- /platform/core/node.js: -------------------------------------------------------------------------------- 1 | const base = require('./base'); 2 | const registry = require('./registry'); 3 | const { hashSig } = require('../util/hash'); 4 | 5 | const node = (signature, func) => { 6 | let _class = class extends base.node.Node { 7 | constructor() { 8 | super(signature); 9 | } 10 | 11 | run(inputs, output, control, error) { 12 | func(inputs, output, control, error, this.context); 13 | } 14 | }; 15 | 16 | if( ! ('key' in signature) ) { 17 | signature.key = hashSig(signature); 18 | } 19 | 20 | if (signature.path) 21 | registry.register(signature, _class); 22 | 23 | return _class; 24 | } 25 | 26 | module.exports = node; 27 | -------------------------------------------------------------------------------- /platform/core/script/context.js: -------------------------------------------------------------------------------- 1 | const vm = require('vm'); 2 | const hash = require('object-hash'); 3 | 4 | _contexts = {}; 5 | 6 | const context = (_context, inputs) => { 7 | let _key = hash(inputs, { ignoreUnknown: true }); 8 | let _cont; 9 | 10 | if (_key in _contexts) 11 | _cont = _contexts[_key]; 12 | else { 13 | _cont = {}; 14 | for (let _k of Object.keys(_context)) 15 | _cont[_k] = null; 16 | 17 | vm.createContext(_cont); 18 | _contexts[_key] = _cont; 19 | } 20 | 21 | for (let [_k, _v] of Object.entries(_context)) 22 | _cont[_k] = _v; 23 | 24 | return _cont; 25 | } 26 | 27 | module.exports = context; 28 | -------------------------------------------------------------------------------- /platform/core/script/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./script'); 2 | -------------------------------------------------------------------------------- /platform/core/script/script.js: -------------------------------------------------------------------------------- 1 | const vm = require('vm'); 2 | const context = require('./context'); 3 | 4 | 5 | const _Result_Key = '___R'; 6 | 7 | class Script { 8 | constructor(_script) { 9 | this._script = new vm.Script(_Result_Key + ' = ' + _script); 10 | } 11 | 12 | evaluate(_context, inputs = {}) { 13 | let _cont = context(_context, inputs); 14 | this._script.runInContext(_cont); 15 | return _cont[_Result_Key]; 16 | } 17 | } 18 | 19 | _scripts = {}; 20 | 21 | const script = _script => { 22 | if (_script in _scripts) 23 | return _scripts[_script]; 24 | 25 | let _s = new Script(_script); 26 | _scripts[_script] = _s; 27 | return _s; 28 | } 29 | 30 | module.exports = script; 31 | -------------------------------------------------------------------------------- /platform/core/switch.js: -------------------------------------------------------------------------------- 1 | const base = require('./base'); 2 | const script = require('./script'); 3 | const { InputMissing } = require('./errors'); 4 | 5 | 6 | const _Target = 'target'; 7 | const _Default = '...'; 8 | 9 | class Switch extends base.node.Node { 10 | constructor(cases) { 11 | super({ inputs: [_Target], controlOutputs: cases }); 12 | 13 | this.pins.target = this.pins.in[_Target]; 14 | this.pins.cases = this.pins.controlOut; 15 | this._cases = cases; 16 | this._scripts = {}; 17 | for (let _case of this._cases) { 18 | if (_case != _Default) 19 | this._scripts[_case] = script(_case); 20 | } 21 | 22 | //this._sync = true; 23 | } 24 | 25 | get cases() { return this._cases; } 26 | 27 | run(inputs, _, control) { 28 | if (!(_Target in inputs)) 29 | throw new InputMissing(_Target, inputs); 30 | 31 | for (let _case of this.cases) { 32 | if (_case == _Default) { 33 | control(_case); 34 | return; 35 | } 36 | 37 | if (inputs[_Target] === this._scripts[_case].evaluate({})) { 38 | control(_case); 39 | return; 40 | } 41 | } 42 | } 43 | } 44 | 45 | module.exports = { 46 | Switch: Switch 47 | } 48 | -------------------------------------------------------------------------------- /platform/core/test/call.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const node = require('../node'); 3 | const registry = require('../registry'); 4 | const { Call } = require('../call'); 5 | const { IOPinEvents } = require('../base/io'); 6 | 7 | 8 | describe('Call', ()=> { 9 | it('should call a node registered by a path in registry.', done => { 10 | console.log('node'); 11 | node({ path: 'X' }, () => { done() }); 12 | 13 | console.log('call'); 14 | let c = new Call('X'); 15 | c.checkActivate(); 16 | }); 17 | 18 | it('should have the same signature as the class it is going to call.', () => { 19 | node({ path: 'X', inputs: ['a']}, ()=>{}); 20 | 21 | let c = new Call('X'); 22 | assert(c.pins.in.a); 23 | }); 24 | 25 | it('should return the same value as the class it is going to call.', done => { 26 | node({ path: 'X', inputs: ['a'], outputs: ['b']}, 27 | (inputs, output) => { output('b', inputs.a * 10)}); 28 | 29 | let c = new Call('X'); 30 | c.pins.out.b.subscribe(IOPinEvents.send, data => { 31 | assert.equal(data, 75); 32 | done(); 33 | }); 34 | 35 | c.pins.in.a.receive(7.5); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /platform/core/test/expression.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { Expression } = require('../expression'); 3 | const { IOPinEvents } = require('../base/io'); 4 | 5 | 6 | describe('Expression', ()=>{ 7 | it('should be a node that evaluates a given expression.', done => { 8 | let e = new Expression("[a + 2, a - 2]", ['a']); 9 | e.pins.result.subscribe(IOPinEvents.send, res => { 10 | assert.equal(res[0], 7); 11 | assert.equal(res[1], 3); 12 | done(); 13 | }); 14 | 15 | e.pins.in.a.receive(5); 16 | }); 17 | 18 | it('should naturally work like a raw value when no input is required.', done => { 19 | let e = new Expression("2 * 3"); 20 | e.pins.result.subscribe(IOPinEvents.send, res => { 21 | assert.equal(res, 6); 22 | done(); 23 | }); 24 | 25 | e.checkActivate(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /platform/core/test/index.js: -------------------------------------------------------------------------------- 1 | describe('core', ()=>{ 2 | require('./registry'); 3 | require('./node'); 4 | require('./callable'); 5 | require('./expression'); 6 | require('./switch'); 7 | require('./call'); 8 | 9 | require('./fib'); 10 | }); 11 | -------------------------------------------------------------------------------- /platform/core/test/node.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const base = require('../base'); 3 | const node = require('../node'); 4 | const registry = require('../registry'); 5 | 6 | 7 | describe('node()', ()=> { 8 | it('should generate a node class.', ()=> { 9 | let N = node({}, ()=>{}); 10 | let n = new N(); 11 | assert(n instanceof base.node.Node); 12 | }); 13 | 14 | it('should register the node class if path is given.', ()=> { 15 | node({path:'X'}, ()=>{}); 16 | assert(registry.registered('X')); 17 | }); 18 | 19 | it('should return a node that has the same signature.', ()=> { 20 | let N = node({inputs:['a', 'b'], outputs: ['c']}, ()=>{}); 21 | let n = new N(); 22 | assert(n.pins.in.a); 23 | assert(n.pins.in.b); 24 | assert(n.pins.out.c); 25 | }); 26 | 27 | it('should return a node that runs the given function.', done => { 28 | let N = node({}, ()=>{done();}); 29 | let n = new N(); 30 | n.checkActivate(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /platform/core/test/switch.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const base = require('../base'); 3 | const { Switch } = require('../switch'); 4 | 5 | 6 | describe('Switch', ()=> { 7 | it('should have a controller pin for each given case.', () => { 8 | let s = new Switch(['2 + 3', '1.5 * 2']); 9 | 10 | assert(s.pins.cases['2 + 3'] instanceof base.control.ControllerPin); 11 | assert(s.pins.cases['1.5 * 2'] instanceof base.control.ControllerPin); 12 | }); 13 | 14 | it('should activate proper control pin based on given input', done => { 15 | let s = new Switch(['4.5 * 2', '9 / 2']); 16 | let c1 = new base.control.ControlPin() 17 | .connect(s.pins.cases['4.5 * 2']) 18 | .subscribe(base.pin.PinEvents.activate, () => {done()}); 19 | let c2 = new base.control.ControlPin() 20 | .connect(s.pins.cases['9 / 2']) 21 | .subscribe(base.pin.PinEvents.activate, () => {done('wrong!')}); 22 | 23 | s.pins.target.receive(9); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /platform/loaders/index.js: -------------------------------------------------------------------------------- 1 | module.exports.native = require('./native'); 2 | module.exports.json = require('./json'); 3 | module.exports.module = require('./module'); 4 | module.exports.service = require('./service'); 5 | -------------------------------------------------------------------------------- /platform/loaders/json.js: -------------------------------------------------------------------------------- 1 | const load = require('./load-node'); 2 | const { Builder, fromJSON } = require('../builder'); 3 | 4 | 5 | module.exports = function(path, searchPaths, config) { 6 | let node = load(path, searchPaths); 7 | if (node) 8 | new Builder(config).build(fromJSON(node)); 9 | } 10 | -------------------------------------------------------------------------------- /platform/loaders/load-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const ct = require('../util/color-text'); 3 | 4 | 5 | module.exports = (originalPath, searchPaths, callback) => { 6 | let searchedPaths = []; 7 | 8 | if (searchPaths.indexOf('') == -1) 9 | searchPaths = searchPaths.concat(['']); 10 | 11 | if (searchPaths) { 12 | for (let searchPath of searchPaths) { 13 | let _path = path.join(searchPath, originalPath); 14 | try { 15 | let loaded = require(_path); 16 | if (callback) callback(loaded, _path); 17 | return loaded; 18 | } catch(error) { 19 | if (error.code == 'MODULE_NOT_FOUND' && `${error}`.indexOf(_path) != -1) 20 | searchedPaths.push(_path); 21 | else { 22 | console.log(ct(ct.red + ct.bright, `ERROR @ ${_path}`)); 23 | console.log(error); 24 | return; 25 | } 26 | } 27 | } 28 | } 29 | 30 | console.log(ct(ct.yellow + ct.bright, 'WARNING:: ') + 31 | `couldn't load node ${ct(ct.cyan, originalPath)}, searched in`+ 32 | `${ct(ct.bg.cyan + ct.cyan + ct.bright, '\n\t' + searchedPaths.join('\n\t'))}\n` 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /platform/loaders/module.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const load = require('./load-node'); 3 | const external = require('./load-external'); 4 | const core = require('../core'); 5 | 6 | 7 | const loaders = { 8 | native: require('./native'), 9 | json: require('./json'), 10 | service: require('./service'), 11 | 12 | module: function(path, searchPaths, config) { 13 | const callback = (mod, modPath, callback) => { 14 | if (mod && mod.platform) { 15 | if (mod.platform.config) { 16 | loadNodesFromConf(mod.platform.config, [modPath], config); 17 | 18 | if (mod.platform.config.aliases) { 19 | for (let [alias, path] of Object.entries(mod.platform.config.aliases)) { 20 | core.registry.alias(alias, path); 21 | } 22 | } 23 | } 24 | } 25 | 26 | if (callback) callback(); 27 | }; 28 | 29 | if (typeof path != 'string') 30 | external(path, callback); 31 | else 32 | load(path, searchPaths, callback); 33 | }, 34 | }; 35 | 36 | const loadNodesFromConf = function(config, searchPaths, globalConf) { 37 | if (config.nodes) { 38 | for (let [type, list] of Object.entries(config.nodes)) { 39 | if (type in loaders) { 40 | let loader = loaders[type]; 41 | for (let node of list) 42 | loader(node, searchPaths, globalConf); 43 | } 44 | } 45 | } 46 | } 47 | 48 | module.exports = loaders.module; 49 | module.exports.loadNodesFromConf = loadNodesFromConf; 50 | -------------------------------------------------------------------------------- /platform/loaders/native.js: -------------------------------------------------------------------------------- 1 | const load = require('./load-node'); 2 | 3 | 4 | module.exports = function(path, searchPaths, config) { 5 | let node = load(path, searchPaths); 6 | }; 7 | -------------------------------------------------------------------------------- /platform/recorder/composite-watcher.js: -------------------------------------------------------------------------------- 1 | const { WatcherEvents, Watcher } = require('./watcher'); 2 | 3 | 4 | class CompositeWatcher extends Watcher { 5 | constructor(events) { 6 | super(events); 7 | this._mounted = {}; 8 | } 9 | 10 | mount(tag, watcher) { 11 | this._mounted[tag] = watcher; 12 | watcher.watched(event => { 13 | this.publish(WatcherEvents.watched, { 14 | tag: tag, 15 | cascaded: event, 16 | }); 17 | }); 18 | 19 | return this; 20 | } 21 | } 22 | 23 | module.exports.CompositeWatcher = CompositeWatcher; 24 | -------------------------------------------------------------------------------- /platform/recorder/composition-scenario.js: -------------------------------------------------------------------------------- 1 | const watch = require('./watch'); 2 | const { TimedScenario } = require('./timed-scenario'); 3 | 4 | 5 | class CompositionScenario extends TimedScenario { 6 | constructor(composition, inputs, configs, timelimit) { 7 | super(timelimit); 8 | this.composition = composition; 9 | this.inputs = inputs; 10 | this.configs = configs; 11 | 12 | this.context = undefined; 13 | } 14 | 15 | watcher() { 16 | return this._watcher; 17 | } 18 | 19 | bind(context) { 20 | this.context = context; 21 | return this; 22 | } 23 | 24 | prepare() { 25 | this._watcher = watch(this.composition); 26 | if (this.context) this.composition.bind(this.context); 27 | return super.prepare(); 28 | } 29 | 30 | start() { 31 | this.composition.start(this.inputs, this.configs); 32 | this._watcher.watched(event => { 33 | if (event.tag == 'out') this.stop(); 34 | else if (event.tag == 'node' && event.cascaded.cascaded.event == 'error') this.stop(); 35 | }); 36 | return super.start(); 37 | } 38 | } 39 | 40 | module.exports.CompositionScenario = CompositionScenario; 41 | -------------------------------------------------------------------------------- /platform/recorder/index.js: -------------------------------------------------------------------------------- 1 | const { JSONScenario } = require('./json-scenario'); 2 | const { ScenarioEvents } = require('./scenario'); 3 | const { ConsoleWatcher } = require('./console-watcher'); 4 | const { Recorder } = require('./recorder'); 5 | 6 | module.exports = (json, inputs, configs, timelimit, context) => { 7 | return new Promise((resolve, reject) => { 8 | let cwatcher = new ConsoleWatcher(); 9 | let scenario = new JSONScenario(json, inputs, configs, timelimit) 10 | .subscribe(ScenarioEvents.prepared, () => { 11 | scenario.watcher().mount('console', cwatcher.watch(console)); 12 | }); 13 | 14 | if (context) scenario.bind(context); 15 | 16 | let recorder = new Recorder().finished(() => { 17 | cwatcher.unhook(console); 18 | resolve(recorder.recording); 19 | }).record(scenario); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /platform/recorder/json-scenario.js: -------------------------------------------------------------------------------- 1 | const { fromJSON } = require('../builder'); 2 | const { Composition } = require('../builder/composition'); 3 | 4 | const { CompositionScenario } = require('./composition-scenario'); 5 | 6 | 7 | class JSONScenario extends CompositionScenario { 8 | constructor(json, inputs, configs, timelimit) { 9 | super(undefined, inputs, configs, timelimit); 10 | this.recipe = fromJSON(json); 11 | } 12 | 13 | prepare() { 14 | this.composition = new Composition(); 15 | this.recipe.apply(this.composition); 16 | 17 | return super.prepare(); 18 | } 19 | } 20 | 21 | module.exports.JSONScenario = JSONScenario; 22 | -------------------------------------------------------------------------------- /platform/recorder/recorder.js: -------------------------------------------------------------------------------- 1 | const now = require('../util/now'); 2 | const core = require('../core'); 3 | const { Subscribable } = require('../core/base/subscribable'); 4 | 5 | const watch = require('./watch'); 6 | 7 | 8 | const RecorderEvents = { 9 | started : 'started', 10 | finished : 'finished', 11 | } 12 | 13 | class Recorder extends Subscribable { 14 | constructor() { 15 | super(); 16 | this._recording = []; 17 | } 18 | 19 | get recording() { return this._recording; } 20 | 21 | record(scenario) { 22 | this.publish(RecorderEvents.started, scenario); 23 | 24 | let start = -1; 25 | this._recording = []; 26 | 27 | scenario 28 | .prepare() 29 | .watcher().watched(event => { 30 | if (start == -1) start = now(); 31 | let timestamp = now() - start; 32 | this._recording.push({ 33 | time : timestamp, 34 | event : event, 35 | }); 36 | }); 37 | 38 | scenario 39 | .stopped(() => this.publish(RecorderEvents.finished)) 40 | .start(); 41 | 42 | return this; 43 | } 44 | 45 | finished(callback) { 46 | this.subscribe(RecorderEvents.finished, callback); 47 | return this; 48 | } 49 | 50 | } 51 | 52 | module.exports = { 53 | Recorder : Recorder, 54 | RecorderEvents : RecorderEvents, 55 | } 56 | -------------------------------------------------------------------------------- /platform/recorder/scenario.js: -------------------------------------------------------------------------------- 1 | const { Subscribable } = require('../core/base/subscribable'); 2 | 3 | 4 | const ScenarioEvents = { 5 | prepared : 'prepared', 6 | started : 'started', 7 | stopped : 'stopped', 8 | } 9 | 10 | class Scenario extends Subscribable { 11 | prepare() { 12 | this.publish(ScenarioEvents.prepared); 13 | return this; 14 | } 15 | 16 | start() { 17 | this.publish(ScenarioEvents.started); 18 | return this; 19 | } 20 | 21 | stop() { 22 | this.publish(ScenarioEvents.stopped); 23 | return this; 24 | } 25 | 26 | stopped(callback) { 27 | this.subscribe(ScenarioEvents.stopped, callback); 28 | return this; 29 | } 30 | 31 | watcher() { } 32 | } 33 | 34 | module.exports = { 35 | Scenario : Scenario, 36 | ScenarioEvents : ScenarioEvents, 37 | } 38 | -------------------------------------------------------------------------------- /platform/recorder/test/index.js: -------------------------------------------------------------------------------- 1 | describe('recorder', () => { 2 | require('./watcher'); 3 | require('./composite-watcher'); 4 | require('./watch-pins'); 5 | require('./watch-node'); 6 | require('./watch-composition'); 7 | require('./recorder'); 8 | require('./json-scenario'); 9 | require('./console-watcher'); 10 | }) 11 | -------------------------------------------------------------------------------- /platform/recorder/test/json-scenario.js: -------------------------------------------------------------------------------- 1 | const { JSONScenario } = require('../json-scenario'); 2 | const { Recorder } = require('../recorder'); 3 | 4 | 5 | describe('JSONScenario', () => { 6 | let json = ` 7 | { 8 | "in": [ "name" ], 9 | "out": [ "message" ], 10 | "configs": [ "greet" ], 11 | "control": [], 12 | "nodes": [ 13 | { 14 | "tag": "e0", 15 | "in": [ "name", "greet" ], 16 | "expr": "greet + ' ' + name" 17 | } 18 | ], 19 | "links": [ 20 | [{ "in": "name" }, { "e0": { "in": "name" }}], 21 | [{ "e0": "result" }, { "out": "message" }], 22 | [{ "config": "greet" }, { "e0": { "in": "greet" }}] 23 | ] 24 | } 25 | `; 26 | 27 | it('should represent a scenario for given json composition and parameters.', done => { 28 | let scenario = new JSONScenario(json, { name : 'Joe'}, { greet : 'Hola' }); 29 | 30 | new Recorder() 31 | .finished((_, recorder) => { 32 | recorder.recording.length.should.equal(8); 33 | recorder.recording[0].event.tag.should.equal('in'); 34 | recorder.recording[7].event.tag.should.equal('out'); 35 | done(); 36 | }) 37 | .record(scenario); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /platform/recorder/test/recorder.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | const { Subscribable } = require('../../core/base/subscribable'); 4 | 5 | const { Watcher } = require('../watcher'); 6 | const { Scenario } = require('../scenario'); 7 | const { Recorder } = require('../recorder'); 8 | 9 | 10 | describe('Recorder', () => { 11 | it('should record a given scenario in action', done => { 12 | class S extends Scenario { 13 | constructor() { 14 | super(); 15 | this._subject = new Subscribable(); 16 | this._events = { a : 'a', b : 'b' }; 17 | this._watcher = new Watcher(this._events).watch(this._subject); 18 | } 19 | 20 | watcher() { return this._watcher; } 21 | start() { 22 | this._subject.publish(this._events.a); 23 | this._subject.publish(this._events.b); 24 | this.stop(); 25 | 26 | return super.start(); 27 | } 28 | }; 29 | 30 | let recorder = new Recorder(); 31 | 32 | recorder.finished(() => { 33 | recorder.recording[0].event.event.should.equal('a'); 34 | recorder.recording[1].event.event.should.equal('b'); 35 | done(); 36 | }); 37 | 38 | recorder.record(new S()); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /platform/recorder/test/watch-node.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | const watch = require('../watch'); 4 | const core = require('../../core'); 5 | 6 | 7 | describe('watch() for nodes', () => { 8 | it('should watch nodes correctly', done => { 9 | let Node = core.node({ 10 | inputs: ['a'], 11 | outputs: ['b'], 12 | }, (inputs, output) => { 13 | output('b', 'world'); 14 | }); 15 | 16 | let node = new Node(); 17 | 18 | let inputWatched = false; 19 | let outputWatched = false; 20 | let nodeActivationWatched = false; 21 | 22 | watch(node).watched(event => { 23 | if (event.tag == 'in') { 24 | inputWatched = true; 25 | 26 | event.cascaded.tag.should.equal('a'); 27 | event.cascaded.cascaded.data.should.equal('hellow'); 28 | } 29 | 30 | if (event.tag == 'out') { 31 | outputWatched = true; 32 | 33 | event.cascaded.tag.should.equal('b'); 34 | event.cascaded.cascaded.data.should.equal('world'); 35 | } 36 | 37 | if (event.event == core.events.node.activate) { 38 | nodeActivationWatched = true; 39 | } 40 | 41 | if (inputWatched && outputWatched && nodeActivationWatched) done(); 42 | }); 43 | 44 | core.callable(() => node)({a : 'hellow'}); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /platform/recorder/test/watch-pins.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | const watch = require('../watch'); 4 | const core = require('../../core'); 5 | 6 | 7 | describe('watch() for pins', () => { 8 | it('should watch receive events on input pins', done => { 9 | let pin = new core.pins.InputPin(); 10 | 11 | watch(pin).watched(event => { 12 | if (event.event == core.events.io.receive) { 13 | event.data.should.equal('hellow'); 14 | done(); 15 | } 16 | }); 17 | 18 | pin.receive('hellow'); 19 | }); 20 | 21 | it('should watch send events on output pins', done => { 22 | let pin = new core.pins.OutputPin(); 23 | 24 | watch(pin).watched(event => { 25 | if (event.event == core.events.io.send) { 26 | event.data.should.equal('world'); 27 | done(); 28 | } 29 | }); 30 | 31 | pin.send('world'); 32 | }); 33 | 34 | it('should watch activate event on controller pins', done => { 35 | let pin = new core.pins.ControllerPin(); 36 | 37 | watch(pin).watched(event => { 38 | if (event.event == core.events.pin.activate) 39 | done(); 40 | }); 41 | 42 | pin.activate(); 43 | }); 44 | 45 | it('should watch activation of control pins', done => { 46 | let pin = new core.pins.ControlPin(); 47 | let controller = new core.pins.ControllerPin(); 48 | pin.connect(controller); 49 | 50 | watch(pin).watched(event => { 51 | if (event.event == core.events.pin.activate) 52 | done(); 53 | }); 54 | 55 | controller.activate(); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /platform/recorder/test/watcher.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const { Subscribable } = require('../../core/base/subscribable'); 3 | const { Watcher, WatcherEvents } = require('../watcher'); 4 | 5 | 6 | describe('Watcher', () => { 7 | it('should watch events on a subscribable', done => { 8 | let subject = new Subscribable(); 9 | let events = { event : 'event' }; 10 | 11 | let watcher = new Watcher(events).watch(subject); 12 | 13 | watcher.subscribe(WatcherEvents.watched, event => { 14 | 15 | event.event.should.equal(events.event); 16 | event.subject.should.equal(subject); 17 | event.data.hellow.should.equal('world'); 18 | 19 | done(); 20 | }); 21 | 22 | subject.publish(events.event, { hellow : 'world' }); 23 | }); 24 | 25 | describe('.watched()', () => { 26 | it('should do callbacks on watching (basically easier subscribing)', done => { 27 | let subject = new Subscribable(); 28 | let events = { event : 'event' }; 29 | 30 | new Watcher(events).watch(subject).watched(event => { 31 | done(); 32 | }); 33 | 34 | subject.publish(events.event); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /platform/recorder/timed-scenario.js: -------------------------------------------------------------------------------- 1 | const { Scenario } = require('./scenario'); 2 | 3 | 4 | const TimedScenarioEvents = { 5 | timedout: 'timedout', 6 | } 7 | 8 | class TimedScenario extends Scenario { 9 | constructor(timelimit) { 10 | super(); 11 | this.timelimit = timelimit; 12 | this._handle = undefined; 13 | } 14 | 15 | timeout() { 16 | this.publish(TimedScenarioEvents.timedout); 17 | return this.stop(); 18 | } 19 | 20 | timedout(callback) { 21 | this.subscribe(TimedScenarioEvents.timedout, callback); 22 | return this; 23 | } 24 | 25 | start() { 26 | if (this.timelimit) { 27 | if (this._handle) clearTimeout(this._handle); 28 | this._handle = setTimeout(() => this.timeout(), this.timelimit); 29 | } 30 | 31 | return super.start(); 32 | } 33 | 34 | stop() { 35 | clearTimeout(this._handle); 36 | return super.stop(); 37 | } 38 | } 39 | 40 | module.exports = { 41 | TimedScenario: TimedScenario, 42 | TimedScenarioEvents: TimedScenarioEvents, 43 | } 44 | -------------------------------------------------------------------------------- /platform/recorder/watcher.js: -------------------------------------------------------------------------------- 1 | const { Subscribable } = require('../core/base/subscribable'); 2 | 3 | 4 | const WatcherEvents = { 5 | watched : 'watched', 6 | } 7 | 8 | class Watcher extends Subscribable { 9 | constructor(events) { 10 | super(); 11 | this._events = events; 12 | } 13 | 14 | watch(subject) { 15 | if (this.events) { 16 | Object.values(this.events).forEach(event => { 17 | subject.subscribe(event, data => { 18 | let _data = data; 19 | if (typeof(data) === 'object') 20 | if (Array.isArray(data)) _data = data.slice(); 21 | else if (data instanceof Error || ( 22 | data.constructor.name.endsWith('Error') && 23 | data.name && data.stack 24 | )) 25 | _data = { 26 | message: data.message, 27 | stack: data.stack, 28 | }; 29 | else _data = Object.assign({}, data); 30 | 31 | this.publish(WatcherEvents.watched, { 32 | event: event, 33 | data: _data, 34 | subject: subject, 35 | }); 36 | }); 37 | }); 38 | 39 | this.publish(WatcherEvents.watching, { 40 | subject: subject, 41 | events: this.events, 42 | }); 43 | } 44 | 45 | return this; 46 | } 47 | 48 | watched(callback) { 49 | this.subscribe(WatcherEvents.watched, callback); 50 | return this; 51 | } 52 | 53 | get events() { return this._events; } 54 | } 55 | 56 | module.exports = { 57 | WatcherEvents : WatcherEvents, 58 | Watcher : Watcher, 59 | } 60 | -------------------------------------------------------------------------------- /platform/test/index.js: -------------------------------------------------------------------------------- 1 | describe('platform', ()=> { 2 | require('../core/base/test'); 3 | require('../core/test'); 4 | 5 | require('../builder/test'); 6 | require('../recorder/test'); 7 | 8 | require('../bind/express/test'); 9 | require('../bind/socket.io/test'); 10 | require('../bind/common/test'); 11 | require('../util/test'); 12 | 13 | require('../bind/panel/test/index'); 14 | }); 15 | -------------------------------------------------------------------------------- /platform/tools/native-call.js: -------------------------------------------------------------------------------- 1 | const core = require('../core'); 2 | const base = require('../core/base'); 3 | 4 | 5 | //TODO: also there are some issues with error handling here. 6 | // 7 | const nativeCall = (pathObject, inputs, callback, error, context) => new Promise((resolve, reject) => { 8 | let path = pathObject; 9 | let method = ''; 10 | 11 | if(path instanceof Object) { 12 | path = pathObject.path; 13 | if('method' in pathObject) { 14 | method = pathObject.method; 15 | } 16 | } 17 | 18 | core.callable(() => new core.Call(path, method), context)(inputs).then(result => { 19 | if (callback) try { 20 | callback(result); 21 | } catch(err) { 22 | if (!(err instanceof base.node.Break) && error) 23 | error(err); 24 | } 25 | 26 | resolve(result); 27 | }).catch(err => { 28 | if (!(err instanceof base.node.Break)) { 29 | if (error) error(err); 30 | reject(err); 31 | } 32 | }); 33 | }); 34 | 35 | module.exports = nativeCall; 36 | -------------------------------------------------------------------------------- /platform/util/build-from-factory-or-class.js: -------------------------------------------------------------------------------- 1 | module.exports = factoryOrClass => { 2 | if (factoryOrClass.toString().startsWith('class')) 3 | return new factoryOrClass(); 4 | else 5 | return factoryOrClass(); 6 | } 7 | -------------------------------------------------------------------------------- /platform/util/color-text.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const bg = { 4 | black : "\x1b[40m", 5 | red : "\x1b[41m", 6 | green : "\x1b[42m", 7 | yellow : "\x1b[43m", 8 | blue : "\x1b[44m", 9 | magenta : "\x1b[45m", 10 | cyan : "\x1b[46m", 11 | white : "\x1b[47m", 12 | } 13 | 14 | const reset = "\x1b[0m"; 15 | 16 | module.exports = (color, text) => { 17 | return color + text + reset; 18 | } 19 | 20 | module.exports.reset = reset; 21 | module.exports.bright = "\x1b[1m"; 22 | module.exports.dim = "\x1b[2m"; 23 | module.exports.underscore = "\x1b[4m"; 24 | module.exports.blink = "\x1b[5m"; 25 | module.exports.reverse = "\x1b[7m"; 26 | module.exports.hidden = "\x1b[8m"; 27 | 28 | module.exports.black = "\x1b[30m"; 29 | module.exports.red = "\x1b[31m"; 30 | module.exports.green = "\x1b[32m"; 31 | module.exports.yellow = "\x1b[33m"; 32 | module.exports.blue = "\x1b[34m"; 33 | module.exports.magenta = "\x1b[35m"; 34 | module.exports.cyan = "\x1b[36m"; 35 | module.exports.white = "\x1b[37m"; 36 | 37 | module.exports.bg = bg; 38 | -------------------------------------------------------------------------------- /platform/util/hash.js: -------------------------------------------------------------------------------- 1 | const hash = require('object-hash'); 2 | 3 | const hashSig = function(signature) { 4 | return hash({ 5 | path: signature.path ? signature.path.replace(/\/$/, "") : '', // No trailing slash 6 | public: signature.public || false, 7 | method: (signature.public && signature.method) ? signature.method.toLowerCase() : '', // No method if it's not public 8 | socket: signature.socket || false 9 | }); 10 | } 11 | 12 | module.exports = { 13 | hashSig 14 | }; -------------------------------------------------------------------------------- /platform/util/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | config: require('./config'), 3 | now: require('./now'), 4 | 5 | colorText: require('./color-text'), 6 | buildFromFactoryOrClass: require('./build-from-factory-or-class'), 7 | } 8 | -------------------------------------------------------------------------------- /platform/util/now.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @func now() 4 | * @returns current timestamp in milli-seconds, with nano-second resolution. 5 | * 6 | */ 7 | module.exports = () => { 8 | let hrtime = process.hrtime(); 9 | return hrtime[0] * 1000 + hrtime[1] / 1000000; 10 | } 11 | -------------------------------------------------------------------------------- /platform/util/test/index.js: -------------------------------------------------------------------------------- 1 | describe('util', () => { 2 | require('./config'); 3 | require('./hash'); 4 | }); 5 | -------------------------------------------------------------------------------- /run-test.js: -------------------------------------------------------------------------------- 1 | const nodemon = require('nodemon'); 2 | 3 | nodemon({ script: 'test-app' }); 4 | 5 | nodemon.on('start', () => { 6 | console.log('starting the test platform ...'); 7 | }); 8 | nodemon.on('crash', () => process.exit(0)); 9 | 10 | module.exports = { 11 | nodemon 12 | }; -------------------------------------------------------------------------------- /run.js: -------------------------------------------------------------------------------- 1 | const nodemon = require('nodemon'); 2 | 3 | nodemon({ script: 'app' }); 4 | 5 | nodemon.on('start', () => console.log('starting the platform ...')); 6 | nodemon.on('crash', () => process.exit(0)); 7 | -------------------------------------------------------------------------------- /test-app/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "panel": { 3 | "expose": true 4 | }, 5 | "port": "{{ CONNECT_PORT || 4000 }}", 6 | "nodes": { 7 | "native": [ ], 8 | "module": [ 9 | "../platform/bind/utils", 10 | "../platform/bind/panel", 11 | "panel-generated" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-app/index.js: -------------------------------------------------------------------------------- 1 | const platform = require('../platform'); 2 | 3 | platform 4 | .configure(require('./config')) 5 | .configure({root: __dirname}); 6 | 7 | platform.config.autoparseFromEnvironmentVars(); 8 | 9 | let start = null; 10 | 11 | if(! start) { 12 | start = platform.start() 13 | .then(server => { 14 | console.log(`running on http://${server.address().address}` + 15 | `:${server.address().port}`); 16 | }); 17 | } 18 | 19 | module.exports = platform; 20 | module.exports.start = start; -------------------------------------------------------------------------------- /test-app/panel-generated/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "json": [] 4 | } 5 | } -------------------------------------------------------------------------------- /test-app/panel-generated/index.js: -------------------------------------------------------------------------------- 1 | module.exports.platform = { 2 | config : require('./config.json'), 3 | } 4 | -------------------------------------------------------------------------------- /test-app/panel-generated/path-map.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | require('./platform/test'); 2 | --------------------------------------------------------------------------------