cache-companion 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +29 -0
- package/package.json +114 -0
- package/src/classes/cache.js +47 -0
- package/src/commands/config-mgr.js +27 -0
- package/src/commands/start.js +35 -0
- package/src/config/schema.json +8 -0
- package/src/index.js +30 -0
- package/src/logger.js +11 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const logger = require('../src/logger')('bin');
|
|
3
|
+
const arg = require('arg');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const getConfig = require('../src/commands/config-mgr.js');
|
|
6
|
+
const main = require('../src/index.js');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const args = arg({
|
|
11
|
+
'--port': Number,
|
|
12
|
+
'--origin': String,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
logger.debug('Received args', args);
|
|
16
|
+
|
|
17
|
+
main(args);
|
|
18
|
+
|
|
19
|
+
} catch (err) {
|
|
20
|
+
logger.warning(err.message);
|
|
21
|
+
console.log();
|
|
22
|
+
usage();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function usage() {
|
|
26
|
+
console.log(`${chalk.whiteBright('Tool [CMD]')}
|
|
27
|
+
${chalk.greenBright('--start')}\t starts the app
|
|
28
|
+
${chalk.greenBright('--build')}\t builds the project`);
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cache-companion",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cache-companion": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"accepts": "^2.0.0",
|
|
11
|
+
"ajv": "^8.20.0",
|
|
12
|
+
"ansi-styles": "^4.3.0",
|
|
13
|
+
"arg": "^5.0.2",
|
|
14
|
+
"argparse": "^2.0.1",
|
|
15
|
+
"better-ajv-errors": "^2.0.3",
|
|
16
|
+
"body-parser": "^2.2.2",
|
|
17
|
+
"bytes": "^3.1.2",
|
|
18
|
+
"call-bind-apply-helpers": "^1.0.2",
|
|
19
|
+
"call-bound": "^1.0.4",
|
|
20
|
+
"callsites": "^3.1.0",
|
|
21
|
+
"chalk": "^4.1.2",
|
|
22
|
+
"color-convert": "^2.0.1",
|
|
23
|
+
"color-name": "^1.1.4",
|
|
24
|
+
"content-disposition": "^1.1.0",
|
|
25
|
+
"content-type": "^1.0.5",
|
|
26
|
+
"cookie": "^0.7.2",
|
|
27
|
+
"cookie-signature": "^1.2.2",
|
|
28
|
+
"cosmiconfig": "^9.0.2",
|
|
29
|
+
"debug": "^4.4.3",
|
|
30
|
+
"depd": "^2.0.0",
|
|
31
|
+
"dunder-proto": "^1.0.1",
|
|
32
|
+
"ee-first": "^1.1.1",
|
|
33
|
+
"encodeurl": "^2.0.0",
|
|
34
|
+
"env-paths": "^2.2.1",
|
|
35
|
+
"error-ex": "^1.3.4",
|
|
36
|
+
"es-define-property": "^1.0.1",
|
|
37
|
+
"es-errors": "^1.3.0",
|
|
38
|
+
"es-object-atoms": "^1.1.2",
|
|
39
|
+
"escape-html": "^1.0.3",
|
|
40
|
+
"etag": "^1.8.1",
|
|
41
|
+
"express": "^5.2.1",
|
|
42
|
+
"fast-deep-equal": "^3.1.3",
|
|
43
|
+
"fast-uri": "^3.1.2",
|
|
44
|
+
"finalhandler": "^2.1.1",
|
|
45
|
+
"forwarded": "^0.2.0",
|
|
46
|
+
"fresh": "^2.0.0",
|
|
47
|
+
"function-bind": "^1.1.2",
|
|
48
|
+
"get-intrinsic": "^1.3.0",
|
|
49
|
+
"get-proto": "^1.0.1",
|
|
50
|
+
"gopd": "^1.2.0",
|
|
51
|
+
"has-flag": "^4.0.0",
|
|
52
|
+
"has-symbols": "^1.1.0",
|
|
53
|
+
"hasown": "^2.0.4",
|
|
54
|
+
"http-errors": "^2.0.1",
|
|
55
|
+
"iconv-lite": "^0.7.2",
|
|
56
|
+
"import-fresh": "^3.3.1",
|
|
57
|
+
"inherits": "^2.0.4",
|
|
58
|
+
"ipaddr.js": "^1.9.1",
|
|
59
|
+
"is-arrayish": "^0.2.1",
|
|
60
|
+
"is-promise": "^4.0.0",
|
|
61
|
+
"js-tokens": "^4.0.0",
|
|
62
|
+
"js-yaml": "^4.2.0",
|
|
63
|
+
"json-parse-even-better-errors": "^2.3.1",
|
|
64
|
+
"json-schema-traverse": "^1.0.0",
|
|
65
|
+
"jsonpointer": "^5.0.1",
|
|
66
|
+
"leven": "^3.1.0",
|
|
67
|
+
"lines-and-columns": "^1.2.4",
|
|
68
|
+
"math-intrinsics": "^1.1.0",
|
|
69
|
+
"media-typer": "^1.1.0",
|
|
70
|
+
"merge-descriptors": "^2.0.0",
|
|
71
|
+
"mime-db": "^1.54.0",
|
|
72
|
+
"mime-types": "^3.0.2",
|
|
73
|
+
"ms": "^2.1.3",
|
|
74
|
+
"negotiator": "^1.0.0",
|
|
75
|
+
"object-inspect": "^1.13.4",
|
|
76
|
+
"on-finished": "^2.4.1",
|
|
77
|
+
"once": "^1.4.0",
|
|
78
|
+
"parent-module": "^1.0.1",
|
|
79
|
+
"parse-json": "^5.2.0",
|
|
80
|
+
"parseurl": "^1.3.3",
|
|
81
|
+
"path-to-regexp": "^8.4.2",
|
|
82
|
+
"picocolors": "^1.1.1",
|
|
83
|
+
"proxy-addr": "^2.0.7",
|
|
84
|
+
"qs": "^6.15.2",
|
|
85
|
+
"range-parser": "^1.2.1",
|
|
86
|
+
"raw-body": "^3.0.2",
|
|
87
|
+
"require-from-string": "^2.0.2",
|
|
88
|
+
"resolve-from": "^4.0.0",
|
|
89
|
+
"router": "^2.2.0",
|
|
90
|
+
"safer-buffer": "^2.1.2",
|
|
91
|
+
"send": "^1.2.1",
|
|
92
|
+
"serve-static": "^2.2.1",
|
|
93
|
+
"setprototypeof": "^1.2.0",
|
|
94
|
+
"side-channel": "^1.1.1",
|
|
95
|
+
"side-channel-list": "^1.0.1",
|
|
96
|
+
"side-channel-map": "^1.0.1",
|
|
97
|
+
"side-channel-weakmap": "^1.0.2",
|
|
98
|
+
"statuses": "^2.0.2",
|
|
99
|
+
"supports-color": "^7.2.0",
|
|
100
|
+
"toidentifier": "^1.0.1",
|
|
101
|
+
"type-is": "^2.1.0",
|
|
102
|
+
"unpipe": "^1.0.0",
|
|
103
|
+
"vary": "^1.1.2",
|
|
104
|
+
"wrappy": "^1.0.2"
|
|
105
|
+
},
|
|
106
|
+
"devDependencies": {},
|
|
107
|
+
"scripts": {
|
|
108
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
109
|
+
},
|
|
110
|
+
"keywords": [],
|
|
111
|
+
"author": "",
|
|
112
|
+
"license": "ISC",
|
|
113
|
+
"type": "commonjs"
|
|
114
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const logger = require('../logger.js')('cacheClass');
|
|
2
|
+
|
|
3
|
+
module.exports = class cache {
|
|
4
|
+
ttl = 10000; // Cache entries expire after 10 seconds
|
|
5
|
+
cache = new Map();
|
|
6
|
+
|
|
7
|
+
constructor(ttl) {
|
|
8
|
+
if (ttl) {
|
|
9
|
+
this.ttl = ttl;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
addEntryToCache(url, data) {
|
|
14
|
+
const timeout = setTimeout(() => {
|
|
15
|
+
this.cache.delete(url);
|
|
16
|
+
logger.log('Cache entry expired for URL:', url);
|
|
17
|
+
}, this.ttl);
|
|
18
|
+
this.cache.set(url, { data, timeout });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getEntryFromCache(url) {
|
|
22
|
+
const entry = this.cache.get(url);
|
|
23
|
+
if (entry) {
|
|
24
|
+
clearTimeout(entry.timeout); // Clear the timeout for the cache entry to prevent it from expiring
|
|
25
|
+
logger.log('Cache hit for URL, expiry timer reset:', url);
|
|
26
|
+
entry.timeout = setTimeout(() => {
|
|
27
|
+
this.cache.delete(url);
|
|
28
|
+
logger.log('Cache entry expired for URL:', url);
|
|
29
|
+
}, this.ttl); // Reset the timeout for the cache entry to expire after 10 seconds of inactivity
|
|
30
|
+
}
|
|
31
|
+
return entry?.data;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
hasEntryInCache(url) {
|
|
35
|
+
return this.cache.has(url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
deleteEntryFromCache(url) {
|
|
39
|
+
this.cache.delete(url);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
clearCache() {
|
|
43
|
+
this.cache.clear();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const logger = require('../logger.js')('config:mgr');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { cosmiconfigSync } = require('cosmiconfig');
|
|
4
|
+
const schema = require('../config/schema.json');
|
|
5
|
+
const configLoader = cosmiconfigSync('tool');
|
|
6
|
+
const Ajv = require('ajv').default;
|
|
7
|
+
const ajv = new Ajv();
|
|
8
|
+
const betterAjvErrors = require('better-ajv-errors').default;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module.exports = function getConfig() {
|
|
12
|
+
const result = configLoader.search(process.cwd());
|
|
13
|
+
if (!result) {
|
|
14
|
+
logger.warning('Could not find configuration, using default');
|
|
15
|
+
return { port: 1234 };
|
|
16
|
+
} else {
|
|
17
|
+
const isValid = ajv.validate(schema, result.config);
|
|
18
|
+
if (!isValid) {
|
|
19
|
+
logger.warning('invalid configuration was supplied');
|
|
20
|
+
console.log();
|
|
21
|
+
console.log(betterAjvErrors(schema, result.config, ajv.errors));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
logger.debug('Found configuration', result.config);
|
|
25
|
+
return result.config;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const logger = require('../logger')('commands:start');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const proxyServer = express();
|
|
5
|
+
const cache = require('../classes/cache.js');
|
|
6
|
+
|
|
7
|
+
const cacheInstance = new cache(10000); // Create a new cache instance with a TTL of 10 seconds
|
|
8
|
+
|
|
9
|
+
module.exports = function start(port, origin) {
|
|
10
|
+
logger.highlight(' Starting the cache proxy server...');
|
|
11
|
+
logger.debug('Received port', { port });
|
|
12
|
+
logger.debug('Received origin', { origin });
|
|
13
|
+
|
|
14
|
+
proxyServer.get('/{*splat}', async (req, res) => {
|
|
15
|
+
//cache hit, return the cached response
|
|
16
|
+
if (cacheInstance.hasEntryInCache(req.url)) {
|
|
17
|
+
res.send(cacheInstance.getEntryFromCache(req.url));
|
|
18
|
+
return;
|
|
19
|
+
} else {
|
|
20
|
+
// cache miss, Simulate fetching data from the origin server, and store it in the cache before sending the response
|
|
21
|
+
const response = await fetch(`${origin}/${req.url}`);
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
cacheInstance.addEntryToCache(req.url, data);
|
|
24
|
+
res.send(data);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
proxyServer.listen(port, (error) => {
|
|
29
|
+
if (error) {
|
|
30
|
+
logger.error('Error occurred while starting the proxy server:', error);
|
|
31
|
+
} else {
|
|
32
|
+
logger.debug(`Proxy server is listening on port ${port}`);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const logger = require('./logger.js')('main');
|
|
2
|
+
const arg = require('arg');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const start = require('./commands/start');
|
|
5
|
+
const getConfig = require('./commands/config-mgr.js');
|
|
6
|
+
|
|
7
|
+
module.exports = function main(args) {
|
|
8
|
+
try {
|
|
9
|
+
if (args['--port'] === undefined || args['--origin'] === undefined) {
|
|
10
|
+
logger.warning('Missing required arguments, searching for configuration file...');
|
|
11
|
+
config = getConfig(); // load configuration from file if arguments are not provided
|
|
12
|
+
const port = config.port;
|
|
13
|
+
const origin = config.origin;
|
|
14
|
+
logger.debug('Using configuration from file', { port, origin });
|
|
15
|
+
start(port, origin);
|
|
16
|
+
} else if (typeof args['--port'] !== "number" || typeof args['--origin'] !== "string" || isNaN(args['--port'])) {
|
|
17
|
+
logger.warning('Invalid argument types, port should be a number and origin should be a string');
|
|
18
|
+
return;
|
|
19
|
+
} else {
|
|
20
|
+
port = args['--port'];
|
|
21
|
+
origin = args['--origin'];
|
|
22
|
+
logger.debug('Using configuration from arguments', { port, origin });
|
|
23
|
+
start(port, origin); // start the cache proxy server with the specified port and origin
|
|
24
|
+
}
|
|
25
|
+
} catch (err) {
|
|
26
|
+
logger.warning(err.message);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const debug = require('debug').default;
|
|
3
|
+
|
|
4
|
+
module.exports = function createLogger(name) {
|
|
5
|
+
return {
|
|
6
|
+
log: (...args) => console.log(chalk.gray(...args)),
|
|
7
|
+
warning: (...args) => console.log(chalk.yellow(...args)),
|
|
8
|
+
highlight: (...args) => console.log(chalk.bgCyanBright(...args)),
|
|
9
|
+
debug: debug(name),
|
|
10
|
+
};
|
|
11
|
+
}
|