@projectinvicta/nails 2.0.15 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +70 -84
  2. package/bin/test/test_init.sh +33 -0
  3. package/index.ts +11 -0
  4. package/lib/Controller.ts +207 -0
  5. package/lib/Nails.ts +210 -0
  6. package/lib/Router.ts +180 -0
  7. package/lib/{application.js → application.ts} +8 -3
  8. package/lib/config.ts +74 -0
  9. package/package.json +14 -11
  10. package/spec/controller.spec.js +5 -51
  11. package/spec/nails.spec.js +1 -0
  12. package/spec/router.spec.js +5 -5
  13. package/spec/services/integration/config/db.ts +7 -0
  14. package/spec/services/integration/config/service.js +8 -8
  15. package/spec/services/integration/server/controllers/classbased_controller.js +2 -2
  16. package/spec/services/integration/server/controllers/default_json_controller.js +19 -7
  17. package/spec/services/integration/server/controllers/error_controller.js +2 -2
  18. package/spec/services/integration/server/controllers/home_controller.js +6 -28
  19. package/spec/services/integration/server/controllers/json_controller.js +2 -2
  20. package/spec/services/integration/server/controllers/manualrenderasync_controller.js +2 -2
  21. package/spec/services/integration/server/controllers/mjs_controller.mjs +2 -6
  22. package/spec/services/integration/server/controllers/modeltest_controller.js +6 -9
  23. package/spec/services/integration/server/controllers/websocket_controller.js +1 -1
  24. package/spec/services/integration/server/models/dog.js +7 -5
  25. package/spec/services/integration/server/models/owner.js +13 -0
  26. package/spec/services/integration/server.js +3 -4
  27. package/spec/services.integration.spec.js +45 -17
  28. package/templates/default/config/db.ts +13 -0
  29. package/{spec/services/integration_sequelize/config/mimes.js → templates/default/config/mimes.ts} +42 -61
  30. package/templates/default/config/routes.ts +22 -0
  31. package/templates/{config/service.js → default/config/service.ts} +2 -2
  32. package/templates/default/package.json +8 -2
  33. package/templates/default/public/README.xml +68 -85
  34. package/templates/default/server/controllers/home_controller.js +2 -2
  35. package/templates/{server/controllers/home_controller.js → default/server/controllers/users_controller.ts} +29 -34
  36. package/templates/default/server/models/Dog.ts +8 -0
  37. package/templates/default/server/models/User.ts +14 -0
  38. package/templates/default/spec/User.test.js +7 -5
  39. package/templates/default/spec/home_controller.test.js +3 -3
  40. package/index.js +0 -6
  41. package/lib/collection.js +0 -6
  42. package/lib/controller.js +0 -182
  43. package/lib/database_connector.js +0 -12
  44. package/lib/firebase_connector.js +0 -94
  45. package/lib/model_v2.js +0 -24
  46. package/lib/mongoose_connector.js +0 -49
  47. package/lib/mongoose_mem_connector.js +0 -21
  48. package/lib/nails.js +0 -244
  49. package/lib/router.js +0 -202
  50. package/lib/sequelize_connector.js +0 -31
  51. package/lib/server.js +0 -1
  52. package/spec/model_v2.spec.js +0 -75
  53. package/spec/mongodb_connector.util.js +0 -30
  54. package/spec/mongoose_connector.util.js +0 -20
  55. package/spec/sequelize_connector.spec.js +0 -91
  56. package/spec/sequelize_connector.util.js +0 -18
  57. package/spec/services/integration/config/db.js +0 -14
  58. package/spec/services/integration_sequelize/README.md +0 -5
  59. package/spec/services/integration_sequelize/client/css/styles.css +0 -0
  60. package/spec/services/integration_sequelize/client/download.jpg +0 -0
  61. package/spec/services/integration_sequelize/client/favicon.ico +0 -0
  62. package/spec/services/integration_sequelize/client/index.html +0 -9
  63. package/spec/services/integration_sequelize/client/js/client.js +0 -0
  64. package/spec/services/integration_sequelize/client/js/components/app.jsx +0 -15
  65. package/spec/services/integration_sequelize/config/db.js +0 -4
  66. package/spec/services/integration_sequelize/config/routes.js +0 -25
  67. package/spec/services/integration_sequelize/config/service.js +0 -48
  68. package/spec/services/integration_sequelize/config/ssl/certificate.pem +0 -22
  69. package/spec/services/integration_sequelize/config/ssl/csr.csr +0 -17
  70. package/spec/services/integration_sequelize/config/ssl/key.pem +0 -28
  71. package/spec/services/integration_sequelize/config/ssl/private_key.pem +0 -30
  72. package/spec/services/integration_sequelize/config/ssl/public_key.pem +0 -9
  73. package/spec/services/integration_sequelize/package.json +0 -23
  74. package/spec/services/integration_sequelize/server/controllers/default_json_controller.js +0 -21
  75. package/spec/services/integration_sequelize/server/controllers/home_controller.js +0 -39
  76. package/spec/services/integration_sequelize/server/models/dog.js +0 -7
  77. package/spec/services/integration_sequelize/server/models/owner.js +0 -10
  78. package/spec/services/integration_sequelize/server/views/defaultjson/testnojson.ejs +0 -1
  79. package/spec/services/integration_sequelize/server/views/testreact/testreact.ejs +0 -15
  80. package/spec/services/integration_sequelize/server.js +0 -9
  81. package/spec/services.integration_sequelize.spec.js +0 -60
  82. package/templates/bin/promote.sh +0 -20
  83. package/templates/bin/rollout.sh +0 -74
  84. package/templates/bin/server.js +0 -6
  85. package/templates/bin/start.sh +0 -16
  86. package/templates/common/readme_fetcher.js +0 -4
  87. package/templates/config/db.js +0 -19
  88. package/templates/config/mimes.js +0 -59
  89. package/templates/config/routes.js +0 -38
  90. package/templates/config/ssl/certificate.pem +0 -22
  91. package/templates/config/ssl/csr.csr +0 -17
  92. package/templates/config/ssl/key.pem +0 -28
  93. package/templates/config/ssl/private_key.pem +0 -30
  94. package/templates/config/ssl/public_key.pem +0 -9
  95. package/templates/default/config/db.js +0 -19
  96. package/templates/default/config/mimes.js +0 -59
  97. package/templates/default/config/routes.js +0 -38
  98. package/templates/default/config/service.js +0 -45
  99. package/templates/default/server/models/User.js +0 -18
  100. package/templates/package-lock.json +0 -9048
  101. package/templates/package.json +0 -43
  102. package/templates/public/README.xml +0 -332
  103. package/templates/public/css/styles.css +0 -17
  104. package/templates/public/download.jpg +0 -0
  105. package/templates/public/favicon.ico +0 -0
  106. package/templates/public/index.html +0 -9
  107. package/templates/public/js/client.js +0 -1
  108. package/templates/server/models/User.js +0 -18
  109. package/templates/server/views/home/index.ejs +0 -14
  110. package/templates/server/views/partials/javascripts.ejs +0 -1
  111. package/templates/server/views/partials/reactapp.ejs +0 -1
  112. package/templates/server/views/partials/styles.ejs +0 -3
  113. package/templates/spec/User.test.js +0 -20
  114. package/templates/spec/home_controller.test.js +0 -28
  115. package/templates/spec/setupTests.js +0 -0
  116. package/templates/src/AboutPage.jsx +0 -9
  117. package/templates/src/HomePage.jsx +0 -9
  118. package/templates/src/Layout.jsx +0 -78
  119. package/templates/src/ReadmePage.jsx +0 -7
  120. package/templates/src/app.jsx +0 -29
  121. package/templates/src/components/ReadmeLoader.jsx +0 -13
  122. package/templates/src/styles/appstyles.css +0 -3
  123. package/templates/vite.config.ts +0 -42
