binhend 1.5.16 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/bin/commands/new-service.js +20 -0
  2. package/bin/commands/new-ui.js +21 -0
  3. package/bin/index.js +4 -0
  4. package/bin/templates/component/Service.js +0 -0
  5. package/bin/templates/component/UI.js +8 -0
  6. package/bin/templates/component/ui/Component.js +26 -0
  7. package/bin/templates/component/ui/Service.js +7 -0
  8. package/bin/templates/component/ui/Style.css +3 -0
  9. package/bin/templates/component/ui/UI.js +17 -0
  10. package/bin/templates/frontend/src/favicon.png +0 -0
  11. package/bin/templates/frontend/src/index.html +5 -0
  12. package/bin/templates/frontend/src/style.css +3 -0
  13. package/package.json +3 -3
  14. package/src/api.js +84 -58
  15. package/src/binh.js +20 -60
  16. package/src/configuration.js +29 -99
  17. package/src/csd/controller.js +93 -0
  18. package/src/csd/dao.js +17 -0
  19. package/src/csd/index.js +15 -0
  20. package/src/csd/service.js +58 -0
  21. package/src/middleware/parseBasicAuthToken.js +11 -0
  22. package/src/server.js +4 -50
  23. package/src/utils/bromise.js +20 -0
  24. package/src/utils/httpCodes.js +69 -0
  25. package/src/utils/typeOf.js +78 -0
  26. package/src/{component.build.js → web/component.build.js} +1 -0
  27. package/src/{component.format.js → web/component.format.js} +1 -0
  28. package/src/web/component.method.js +141 -0
  29. package/src/web/index.js +46 -0
  30. package/src/binh.server.app.js +0 -78
  31. package/src/binh.server.config.js +0 -80
  32. package/src/cors.js +0 -17
  33. package/src/enum.httpCodes.js +0 -49
  34. /package/src/{cryptography.js → security.js} +0 -0
  35. /package/src/{binh.web.builder.js → web/binh.web.builder.js} +0 -0
  36. /package/src/{code.js → web/code.js} +0 -0
  37. /package/src/{component.file.js → web/component.file.js} +0 -0
  38. /package/src/{component.js → web/component.js} +0 -0
@@ -0,0 +1,20 @@
1
+
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const cwdJoin = require('../utils/cwdJoin');
5
+
6
+ module.exports = (args) => {
7
+ try {
8
+ var destinationPath = cwdJoin(args[0] || 'frontend'),
9
+ sourcePath = path.join(__dirname, '../templates/frontend');
10
+
11
+ console.log('[BINHEND][CLI] Generate front-end structure:', destinationPath);
12
+ fs.cpSync(sourcePath, destinationPath, {recursive: true});
13
+
14
+ // TODO logic adding excluded directories to .gitignore
15
+ }
16
+ catch (error) {
17
+ console.error('[BINHEND][CLI] Error generating front-end structure!');
18
+ throw error;
19
+ }
20
+ };
@@ -0,0 +1,21 @@
1
+
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const cwdJoin = require('../utils/cwdJoin');
5
+
6
+ module.exports = (args) => {
7
+ try {
8
+ var itemName = args[0] || 'ui-component';
9
+
10
+ if (itemName.endsWith('.js')) {
11
+ // handle copying file
12
+ return;
13
+ }
14
+
15
+ // copy UI sample folder
16
+ }
17
+ catch (error) {
18
+ console.error('[BINHEND][CLI] Error generating front-end structure!');
19
+ throw error;
20
+ }
21
+ };
package/bin/index.js CHANGED
@@ -8,6 +8,10 @@ const commandMap = {
8
8
  start: () => require('./commands/start-server'),
9
9
  build: () => require('./commands/build-project'),
10
10
  test: () => require('./commands/test-project'),
11
+ new: {
12
+ ui: () => require('./commands/new-ui'),
13
+ service: () => require('./commands/new-service'),
14
+ }
11
15
  };
12
16
 
13
17
  var command = commandMap,
