@awesomeness-js/server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.editorconfig +4 -0
  2. package/.gitattributes +2 -0
  3. package/.jshintrc +3 -0
  4. package/.vscode/settings.json +17 -0
  5. package/CLA.md +9 -0
  6. package/CONTRIBUTING.md +18 -0
  7. package/LICENSE +13 -0
  8. package/NOTICE +15 -0
  9. package/README.md +15 -0
  10. package/SECURITY.md +7 -0
  11. package/config.js +29 -0
  12. package/eslint.config.js +101 -0
  13. package/example-.awesomeness/applicationMap.js +4 -0
  14. package/example-.awesomeness/beforeRouteMiddleware.js +15 -0
  15. package/example-.awesomeness/checkSession.js +15 -0
  16. package/example-.awesomeness/config.js +40 -0
  17. package/example-.awesomeness/hostMap.js +44 -0
  18. package/example-.awesomeness/initDB.js +65 -0
  19. package/example-.awesomeness/setupDB/applications.js +47 -0
  20. package/example-.awesomeness/setupDB/users.js +65 -0
  21. package/example-.awesomeness/setupDB/websites.js +49 -0
  22. package/example-.awesomeness/specialRoutes.js +7 -0
  23. package/example-.awesomeness/wsHandler.js +13 -0
  24. package/index.js +22 -0
  25. package/package.json +34 -0
  26. package/server/applicationMap.js +33 -0
  27. package/server/awesomenessNormalizeRequest.js +131 -0
  28. package/server/brotliJsonResponse.js +28 -0
  29. package/server/checkAccess.js +34 -0
  30. package/server/componentDependencies.js +301 -0
  31. package/server/errors.js +11 -0
  32. package/server/fetchPage.js +269 -0
  33. package/server/getMD.js +22 -0
  34. package/server/koa/attachAwesomenessRequest.js +24 -0
  35. package/server/koa/cors.js +22 -0
  36. package/server/koa/errorHandler.js +32 -0
  37. package/server/koa/finalFormat.js +34 -0
  38. package/server/koa/jsonBodyParser.js +172 -0
  39. package/server/koa/routeRequest.js +288 -0
  40. package/server/koa/serverUp.js +7 -0
  41. package/server/koa/staticFiles.js +97 -0
  42. package/server/koa/timeout.js +42 -0
  43. package/server/pageInfo.js +121 -0
  44. package/server/reRoute.js +54 -0
  45. package/server/resolveRealCasePath.js +56 -0
  46. package/server/specialPaths.js +107 -0
  47. package/server/validateRequest.js +127 -0
  48. package/server/ws/handlers.js +67 -0
  49. package/server/ws/index.js +50 -0
  50. package/start.js +122 -0
  51. package/vitest.config.js +15 -0