package/lib/Nails.ts ADDED
@@ -0,0 +1,210 @@
1
+
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import https, {type Server as HttpsServer} from 'node:https';
5
+ import {type Server as HttpServer} from 'node:http';
6
+
7
+ import { type Application } from 'express-ws';
8
+ import { type InitOptions, Model, Sequelize, type ModelAttributes, type ModelStatic } from 'sequelize';
9
+ import expressApp from './application.ts';
10
+ import Router from './Router.ts';
11
+
12
+ import { type Config } from './config.ts';
13
+ import Controller from './Controller.ts';
14
+
15
+ export default class Nails {
16
+ static Controller = Controller;
17
+ private static instance: Nails;
18
+
19
+ static get sequelize() {
20
+ return this.instance.sequelize;
21
+ }
22
+
23
+ static get initialized() {
24
+ return this.instance.initialized;
25
+ }
26
+
27
+ static get config() {
28
+ return this.instance.config;
29
+ }
30
+
31
+ static get application() {
32
+ return this.instance.application;
33
+ }
34
+
35
+ static get httpServer() {
36
+ return this.instance.httpServer;
37
+ }
38
+
39
+ static get httpsServer() {
40
+ return this.instance.httpsServer;
41
+ }
42
+
43
+ // static initModel<T extends Model>(modelClass: ModelStatic<T>, attributes: ModelAttributes, options?: InitOptions) {
44
+ // const actualOptions = {...options, sequelize: this.sequelize}
45
+ // modelClass.init(attributes, actualOptions);
46
+ // }
47
+
48
+ declare ready: Promise<boolean>;
49
+ declare config: Config;
50
+ declare application: Application;
51
+ declare Controller: typeof Controller;
52
+ declare sequelize: Sequelize;
53
+ declare initialized: Promise<void>;
54
+ declare router: Router;
55
+ declare Model: Model;
56
+ declare httpServer: HttpServer;
57
+ declare httpsServer: HttpsServer;
58
+
59
+ private modelFinalizations: (() => Promise<void>)[] = [];
60
+ private modelMigrations: (() => Promise<void>)[] = [];
61
+
62
+ constructor(appConfig: Config) {
63
+ Nails.instance = this;
64
+ this.config = appConfig;
65
+ this.application = expressApp;
66
+ this.Controller = Controller;
67
+ this.sequelize = this.config.db.options
68
+ ? new Sequelize(
69
+ this.config.db.address,
70
+ this.config.db.options)
71
+ : new Sequelize(this.config.db.address);
72
+ this.router = new Router([]);
73
+ this.initialized = this.configure();
74
+ }
75
+
76
+ async configure() {
77
+ expressApp.set('nails_config', { config: this.config });
78
+ expressApp.set('view engine', 'ejs');
79
+ expressApp.set('views', this.config.service.VIEWS_ROOT);
80
+ expressApp.set("public_root", this.config.service.PUBLIC_ROOT);
81
+ console.log("Initializing Router...");
82
+ // this.router = new Router( app_config.routes || [] );
83
+ console.log("Application Router initialized");
84
+ // init models
85
+ try {
86
+ await this.sequelize.authenticate();
87
+ console.log('Connection has been established successfully.');
88
+ } catch (error) {
89
+ console.error('Unable to connect to the database:', error);
90
+ }
91
+ await this.initializeModels();
92
+
93
+ Controller.setRouter(this.router);
94
+ await this.initializeControllers();
95
+ this.router.addRoutes(this.config.routes);
96
+ }
97
+
98
+ async initializeModels() {
99
+ await this.loadModels(this.config.service.MODELS_ROOT);
100
+ await this.sequelize.sync();
101
+ }
102
+
103
+ async startServer() {
104
+ await this.initialized;
105
+ console.log("CONFIGURATION COMPLETE");
106
+ // TODO: Use logging middleware.
107
+ // Use the router middleware.
108
+ expressApp.use(this.router.expressRouter);
109
+ var host = this.config.service.HOST || 'localhost';
110
+ var port = this.config.service.PORT || 3000;
111
+
112
+ let startHttp = 'ENABLE_HTTP' in this.config.service && !!this.config.service.ENABLE_HTTP;
113
+ let startHttps = !!this.config.service.ENABLE_HTTPS;
114
+
115
+ if (!startHttp && !startHttps) {
116
+ console.error("Either ENABLE_HTTPS or ENABLE_HTTP must be set for nails to start");
117
+ }
118
+
119
+ let atLeastOneServerStarted = false;
120
+ return new Promise<void>((resolve, reject) => {
121
+ if (atLeastOneServerStarted) return resolve();
122
+ let serverStartedCallback = () => {
123
+ console.log("Started");
124
+ atLeastOneServerStarted = true;
125
+ resolve();
126
+ }
127
+
128
+ if (startHttp) {
129
+ console.log("starting nails HTTP server. listening to ", host + ':' + port);
130
+ if (this.httpServer) {
131
+ console.warn("Attempted to recreate http server");
132
+ } else {
133
+ this.httpServer = expressApp.listen(port, host, serverStartedCallback);
134
+ console.log("Done starting HTTP server");
135
+ }
136
+ }
137
+ if (startHttps) {
138
+ if (this.httpsServer) {
139
+ console.warn("Attempted to recreate HTTPS server");
140
+ } else {
141
+ console.log(`starting nails HTTPS server. Listening to ${host}:${this.config.service.SSL_PORT}`);
142
+ this.httpsServer = https.createServer({
143
+ key: this.config.service.PRIVATE_KEY,
144
+ cert: this.config.service.CERTIFICATE
145
+ }, expressApp).listen(this.config.service.SSL_PORT, serverStartedCallback);
146
+ }
147
+ }
148
+ // TODO: Set a startup timeout and reject the promise so it does not hang forever;
149
+ });
150
+ }
151
+
152
+ async loadModels(absolutePath: string) {
153
+ if (!fs.existsSync(absolutePath))
154
+ return console.log('Cannot initialize. Path not found.', absolutePath);
155
+ if (!fs.statSync(absolutePath).isFile()) {
156
+ const directory_contents = fs.readdirSync(absolutePath);
157
+ for (const rel_path of directory_contents) {
158
+ await this.loadModels(path.join(absolutePath, rel_path));
159
+ };
160
+ return;
161
+ }
162
+ console.log('attempting to import Model:', absolutePath);
163
+ // We just need to import each model once so the generateSuperclass
164
+ // method is called at least once for each model.
165
+ const modelModule = await import(absolutePath);
166
+ // TODO: make sure the name isn't constantly "model"
167
+ const modelClass = modelModule.default;
168
+ if (!(modelClass.prototype instanceof Model)) {
169
+ const errorMessage = `Default export is not a sequelize Model: ${absolutePath}`;
170
+ console.error(errorMessage);
171
+ throw errorMessage;
172
+ }
173
+ const schema = modelModule.schema;
174
+ const options = modelModule.options;
175
+ const defer = modelModule.defer as (() => Promise<void>) | undefined;
176
+ this.modelFinalizations.push(modelModule.finalize as () => Promise<void>);
177
+ this.modelMigrations.push(modelModule.migrate as () => Promise<void>);
178
+
179
+ modelClass.init(schema, { sequelize: this.sequelize, ...options });
180
+ if (modelClass && modelClass.name) {
181
+ console.log('imported model:', modelClass.name);
182
+ if (defer) {
183
+ await defer();
184
+ }
185
+ await modelClass.sync();
186
+ }
187
+ else console.warn("No model found at:", absolutePath);
188
+ }
189
+
190
+ private async initializeControllers(directory?: string) {
191
+ if (!directory) directory = this.config.service.CONTROLLERS_ROOT;
192
+ console.log('attempting to import:', directory);
193
+ if (!fs.existsSync(directory))
194
+ return console.error('Cannot initialize. Path not found.', directory);
195
+ if (fs.statSync(directory).isFile()) {
196
+ let subclass = (await import(directory)).default;
197
+ if (!(Controller.isPrototypeOf(subclass)))
198
+ return console.error(`Non-Controller export "${subclass.name}: ${typeof subclass}" found: ${directory}`);
199
+ const controller = new subclass();
200
+ controller._registerControllerRoutes();
201
+ return controller;
202
+ }
203
+ for (const rel_path of fs.readdirSync(directory)) {
204
+ await this.initializeControllers(path.join(directory, rel_path));
205
+ }
206
+ }
207
+
208
+ }
209
+
210
+ // const sequelize = new Sequelize();
package/lib/Router.ts ADDED
@@ -0,0 +1,180 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import {type WebSocket} from "ws";
3
+ import express, {type NextFunction, type Request, type Response} from 'express';
4
+ import expressWs, {type Router as WsRouter, type Application as WsApplication, type WebsocketRequestHandler} from 'express-ws';
5
+ import app, {expressRouter, ExpressRouter, expressStatic} from './application.ts';
6
+ import { type RouteDefinition, type RouteOptions } from './config.ts';
7
+
8
+ interface ExpressWsType {
9
+ app: any;
10
+ getWss: () => any;
11
+ applyTo: (router: any) => void;
12
+ }
13
+
14
+ class NailsRouter extends EventEmitter {
15
+ private static _webSocketsEnabled: boolean = true;
16
+ private static _expressWs: ExpressWsType;
17
+
18
+ static get webSocketsEnabled(): boolean {
19
+ return this._webSocketsEnabled;
20
+ }
21
+
22
+ static set webSocketsEnabled(enabled: boolean) {
23
+ this._webSocketsEnabled = enabled;
24
+ }
25
+
26
+ static set expressWs(ews: ExpressWsType) {
27
+ this._expressWs = ews;
28
+ }
29
+
30
+ static get expressWs(): ExpressWsType {
31
+ return this._expressWs;
32
+ }
33
+
34
+ private application: WsApplication = app; // TODO: Type this more specifically if application.js is also converted to TS
35
+ public expressRouter: WsRouter = expressRouter; // express-ws adds a ws method
36
+ public routes: any[]; // TODO: Type this more specifically
37
+
38
+ constructor(routes: RouteDefinition[]) { // TODO: Type routes more specifically
39
+ super();
40
+ this.addRoutes(routes);
41
+ }
42
+
43
+ addRoutes(routes: RouteDefinition[]): void { // TODO: Type routes more specifically
44
+ this.routes = routes;
45
+ routes.forEach(this.addRoute.bind(this));
46
+ }
47
+
48
+ addRoute(route: RouteDefinition): void { // TODO: Type route more specifically
49
+ const method = route[0].toLowerCase();
50
+ const path_matcher = route[1];
51
+ const consequences = route[2];
52
+
53
+ console.log("setting route for", method, path_matcher, consequences);
54
+
55
+ if (method.match(/ws/i)) {
56
+ this.expressRouter.ws(
57
+ path_matcher,
58
+ this.get_websocket_handler(consequences)
59
+ );
60
+ } else if (consequences && consequences.public) {
61
+ this.expressRouter.use(
62
+ path_matcher,
63
+ expressStatic(this.application.get('public_root'))
64
+ );
65
+ } else {
66
+ this.expressRouter[method](
67
+ path_matcher,
68
+ this.get_express_route_handler(consequences)
69
+ );
70
+ }
71
+ }
72
+
73
+ routeWs(ws: WebSocket, request: Request): void { // TODO: Define a proper WebSocket type
74
+ console.log(
75
+ '\nSERVER_LOG:: WebSocket Router received request for: ',
76
+ request.method,
77
+ request.url,
78
+ '\n'
79
+ );
80
+
81
+ const params: any = {...request.params, ...request.query};
82
+
83
+ const controller: string = params._controller;
84
+ const action: string = params._action;
85
+ console.log('the action is:', controller, action, params);
86
+ console.log('the params are:', params);
87
+ console.log('ws router emitting:', 'dispatchTo:' + controller);
88
+ this.emit('dispatchTo:' + controller, action, params, request, null, ws);
89
+
90
+ if (!(request as any).handled_by_controller) {
91
+ params.error = { message: 'controller ' + controller + ' does not exist' };
92
+ console.log('closing websocket');
93
+ return ws.close(1003, "Action Not available: #" + action);
94
+ }
95
+ }
96
+
97
+ route(request: Request, response: Response, next: NextFunction): void {
98
+ console.log(
99
+ '\nSERVER_LOG:: Router recieved request for: ',
100
+ request.method,
101
+ request.url,
102
+ '\n'
103
+ );
104
+
105
+ const params: any = { ...request.params, ...request.query};
106
+
107
+ const controller: string = params._controller;
108
+ const action: string = params._action;
109
+ console.log('the action is:', controller, action, params);
110
+ console.log('the params are:', params);
111
+
112
+ console.log('router emitting:', 'dispatchTo:' + controller);
113
+ this.emit('dispatchTo:' + controller, {action, params, request, response, next});
114
+
115
+ params.error = { message: 'controller ' + controller + ' does not exist' };
116
+ if (!(request as any).handled_by_controller) {
117
+ this.emit('dispatchTo:application', {action: 404, params, request, response});
118
+ }
119
+ }
120
+
121
+ get_websocket_handler(routeOptions: RouteOptions | undefined): WebsocketRequestHandler {
122
+ const route_options = routeOptions || {};
123
+ return (ws: WebSocket, request: express.Request) => { // TODO: Define a proper WebSocket type
124
+ console.log("handling a websocket request");
125
+ let controller = route_options.controller;
126
+ let action = route_options.action;
127
+
128
+ for (let i = 0; route_options[i]; i++) {
129
+ if (route_options[i] === 'action') {
130
+ action = action || (request.params as any)[i];
131
+ } else if (route_options[i] === 'controller') {
132
+ controller = controller || (request.params as any)[i];
133
+ } else {
134
+ (request.params as any)[route_options[i]] = (request.params as any)[i];
135
+ }
136
+ }
137
+ (request.params as any)._action = action || (request.params as any).action || "index";
138
+ (request.params as any)._controller =
139
+ controller || (request.params as any).controller || "application";
140
+
141
+ this.routeWs(ws, request);
142
+ };
143
+ }
144
+
145
+ get_express_route_handler(route_options: RouteOptions | undefined) {
146
+ route_options = route_options || {};
147
+ return async (request: Request, response: Response, next: NextFunction) => {
148
+ let controller: string | undefined = route_options['controller'];
149
+ let action = route_options['action'];
150
+ let json: boolean = !!route_options['json'];
151
+ let path: string | undefined = route_options['path'];
152
+ let async: boolean | undefined = route_options['async'];
153
+ let autorender: boolean | undefined = route_options['disable_autorender'];
154
+
155
+ for (let i = 0; route_options[i]; i++) {
156
+ if (route_options[i] === 'action') {
157
+ action = action || (request.params as any)[i];
158
+ } else if (route_options[i] === 'controller') {
159
+ controller = controller || (request.params as any)[i];
160
+ } else if (route_options[i] === 'json') {
161
+ json = json || (request.params as any)[i];
162
+ } else {
163
+ (request.params as any)[route_options[i]] = (request.params as any)[i];
164
+ }
165
+ }
166
+
167
+ (request.params as any)._action = action || (request.params as any).action || "index";
168
+ (request.params as any)._controller =
169
+ controller || (request.params as any).controller || "application";
170
+ (request.params as any)._json = json || !!(request.params as any).json;
171
+ (request.params as any)._async = async == null || async == undefined ?
172
+ !!this.application.get('nails_config').config.ASYNC : async;
173
+ (request.params as any)._autorender = autorender ? !!autorender : true;
174
+
175
+ this.route(request, response, next);
176
+ };
177
+ }
178
+ }
179
+
180
+ export default NailsRouter;
@@ -9,7 +9,7 @@ import cookieParser from 'cookie-parser';
9
9
  * Singleton express application.