File without changes
@@ -0,0 +1,8 @@
1
+
2
+ tag('div');
3
+
4
+ function ui() {
5
+ var content = div({ class: 'new-ui' });
6
+
7
+ return content;
8
+ }
@@ -0,0 +1,26 @@
1
+
2
+ require('./UI');
3
+ require('./Service');
4
+
5
+ css(
6
+ require('./Style.css')
7
+ );
8
+
9
+ function ui() {
10
+ Service(); // initialize service instance (singleton)
11
+
12
+ var component = UI(),
13
+ handleStateChanged = component.action('handleStateChanged'),
14
+ makeRequest = Service.instant('makeRequest');
15
+
16
+ component.listen('stateId', function(data) {
17
+ // ... transform data if need
18
+ handleStateChanged(data);
19
+ })
20
+ .when('request', function(data) {
21
+ // ... transform data if need
22
+ makeRequest(data);
23
+ });
24
+
25
+ return component;
26
+ }
@@ -0,0 +1,7 @@
1
+
2
+ function service({ state, http, sid, define, _this }) {
3
+ this.makeRequest = function(data) {
4
+ // use data to handle logic, call API, or change state
5
+ console.log('Service received data from UI:', data);
6
+ }
7
+ }
@@ -0,0 +1,3 @@
1
+ .new-ui {
2
+ color: red;
3
+ }
@@ -0,0 +1,17 @@
1
+
2
+ tag('div');
3
+
4
+ function ui() {
5
+ var newUI = div({ class: 'new-ui' }),
6
+ requestService = newUI.action('request');
7
+
8
+ newUI.when('handleStateChanged', function(data) {
9
+ // use data to handle logic, e.g. render UI
10
+ console.log('UI received state data:', data);
11
+ })
12
+ .on('click', function(event) {
13
+ requestService(event.currentTarget.innerText);
14
+ });
15
+
16
+ return newUI('New UI');
17
+ }
@@ -4,8 +4,13 @@
4
4
 
5
5
  <head>
6
6
  <title>Binhjs App</title>
7
+
7
8
  <meta charset="utf-8">
8
9
  <meta name="viewport" content="width=device-width, initial-scale=1">
10
+
11
+ <link rel="icon" type="image/x-icon" href="/favicon.png">
12
+ <link href="/style.css" rel="stylesheet">
13
+
9
14
  <script src="https://binhjs.pages.dev/dist/binh.min.js"></script>
10
15
  <script src="/main.js"></script>
11
16
  </head>
@@ -0,0 +1,3 @@
1
+ body {
2
+ margin: 0;
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "binhend",
3
- "version": "1.5.16",
3
+ "version": "2.0.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "author": "Nguyen Duc Binh",
@@ -21,7 +21,7 @@
21
21
  "uglifycss": "^0.0.29"
22
22
  },
23
23
  "engines": {
24
- "node": ">=16.17.1",
25
- "npm": ">=8.15.0"
24
+ "node": ">=20.18.0",
25
+ "npm": ">=10.8.2"
26
26
  }
27
27
  }
package/src/api.js CHANGED
@@ -1,78 +1,83 @@
1
1
  const { Module } = require('module');
2
- const { readdir, statSync } = require('fs');
3
- const { join, parse } = require('path');
2
+ const { readdirSync, statSync } = require('fs');
3
+ const { join, parse, resolve } = require('path');
4
+ const { isUndefined, isFunction } = require('./utils/typeOf');
5
+ const { ServerResponse } = require('http');
4
6
 
5
7
  const express = require('express');
8
+ const { server } = require('./server');
9
+ const parseBasicAuthToken = require('./middleware/parseBasicAuthToken');
6
10
 