package/.editorconfig ADDED
@@ -0,0 +1,4 @@
1
+ [*]
2
+ indent_style = tab
3
+ indent_size = tab
4
+ tab_width = 4
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
package/.jshintrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "esversion":6
3
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "editor.tabSize": 4,
3
+ "editor.detectIndentation": false,
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll": "explicit",
6
+ "source.fixAll.eslint": "explicit"
7
+ },
8
+ "eslint.workingDirectories": [
9
+ {
10
+ "mode": "auto"
11
+ }
12
+ ],
13
+ "github.copilot.chat.codeGeneration.useInstructionFiles": true,
14
+ "code-runner.runInTerminal": true,
15
+ "code-runner.fileDirectoryAsCwd": true,
16
+ "terminal.integrated.defaultProfile.windows": "Git Bash",
17
+ }
package/CLA.md ADDED
@@ -0,0 +1,9 @@
1
+ # Contributor License Agreement
2
+
3
+ By contributing to this repository, you agree that:
4
+
5
+ 1. You have the right to submit the contribution
6
+ 2. You grant the project owner a perpetual, worldwide, royalty-free license to use, modify, distribute, and relicense your contribution
7
+ 3. Your contribution does not infringe on third-party rights
8
+
9
+ This agreement applies to all contributions submitted via pull request, issue, or other means.
@@ -0,0 +1,18 @@
1
+ # Contributing
2
+
3
+ Thank you for your interest in contributing!
4
+
5
+ ## License of Contributions
6
+
7
+ By submitting a contribution to this project, you agree that:
8
+
9
+ - Your contribution is licensed under the Apache License 2.0
10
+ - You grant the project owner the right to relicense your contribution as part of future versions of the project
11
+
12
+ If you do not agree to these terms, please do not submit a contribution.
13
+
14
+ ## How to Contribute
15
+
16
+ - Fork the repository
17
+ - Create a feature branch
18
+ - Submit a pull request
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2026 Scott Forte
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright © 2026 Scott Forte
2
+
3
+ This product includes software developed by Scott Forte.
4
+
5
+ This product includes software developed by third parties.
6
+
7
+ - @awesomeness-js/server (Apache License 2.0)
8
+ Copyright © 2026 Scott Forte
9
+
10
+ - busboy (MIT License)
11
+ - koa (MIT License)
12
+ - koa-compress (MIT License)
13
+ - koa-send (MIT License)
14
+ - ws (MIT License)
15
+ - zlib (zlib License)
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Awesomeness JS Configuration
2
+
3
+ Most configurations for Awesomeness JS are done through the `.awesomeness/config.js` file.
4
+
5
+ The `./awesomeness/` folder and you can customize the location by modifying the import paths in `package.json`.
6
+
7
+ ## For an Example Application see the repo
8
+
9
+ [@awesomeness-js/app-template](https://github.com/awesomeness-js/app-template)
10
+
11
+ ## License
12
+
13
+ This project is licensed under the Apache License 2.0.
14
+
15
+ © 2026 Your Name. All rights reserved.
package/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Security Policy
2
+
3
+ If you discover a security issue, please report it privately.
4
+
5
+ Email: security@email.awesomenessjs.com
6
+
7
+ Do not open public issues for security vulnerabilities.
package/config.js ADDED
@@ -0,0 +1,29 @@
1
+ let _config = Object.freeze({});
2
+
3
+ export function init(userConfig = {}) {
4
+
5
+ const defaults = {
6
+ applicationMap: {},
7
+ beforeRouteMiddleware: [],
8
+ checkSession: null,
9
+ hostMap: null,
10
+ initDB: null,
11
+ specialRoutes: {},
12
+ wsHandler: null,
13
+ componentLocations: [],
14
+ };
15
+
16
+ _config = Object.freeze({
17
+ ...defaults,
18
+ ...userConfig,
19
+ });
20
+
21
+ return _config;
22
+
23
+ }
24
+
25
+ export function getConfig() {
26
+
27
+ return _config;
28
+
29
+ }
@@ -0,0 +1,101 @@
1
+ export default [
2
+ {
3
+ rules: {
4
+ // Ignore ALL line-break rules around import statements ONLY
5
+ 'padding-line-between-statements': [
6
+ 'error',
7
+
8
+ // imports are exempt
9
+ {
10
+ blankLine: 'any',
11
+ prev: 'import',
12
+ next: '*'
13
+ },
14
+ {
15
+ blankLine: 'any',
16
+ prev: '*',
17
+ next: 'import'
18
+ },
19
+
20
+ // everything else still padded like before
21
+ {
22
+ blankLine: 'always',
23
+ prev: '*',
24
+ next: 'block-like'
25
+ },
26
+ {
27
+ blankLine: 'always',
28
+ prev: 'block-like',
29
+ next: '*'
30
+ },
31
+ {
32
+ blankLine: 'always',
33
+ prev: '*',
34
+ next: 'function'
35
+ },
36
+ {
37
+ blankLine: 'always',
38
+ prev: 'function',
39
+ next: '*'
40
+ },
41
+ {
42
+ blankLine: 'always',
43
+ prev: '*',
44
+ next: 'throw'
45
+ }
46
+ ],
47
+
48
+ // ✅ This enforces ONE blank line at top + bottom of function bodies
49
+ // (and other normal blocks), matching your "inside curly braces" requirement.
50
+ 'padded-blocks': [
51
+ 'error',
52
+ {
53
+ blocks: 'always',
54
+ classes: 'always',
55
+ switches: 'always'
56
+ }
57
+ ],
58
+
59
+ // Keep brace-newline rules, BUT disable them for imports only
60
+ 'object-curly-newline': [
61
+ 'error',
62
+ {
63
+ ObjectExpression: {
64
+ minProperties: 2,
65
+ multiline: true,
66
+ consistent: true
67
+ },
68
+ ObjectPattern: {
69
+ minProperties: 2,
70
+ multiline: true,
71
+ consistent: true
72
+ },
73
+
74
+ // imports ignore curly newline formatting
75
+ ImportDeclaration: {
76
+ multiline: false,
77
+ minProperties: 99999,
78
+ consistent: false
79
+ },
80
+
81
+ ExportDeclaration: {
82
+ multiline: true,
83
+ minProperties: 2
84
+ }
85
+ }
86
+ ],
87
+
88
+ 'object-property-newline': [ 'error', { allowAllPropertiesOnSameLine: false } ],
89
+ indent: [ 'error', 'tab', { SwitchCase: 1 } ],
90
+ semi: [ 'error', 'always' ],
91
+ 'arrow-parens': [ 'error', 'always' ],
92
+ 'object-curly-spacing': [ 'error', 'always' ],
93
+ 'array-bracket-spacing': [ 'error', 'always' ],
94
+ 'lines-between-class-members': [ 'error', 'always' ],
95
+ 'newline-after-var': [ 'error', 'always' ],
96
+ 'newline-before-return': 'error',
97
+ 'brace-style': [ 'error', '1tbs', { allowSingleLine: false } ],
98
+ "no-unreachable": "off"
99
+ }
100
+ }
101
+ ];
@@ -0,0 +1,4 @@
1
+ // KV using awesomenessRequest.host (after mapping) to get set v on awesomenessRequest.applicationName
2
+ export default {
3
+ 'some.known.host.com': 'exampleApplicationName',
4
+ };
@@ -0,0 +1,15 @@
1
+ // All functions called before request routing
2
+
3
+ async function customMiddleware1(ctx, next) {
4
+
5
+ // console.log('Custom middleware before routeRequest', ctx.awesomenessRequest.path);
6
+
7
+ // custom logic here
8
+
9
+ await next();
10
+
11
+ }
12
+
13
+ export default [
14
+ customMiddleware1
15
+ ];
@@ -0,0 +1,15 @@
1
+ import app from '#app';
2
+
3
+ export default async (awesomenessRequest) => {
4
+
5
+ //console.log('checking session');
6
+
7
+ const session = await app.session.check(awesomenessRequest);
8
+
9
+ const user = await app.user.get({ id: session.userId });
10
+
11
+ awesomenessRequest.user = user;
12
+
13
+ //console.log('session valid', { user });
14
+
15
+ };
@@ -0,0 +1,40 @@
1
+ import hostMap from './hostMap.js';
2
+ import initDB from './initDB.js';
3
+ import specialRoutes from './specialRoutes.js';
4
+ import wsHandler from './wsHandler.js';
5
+ import beforeRouteMiddleware from './beforeRouteMiddleware.js';
6
+ import checkSession from './checkSession.js';
7
+ import applicationMap from './applicationMap.js';
8
+
9
+ export default {
10
+
11
+ configURL: new URL(import.meta.url),
12
+ siteURL: new URL("../sites/", import.meta.url),
13
+ commonPublicDir: new URL("../awesomeness-ui/core/public/", import.meta.url),
14
+
15
+ componentLocations: (awesomenessRequest) => {
16
+
17
+ const siteURL = new URL(`../sites/${awesomenessRequest.site}/`, import.meta.url);
18
+
19
+ return [
20
+
21
+ // first match wins (site first)
22
+ new URL('./components/', siteURL),
23
+
24
+ // last item is the default
25
+ new URL('../awesomeness-ui/components/', import.meta.url),
26
+ ];
27
+
28
+ },
29
+
30
+ debug: true,
31
+ byPassAccessRequirementsInDev: true,
32
+
33
+ applicationMap,
34
+ beforeRouteMiddleware,
35
+ checkSession,
36
+ hostMap,
37
+ initDB,
38
+ specialRoutes,
39
+ wsHandler,
40
+ };
@@ -0,0 +1,44 @@
1
+ import * as fs from 'fs/promises';
2
+ const sites = await fs.readdir('sites');
3
+
4
+ export default function({
5
+ host, // already lower case - starts as req.headers.host (lower case)
6
+ mainDomain // already lower case
7
+ }){
8
+
9
+ const simpleMap = {
10
+ "localhost:3000": "example.awesomenessjs.com",
11
+ };
12
+
13
+ if (simpleMap[host]) {
14
+
15
+ host = simpleMap[host];
16
+
17
+ } else {
18
+
19
+ if (simpleMap[mainDomain]) {
20
+
21
+ host = simpleMap[mainDomain];
22
+
23
+ }
24
+
25
+ }
26
+
27
+ if (!sites.includes(host)) {
28
+
29
+ // try the root
30
+ if (sites.includes(mainDomain)) {
31
+
32
+ host = mainDomain;
33
+
34
+ } else {
35
+
36
+ host = 'awesomenessjs.com';
37
+
38
+ }
39
+
40
+ }
41
+
42
+ return host;
43
+
44
+ }
@@ -0,0 +1,65 @@
1
+ import graph from '@awesomeness-js/graph-postgres';
2
+ import setupApplication from './setupDB/applications.js';
3
+ import setupUsers from './setupDB/users.js';
4
+ import setupWebsites from './setupDB/websites.js';
5
+
6
+ async function setup(){
7
+
8
+ let applications = await setupApplication();
9
+ let users = await setupUsers(applications);
10
+ let websites = await setupWebsites();
11
+
12
+ }
13
+
14
+ export default async ()=>{
15
+
16
+
17
+ // is graph available?
18
+ try {
19
+
20
+ graph.config.init({
21
+ host: process.env.AWESOMENESS_GRAPH_POSTGRES_HOST,
22
+ port: process.env.AWESOMENESS_GRAPH_POSTGRES_PORT,
23
+ user: process.env.AWESOMENESS_GRAPH_POSTGRES_USER,
24
+ password: process.env.AWESOMENESS_GRAPH_POSTGRES_PASSWORD,
25
+ database: process.env.AWESOMENESS_GRAPH_POSTGRES_DB,
26
+ ssl: {
27
+ rejectUnauthorized: false
28
+ }
29
+ });
30
+
31
+ // await graph.utils.createDB();
32
+
33
+ await graph.kv.add('serverStarted', new Date().toISOString());
34
+
35
+ let testGet = await graph.kv.get('serverStarted');
36
+
37
+ if (testGet) {
38
+
39
+ console.log('Graph is available:');
40
+
41
+ try {
42
+
43
+ await setup();
44
+
45
+ } catch (err) {
46
+
47
+ console.log('Setup failed:');
48
+
49
+ }
50
+
51
+
52
+ } else {
53
+
54
+ console.error('Graph is not available ... testGet');
55
+
56
+ }
57
+
58
+ } catch (err) {
59
+
60
+ console.error('Graph is not available:');
61
+
62
+ }
63
+
64
+
65
+ };
@@ -0,0 +1,47 @@
1
+ import app from '#app';
2
+ import utils from '@awesomeness-js/utils';
3
+
4
+ export default async function setup(){
5
+
6
+ let applications = {
7
+ 'scottmforte.com': {}
8
+ };
9
+
10
+
11
+ await utils.eachAsync(applications, async ( d, site ) => {
12
+
13
+ let application;
14
+
15
+ try {
16
+
17
+ application = await app.application.get({ name: site });
18
+
19
+ } catch(e) {
20
+
21
+ try {
22
+
23
+ if(e.applicationDoesNotExist){
24
+
25
+ application = await app.application.create({ name: site });
26
+
27
+ }
28
+
29
+ } catch (e2){
30
+
31
+ throw {
32
+ couldNotCreateApplication: e2
33
+ };
34
+
35
+ }
36
+
37
+ }
38
+
39
+ applications[site] = application;
40
+
41
+ });
42
+
43
+ console.log('✔️ Applications exist');
44
+
45
+ return applications;
46
+
47
+ }
@@ -0,0 +1,65 @@
1
+ import app from '#app';
2
+ import utils from '@awesomeness-js/utils';
3
+
4
+ export default async function(applications){
5
+
6
+ let me = {
7
+ first: 'Scott',
8
+ last: 'Forte',
9
+ email: 'scott@scottmforte.com',
10
+ password: 'Profile4-Unfitted4-Next3-Lesser2-Gains8-Seizing3-Wireless2-Thinning6-Disclose0-Provider9'
11
+ };
12
+
13
+ let usersPerApplication = {
14
+ 'scottmforte.com': [ me ]
15
+ };
16
+
17
+ await utils.eachAsync(usersPerApplication, async ( users, site )=>{
18
+
19
+ await utils.eachAsync(users, async (user,k) => {
20
+
21
+ try {
22
+
23
+ user = await app.user.get({
24
+ email: user.email,
25
+ applicationId: applications[site].id
26
+ });
27
+
28
+ users[k] = user;
29
+
30
+ } catch(e) {
31
+
32
+ try {
33
+
34
+ users[k] = await app.user.create({
35
+ ...user,
36
+ applicationId: applications[site].id
37
+ });
38
+
39
+ } catch(e){
40
+
41
+ console.log('❌ User does not exist and could not be created', e);
42
+
43
+ throw {
44
+ couldNotCreateUser: {
45
+ application: k,
46
+ email: user.email,
47
+ error: e
48
+ }
49
+ };
50
+
51
+ }
52
+
53
+
54
+ }
55
+
56
+ });
57
+
58
+ });
59
+
60
+
61
+ console.log('✔️ Users exist');
62
+
63
+ return usersPerApplication;
64
+
65
+ }
@@ -0,0 +1,49 @@
1
+ import app from '#app';
2
+ import utils from '@awesomeness-js/utils';
3
+
4
+ export default async function setup(){
5
+
6
+ let websites = {
7
+ 'scottmforte.com': {
8
+ id: '5aaa5f12-4223-4801-b6a3-c7e0630f99a2'
9
+ }
10
+ };
11
+
12
+
13
+ await utils.eachAsync(websites, async ( d, site ) => {
14
+
15
+ let website;
16
+
17
+ try {
18
+
19
+ website = await app.website.get({ name: site });
20
+
21
+ } catch(e) {
22
+
23
+ try {
24
+
25
+ website = await app.website.create({
26
+ name: site,
27
+ ... d
28
+ });
29
+
30
+
31
+ } catch (e2){
32
+
33
+ throw {
34
+ couldNotCreateWebsite: e2
35
+ };
36
+
37
+ }
38
+
39
+ }
40
+
41
+ websites[site] = website;
42
+
43
+ });
44
+
45
+ console.log('✔️ websites exist');
46
+
47
+ return websites;
48
+
49
+ }
@@ -0,0 +1,7 @@
1
+ import example from '#sites/example.awesomenessjs.com/specialRoutes.js';
2
+
3
+ const specialRoutes = {
4
+ 'example.awesomenessjs.com': example
5
+ };
6
+
7
+ export default specialRoutes;
@@ -0,0 +1,13 @@
1
+ export default async ({
2
+ socket,
3
+ message,
4
+ })=>{
5
+
6
+ socket.send(JSON.stringify({
7
+ type: message.type ?? "echo",
8
+ clientId: socket.clientId,
9
+ echo: message,
10
+ ts: Date.now()
11
+ }));
12
+
13
+ };
package/index.js ADDED
@@ -0,0 +1,22 @@
1
+ import checkAccess from "./server/checkAccess.js";
2
+ import reRoute from "./server/reRoute.js";
3
+ import { init, getConfig } from "./config.js";
4
+ import start from "./start.js";
5
+
6
+ export {
7
+ init,
8
+ getConfig,
9
+ checkAccess,
10
+ reRoute,
11
+ start
12
+ };
13
+
14
+ const server = {
15
+ init,
16
+ getConfig,
17
+ checkAccess,
18
+ reRoute,
19
+ start
20
+ };
21
+
22
+ export default server;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@awesomeness-js/server",
3
+ "version": "1.0.0",
4
+ "description": "Awesomeness Multi-Site Server",
5
+ "author": "Scott Forte",
6
+ "type": "module",
7
+ "main": "start.js",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/awesomeness-js/server.git"
11
+ },
12
+ "scripts": {
13
+ "test": "vitest"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "import": "./index.js"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "@awesomeness-js/utils": "latest",
22
+ "busboy": "^1.6.0",
23
+ "koa": "^3.0.1",
24
+ "koa-compress": "^5.1.1",
25
+ "koa-send": "^5.0.1",
26
+ "ws": "^8.18.3",
27
+ "zlib": "^1.0.5"
28
+ },
29
+ "license": "none",
30
+ "devDependencies": {
31
+ "eslint": "^9.23.0",
32
+ "vitest": "^3.0.9"
33
+ }
34
+ }