@iebh/tera-fy 1.7.4 → 1.9.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/CHANGELOG.md +24 -0
- package/README.md +48 -0
- package/api.md +334 -286
- package/dist/terafy.es2019.js +2 -2
- package/dist/terafy.js +2 -2
- package/lib/projectFile.js +1 -1
- package/lib/terafy.client.js +88 -23
- package/lib/terafy.proxy.js +115 -0
- package/lib/terafy.server.js +69 -1
- package/package.json +5 -2
- package/plugins/vite.js +18 -0
- package/plugins/vue2.js +1 -1
- package/plugins/vue3.js +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {create as createDomain} from 'node:domain';
|
|
2
|
+
import detectPort from 'detect-port';
|
|
3
|
+
import Proxy from 'http-proxy';
|
|
4
|
+
|
|
5
|
+
export class TeraProxy {
|
|
6
|
+
/**
|
|
7
|
+
* Setup a local loopback proxy for TERA-tools.com
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
10
|
+
* @param {Boolean} [options.force=false] Restart the server even if its apparently running
|
|
11
|
+
* @param {Boolean} [options.autoStart=true] Automatically start the proxy without calling `Plugin.start()`
|
|
12
|
+
* @param {String} [options.host='0.0.0.0'] Host IP to listen on
|
|
13
|
+
* @param {Number} [options.port=7334] Host port to listen on
|
|
14
|
+
* @param {String} [options.targetProtocol='https'] Target protocol to forward to
|
|
15
|
+
* @param {String} [options.targetHost='tera-tools.com'] Target host to forward to
|
|
16
|
+
* @param {Number} [options.targetPort=443] Target port to forward to
|
|
17
|
+
* @param {'ignore'|'throw'} [options.portConflict='ignore'] Action to take when something is already listening on the allocated port
|
|
18
|
+
* @param {Function} [options.onLog=console.log] Function to call with any logging output. Defaults to using console.log. Called as `(level:'INFO|'WARN', ...msg:any)`
|
|
19
|
+
*
|
|
20
|
+
* @returns {VitePlugin}
|
|
21
|
+
*/
|
|
22
|
+
settings = {
|
|
23
|
+
autoStart: true,
|
|
24
|
+
force: false,
|
|
25
|
+
host: '0.0.0.0',
|
|
26
|
+
port: 7334,
|
|
27
|
+
targetProtocol: 'https',
|
|
28
|
+
targetHost: 'tera-tools.com',
|
|
29
|
+
targetPort: 443,
|
|
30
|
+
portConflict: 'ignore',
|
|
31
|
+
onLog: (level, ...msg) => console.log(...msg),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Eventual proxy server when the plugin has booted
|
|
37
|
+
* @type {ProxyServer}
|
|
38
|
+
*/
|
|
39
|
+
proxyServer;
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Boot the proxy
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
46
|
+
*/
|
|
47
|
+
start() {
|
|
48
|
+
if (this.proxyServer && !this.settings.force) return Promise.resolve(); // Server already running skip
|
|
49
|
+
|
|
50
|
+
return Promise.resolve()
|
|
51
|
+
.then(()=> detectPort(this.settings.port))
|
|
52
|
+
.then(gotPort => gotPort != this.settings.port && Promise.reject('PORT-CONFLICT'))
|
|
53
|
+
.then(() => this.proxyServer = Proxy.createProxyServer({ // Create proxy pass-thru
|
|
54
|
+
changeOrigin: true,
|
|
55
|
+
target: {
|
|
56
|
+
protocol: this.settings.targetProtocol + ':',
|
|
57
|
+
host: this.settings.targetHost,
|
|
58
|
+
port: this.settings.targetPort,
|
|
59
|
+
},
|
|
60
|
+
}))
|
|
61
|
+
.then(()=> new Promise((resolve, reject) => {
|
|
62
|
+
// Wrap listener in a domain so we can correctly catch EADDRINUSE
|
|
63
|
+
let domain = createDomain();
|
|
64
|
+
domain.on('error', err => {
|
|
65
|
+
if (err.code == 'EADDRINUSE') {
|
|
66
|
+
throw 'PORT-CONFLICT';
|
|
67
|
+
} else {
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
this.proxyServer.listen(this.settings.port, ()=> {
|
|
72
|
+
this.settings.onLog('INFO', 'Routing TERA traffic from', `http://${this.settings.host}:${this.settings.port}`, '→', `${this.settings.targetProtocol}://${this.settings.targetHost}:${this.settings.targetPort}`);
|
|
73
|
+
resolve();
|
|
74
|
+
})
|
|
75
|
+
}))
|
|
76
|
+
.catch(e => {
|
|
77
|
+
if (e === 'PORT-CONFLICT') {
|
|
78
|
+
if (this.settings.portConflict == 'ignore') {
|
|
79
|
+
this.settings.onLog('WARN', 'Port', this.settings.port, 'is already allocated - assuming TERA is already running locally and skipping proxy');
|
|
80
|
+
return false; // Do nothing
|
|
81
|
+
} else {
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
throw e; // Re-throw everything else
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Stop the proxy server
|
|
93
|
+
* @returns {Promise} A promise which will resolve when the close operation has completed
|
|
94
|
+
*/
|
|
95
|
+
stop() {
|
|
96
|
+
return Promise.resolve()
|
|
97
|
+
.then(()=> this.proxyServer && new Promise(resolve => this.proxyServer.close(()=> resolve())))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
constructor(options) {
|
|
102
|
+
if (options) Object.assign(this.settings, options);
|
|
103
|
+
|
|
104
|
+
// Auto start?
|
|
105
|
+
if (options?.autoStart ?? true) this.start();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Utility function to return a new TeraProxy instance
|
|
112
|
+
*/
|
|
113
|
+
export default function(options) {
|
|
114
|
+
return new TeraProxy(options);
|
|
115
|
+
}
|
package/lib/terafy.server.js
CHANGED
|
@@ -327,6 +327,17 @@ export default class TeraFyServer {
|
|
|
327
327
|
payload: args,
|
|
328
328
|
});
|
|
329
329
|
}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* RPC callback to set the server verbostiy level
|
|
334
|
+
*
|
|
335
|
+
* @param {Number} verbosity The desired server verbosity level
|
|
336
|
+
*/
|
|
337
|
+
setServerVerbosity(verbosity) {
|
|
338
|
+
this.settinigs.verbosity = +verbosity;
|
|
339
|
+
this.debug('INFO', 1, 'Server verbosity set to', this.settinigs.verbosity);
|
|
340
|
+
}
|
|
330
341
|
// }}}
|
|
331
342
|
|
|
332
343
|
// Session / User - getUser(), requireUser() {{{
|
|
@@ -1345,7 +1356,7 @@ export default class TeraFyServer {
|
|
|
1345
1356
|
}
|
|
1346
1357
|
// }}}
|
|
1347
1358
|
|
|
1348
|
-
// UI - uiAlert() {{{
|
|
1359
|
+
// UI - uiAlert(), uiProgress(), uiWindow(), uiSplat() {{{
|
|
1349
1360
|
/**
|
|
1350
1361
|
* Display simple text within TERA
|
|
1351
1362
|
*
|
|
@@ -1380,6 +1391,63 @@ export default class TeraFyServer {
|
|
|
1380
1391
|
}
|
|
1381
1392
|
|
|
1382
1393
|
|
|
1394
|
+
/**
|
|
1395
|
+
* Display, update or dispose of windows for long running tasks
|
|
1396
|
+
* All options are cumulative - i.e. they are merged with other options previously provided
|
|
1397
|
+
*
|
|
1398
|
+
* @param {Object|Boolean} [options] Additional options to mutate behaviour, if boolean false `{close: true}` is assumed
|
|
1399
|
+
* @param {String} [options.title='TERA'] Window title, can only be set on the initial call
|
|
1400
|
+
* @param {String} [options.body=''] Window body text, can only be set on the initial call
|
|
1401
|
+
* @param {Boolean} [options.bodyHtml=false] Treat body text as HTML
|
|
1402
|
+
* @param {Boolean} [options.close=false] Close the existing dialog, if true the dialog is disposed and options reset
|
|
1403
|
+
*
|
|
1404
|
+
* @FIXME None of the below have been implemented yet
|
|
1405
|
+
* @param {String} [options.text] The text of the task being conducted
|
|
1406
|
+
* @param {Number} [options.progress] The current progress of the task being conducted, this is assumed to be a value less than `maxProgress`
|
|
1407
|
+
* @param {Number} [options.maxProgress] The maximum value that the progress can be
|
|
1408
|
+
*
|
|
1409
|
+
* @returns {Promise} A promise which resolves when the dialog has been updated
|
|
1410
|
+
*/
|
|
1411
|
+
uiProgress(options) {
|
|
1412
|
+
if (options === false) { // Close existing ui progress window
|
|
1413
|
+
uiProgressOptions.close = true;
|
|
1414
|
+
} else if (!this.uiProgressOptions) { // New uiProgress window
|
|
1415
|
+
this.uiProgressOptions = {
|
|
1416
|
+
title: 'TERA',
|
|
1417
|
+
body: '',
|
|
1418
|
+
bodyHtml: false,
|
|
1419
|
+
close: false,
|
|
1420
|
+
...options,
|
|
1421
|
+
};
|
|
1422
|
+
} else { // Merge options with existing uiProgress window
|
|
1423
|
+
Object.assign(this.uiProgressOptions, options);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
console.log('FIXME: UI PROGRESS - is unfinished', {options: this.uiProgressOptions});
|
|
1427
|
+
|
|
1428
|
+
if (!this.uiProgressPromise && !this.uiProgressOptions.close) { // Not created the dialog yet
|
|
1429
|
+
this.uiProgressPromise = this.requestFocus(()=>
|
|
1430
|
+
app.service('$prompt').dialog({
|
|
1431
|
+
title: this.uiProgressOptions.title,
|
|
1432
|
+
body: this.uiProgressOptions.body,
|
|
1433
|
+
isHtml: this.uiProgressOptions.bodyHtml,
|
|
1434
|
+
})
|
|
1435
|
+
);
|
|
1436
|
+
return Promise.resolve();
|
|
1437
|
+
} else if (!this.uiProgressOptions.close) { // Asked to close the dialog
|
|
1438
|
+
return Promise.resolve()
|
|
1439
|
+
.then(()=> this.uiProgressPromise && app.service('$prompt').close(true)) // Close the dialog if its open
|
|
1440
|
+
.then(()=> { // Release state
|
|
1441
|
+
this.uiProgressOptions = {};
|
|
1442
|
+
this.uiProgressPromise = null;
|
|
1443
|
+
})
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
uiProgressOptions;
|
|
1448
|
+
uiProgressPromise;
|
|
1449
|
+
|
|
1450
|
+
|
|
1383
1451
|
/**
|
|
1384
1452
|
* Open a popup window containing a new site
|
|
1385
1453
|
*
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iebh/tera-fy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "TERA website worker",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --serve --servedir=.",
|
|
7
7
|
"build": "concurrently 'npm:build:*'",
|
|
8
8
|
"build:client": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify",
|
|
9
9
|
"build:client:es2019": "esbuild --platform=browser --format=esm --target=es2019 --bundle lib/terafy.client.js --outfile=dist/terafy.es2019.js --minify",
|
|
10
|
-
"build:plugins
|
|
10
|
+
"build:plugins:vue2:es2019": "esbuild --platform=browser --format=esm --target=es2019 --bundle plugins/vue2.js --outfile=dist/plugin.vue2.es2019.js --minify",
|
|
11
11
|
"build:docs:api": "documentation build lib/terafy.client.js lib/projectFile.js --format html --config documentation.yml --output docs/",
|
|
12
12
|
"build:docs:markdown": "documentation build lib/terafy.client.js lib/projectFile.js --format md --markdown-toc --output api.md",
|
|
13
13
|
"lint": "eslint lib plugins utils widgets",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"./dist/*": "./dist/*",
|
|
30
30
|
"./projectFile": "./lib/projectFile.js",
|
|
31
|
+
"./proxy": "./lib/terafy.proxy.js",
|
|
31
32
|
"./server": "./lib/terafy.server.js",
|
|
32
33
|
"./plugins/*": "./plugins/*.js",
|
|
33
34
|
"./widgets/*": "./widgets/*"
|
|
@@ -68,7 +69,9 @@
|
|
|
68
69
|
"node": ">=18"
|
|
69
70
|
},
|
|
70
71
|
"dependencies": {
|
|
72
|
+
"detect-port": "^1.6.1",
|
|
71
73
|
"filesize": "^10.1.1",
|
|
74
|
+
"http-proxy": "^1.18.1",
|
|
72
75
|
"just-diff": "^6.0.2",
|
|
73
76
|
"lodash-es": "^4.17.21",
|
|
74
77
|
"mitt": "^3.0.1",
|
package/plugins/vite.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import TeraProxy from '../lib/terafy.proxy.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setup a Vite local loopback
|
|
5
|
+
*
|
|
6
|
+
* @see lib/terafy.proxy.js
|
|
7
|
+
*
|
|
8
|
+
* @returns {VitePlugin}
|
|
9
|
+
*/
|
|
10
|
+
export default function vitePluginTeraFy(options) {
|
|
11
|
+
TeraProxy(options);
|
|
12
|
+
|
|
13
|
+
// Vite output
|
|
14
|
+
return {
|
|
15
|
+
name: 'tera-fy',
|
|
16
|
+
apply: 'dev', // Only run on dev-serve-mode rather than each build
|
|
17
|
+
}
|
|
18
|
+
}
|
package/plugins/vue2.js
CHANGED
|
@@ -17,7 +17,7 @@ import TeraFyPluginBase from './base.js';
|
|
|
17
17
|
* import TerafyVue from '@iebh/tera-fy/plugins/vue2';
|
|
18
18
|
* let terafy = new TeraFy()
|
|
19
19
|
* .set('devMode', true) // Uncomment this line if you want TeraFy to be chatty
|
|
20
|
-
* .set('siteUrl', 'http://localhost:
|
|
20
|
+
* .set('siteUrl', 'http://localhost:7334/embed') // Uncomment this line if running TERA locally
|
|
21
21
|
* .use(TerafyVue, { // Add the Vue plugin
|
|
22
22
|
* vue: window.Vue, // Assumes Vue is available on the window object
|
|
23
23
|
* })
|
package/plugins/vue3.js
CHANGED
|
@@ -14,7 +14,7 @@ import {reactive, watch} from 'vue';
|
|
|
14
14
|
* import TerafyVue from '@iebh/tera-fy/plugins/vue';
|
|
15
15
|
* let terafy = new TeraFy()
|
|
16
16
|
* .set('devMode', import.meta.env.DEV)
|
|
17
|
-
* .set('siteUrl', 'http://localhost:
|
|
17
|
+
* .set('siteUrl', 'http://localhost:7334/embed') // Uncomment this line if running TERA locally
|
|
18
18
|
* .use(TerafyVue) // Add the Vue plugin
|
|
19
19
|
*
|
|
20
20
|
* terafy.init(); // Initialize everything
|