7
- function mapAPIs(dirpath, router) {
8
- readdir(dirpath, function(error, item_names) {
9
- if (error) {
10
- console.log('Error scanning items in directory:', dirpath, '. Error:', error);
11
- return;
11
+
12
+ function mapRoutes(router, dirpath) {
13
+ readdirSync(dirpath).forEach(function(direntName) {
14
+ var path = join(dirpath, direntName),
15
+ route = parse(direntName).name;
16
+
17
+ var stat = statSync(path);
18
+ var childRouter;
19
+
20
+ if (stat.isFile()) {
21
+ childRouter = require(path);
22
+ }
23
+ else if (stat.isDirectory()) {
24
+ childRouter = Router().router;
25
+ mapRoutes(childRouter, path);
12
26
  }
13
27
 
14
- item_names.forEach(function(item) {
15
- var path = join(dirpath, item),
16
- route = parse(item).name;
17
-
18
- try {
19
- var stat = statSync(path);
20
- var child_router;
21
-
22
- if (stat.isFile()) {
23
- child_router = require(path);
24
- }
25
- else if (stat.isDirectory()) {
26
- child_router = Router().router;
27
- mapAPIs(path, child_router);
28
- }
29
-
30
- router.use(`/${route}`, child_router);
31
- console.log('[NODE] Mapping Restful APIs from:', path);
32
- }
33
- catch (error) {
34
- console.log('Error check status of file path:', path, '. Error:', error);
35
- }
36
- });
28
+ router.use(`/${route}`, childRouter);
29
+ console.log('[BINHEND] Mapping routes from:', path);
37
30
  });
38
31
  }
39
32
 
40
- function APIs(rootpath) {
41
- if (rootpath == undefined) return;
33
+ function loadRoutes(dirPath) {
34
+ if (isUndefined(dirPath)) {
35
+ console.error(`[BINHEND] Error missing directory path: ${dirPath}.\n`);
36
+ throw new Error('Require directory path to load routes.');
37
+ }
42
38
 
43
39
  const router = express.Router();
44
40
  router.use(express.urlencoded({ extended: false }));
45
41
  router.use(express.json());
46
- router.use(parseAuthToken);
47
-
48
- setTimeout(function() {
49
- mapAPIs(rootpath, router);
50
- });
51
-
52
- return router;
53
- }
42
+ router.use(parseBasicAuthToken);
54
43
 
55
- function parseAuthToken(request, response, next) {
56
- var authString = request.headers['authorization'];
57
- if (typeof authString === 'string' && authString.startsWith('Bearer ')) {
58
- request.token = authString.split('Bearer ')[1];
44
+ try {
45
+ mapRoutes(router, resolve(dirPath));
59
46
  }
60
- next();
47
+ catch (error) {
48
+ console.error(`[BINHEND] Error mapping routes from: ${resolve(dirPath)}.\n` + error);
49
+ throw error;
50
+ }
51
+
52
+ server.use(router);
53
+
54
+ return router;
61
55
  }
62
56
 
63
- function Router(module) {
57
+ function Router(moduleInstance) {
64
58
  var router = express.Router();
65
59
 
66
60
  var output = { router };
67
61
 
68
62
  for (var key in router) {
69
- if (router[key] instanceof Function) {
70
- output[key] = router[key].bind(router);
71
- }
63
+ let method = router[key]; // use 'let' to not lose reference, 'var' will override 'method' reference in this function scope => error on run-time
64
+
65
+ if (!isFunction(method)) continue;
66
+
67
+ output[key] = function() {
68
+ let args = [];
69
+
70
+ Array.from(arguments).forEach((arg) => {
71
+ if (!isFunction(arg)) return args.push(arg);
72
+ args.push(createErrorHandler(router, arg));
73
+ });
74
+
75
+ return method.apply(router, args);
76
+ };
72
77
  }
73
78
 
74
- if (module instanceof Module) {
75
- module.exports = router;
79
+ if (moduleInstance instanceof Module) {
80
+ moduleInstance.exports = router;
76
81
  }
77
82
 
78
83
  output.trycatch = trycatch;
@@ -80,20 +85,40 @@ function Router(module) {
80
85
  return output;
81
86
  }
82
87
 
83
- function trycatch(callback, onerror) {
88
+ function createErrorHandler(router, callback) {
89
+ if (callback.isAppliedTryCatch) return callback;
90
+
84
91
  return async function(request, response, next) {
85
92
  try {
86
- return await callback(request, response, next);
93
+ return await callback.apply(router, arguments);
87
94
  }
88
95
  catch (error) {
89
- if (onerror instanceof Function) return onerror(error);
90
- var message = error instanceof HttpError && error.message || 'Internal Server Error';
91
- response.status(error.httpCode || 500).json({ error: message });
92
- if (onerror === true) console.error('\x1b[31m', error);
96
+ if (arguments.length < 3 || !(response instanceof ServerResponse)) throw error;
97
+ responseErrorByDefault(error, response, next);
93
98
  }
94
99
  }
95
100
  }
96
101
 
102
+ function trycatch(callback) {
103
+ var trycatchCallback = async function(request, response, next) {
104
+ try {
105
+ return await callback(request, response, next);
106
+ }
107
+ catch (error) {
108
+ responseErrorByDefault(error, response, next);
109
+ }
110
+ };
111
+
112
+ trycatchCallback.isAppliedTryCatch = true;
113
+
114
+ return trycatchCallback;
115
+ }
116
+
117
+ function responseErrorByDefault(error, response, next) {
118
+ if (!(error instanceof HttpError)) return next(error);
119
+ response.status(error.httpCode || 500).json({ error: error.message || 'Internal Server Error' });
120
+ }
121
+
97
122
  class HttpError extends Error {
98
123
  constructor(httpCode, message) {
99
124
  super(message);
@@ -103,5 +128,6 @@ class HttpError extends Error {
103
128
  }
104
129
 
105
130
  module.exports = {
106
- APIs, Router, HttpError
131
+ loadRoutes, mapRoutes,
132
+ trycatch, HttpError, Router
107
133
  };
package/src/binh.js CHANGED
@@ -1,63 +1,23 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const Crypto = require('./cryptography');
4
1
 
5
- const { HTTPS } = require('./https');
2
+ const typeOf = require('./utils/typeOf');
3
+ const security = require('./security');
4
+ const { server } = require('./server');
5
+ const { loadRoutes, mapRoutes, trycatch, HttpError, Router } = require('./api');
6
6
  const { ConfigLoader } = require('./configuration');
7
+ const { WebBuilder } = require('./web');
8
+ const { binh } = require('./web/component.method');
9
+ const { HttpCodes } = require('./utils/httpCodes');
10
+ const { HTTPS } = require('./https');
7
11
 
8
- const { ServerApp } = require('./binh.server.app');
9
- const { ServerConfig } = require('./binh.server.config');
10
- const { WebBuilder } = require('./binh.web.builder');
11
-
12
- function Binh() {
13
- var rootpath = require.main.path;
14
-
15
- this.id = function(id) {
16
- binh.id = id == undefined ? Crypto.randomUUID() : JSON.stringify(id);
17
- return this;
18
- };
19
-
20
- this.global = function(name, target) {
21
- global[name] = target;
22
- return this;
23
- };
24
-
25
- this.rootpath = function(path) {
26
- rootpath = typeof path === 'string' ? path : require.main.path;
27
- return this;
28
- };
29
-
30
- this.getRootpath = function() {
31
- return rootpath;
32
- };
33
-
34
- this.remove = function(path, callbackError) {
35
- try {
36
- fs.rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 1000 });
37
- }
38
- catch (error) {
39
- callbackError(error);
40
- }
41
- return this;
42
- };
43
-
44
- function binh(modulePath) {
45
- return modulePath == undefined ? module.exports : require(path.join(rootpath, modulePath));
46
- };
47
-
48
- binh.path = function(anyPath = '') {
49
- return path.join(rootpath, anyPath);
50
- };
51
-
52
- ServerApp(binh, this, ServerConfig(binh, this));
53
- WebBuilder(binh, this);
54
-
55
- global.binh = binh;
56
- global.config = binh.config;
57
- }
58
-
59
-
60
- const { HttpError } = require('./api');
61
- const { HttpCodes } = require('./enum.httpCodes');
62
-
63
- module.exports = { Binh, HTTPS, ConfigLoader, Crypto, HttpError, HttpCodes };
12
+ module.exports = {
13
+ config: {},
14
+ typeOf,
15
+ security,
16
+ server,
17
+ loadRoutes, mapRoutes, trycatch, HttpError, Router,
18
+ ConfigLoader,
19
+ WebBuilder,
20
+ binh,
21
+ HttpCodes,
22
+ HTTPS
23
+ };
@@ -1,131 +1,68 @@
1
- 'use strict';
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { isEmptyArray, isArray, isObject } = require('./utils/typeOf');
2
4
 
3
- var fs = require('fs');
4
5
 
5
- function ConfigLoader(config_object) {
6
- const configs = config_object || {};
6
+ function ConfigLoader(configObject, { module } = {}) {
7
+ const configs = configObject || {};
8
+ const rootPath = module?.path || require.main.path;
7
9
 
8
- var deferred = [];
9
-
10
- this.getConfigs = function() {
10
+ this.getConfigs = () => {
11
11
  return configs;
12
12
  };
13
13
 
14
14
  function getConfigPosition(key) {
15
- return key == undefined ? configs : (configs[key] = configs[key] || {});
16
- };
17
-
18
- this.filter = function(method, input, filters, key) {
19
- if (!(filters instanceof Array) || !filters.length) return {};
20
-
21
- if (method && method.fetch instanceof Function) {
22
- var filtered = filter(method.fetch(input), filters);
23
- return this.object(filtered, key);
24
- }
25
-
26
- if (method && method.is_promise === true) {
27
- var _this = this;
28
-
29
- var promise = new Promise(callback).then(function(loaded_config) {
30
- var filtered = filter(loaded_config, filters);
31
- _this.object(filtered, key);
32
- });
33
-
34
- deferred.push(promise);
35
-
36
- return this;
37
- }
38
-
39
- return filter(input, filters);
15
+ return key == undefined ? configs : configs[key] || (configs[key] = {});
40
16
  };
41
17
 
42
- this.object = function(object, key) {
18
+ this.object = (object, { key, filter: filters } = {}) => {
43
19
  try {
44
20
  var config = getConfigPosition(key);
45
- Object.assign(config, JSON.parse(JSON.stringify({ ...object })));
21
+ var filtered = isArray(filters) ? filter(object, filters) : object;
22
+ Object.assign(config, filtered);
46
23
  }
47
24
  catch(e) {
48
25
  return failed(e, object);
49
26
  }
50
- return this;
51
- };
52
-
53
- this.json = function(filepath, key) {
54
- return this.object(json(filepath), key);
55
- };
56
-
57
- this.cli = function(key) {
58
- return this.object(cli(), key);
59
- };
60
-
61
- this.file = function(path, encoding, key) {
62
- return this.object(file(path, encoding), key);
63
- };
64
-
65
- this.promise = function(callback, key) {
66
- var _this = this;
67
-
68
- var promise = new Promise(callback).then(function(loaded_config) {
69
- _this.object(loaded_config, key);
70
- });
71
-
72
- deferred.push(promise);
73
27
 
74
28
  return this;
75
29
  };
76
30
 
77
- this.done = function(callback) {
78
- var _this = this;
79
-
80
- setTimeout(function() {
81
- Promise.all(deferred).then(function() {
82
- deferred = [];
83
- callback(_this.getConfigs());
84
- });
85
- });
86
-
87
- return this;
31
+ this.cli = (options) => {
32
+ return this.object(cli(), options);
88
33
  };
89
34
 
90
- this.target = function(config_object) {
91
- return new ConfigLoader(config_object);
35
+ this.json = (filepath, options) => {
36
+ var filePath = path.resolve(rootPath, filepath);
37
+ return this.object(json(filePath), options);
92
38
  };
93
39
 
94
- for (var key in this) {
95
- var method = this[key];
96
- if (method instanceof Function) {
97
- this[key] = method.bind(this);
98
- }
99
- }
100
-
101
- this.json.fetch = json;
102
- this.cli.fetch = cli;
103
- this.file.fetch = file;
104
- this.object.fetch = function(object) { return object; };
105
- this.promise.is_promise = true;
40
+ this.file = (filepath, options) => {
41
+ var filePath = path.resolve(rootPath, filepath);
42
+ return this.object(file(filePath, options?.encoding), options);
43
+ };
106
44
  }
107
45
 
108
- function filter(object, filters) {
109
- if (!(object instanceof Object) || !(filters instanceof Array)) return {};
46
+ function filter(object, filterKeys) {
47
+ if (!isObject(object) || isEmptyArray(filterKeys)) return {};
110
48
 
111
- var filtered = {}, length = filters.length;
49
+ var filtered = {};
112
50
 
113
- for (var i = 0; i < length; i++) {
114
- var field = filters[i];
51
+ filterKeys.forEach((field) => {
115
52
  if (object.hasOwnProperty(field)) {
116
53
  filtered[field] = object[field];
117
54
  }
118
- }
55
+ });
119
56
 
120
57
  return filtered;
121
58
  };
122
59
 
123
- function json(filepath) {
60
+ function json(path) {
124
61
  try {
125
- return require.main.require(filepath);
62
+ return require.main.require(path);
126
63
  }
127
64
  catch(e) {
128
- return failed(e, filepath);
65
+ return failed(e, path);
129
66
  }
130
67
  }
131
68
 
@@ -180,17 +117,10 @@ function parseKeyValue(string) {
180
117
  return { key, value, success: true };
181
118
  }
182
119
 
183
- function config(config_object) {
184
- return new ConfigLoader(config_object);
185
- }
186
-
187
120
  function failed(error, source) {
188
121
  console.log(`[BINHEND] Failed loading config from: ${source}`);
189
122
  console.log('[BINHEND] Details:', error);
190
123
  return {};
191
124
  };
192
125
 
193
- module.exports = {
194
- ConfigLoader, config,
195
- json, cli, file
196
- };
126
+ module.exports = { ConfigLoader };