@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.
Files changed (66) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/CHANGELOG.md +22 -0
  3. package/api.md +35 -36
  4. package/dist/lib/projectFile.d.ts +3 -3
  5. package/dist/lib/projectFile.js +4 -3
  6. package/dist/lib/projectFile.js.map +1 -1
  7. package/dist/lib/syncro/entities.js +11 -10
  8. package/dist/lib/syncro/entities.js.map +1 -1
  9. package/dist/lib/syncro/keyed.d.ts +2 -2
  10. package/dist/lib/syncro/keyed.js +23 -23
  11. package/dist/lib/syncro/keyed.js.map +1 -1
  12. package/dist/lib/syncro/syncro.d.ts +15 -13
  13. package/dist/lib/syncro/syncro.js +84 -59
  14. package/dist/lib/syncro/syncro.js.map +1 -1
  15. package/dist/lib/terafy.bootstrapper.d.ts +2 -2
  16. package/dist/lib/terafy.bootstrapper.js +15 -16
  17. package/dist/lib/terafy.bootstrapper.js.map +1 -1
  18. package/dist/lib/terafy.client.d.ts +24 -25
  19. package/dist/lib/terafy.client.js +50 -48
  20. package/dist/lib/terafy.client.js.map +1 -1
  21. package/dist/lib/terafy.proxy.js +4 -2
  22. package/dist/lib/terafy.proxy.js.map +1 -1
  23. package/dist/lib/terafy.server.d.ts +22 -8
  24. package/dist/lib/terafy.server.js +54 -58
  25. package/dist/lib/terafy.server.js.map +1 -1
  26. package/dist/plugin.vue2.es2019.js +210 -1224
  27. package/dist/plugins/base.d.ts +2 -2
  28. package/dist/plugins/base.js +1 -0
  29. package/dist/plugins/base.js.map +1 -1
  30. package/dist/plugins/firebase.d.ts +4 -4
  31. package/dist/plugins/firebase.js +7 -7
  32. package/dist/plugins/firebase.js.map +1 -1
  33. package/dist/plugins/vue2.d.ts +1 -1
  34. package/dist/plugins/vue2.js +6 -5
  35. package/dist/plugins/vue2.js.map +1 -1
  36. package/dist/plugins/vue3.js +6 -5
  37. package/dist/plugins/vue3.js.map +1 -1
  38. package/dist/terafy.bootstrapper.es2019.js +2 -2
  39. package/dist/terafy.bootstrapper.js +2 -2
  40. package/dist/terafy.es2019.js +2 -2
  41. package/dist/terafy.js +2 -2
  42. package/dist/utils/mixin.js +1 -1
  43. package/dist/utils/mixin.js.map +1 -1
  44. package/dist/utils/pDefer.d.ts +5 -1
  45. package/dist/utils/pDefer.js +6 -1
  46. package/dist/utils/pDefer.js.map +1 -1
  47. package/dist/utils/pathTools.d.ts +1 -1
  48. package/dist/utils/pathTools.js +2 -2
  49. package/dist/utils/pathTools.js.map +1 -1
  50. package/eslint.config.js +21 -7
  51. package/lib/projectFile.ts +5 -4
  52. package/lib/syncro/entities.ts +11 -10
  53. package/lib/syncro/keyed.ts +24 -24
  54. package/lib/syncro/syncro.ts +97 -60
  55. package/lib/terafy.bootstrapper.ts +15 -16
  56. package/lib/terafy.client.ts +62 -62
  57. package/lib/terafy.proxy.ts +8 -5
  58. package/lib/terafy.server.ts +75 -64
  59. package/package.json +5 -3
  60. package/plugins/base.ts +3 -2
  61. package/plugins/firebase.ts +12 -11
  62. package/plugins/vue2.ts +7 -6
  63. package/plugins/vue3.ts +6 -5
  64. package/utils/mixin.ts +1 -1
  65. package/utils/pDefer.ts +7 -2
  66. package/utils/pathTools.ts +3 -3
@@ -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 tolerent of weird iframe origins
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 sanning for a handshake
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: 1_000, // ~1s
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-ignore
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 equivelent
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
- let id = nanoid();
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
- let message = rawMessage.data;
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, 'Recieved message', message);
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 overriden by plugins
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) { // eslint-disable-line no-unused-vars
355
- console.warn('teraFy._mountNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
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 overriden by plugins
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) { // eslint-disable-line no-unused-vars
385
- console.warn('teraFy.unbindNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
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
- * Initalize the TERA client singleton
404
- * This function can only be called once and will return the existing init() worker Promise if its called againt
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 resovle with this terafy instance
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; // Aleady been called - return init promise
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
- let timeoutHandle = setTimeout(()=> reject('TIMEOUT'), this.settings.modeTimeout);
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 'parent')
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 initalize one
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 initiallized a child
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, 'Embeded iframe ready');
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 sanning for a handshake, defaults to global setting
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
- let settings = {
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
- let handshakeTimeout = setTimeout(()=> {
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 initiallized a child
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.appendChild(this.dom.stylesheet!);
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
- let settings = {
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 intialize later options)
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
- let mod: any =
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 initalizer') })();
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 Initalied class instance to extend
863
- * @param {Object} source Initalized source object to extend from
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
- let prototypeStack = [];
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 as any)[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
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 as any)[prop].bind(target), // Rebind functions
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 as any)[prop];
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!.classList.toggle('dev-mode', this.settings.devMode);
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 lengh of the genrated string
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.fromCharCode(...new Uint8Array(array.buffer)))
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 verbostiy level
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] Forcabily try to refresh the user state
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] Forcabily try to refresh the user state
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 percist in asking the uesr even if they try to cancel
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 equivelent
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 equivelent and just want to quickly set something
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 acationed
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] Additioanl options to mutate behaviour
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', 'dedisputed'
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 acationed
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 bevahiour) or a whatever format was requested
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 popupate `url`
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} [data] Data to display, if (and this doesn't contain 'body' or 'title') this populates `options.body`
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
@@ -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-thru
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
- let domain = createDomain();
82
- domain.on('error', (err: NodeJS.ErrnoException) => { // Add type to 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: NodeJS.ErrnoException) => {
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(); // Use resolved settings.autoStart
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