@iebh/tera-fy 2.3.0 → 2.3.2
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/.vscode/settings.json +5 -0
- package/CHANGELOG.md +22 -0
- package/api.md +35 -36
- package/dist/lib/projectFile.d.ts +3 -3
- package/dist/lib/projectFile.js +4 -3
- package/dist/lib/projectFile.js.map +1 -1
- package/dist/lib/syncro/entities.js +11 -10
- package/dist/lib/syncro/entities.js.map +1 -1
- package/dist/lib/syncro/keyed.d.ts +2 -2
- package/dist/lib/syncro/keyed.js +23 -23
- package/dist/lib/syncro/keyed.js.map +1 -1
- package/dist/lib/syncro/syncro.d.ts +15 -13
- package/dist/lib/syncro/syncro.js +84 -59
- package/dist/lib/syncro/syncro.js.map +1 -1
- package/dist/lib/terafy.bootstrapper.d.ts +2 -2
- package/dist/lib/terafy.bootstrapper.js +15 -16
- package/dist/lib/terafy.bootstrapper.js.map +1 -1
- package/dist/lib/terafy.client.d.ts +24 -25
- package/dist/lib/terafy.client.js +50 -48
- package/dist/lib/terafy.client.js.map +1 -1
- package/dist/lib/terafy.proxy.js +4 -2
- package/dist/lib/terafy.proxy.js.map +1 -1
- package/dist/lib/terafy.server.d.ts +22 -8
- package/dist/lib/terafy.server.js +54 -58
- package/dist/lib/terafy.server.js.map +1 -1
- package/dist/plugin.vue2.es2019.js +210 -1224
- package/dist/plugins/base.d.ts +2 -2
- package/dist/plugins/base.js +1 -0
- package/dist/plugins/base.js.map +1 -1
- package/dist/plugins/firebase.d.ts +4 -4
- package/dist/plugins/firebase.js +7 -7
- package/dist/plugins/firebase.js.map +1 -1
- package/dist/plugins/vue2.d.ts +1 -1
- package/dist/plugins/vue2.js +6 -5
- package/dist/plugins/vue2.js.map +1 -1
- package/dist/plugins/vue3.js +6 -5
- package/dist/plugins/vue3.js.map +1 -1
- package/dist/terafy.bootstrapper.es2019.js +2 -2
- package/dist/terafy.bootstrapper.js +2 -2
- package/dist/terafy.es2019.js +2 -2
- package/dist/terafy.js +2 -2
- package/dist/utils/mixin.js +1 -1
- package/dist/utils/mixin.js.map +1 -1
- package/dist/utils/pDefer.d.ts +5 -1
- package/dist/utils/pDefer.js +6 -1
- package/dist/utils/pDefer.js.map +1 -1
- package/dist/utils/pathTools.d.ts +1 -1
- package/dist/utils/pathTools.js +2 -2
- package/dist/utils/pathTools.js.map +1 -1
- package/eslint.config.js +21 -7
- package/lib/projectFile.ts +5 -4
- package/lib/syncro/entities.ts +11 -10
- package/lib/syncro/keyed.ts +24 -24
- package/lib/syncro/syncro.ts +97 -60
- package/lib/terafy.bootstrapper.ts +15 -16
- package/lib/terafy.client.ts +62 -62
- package/lib/terafy.proxy.ts +8 -5
- package/lib/terafy.server.ts +75 -64
- package/package.json +5 -3
- package/plugins/base.ts +3 -2
- package/plugins/firebase.ts +12 -11
- package/plugins/vue2.ts +7 -6
- package/plugins/vue3.ts +6 -5
- package/utils/mixin.ts +1 -1
- package/utils/pDefer.ts +7 -2
- package/utils/pathTools.ts +3 -3
package/lib/terafy.client.ts
CHANGED
|
@@ -3,9 +3,6 @@ import Mitt from 'mitt';
|
|
|
3
3
|
import {nanoid} from 'nanoid';
|
|
4
4
|
import ProjectFile from './projectFile.js';
|
|
5
5
|
|
|
6
|
-
/* globals window, document */
|
|
7
|
-
|
|
8
|
-
|
|
9
6
|
/**
|
|
10
7
|
* Main Tera-Fy Client (class singleton) to be used in a frontend browser
|
|
11
8
|
*
|
|
@@ -17,7 +14,7 @@ export default class TeraFy {
|
|
|
17
14
|
*
|
|
18
15
|
* @type {Object}
|
|
19
16
|
* @property {String} session Unique session signature for this instance of TeraFy, used to sign server messages, if falsy `getEntropicString(16)` is used to populate
|
|
20
|
-
* @property {Boolean} devMode Operate in Dev-Mode - i.e. force outer refresh when encountering an existing TeraFy instance + be more
|
|
17
|
+
* @property {Boolean} devMode Operate in Dev-Mode - i.e. force outer refresh when encountering an existing TeraFy instance + be more tolerant of weird iframe origins
|
|
21
18
|
* @property {Number} verbosity Verbosity level, the higher the more chatty TeraFY will be. Set to zero to disable all `debug()` call output
|
|
22
19
|
* @property {'detect'|'parent'|'child'|'popup'} mode How to communicate with TERA. 'parent' assumes that the parent of the current document is TERA, 'child' spawns an iFrame and uses TERA there, 'detect' tries parent and switches to `modeFallback` if communication fails
|
|
23
20
|
* @property {String} modeFallback Method to use when all method detection fails
|
|
@@ -26,7 +23,7 @@ export default class TeraFy {
|
|
|
26
23
|
* @property {String} siteUrl The TERA URL to connect to
|
|
27
24
|
* @property {String} restrictOrigin URL to restrict communications to
|
|
28
25
|
* @property {Array<String>} List of sandbox allowables for the embedded if in embed mode
|
|
29
|
-
* @property {Number} handshakeInterval Interval in milliseconds when
|
|
26
|
+
* @property {Number} handshakeInterval Interval in milliseconds when scanning for a handshake
|
|
30
27
|
* @property {Number} handshakeTimeout Interval in milliseconds for when to give up trying to handshake
|
|
31
28
|
* @property {Array<String|Array<String>>} [debugPaths] List of paths (in either dotted or array notation) to enter debugging mode if a change is detected in dev mode e.g. `{debugPaths: ['foo.bar.baz']}`. This really slows down state writes so should only be used for debugging
|
|
32
29
|
*/
|
|
@@ -45,6 +42,7 @@ export default class TeraFy {
|
|
|
45
42
|
config.siteUrl = 'https://dev.tera-tools.com/embed'; // Repoint URL to dev site
|
|
46
43
|
}
|
|
47
44
|
},
|
|
45
|
+
// eslint-disable-next-line no-unused-vars
|
|
48
46
|
} as { [key: string]: (config: any) => void },
|
|
49
47
|
siteUrl: 'https://tera-tools.com/embed',
|
|
50
48
|
restrictOrigin: '*',
|
|
@@ -60,7 +58,7 @@ export default class TeraFy {
|
|
|
60
58
|
'allow-scripts',
|
|
61
59
|
'allow-top-navigation',
|
|
62
60
|
],
|
|
63
|
-
handshakeInterval:
|
|
61
|
+
handshakeInterval: 1000, // ~1s
|
|
64
62
|
handshakeTimeout: 10_000, // ~10s
|
|
65
63
|
debugPaths: null as Array<string | Array<string>> | null, // Transformed into a Array<String> (in Lodash dotted notation) on init()
|
|
66
64
|
};
|
|
@@ -70,7 +68,7 @@ export default class TeraFy {
|
|
|
70
68
|
* Event emitter subscription endpoint
|
|
71
69
|
* @type {Mitt}
|
|
72
70
|
*/
|
|
73
|
-
// @ts-
|
|
71
|
+
// @ts-expect-error Because mitt is exported as cjs typescript has trouble resolving default export
|
|
74
72
|
events = Mitt();
|
|
75
73
|
|
|
76
74
|
|
|
@@ -176,7 +174,7 @@ export default class TeraFy {
|
|
|
176
174
|
|
|
177
175
|
/**
|
|
178
176
|
* Active namespaces we are subscribed to
|
|
179
|
-
* Each key is the namespace name with the value as the local reactive \ observer \ object
|
|
177
|
+
* Each key is the namespace name with the value as the local reactive \ observer \ object equivalent
|
|
180
178
|
* The key string is always of the form `${ENTITY}::${ID}` e.g. `projects:1234`
|
|
181
179
|
*
|
|
182
180
|
* @type {Object<Object>}
|
|
@@ -193,7 +191,7 @@ export default class TeraFy {
|
|
|
193
191
|
* @returns {Promise<*>} A promise which resolves when the operation has completed with the remote reply
|
|
194
192
|
*/
|
|
195
193
|
send(message: any) {
|
|
196
|
-
|
|
194
|
+
const id = nanoid();
|
|
197
195
|
|
|
198
196
|
this.acceptPostboxes[id] = {}; // Stub for the deferred promise
|
|
199
197
|
this.acceptPostboxes[id].promise = new Promise((resolve, reject) => {
|
|
@@ -234,6 +232,7 @@ export default class TeraFy {
|
|
|
234
232
|
} else if (this.settings.mode == 'detect') {
|
|
235
233
|
throw new Error('Call init() or detectMode() before trying to send data to determine the mode');
|
|
236
234
|
} else {
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
237
236
|
throw new Error(`Unknown TERA communication mode "${this.settings.mode}"`);
|
|
238
237
|
}
|
|
239
238
|
} catch (e) {
|
|
@@ -271,9 +270,9 @@ export default class TeraFy {
|
|
|
271
270
|
acceptMessage(rawMessage: any) {
|
|
272
271
|
if (rawMessage.origin == window.location.origin) return Promise.resolve(); // Message came from us
|
|
273
272
|
|
|
274
|
-
|
|
273
|
+
const message = rawMessage.data;
|
|
275
274
|
if (!message.TERA || !message.id) return Promise.resolve(); // Ignore non-TERA signed messages
|
|
276
|
-
this.debug('INFO', 3, '
|
|
275
|
+
this.debug('INFO', 3, 'Received message', message);
|
|
277
276
|
|
|
278
277
|
if (message?.action == 'response' && this.acceptPostboxes[message.id]) { // Postbox waiting for reply
|
|
279
278
|
if (message.isError === true) {
|
|
@@ -345,14 +344,14 @@ export default class TeraFy {
|
|
|
345
344
|
|
|
346
345
|
/**
|
|
347
346
|
* @interface
|
|
348
|
-
* Actual namespace mounting function designed to be
|
|
347
|
+
* Actual namespace mounting function designed to be overridden by plugins
|
|
349
348
|
*
|
|
350
349
|
* @param {String} name The alias of the namespace, this should be alphanumeric + hyphens + underscores
|
|
351
350
|
*
|
|
352
351
|
* @returns {Promise} A promise which resolves when the mount operation has completed
|
|
353
352
|
*/
|
|
354
|
-
_mountNamespace(name: any) {
|
|
355
|
-
console.warn(
|
|
353
|
+
_mountNamespace(name: any) {
|
|
354
|
+
console.warn(`teraFy._mountNamespace() for ${name} has not been overridden by a TERA-fy plugin, load one to add this functionality for your preferred framework`);
|
|
356
355
|
throw new Error('teraFy._mountNamespace() is not supported');
|
|
357
356
|
}
|
|
358
357
|
|
|
@@ -375,14 +374,14 @@ export default class TeraFy {
|
|
|
375
374
|
|
|
376
375
|
/**
|
|
377
376
|
* @interface
|
|
378
|
-
* Actual namespace unmounting function designed to be
|
|
377
|
+
* Actual namespace unmounting function designed to be overridden by plugins
|
|
379
378
|
*
|
|
380
379
|
* @param {String} name The name of the namespace to unmount
|
|
381
380
|
*
|
|
382
381
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
383
382
|
*/
|
|
384
|
-
_unmountNamespace(name: any) {
|
|
385
|
-
console.warn(
|
|
383
|
+
_unmountNamespace(name: any) {
|
|
384
|
+
console.warn(`teraFy.unbindNamespace() for ${name} has not been overridden by a TERA-fy plugin, load one to add this functionality for your preferred framework`);
|
|
386
385
|
}
|
|
387
386
|
// }}}
|
|
388
387
|
|
|
@@ -400,18 +399,20 @@ export default class TeraFy {
|
|
|
400
399
|
private initPromise: Promise<TeraFy> | null = null;
|
|
401
400
|
|
|
402
401
|
/**
|
|
403
|
-
*
|
|
404
|
-
* This function can only be called once and will return the existing init() worker Promise if its called
|
|
402
|
+
* Initialize the TERA client singleton
|
|
403
|
+
* This function can only be called once and will return the existing init() worker Promise if its called against
|
|
405
404
|
*
|
|
406
405
|
* @param {Object} [options] Additional options to merge into `settings` via `set`
|
|
407
|
-
* @returns {Promise<TeraFy>} An eventual promise which will
|
|
406
|
+
* @returns {Promise<TeraFy>} An eventual promise which will resolve with this terafy instance
|
|
408
407
|
*/
|
|
409
408
|
init(options?: any): Promise<TeraFy> {
|
|
410
409
|
if (options) this.set(options);
|
|
411
|
-
if (this.initPromise) return this.initPromise; //
|
|
410
|
+
if (this.initPromise) return this.initPromise; // Already been called - return init promise
|
|
412
411
|
|
|
412
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
413
413
|
window.addEventListener('message', this.acceptMessage.bind(this));
|
|
414
414
|
|
|
415
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
415
416
|
const context = this;
|
|
416
417
|
this.initPromise = Promise.resolve()
|
|
417
418
|
.then(()=> this.settings.session ||= 'tfy-' + this.getEntropicString(16))
|
|
@@ -461,7 +462,7 @@ export default class TeraFy {
|
|
|
461
462
|
this.settings.mode == 'child' ? 'embedded'
|
|
462
463
|
: this.settings.mode == 'parent' ? 'frame'
|
|
463
464
|
: this.settings.mode == 'popup' ? 'popup'
|
|
464
|
-
: (()=> { throw(`Unknown server mode "${this.settings.mode}"`) })()
|
|
465
|
+
: (()=> { throw new Error(`Unknown server mode "${this.settings.mode}"`) })()
|
|
465
466
|
))
|
|
466
467
|
.then(()=> this.debug('INFO', 4, '[5/6] Run client plugins'))
|
|
467
468
|
.then(()=> Promise.all( // Init all plugins (with this outer module as the context)
|
|
@@ -498,23 +499,23 @@ export default class TeraFy {
|
|
|
498
499
|
return Promise.resolve()
|
|
499
500
|
.then(()=> this.settings.mode = 'parent') // Switch to parent mode...
|
|
500
501
|
.then(()=> new Promise<void>((resolve, reject) => { // And try messaging with a timeout
|
|
501
|
-
|
|
502
|
+
const timeoutHandle = setTimeout(()=> reject('TIMEOUT'), this.settings.modeTimeout);
|
|
502
503
|
|
|
503
504
|
this.rpc('handshake')
|
|
504
505
|
.then(()=> clearTimeout(timeoutHandle))
|
|
505
506
|
.then(()=> resolve())
|
|
506
507
|
.catch(reject); // Propagate RPC errors
|
|
507
508
|
}))
|
|
508
|
-
.then(()=> 'parent' as
|
|
509
|
+
.then(()=> 'parent' as const)
|
|
509
510
|
.catch(()=> this.settings.modeFallback as 'child' | 'popup')
|
|
510
511
|
}
|
|
511
512
|
}
|
|
512
513
|
|
|
513
514
|
|
|
514
515
|
/**
|
|
515
|
-
* Find an existing active TERA server OR
|
|
516
|
+
* Find an existing active TERA server OR initialize one
|
|
516
517
|
*
|
|
517
|
-
* @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or
|
|
518
|
+
* @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or initialized a child
|
|
518
519
|
*/
|
|
519
520
|
injectComms() {
|
|
520
521
|
switch (this.settings.mode) {
|
|
@@ -526,7 +527,7 @@ export default class TeraFy {
|
|
|
526
527
|
this.dom.el.id = 'tera-fy';
|
|
527
528
|
this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
|
|
528
529
|
this.dom.el.classList.add('minimized');
|
|
529
|
-
document.body.append(this.dom.el
|
|
530
|
+
document.body.append(this.dom.el);
|
|
530
531
|
|
|
531
532
|
this.dom.el.addEventListener('click', ()=> this.dom.el!.classList.toggle('minimized'));
|
|
532
533
|
|
|
@@ -535,13 +536,13 @@ export default class TeraFy {
|
|
|
535
536
|
// Queue up event chain when document loads
|
|
536
537
|
this.dom.iframe.setAttribute('sandbox', this.settings.frameSandbox.join(' '));
|
|
537
538
|
this.dom.iframe.addEventListener('load', ()=> {
|
|
538
|
-
this.debug('INFO', 3, '
|
|
539
|
+
this.debug('INFO', 3, 'Embedded iframe ready');
|
|
539
540
|
resolve();
|
|
540
541
|
});
|
|
541
542
|
|
|
542
543
|
// Start document load sequence + append to DOM
|
|
543
544
|
this.dom.iframe.src = this.settings.siteUrl;
|
|
544
|
-
this.dom.el.append(this.dom.iframe
|
|
545
|
+
this.dom.el.append(this.dom.iframe);
|
|
545
546
|
}))
|
|
546
547
|
.then(()=> this.handshakeLoop())
|
|
547
548
|
|
|
@@ -566,13 +567,13 @@ export default class TeraFy {
|
|
|
566
567
|
* Keep trying to handshake until the target responds
|
|
567
568
|
*
|
|
568
569
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
569
|
-
* @property {Number} [handshakeInterval] Interval in milliseconds when
|
|
570
|
+
* @property {Number} [handshakeInterval] Interval in milliseconds when scanning for a handshake, defaults to global setting
|
|
570
571
|
* @property {Number} [handshakeTimeout] Interval in milliseconds for when to give up trying to handshake, defaults to global setting
|
|
571
572
|
*
|
|
572
573
|
* @returns {Promise} A promise which will either resolve when the handshake is successful OR fail with 'TIMEOUT'
|
|
573
574
|
*/
|
|
574
575
|
handshakeLoop(options?: any) {
|
|
575
|
-
|
|
576
|
+
const settings = {
|
|
576
577
|
handshakeInterval: this.settings.handshakeInterval,
|
|
577
578
|
handshakeTimeout: this.settings.handshakeTimeout,
|
|
578
579
|
...options,
|
|
@@ -583,7 +584,7 @@ export default class TeraFy {
|
|
|
583
584
|
let handshakeCount = 0;
|
|
584
585
|
let handshakeTimer: any;
|
|
585
586
|
|
|
586
|
-
|
|
587
|
+
const handshakeTimeout = setTimeout(()=> {
|
|
587
588
|
clearTimeout(handshakeTimer);
|
|
588
589
|
reject('TIMEOUT');
|
|
589
590
|
}, settings.handshakeTimeout);
|
|
@@ -610,7 +611,7 @@ export default class TeraFy {
|
|
|
610
611
|
/**
|
|
611
612
|
* Inject a local stylesheet to handle TERA server functionality
|
|
612
613
|
*
|
|
613
|
-
* @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or
|
|
614
|
+
* @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or initialized a child
|
|
614
615
|
*/
|
|
615
616
|
injectStylesheet() {
|
|
616
617
|
switch (this.settings.mode) {
|
|
@@ -729,7 +730,7 @@ export default class TeraFy {
|
|
|
729
730
|
'}',
|
|
730
731
|
// }}}
|
|
731
732
|
].join('\n');
|
|
732
|
-
document.head.
|
|
733
|
+
document.head.append(this.dom.stylesheet);
|
|
733
734
|
break;
|
|
734
735
|
case 'parent':
|
|
735
736
|
case 'popup':
|
|
@@ -802,7 +803,7 @@ export default class TeraFy {
|
|
|
802
803
|
* @returns {TeraFy} This chainable terafy instance
|
|
803
804
|
*/
|
|
804
805
|
set(key: string | object, value?: any, options?: { ignoreNullish?: boolean }) {
|
|
805
|
-
|
|
806
|
+
const settings = {
|
|
806
807
|
ignoreNullish: true,
|
|
807
808
|
...options,
|
|
808
809
|
};
|
|
@@ -838,16 +839,16 @@ export default class TeraFy {
|
|
|
838
839
|
* Include a TeraFy client plugin
|
|
839
840
|
*
|
|
840
841
|
* @param {Function|Object|String} source Either the JS module class, singleton object or URL to fetch it from. Eventually constructed as invoked as `(teraClient:TeraFy, options:Object)`
|
|
841
|
-
* @param {Object} [options] Additional options to mutate behaviour during construction (pass options to init() to
|
|
842
|
+
* @param {Object} [options] Additional options to mutate behaviour during construction (pass options to init() to initialize later options)
|
|
842
843
|
*
|
|
843
844
|
* @returns {TeraFy} This chainable terafy instance
|
|
844
845
|
*/
|
|
845
846
|
use(source: any, options?: any) {
|
|
846
|
-
|
|
847
|
+
const mod: any =
|
|
847
848
|
typeof source == 'function' ? new source(this, options)
|
|
848
849
|
: typeof source == 'object' ? source
|
|
849
850
|
: typeof source == 'string' ? (()=> { throw new Error('use(String) is not yet supported') })()
|
|
850
|
-
: (()=> { throw new Error('Expected use() call to be provided with a class
|
|
851
|
+
: (()=> { throw new Error('Expected use() call to be provided with a class initializer') })();
|
|
851
852
|
|
|
852
853
|
this.mixin(this, mod);
|
|
853
854
|
|
|
@@ -859,16 +860,16 @@ export default class TeraFy {
|
|
|
859
860
|
/**
|
|
860
861
|
* Internal function used by use() to merge an external declared singleton against this object
|
|
861
862
|
*
|
|
862
|
-
* @param {Object} target
|
|
863
|
-
* @param {Object} source
|
|
863
|
+
* @param {Object} target Installed class instance to extend
|
|
864
|
+
* @param {Object} source Initialized source object to extend from
|
|
864
865
|
*/
|
|
865
866
|
mixin(target: any, source: any) {
|
|
866
867
|
// Iterate through the source object upwards extracting each prototype
|
|
867
|
-
|
|
868
|
+
const prototypeStack = [];
|
|
868
869
|
let node = source;
|
|
869
870
|
do {
|
|
870
871
|
prototypeStack.unshift(node);
|
|
871
|
-
} while (node = Object.getPrototypeOf(node)); // Walk upwards until we hit null (no more inherited classes)
|
|
872
|
+
} while ((node = Object.getPrototypeOf(node))); // Walk upwards until we hit null (no more inherited classes)
|
|
872
873
|
|
|
873
874
|
// Iterate through stacks inheriting each prop into the target
|
|
874
875
|
prototypeStack.forEach(stack =>
|
|
@@ -878,17 +879,17 @@ export default class TeraFy {
|
|
|
878
879
|
&& !prop.startsWith('__') // Ignore double underscore meta properties
|
|
879
880
|
)
|
|
880
881
|
.forEach(prop => {
|
|
881
|
-
if (typeof (source
|
|
882
|
+
if (typeof (source)[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
|
|
882
883
|
Object.defineProperty(
|
|
883
884
|
target,
|
|
884
885
|
prop,
|
|
885
886
|
{
|
|
886
887
|
enumerable: false,
|
|
887
|
-
value: (source
|
|
888
|
+
value: (source)[prop].bind(target), // Rebind functions
|
|
888
889
|
},
|
|
889
890
|
);
|
|
890
891
|
} else { // Everything else, just glue onto the object
|
|
891
|
-
target[prop] = (source
|
|
892
|
+
target[prop] = (source)[prop];
|
|
892
893
|
}
|
|
893
894
|
})
|
|
894
895
|
)
|
|
@@ -923,7 +924,7 @@ export default class TeraFy {
|
|
|
923
924
|
this.settings.restrictOrigin = '*'; // Allow all upstream iframes
|
|
924
925
|
|
|
925
926
|
if (this.dom?.el) // Have we actually set up yet?
|
|
926
|
-
this.dom.el
|
|
927
|
+
this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
|
|
927
928
|
|
|
928
929
|
return this;
|
|
929
930
|
}
|
|
@@ -945,13 +946,13 @@ export default class TeraFy {
|
|
|
945
946
|
/**
|
|
946
947
|
* Generate random entropic character string in Base64
|
|
947
948
|
*
|
|
948
|
-
* @param {Number} [maxLength=32] Maximum
|
|
949
|
+
* @param {Number} [maxLength=32] Maximum length of the generated string
|
|
949
950
|
* @returns {String}
|
|
950
951
|
*/
|
|
951
952
|
getEntropicString(maxLength = 32) {
|
|
952
953
|
const array = new Uint32Array(4);
|
|
953
954
|
window.crypto.getRandomValues(array);
|
|
954
|
-
return btoa(String.
|
|
955
|
+
return btoa(String.fromCodePoint(...new Uint8Array(array.buffer)))
|
|
955
956
|
.replace(/[+/]/g, '') // Remove + and / characters
|
|
956
957
|
.slice(0, maxLength) // Trim
|
|
957
958
|
}
|
|
@@ -968,7 +969,7 @@ export default class TeraFy {
|
|
|
968
969
|
|
|
969
970
|
|
|
970
971
|
/**
|
|
971
|
-
* RPC callback to set the server
|
|
972
|
+
* RPC callback to set the server verbosity level
|
|
972
973
|
*
|
|
973
974
|
* @function setServerVerbosity
|
|
974
975
|
* @param {Number} verbosity The desired server verbosity level
|
|
@@ -990,7 +991,7 @@ export default class TeraFy {
|
|
|
990
991
|
* Fetch the current session user
|
|
991
992
|
*
|
|
992
993
|
* @function getUser
|
|
993
|
-
* @param {Boolean} [options.forceRetry=false]
|
|
994
|
+
* @param {Boolean} [options.forceRetry=false] Forcibly try to refresh the user state
|
|
994
995
|
* @param {Boolean} [options.waitPromises=true] Wait for $auth + $subscriptions to resolve before fetching the user (mainly internal use)
|
|
995
996
|
* @returns {Promise<User>} The current logged in user or null if none
|
|
996
997
|
*/
|
|
@@ -1012,7 +1013,7 @@ export default class TeraFy {
|
|
|
1012
1013
|
* @function requireUser
|
|
1013
1014
|
*
|
|
1014
1015
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
1015
|
-
* @param {Boolean} [options.forceRetry=false]
|
|
1016
|
+
* @param {Boolean} [options.forceRetry=false] Forcibly try to refresh the user state
|
|
1016
1017
|
*
|
|
1017
1018
|
* @returns {Promise<User>} The current logged in user or null if none
|
|
1018
1019
|
*/
|
|
@@ -1061,7 +1062,7 @@ export default class TeraFy {
|
|
|
1061
1062
|
|
|
1062
1063
|
/**
|
|
1063
1064
|
* Ask the user to select a project from those available - if one isn't already active
|
|
1064
|
-
* Note that this function will
|
|
1065
|
+
* Note that this function will persist in asking the user even if they try to cancel
|
|
1065
1066
|
*
|
|
1066
1067
|
* @function requireProject
|
|
1067
1068
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
@@ -1089,7 +1090,7 @@ export default class TeraFy {
|
|
|
1089
1090
|
|
|
1090
1091
|
/**
|
|
1091
1092
|
* Get a one-off snapshot of a namespace without mounting it
|
|
1092
|
-
* This can be used for simpler apps which don't have their own reactive / observer
|
|
1093
|
+
* This can be used for simpler apps which don't have their own reactive / observer equivalent
|
|
1093
1094
|
*
|
|
1094
1095
|
* @function getNamespace
|
|
1095
1096
|
* @param {String} name The alias of the namespace, this should be alphanumeric + hyphens + underscores
|
|
@@ -1100,7 +1101,7 @@ export default class TeraFy {
|
|
|
1100
1101
|
|
|
1101
1102
|
/**
|
|
1102
1103
|
* Set (or merge by default) a one-off snapshot over an existing namespace
|
|
1103
|
-
* This can be used for simpler apps which don't have their own reactive / observer
|
|
1104
|
+
* This can be used for simpler apps which don't have their own reactive / observer equivalent and just want to quickly set something
|
|
1104
1105
|
*
|
|
1105
1106
|
* @function setNamespace
|
|
1106
1107
|
* @param {String} name The name of the namespace
|
|
@@ -1197,10 +1198,9 @@ export default class TeraFy {
|
|
|
1197
1198
|
* @param {Boolean} [options.allowUpload=true] Allow uploading new files
|
|
1198
1199
|
* @param {Boolean} [options.allowRefresh=true] Allow the user to manually refresh the file list
|
|
1199
1200
|
* @param {Boolean} [options.allowDownloadZip=true] Allow the user to download a Zip of all files
|
|
1200
|
-
* @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if
|
|
1201
|
+
* @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if actioned
|
|
1201
1202
|
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
|
|
1202
1203
|
* @param {Boolean} [options.showHiddenFiles=false] Whether hidden data.json files should be shown
|
|
1203
|
-
* @param {FileFilters} [options.filter] Optional file filters
|
|
1204
1204
|
*
|
|
1205
1205
|
* @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
|
|
1206
1206
|
*/
|
|
@@ -1243,7 +1243,7 @@ export default class TeraFy {
|
|
|
1243
1243
|
*
|
|
1244
1244
|
* @function getProjectFileContents
|
|
1245
1245
|
* @param {String} [id] File ID to retrieve the contents of
|
|
1246
|
-
* @param {Object} [options]
|
|
1246
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
1247
1247
|
* @param {'blob'|'json'} [options.format='blob'] The format to retrieve the file in
|
|
1248
1248
|
*
|
|
1249
1249
|
* @returns {*} The file contents in the requested format
|
|
@@ -1382,11 +1382,11 @@ export default class TeraFy {
|
|
|
1382
1382
|
* @function selectProjectLibrary
|
|
1383
1383
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
1384
1384
|
* @param {String} [options.title="Select a citation library"] The title of the dialog to display
|
|
1385
|
-
* @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', '
|
|
1385
|
+
* @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', 'demisted'
|
|
1386
1386
|
* @param {Boolean} [options.allowUpload=true] Allow uploading new files
|
|
1387
1387
|
* @param {Boolean} [options.allowRefresh=true] Allow the user to manually refresh the file list
|
|
1388
1388
|
* @param {Boolean} [options.allowDownloadZip=true] Allow the user to download a Zip of all files
|
|
1389
|
-
* @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if
|
|
1389
|
+
* @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if actioned
|
|
1390
1390
|
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
|
|
1391
1391
|
* @param {FileFilters} [options.filters] Optional file filters, defaults to citation library selection only
|
|
1392
1392
|
* @param {...*} [options] Additional options - see `getProjectLibrary()`
|
|
@@ -1407,7 +1407,7 @@ export default class TeraFy {
|
|
|
1407
1407
|
* @param {Function} [options.filter] Optional async file filter, called each time as `(File:ProjectFile)`
|
|
1408
1408
|
* @param {Function} [options.find] Optional async final stage file filter to reduce all candidates down to one subject file
|
|
1409
1409
|
*
|
|
1410
|
-
* @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default
|
|
1410
|
+
* @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default behaviour) or a whatever format was requested
|
|
1411
1411
|
*/
|
|
1412
1412
|
|
|
1413
1413
|
|
|
@@ -1449,7 +1449,7 @@ export default class TeraFy {
|
|
|
1449
1449
|
* This is usually called by a tool nested within the tera-tools.com embed
|
|
1450
1450
|
*
|
|
1451
1451
|
* @function setPage
|
|
1452
|
-
* @param {Object|String} options Context information about the page, if this is a string, its assumed to
|
|
1452
|
+
* @param {Object|String} options Context information about the page, if this is a string, its assumed to populate `url`
|
|
1453
1453
|
* @param {String} [options.path] The URL path segment to restore on next refresh
|
|
1454
1454
|
* @param {String} [options.title] The page title associated with the path
|
|
1455
1455
|
*/
|
|
@@ -1490,7 +1490,7 @@ export default class TeraFy {
|
|
|
1490
1490
|
* Present some JSON to the user
|
|
1491
1491
|
*
|
|
1492
1492
|
* @function uiJson
|
|
1493
|
-
* @param {String|Object}
|
|
1493
|
+
* @param {String|Object} data Data to display
|
|
1494
1494
|
*
|
|
1495
1495
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
1496
1496
|
* @param {String} [options.body=""] The body text to display above the JSON
|
package/lib/terafy.proxy.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface TeraProxyOptions {
|
|
|
12
12
|
targetHost?: string;
|
|
13
13
|
targetPort?: number;
|
|
14
14
|
portConflict?: 'ignore' | 'throw';
|
|
15
|
+
// eslint-disable-next-line no-unused-vars
|
|
15
16
|
onLog?: (level: 'INFO' | 'WARN', ...msg: any[]) => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -68,7 +69,7 @@ export class TeraProxy {
|
|
|
68
69
|
}
|
|
69
70
|
// Only return void if the port is available
|
|
70
71
|
})
|
|
71
|
-
.then(() => this.proxyServer = Proxy.createProxyServer({ // Create proxy pass-
|
|
72
|
+
.then(() => this.proxyServer = Proxy.createProxyServer({ // Create proxy pass-through
|
|
72
73
|
changeOrigin: true,
|
|
73
74
|
target: {
|
|
74
75
|
protocol: this.settings.targetProtocol + ':',
|
|
@@ -78,8 +79,8 @@ export class TeraProxy {
|
|
|
78
79
|
}))
|
|
79
80
|
.then(()=> new Promise<void>((resolve, reject) => {
|
|
80
81
|
// Wrap listener in a domain so we can correctly catch EADDRINUSE
|
|
81
|
-
|
|
82
|
-
domain.on('error', (err:
|
|
82
|
+
const domain = createDomain();
|
|
83
|
+
domain.on('error', (err: Error & { code?: string }) => { // Add type to err
|
|
83
84
|
if (err.code == 'EADDRINUSE') {
|
|
84
85
|
reject('PORT-CONFLICT');
|
|
85
86
|
} else {
|
|
@@ -91,7 +92,7 @@ export class TeraProxy {
|
|
|
91
92
|
if (!this.proxyServer) return reject(new Error('Proxy server not initialized')); // Guard against undefined proxyServer
|
|
92
93
|
this.proxyServer.listen(this.settings.port, this.settings.host)
|
|
93
94
|
// Handle errors on the proxy server itself, although domain should catch listen errors
|
|
94
|
-
this.proxyServer.on('error', (err:
|
|
95
|
+
this.proxyServer.on('error', (err: Error & { code?: string }) => {
|
|
95
96
|
if (err.code == 'EADDRINUSE') {
|
|
96
97
|
reject('PORT-CONFLICT');
|
|
97
98
|
} else {
|
|
@@ -129,7 +130,9 @@ export class TeraProxy {
|
|
|
129
130
|
if (options) Object.assign(this.settings, options);
|
|
130
131
|
|
|
131
132
|
// Auto start?
|
|
132
|
-
if (this.settings.autoStart) this.start()
|
|
133
|
+
if (this.settings.autoStart) this.start().catch(err => { // Use resolved settings.autoStart
|
|
134
|
+
this.settings.onLog('WARN', 'Proxy auto-start failed:', err);
|
|
135
|
+
});
|
|
133
136
|
}
|
|
134
137
|
}
|
|
135
138
|
|