@iebh/tera-fy 1.7.3 → 1.8.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.
@@ -84,6 +84,7 @@ export default class TeraFy {
84
84
  methods = [
85
85
  // Messages
86
86
  'handshake',
87
+ 'setServerVerbosity',
87
88
 
88
89
  // Session
89
90
  'getUser',
@@ -130,6 +131,7 @@ export default class TeraFy {
130
131
 
131
132
  // UI
132
133
  'uiAlert',
134
+ 'uiProgress',
133
135
  'uiSplat',
134
136
  'uiWindow',
135
137
  ];
@@ -186,6 +188,11 @@ export default class TeraFy {
186
188
  if (this.settings.mode == 'parent') {
187
189
  window.parent.postMessage(payload, this.settings.restrictOrigin);
188
190
  } else if (this.settings.mode == 'child') {
191
+ console.log('SEND DOWN', {
192
+ target: this.dom.iframe.contentWindow,
193
+ payload,
194
+ restrictOrigin: this.settings.restrictOrigin,
195
+ });
189
196
  this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
190
197
  } else if (this.settings.mode == 'popup') {
191
198
  this.dom.popup.postMessage(payload, this.settings.restrictOrigin);
@@ -335,34 +342,43 @@ export default class TeraFy {
335
342
 
336
343
  const context = this;
337
344
  return this.init.promise = Promise.resolve()
338
- .then(()=> this.debug('INFO', 4, '[0/5] Init using server', this.settings.siteUrl))
345
+ .then(()=> this.debug('INFO', 4, '[0/6] Init using server', this.settings.siteUrl))
339
346
  .then(()=> this.detectMode())
340
347
  .then(mode => {
341
- this.debug('INFO', 4, '[1/5] Setting client mode to', mode);
348
+ this.debug('INFO', 4, '[1/6] Setting client mode to', mode);
342
349
  this.settings.mode = mode;
343
350
  })
344
- .then(()=> this.debug('INFO', 4, '[2/5] Injecting comms + styles + methods'))
351
+ .then(()=> this.debug('INFO', 4, '[2/6] Injecting comms + styles + methods'))
345
352
  .then(()=> Promise.all([
346
353
  // Init core functions async
347
354
  this.injectComms(),
348
355
  this.injectStylesheet(),
349
356
  this.injectMethods(),
350
357
  ]))
351
- .then(()=> this.debug('INFO', 4, '[3/5] Set server mode'))
358
+ .then(()=> {
359
+ if (this.settings.verbosity <=1) {
360
+ this.debug('INFO', 4, '[3/6] Skip - Server verbosity is already at 1');
361
+ return;
362
+ } else {
363
+ this.debug('INFO', 4, `[3/6] Set server verbosity to ${this.settings.verbosity}`);
364
+ return this.rpc('setServerVerbsity', this.settings.verbosity);
365
+ }
366
+ })
367
+ .then(()=> this.debug('INFO', 4, `[4/6] Set server mode to "${this.settings.mode}"`))
352
368
  .then(()=> this.rpc('setServerMode', // Tell server what mode its in
353
369
  this.settings.mode == 'child' ? 'embedded'
354
370
  : this.settings.mode == 'parent' ? 'frame'
355
371
  : this.settings.mode == 'popup' ? 'popup'
356
372
  : (()=> { throw(`Unknown server mode "${this.settings.mode}"`) })()
357
373
  ))
358
- .then(()=> this.debug('INFO', 4, '[4/5] Run client plugins'))
374
+ .then(()=> this.debug('INFO', 4, '[5/6] Run client plugins'))
359
375
  .then(()=> Promise.all( // Init all plugins (with this outer module as the context)
360
376
  this.plugins.map(plugin =>
361
377
  plugin.init.call(context, this.settings)
362
378
  )
363
379
  ))
364
- .then(()=> this.debug('INFO', 4, '[5/5] Init complete'))
365
- .catch(e => this.debug('WARN', 0, 'Init caught error', e))
380
+ .then(()=> this.debug('INFO', 4, '[6/6] Init complete'))
381
+ .catch(e => this.debug('WARN', 0, 'Init process fault', e))
366
382
  }
367
383
 
368
384
 
@@ -573,11 +589,20 @@ export default class TeraFy {
573
589
  * @param {String|Object} key Either a single setting key to set or an object to merge
574
590
  * @param {*} value The value to set if `key` is a string
575
591
  *
592
+ * @param {Object} [options] Additional options to mutate behaviour
593
+ * @param {Boolean} [options.ignoreNullish=true] If falsy, this forces the setting of undefined or null values rather than ignoring them when specifying values by string
594
+ *
576
595
  * @returns {TeraFy} This chainable terafy instance
577
596
  */
578
- set(key, value) {
597
+ set(key, value, options) {
598
+ let settings = {
599
+ ignoreNullish: true,
600
+ ...options,
601
+ };
602
+
579
603
  if (typeof key == 'string') {
580
- this.settings[key] = value;
604
+ if (!settings.ignoreNullish || (value !== null && value !== undefined))
605
+ this.settings[key] = value;
581
606
  } else {
582
607
  Object.assign(this.settings, key);
583
608
  }
@@ -592,12 +617,13 @@ export default class TeraFy {
592
617
  * @see set()
593
618
  * @param {String|Object} key Either a single setting key to set or an object to merge
594
619
  * @param {*} value The value to set if `key` is a string
620
+ * @param {Object} [options] Additional options to mutate behaviour
595
621
  *
596
622
  * @returns {TeraFy} This chainable terafy instance
597
623
  */
598
- setIfDev(key, value) {
624
+ setIfDev(key, value, options) {
599
625
  if (!this.settings.devMode || value === undefined) return this;
600
- return this.set(key, value);
626
+ return this.set(key, value, options);
601
627
  }
602
628
 
603
629
 
@@ -645,15 +671,27 @@ export default class TeraFy {
645
671
 
646
672
  /**
647
673
  * Set or toggle devMode
674
+ * This function also accepts meta values:
675
+ *
676
+ * 'toggle' - Set dev mode to whatever the opposing value of the current mode
677
+ * 'proxy' - Optimize for using a loopback proxy
648
678
  *
649
- * @param {String|Boolean} [devModeEnabled='toggle'] Optional boolean to force dev mode
679
+ * @param {'toggle'|'proxy'|Boolean} [devModeEnabled='toggle'] Optional boolean to force dev mode or specify other behaviour
650
680
  *
651
681
  * @returns {TeraFy} This chainable terafy instance
652
682
  */
653
683
  toggleDevMode(devModeEnabled = 'toggle') {
654
- this.settings.devMode = devModeEnabled === 'toggle'
655
- ? !this.settings.devMode
656
- : devModeEnabled;
684
+ if (devModeEnabled === 'toggle') {
685
+ this.settings.devMode = !this.settings.devMode;
686
+ } else if (devModeEnabled === 'proxy') {
687
+ Object.assign(this.settings, {
688
+ devMode: true,
689
+ siteUrl: 'http://localhost:7334/embed',
690
+ mode: 'child',
691
+ });
692
+ } else {
693
+ this.settings.devMode = !! devModeEnabled;
694
+ }
657
695
 
658
696
  if (this.dom.el) // Have we actually set up yet?
659
697
  this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
@@ -684,6 +722,14 @@ export default class TeraFy {
684
722
  */
685
723
 
686
724
 
725
+ /**
726
+ * RPC callback to set the server verbostiy level
727
+ *
728
+ * @function setServerVerbosity
729
+ * @param {Number} verbosity The desired server verbosity level
730
+ */
731
+
732
+
687
733
  /**
688
734
  * User / active session within TERA
689
735
  *
@@ -931,6 +977,7 @@ export default class TeraFy {
931
977
  *
932
978
  * @param {Object} options Options which mutate behaviour
933
979
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
980
+ * @param {Boolean} [options.lazy=true] If true, use the fastest method to retrieve the file list such as the cache. If false, force a refresh each time
934
981
  * @param {Boolean} [options.meta=true] Pull meta information for each file entity
935
982
  *
936
983
  * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
@@ -1109,14 +1156,20 @@ export default class TeraFy {
1109
1156
 
1110
1157
 
1111
1158
  /**
1112
- * Display HTML content full-screen within TERA
1113
- * This function is ideally called within a requestFocus() wrapper
1114
- *
1115
- * @function uiSplat
1116
- * @param {DOMElement|String|false} content Either a prepared DOM element or string to compile, set to falsy to remove existing content
1117
- *
1118
- * @param {Object} [options] Additional options to mutate behaviour
1119
- * @param {Boolean|String} [options.logo=false] Add a logo to the output, if boolean true the Tera-tools logo is used otherwise specify a path or URL
1159
+ * Display, update or dispose of windows for long running tasks
1160
+ * All options are cumulative - i.e. they are merged with other options previously provided
1161
+ *
1162
+ * @function uiProgress
1163
+ * @param {Object|Boolean} [options] Additional options to mutate behaviour, if boolean false `{close: true}` is assumed
1164
+ * @param {String} [options.title='TERA'] Window title, can only be set on the initial call
1165
+ * @param {String} [options.body=''] Window body text, can only be set on the initial call
1166
+ * @param {Boolean} [options.bodyHtml=false] Treat body text as HTML
1167
+ * @param {Boolean} [options.close=false] Close the existing dialog, if true the dialog is disposed and options reset
1168
+ * @param {String} [options.text] The text of the task being conducted
1169
+ * @param {Number} [options.progress] The current progress of the task being conducted, this is assumed to be a value less than `maxProgress`
1170
+ * @param {Number} [options.maxProgress] The maximum value that the progress can be
1171
+ *
1172
+ * @returns {Promise} A promise which resolves when the dialog has been updated
1120
1173
  */
1121
1174
 
1122
1175
 
@@ -1135,5 +1188,17 @@ export default class TeraFy {
1135
1188
  * @returns {WindowProxy} The opened window object (if `noopener` is not set in permissions)
1136
1189
  */
1137
1190
 
1191
+
1192
+ /**
1193
+ * Display HTML content full-screen within TERA
1194
+ * This function is ideally called within a requestFocus() wrapper
1195
+ *
1196
+ * @function uiSplat
1197
+ * @param {DOMElement|String|false} content Either a prepared DOM element or string to compile, set to falsy to remove existing content
1198
+ *
1199
+ * @param {Object} [options] Additional options to mutate behaviour
1200
+ * @param {Boolean|String} [options.logo=false] Add a logo to the output, if boolean true the Tera-tools logo is used otherwise specify a path or URL
1201
+ */
1202
+
1138
1203
  // }}}
1139
1204
  }
@@ -0,0 +1,81 @@
1
+ import Proxy from 'http-proxy';
2
+
3
+ export class TeraProxy {
4
+ /**
5
+ * Setup a local loopback proxy for TERA-tools.com
6
+ *
7
+ * @param {Object} [options] Additional options to mutate behaviour
8
+ * @param {Boolean} [options.force=false] Restart the server even if its apparently running
9
+ * @param {Boolean} [options.autoStart=true] Automatically start the proxy without calling `Plugin.start()`
10
+ * @param {String} [options.host='0.0.0.0'] Host IP to listen on
11
+ * @param {Number} [options.port=7334] Host port to listen on
12
+ * @param {String} [options.targetProtocol='https'] Target protocol to forward to
13
+ * @param {String} [options.targetHost='tera-tools.com'] Target host to forward to
14
+ * @param {Number} [options.targetPort=443] Target port to forward to
15
+ *
16
+ * @returns {VitePlugin}
17
+ */
18
+ settings = {
19
+ autoStart: true,
20
+ force: false,
21
+ host: '0.0.0.0',
22
+ port: 7334,
23
+ targetProtocol: 'https',
24
+ targetHost: 'tera-tools.com',
25
+ targetPort: 443,
26
+ }
27
+
28
+
29
+ /**
30
+ * Eventual proxy server when the plugin has booted
31
+ * @type {ProxyServer}
32
+ */
33
+ proxyServer;
34
+
35
+
36
+ /**
37
+ * Boot the proxy
38
+ */
39
+ start() {
40
+ if (this.proxyServer && !this.settings.force) return; // Server already running skip
41
+
42
+ // Create proxy pass-thru
43
+ this.proxyServer = Proxy.createProxyServer({
44
+ changeOrigin: true,
45
+ target: {
46
+ protocol: this.settings.targetProtocol + ':',
47
+ host: this.settings.targetHost,
48
+ port: this.settings.targetPort,
49
+ },
50
+ });
51
+
52
+ this.proxyServer.listen(this.settings.port, ()=> {
53
+ console.log('Routing TERA traffic from', `http://${this.settings.host}:${this.settings.port}`, '→', `${this.settings.targetProtocol}://${this.settings.targetHost}:${this.settings.targetPort}`);
54
+ });
55
+ }
56
+
57
+
58
+ /**
59
+ * Stop the proxy server
60
+ * @returns {Promise} A promise which will resolve when the close operation has completed
61
+ */
62
+ async stop() {
63
+ if (this.proxyServer) await new Promise(resolve => this.proxyServer.close(()=> resolve()));
64
+ }
65
+
66
+
67
+ constructor(options) {
68
+ if (options) Object.assign(this.settings, options);
69
+
70
+ // Auto start?
71
+ if (options?.autoStart ?? true) this.start();
72
+ }
73
+ }
74
+
75
+
76
+ /**
77
+ * Utility function to return a new TeraProxy instance
78
+ */
79
+ export default function(options) {
80
+ return new TeraProxy(options);
81
+ }
@@ -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() {{{
@@ -968,6 +979,7 @@ export default class TeraFyServer {
968
979
  *
969
980
  * @param {Object} options Options which mutate behaviour
970
981
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
982
+ * @param {Boolean} [options.lazy=true] If true, use the fastest method to retrieve the file list such as the cache. If false, force a refresh each time
971
983
  * @param {Boolean} [options.meta=true] Pull meta information for each file entity
972
984
  *
973
985
  * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
@@ -975,6 +987,7 @@ export default class TeraFyServer {
975
987
  getProjectFiles(options) {
976
988
  let settings = {
977
989
  autoRequire: true,
990
+ lazy: true,
978
991
  meta: true,
979
992
  ...options,
980
993
  };
@@ -982,9 +995,14 @@ export default class TeraFyServer {
982
995
  return Promise.resolve()
983
996
  .then(()=> app.service('$projects').promise())
984
997
  .then(()=> settings.autoRequire && this.requireProject())
985
- .then(project => app.service('$supabase').fileList(`/projects/${project.id}`, {
986
- meta: settings.meta,
987
- }))
998
+ .then(()=>
999
+ app.service('$projects').activeFiles.length == 0 // If we have no files in the cache
1000
+ || !settings.lazy // OR lazy/cache use is disabled
1001
+ ? app.service('$projects').refreshFiles({ // Go refresh files first
1002
+ lazy: false,
1003
+ })
1004
+ : app.service('$projects').activeFiles // Otherwise use file cache
1005
+ )
988
1006
  }
989
1007
 
990
1008
 
@@ -1338,7 +1356,7 @@ export default class TeraFyServer {
1338
1356
  }
1339
1357
  // }}}
1340
1358
 
1341
- // UI - uiAlert() {{{
1359
+ // UI - uiAlert(), uiProgress(), uiWindow(), uiSplat() {{{
1342
1360
  /**
1343
1361
  * Display simple text within TERA
1344
1362
  *
@@ -1373,6 +1391,63 @@ export default class TeraFyServer {
1373
1391
  }
1374
1392
 
1375
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
+
1376
1451
  /**
1377
1452
  * Open a popup window containing a new site
1378
1453
  *
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "1.7.3",
3
+ "version": "1.8.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-vue2:es2019": "esbuild --platform=browser --format=esm --target=es2019 --bundle plugins/vue2.js --outfile=dist/plugin.vue2.es2019.js --minify",
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/*"
@@ -69,6 +70,7 @@
69
70
  },
70
71
  "dependencies": {
71
72
  "filesize": "^10.1.1",
73
+ "http-proxy": "^1.18.1",
72
74
  "just-diff": "^6.0.2",
73
75
  "lodash-es": "^4.17.21",
74
76
  "mitt": "^3.0.1",
@@ -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:8000/embed') // Uncomment this line if running TERA locally
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:8000/embed') // Uncomment this line if running TERA locally
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