10
10
  */
11
11
  // var express = require('express');
12
- var app = express();
12
+ const {app, applyTo} = expressWs(express());
13
13
  app.use(cookieParser());
14
14
  // TODO: this has to be done before routes in order to work. Can consider allowing the config to turn this off
15
15
  // expressWs(app);
@@ -18,7 +18,12 @@ app.use(urlencoded({ limit: '2mb', extended: false }));
18
18
  // Parse application/json
19
19
  app.use(json({limit: '2mb'}));
20
20
 
21
- app.Router = express.Router;
22
- app.static = express.static;
21
+ export const ExpressRouter = express.Router;
22
+ export const expressRouter = express.Router();
23
+ applyTo(expressRouter);
24
+ export const expressStatic = express.static;
25
+
26
+ // export ExpressRouter;
27
+ // export expressStatic;
23
28
 
24
29
  export default app;
package/lib/config.ts ADDED
@@ -0,0 +1,74 @@
1
+ import {type Options } from "sequelize";
2
+ import { type NextFunction, type Request, type Response } from "express";
3
+
4
+ export interface ControllerDoRouteParams {
5
+ action: string,
6
+ params: Record<string, any>,
7
+ request: Request,
8
+ response: Response,
9
+ ws?: WebSocket,
10
+ next: NextFunction,
11
+ }
12
+
13
+ interface ActionParams<T> {
14
+ params: T;
15
+ request: Request;
16
+ response: Response;
17
+ }
18
+
19
+ export interface RouteOptions {
20
+ controller?: string;
21
+ action?: string | (<T>(actionParams: ActionParams<T>) => any);
22
+ public?: boolean;
23
+ json?: boolean;
24
+ path?: string;
25
+ async?: boolean;
26
+ disable_autorender?: boolean;
27
+ [key: number]: string; // For dynamic route parameters
28
+ }
29
+
30
+ type RouteMatcher = string;
31
+
32
+ type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'ws';
33
+
34
+ export type RouteDefinition = [HttpMethod, RouteMatcher, RouteOptions?];
35
+
36
+ export interface RoutesConfig extends Array<RouteDefinition> {}
37
+
38
+ interface MimesConfig {
39
+ [key: string]: any;
40
+ }
41
+
42
+ export interface DbConfig {
43
+ address: string;
44
+ username?: string;
45
+ password?: string;
46
+ options?: Options;
47
+ }
48
+
49
+ interface AppConfig {
50
+ APP_ROOT: string;
51
+ PUBLIC_ROOT: string;
52
+ CONTROLLERS_ROOT: string;
53
+ VIEWS_ROOT: string;
54
+ MODELS_ROOT: string;
55
+ SERVER_ROOT: string;
56
+ ENABLE_HTTP: boolean;
57
+ HOST: string;
58
+ PORT: number;
59
+ ENABLE_HTTPS: boolean;
60
+ SSL_PORT: number;
61
+ PRIVATE_KEY: Buffer;
62
+ CERTIFICATE: Buffer;
63
+ // Optional properties from the original config, if they were uncommented
64
+ // VIEW_ENGINE?: (path: string, options: object, callback: (err: any, html?: string) => void) => void;
65
+ // VIEW_ENGINE_EXT?: string;
66
+ // IP?: string;
67
+ }
68
+
69
+ export interface Config {
70
+ service: AppConfig;
71
+ routes: RoutesConfig;
72
+ mimes: MimesConfig;
73
+ db: DbConfig;
74
+ }
package/package.json CHANGED
@@ -1,20 +1,24 @@
1
1
  {
2
2
  "name": "@projectinvicta/nails",
3
- "version": "2.0.15",
3
+ "version": "3.0.0",
4
4
  "description": "A node.js webserver scaffold",
5
5
  "type": "module",
6
- "main": "index.js",
6
+ "main": "index.ts",
7
+ "engines": {
8
+ "node": ">=24.0.0"
9
+ },
7
10
  "bin": {
8
- "nails-boilerplate": "bin/lib/nails.js"
11
+ "nails": "bin/lib/nails.js"
9
12
  },
10
13
  "scripts": {
11
14
  "copyreadme": "node bin/convertreadmetohtml.js",
12
15
  "test": "mocha --exit spec",
13
- "debug": "mocha --exit debug spec"
16
+ "debug": "mocha --exit debug spec",
17
+ "test_init": "bash bin/test/test_init.sh"
14
18
  },
15
19
  "repository": {
16
20
  "type": "git",
17
- "url": "git+ssh://git@github.com/stantonwjones/nails-boilerplate.git"
21
+ "url": "git+ssh://git@github.com/projectinvicta/nails.git"
18
22
  },
19
23
  "keywords": [
20
24
  "nails",
@@ -25,8 +29,6 @@
25
29
  "rails"
26
30
  ],
27
31
  "dependencies": {
28
- "@babel/core": "*",
29
- "@babel/preset-react": "*",
30
32
  "body-parser": "^2.2.0",
31
33
  "cookie-parser": "^1.4.7",
32
34
  "ejs": "*",
@@ -34,17 +36,18 @@
34
36
  "express-ws": "*",
35
37
  "fs-extra": "^11.3.2",
36
38
  "mime": "*",
37
- "mongodb": "^4.17.2",
38
- "mongodb-memory-server": "^8.16.0",
39
- "mongoose": "^6.13.0",
40
39
  "react": "*",
41
40
  "react-dom": "^16.13.1",
42
41
  "sequelize": "^6.37.7",
43
42
  "showdown": "^2.1.0",
44
- "sqlite3": "^5.1.7",
43
+ "sqlite3": "^5.0.2",
45
44
  "ws": "*"
46
45
  },
47
46
  "devDependencies": {
47
+ "@types/cookie-parser": "*",
48
+ "@types/express": "*",
49
+ "@types/express-ws": "*",
50
+ "@types/node": "*",
48
51
  "chai": "*",
49
52
  "chai-http": "*",
50
53
  "mocha": "^10.2.0",
@@ -1,10 +1,10 @@
1
- import Controller from '../lib/controller.js';
1
+ import Controller from '../lib/Controller.ts';
2
2
  import assert from 'assert';
3
3
  import sinon from 'sinon';
4
4
  import {EventEmitter} from 'node:events';
5
5
 
6
- function TestController() {
7
- this.testAction = function () {};
6
+ class TestController extends Controller {
7
+ testAction() {};
8
8
  };
9
9
 
10
10
  class TestEs6Controller extends Controller {
@@ -34,59 +34,13 @@ describe('Controller', function() {
34
34
  sinon.spy(testController, "testEs6Action");
35
35
 
36
36
  mockRouter.emit(
37
- 'dispatchTo:testes6', 'testEs6Action', mockParams, mockRequest, mockResponse);
37
+ 'dispatchTo:testes6',
38
+ {action: 'testEs6Action', params: mockParams, request: mockRequest, response: mockResponse});
38
39
 
39
40
  assert(testController.testEs6Action.calledWith(
40
41
  mockParams, mockRequest, mockResponse));
41
42
  });
42
43
  });
43
- describe('Controller.extend', function() {
44
- it('should error if an anonymous function is passed', function() {
45
- try {
46
- let testController = Controller.extend(function(){});
47
- assert(false); // Fail if we get here.
48
- } catch(e) {
49
- }
50
- });
51
-
52
- it('should set the appropriate listener on the router',
53
- function() {
54
- let mockRouter = new EventEmitter();
55
- let mockParams = {_controller: "test"};
56
- let mockRequest = {};
57
- let mockResponse = {headersSent: true};
58
- Controller.setRouter(mockRouter);
59
- let testController = Controller.extend(TestController);
60
- sinon.spy(testController, "testAction");
61
-
62
- mockRouter.emit(
63
- 'dispatchTo:test', 'testAction', mockParams, mockRequest, mockResponse);
64
-
65
- assert(testController.testAction.calledWith(
66
- mockParams, mockRequest, mockResponse));
67
- });
68
- it('should set itself as the prototype of the passed constructor method',
69
- function() {
70
- let mockRouter = new EventEmitter();
71
- Controller.setRouter(mockRouter);
72
- let testController = Controller.extend(TestController);
73
- assert(testController instanceof Controller);
74
- }
75
- );
76
- it('should return an initialized instance of the constructor', function() {
77
- let mockRouter = new EventEmitter();
78
- Controller.setRouter(mockRouter);
79
- let testController = Controller.extend(TestController);
80
- assert(testController instanceof TestController);
81
- });
82
- it('should raise an error if no router has been set', function() {
83
- try {
84
- let testController = Controller.extend(TestController);
85
- assert(false); // Fail if we get here.
86
- } catch (expectedError) {
87
- }
88
- });
89
- });
90
44
  describe('#_do', function() {
91
45
  it('should call the function on the controller matching the given action');
92
46
  it('should call 404 if the action does not exist on the controller instance');
@@ -0,0 +1 @@
1
+ // TODO test all initialization here
@@ -1,7 +1,7 @@
1
1
  // var express_app = require('../lib/application.js');
2
- import Router from '../lib/router.js';
2
+ import Router from '../lib/Router.ts';
3
3
  import assert from 'assert';
4
- import express_app from '../lib/application.js';
4
+ import express_app from '../lib/application.ts';
5
5
 
6
6
  express_app.set("public_root", import.meta.dirname + "/services/integration/public");
7
7
  var testRoutes = [
@@ -28,7 +28,7 @@ describe('Router', function() {
28
28
  var testFunctionCalled;
29
29
  router.on('dispatchTo:application', test);
30
30
 
31
- function test(action, params, request, response) {
31
+ function test({action, params, request, response}) {
32
32
  testFunctionCalled = true;
33
33
  assert.equal(action, expectedAction);
34
34
  assert.equal(typeof(params), 'object');
@@ -56,7 +56,7 @@ describe('Router', function() {
56
56
  var testFunctionCalled;
57
57
  router.on('dispatchTo:test', test);
58
58
 
59
- function test(action, params, request, response) {
59
+ function test({action, params, request, response}) {
60
60
  testFunctionCalled = true;
61
61
  assert.equal(action, expectedAction);
62
62
  assert.equal(params._controller, mockParams._controller);
@@ -84,7 +84,7 @@ describe('Router', function() {
84
84
  var testFunctionCalled;
85
85
  router.on('dispatchTo:test', test);
86
86
 
87
- function test(action, params, request, response) {
87
+ function test({action, params, request, response}) {
88
88
  testFunctionCalled = true;
89
89
  assert.equal(action, expectedAction);
90
90
  assert.equal(params._controller, mockQuery._controller);
@@ -0,0 +1,7 @@
1
+ import {type DbConfig} from '../../../../index.ts';
2
+
3
+ const config: DbConfig = {
4
+ address: 'sqlite::memory:',
5
+ };
6
+
7
+ export default config;