@iebh/tera-fy 2.0.21 → 2.2.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/api.md +68 -66
  3. package/dist/lib/projectFile.d.ts +182 -0
  4. package/dist/lib/projectFile.js +157 -0
  5. package/dist/lib/projectFile.js.map +1 -0
  6. package/dist/lib/syncro/entities.d.ts +28 -0
  7. package/dist/lib/syncro/entities.js +203 -0
  8. package/dist/lib/syncro/entities.js.map +1 -0
  9. package/dist/lib/syncro/keyed.d.ts +95 -0
  10. package/dist/lib/syncro/keyed.js +286 -0
  11. package/dist/lib/syncro/keyed.js.map +1 -0
  12. package/dist/lib/syncro/syncro.d.ts +328 -0
  13. package/dist/lib/syncro/syncro.js +633 -0
  14. package/dist/lib/syncro/syncro.js.map +1 -0
  15. package/dist/lib/terafy.bootstrapper.d.ts +42 -0
  16. package/dist/lib/terafy.bootstrapper.js +130 -0
  17. package/dist/lib/terafy.bootstrapper.js.map +1 -0
  18. package/dist/lib/terafy.client.d.ts +532 -0
  19. package/dist/lib/terafy.client.js +1110 -0
  20. package/dist/lib/terafy.client.js.map +1 -0
  21. package/dist/lib/terafy.proxy.d.ts +66 -0
  22. package/dist/lib/terafy.proxy.js +123 -0
  23. package/dist/lib/terafy.proxy.js.map +1 -0
  24. package/dist/lib/terafy.server.d.ts +607 -0
  25. package/dist/lib/terafy.server.js +1774 -0
  26. package/dist/lib/terafy.server.js.map +1 -0
  27. package/dist/plugin.vue2.es2019.js +30 -13
  28. package/dist/plugins/base.d.ts +20 -0
  29. package/dist/plugins/base.js +21 -0
  30. package/dist/plugins/base.js.map +1 -0
  31. package/dist/plugins/firebase.d.ts +62 -0
  32. package/dist/plugins/firebase.js +111 -0
  33. package/dist/plugins/firebase.js.map +1 -0
  34. package/dist/plugins/vite.d.ts +12 -0
  35. package/dist/plugins/vite.js +22 -0
  36. package/dist/plugins/vite.js.map +1 -0
  37. package/dist/plugins/vue2.d.ts +68 -0
  38. package/dist/plugins/vue2.js +96 -0
  39. package/dist/plugins/vue2.js.map +1 -0
  40. package/dist/plugins/vue3.d.ts +64 -0
  41. package/dist/plugins/vue3.js +96 -0
  42. package/dist/plugins/vue3.js.map +1 -0
  43. package/dist/terafy.bootstrapper.es2019.js +2 -2
  44. package/dist/terafy.bootstrapper.js +2 -2
  45. package/dist/terafy.es2019.js +2 -2
  46. package/dist/terafy.js +1 -1
  47. package/dist/utils/mixin.d.ts +11 -0
  48. package/dist/utils/mixin.js +15 -0
  49. package/dist/utils/mixin.js.map +1 -0
  50. package/dist/utils/pDefer.d.ts +12 -0
  51. package/dist/utils/pDefer.js +14 -0
  52. package/dist/utils/pDefer.js.map +1 -0
  53. package/dist/utils/pathTools.d.ts +70 -0
  54. package/dist/utils/pathTools.js +120 -0
  55. package/dist/utils/pathTools.js.map +1 -0
  56. package/eslint.config.js +44 -8
  57. package/lib/{projectFile.js → projectFile.ts} +83 -40
  58. package/lib/syncro/entities.ts +288 -0
  59. package/lib/syncro/{keyed.js → keyed.ts} +114 -57
  60. package/lib/syncro/{syncro.js → syncro.ts} +204 -169
  61. package/lib/{terafy.bootstrapper.js → terafy.bootstrapper.ts} +49 -31
  62. package/lib/{terafy.client.js → terafy.client.ts} +94 -86
  63. package/lib/{terafy.proxy.js → terafy.proxy.ts} +43 -16
  64. package/lib/{terafy.server.js → terafy.server.ts} +364 -223
  65. package/package.json +65 -26
  66. package/plugins/{base.js → base.ts} +3 -1
  67. package/plugins/{firebase.js → firebase.ts} +34 -16
  68. package/plugins/{vite.js → vite.ts} +3 -3
  69. package/plugins/{vue2.js → vue2.ts} +17 -10
  70. package/plugins/{vue3.js → vue3.ts} +11 -9
  71. package/tsconfig.json +30 -0
  72. package/utils/{mixin.js → mixin.ts} +1 -1
  73. package/utils/{pDefer.js → pDefer.ts} +10 -3
  74. package/utils/{pathTools.js → pathTools.ts} +11 -9
  75. package/lib/syncro/entities.js +0 -232
@@ -31,21 +31,21 @@ export default class TeraFy {
31
31
  * @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
32
  */
33
33
  settings = {
34
- session: null,
34
+ session: null as string | null,
35
35
  // client: 'tera-fy', // Reserved by terafy.bootstrapper.js
36
36
  // clientType: 'esm', // Reserved by terafy.bootstrapper.js
37
37
  devMode: false,
38
38
  verbosity: 1,
39
- mode: 'detect',
39
+ mode: 'detect' as 'detect' | 'parent' | 'child' | 'popup',
40
40
  modeTimeout: 300,
41
41
  modeFallback: 'child', // ENUM: 'child' (use iframes), 'popup' (use popup windows)
42
42
  modeOverrides: {
43
- child(config) { // When we're in child mode assume a local dev environment and use the dev.tera-tools.com site instead to work around CORS restrictions
43
+ child(config: any) { // When we're in child mode assume a local dev environment and use the dev.tera-tools.com site instead to work around CORS restrictions
44
44
  if (config.siteUrl == 'https://tera-tools.com/embed') { // Only if we're using the default URL...
45
45
  config.siteUrl = 'https://dev.tera-tools.com/embed'; // Repoint URL to dev site
46
46
  }
47
47
  },
48
- },
48
+ } as { [key: string]: (config: any) => void },
49
49
  siteUrl: 'https://tera-tools.com/embed',
50
50
  restrictOrigin: '*',
51
51
  frameSandbox: [
@@ -62,7 +62,7 @@ export default class TeraFy {
62
62
  ],
63
63
  handshakeInterval: 1_000, // ~1s
64
64
  handshakeTimeout: 10_000, // ~10s
65
- debugPaths: null, // Transformed into a Array<String> (in Lodash dotted notation) on init()
65
+ debugPaths: null as Array<string | Array<string>> | null, // Transformed into a Array<String> (in Lodash dotted notation) on init()
66
66
  };
67
67
 
68
68
 
@@ -70,6 +70,7 @@ export default class TeraFy {
70
70
  * Event emitter subscription endpoint
71
71
  * @type {Mitt}
72
72
  */
73
+ // @ts-ignore
73
74
  events = Mitt();
74
75
 
75
76
 
@@ -83,10 +84,10 @@ export default class TeraFy {
83
84
  * @property {DOMElement} stylesheet The corresponding stylesheet
84
85
  */
85
86
  dom = {
86
- el: null,
87
- iframe: null,
88
- popup: null,
89
- stylesheet: null,
87
+ el: null as HTMLDivElement | null,
88
+ iframe: null as HTMLIFrameElement | null,
89
+ popup: null as Window | null,
90
+ stylesheet: null as HTMLStyleElement | null,
90
91
  };
91
92
 
92
93
 
@@ -156,14 +157,14 @@ export default class TeraFy {
156
157
  'uiThrow',
157
158
  'uiSplat',
158
159
  'uiWindow',
159
- ];
160
+ ] as const;
160
161
 
161
162
 
162
163
  /**
163
164
  * Loaded plugins via Use()
164
165
  * @type {Array<TeraFyPlugin>}
165
166
  */
166
- plugins = [];
167
+ plugins: any[] = [];
167
168
 
168
169
 
169
170
  /**
@@ -173,7 +174,7 @@ export default class TeraFy {
173
174
  *
174
175
  * @type {Object<Object>}
175
176
  */
176
- namespaces = {};
177
+ namespaces: { [key: string]: any } = {};
177
178
 
178
179
 
179
180
  // Messages - send(), sendRaw(), rpc(), acceptMessage() {{{
@@ -184,7 +185,7 @@ export default class TeraFy {
184
185
  * @param {Object} message Message object to send
185
186
  * @returns {Promise<*>} A promise which resolves when the operation has completed with the remote reply
186
187
  */
187
- send(message) {
188
+ send(message: any) {
188
189
  let id = nanoid();
189
190
 
190
191
  this.acceptPostboxes[id] = {}; // Stub for the deferred promise
@@ -208,8 +209,8 @@ export default class TeraFy {
208
209
  *
209
210
  * @param {Object} message Message object to send
210
211
  */
211
- sendRaw(message) {
212
- let payload;
212
+ sendRaw(message: any) {
213
+ let payload: any;
213
214
  try {
214
215
  payload = {
215
216
  TERA: 1,
@@ -220,9 +221,9 @@ export default class TeraFy {
220
221
  if (this.settings.mode == 'parent') {
221
222
  window.parent.postMessage(payload, this.settings.restrictOrigin);
222
223
  } else if (this.settings.mode == 'child') {
223
- this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
224
+ this.dom.iframe!.contentWindow!.postMessage(payload, this.settings.restrictOrigin);
224
225
  } else if (this.settings.mode == 'popup') {
225
- this.dom.popup.postMessage(payload, this.settings.restrictOrigin);
226
+ this.dom.popup!.postMessage(payload, this.settings.restrictOrigin);
226
227
  } else if (this.settings.mode == 'detect') {
227
228
  throw new Error('Call init() or detectMode() before trying to send data to determine the mode');
228
229
  } else {
@@ -244,7 +245,7 @@ export default class TeraFy {
244
245
  *
245
246
  * @returns {Promise<*>} The resolved output of the server function
246
247
  */
247
- rpc(method, ...args) {
248
+ rpc(method: any, ...args: any[]) {
248
249
  return this.send({
249
250
  action: 'rpc',
250
251
  method,
@@ -260,7 +261,7 @@ export default class TeraFy {
260
261
  *
261
262
  * @returns {Promise} A promise which will resolve when the message has been processed
262
263
  */
263
- acceptMessage(rawMessage) {
264
+ acceptMessage(rawMessage: any) {
264
265
  if (rawMessage.origin == window.location.origin) return Promise.resolve(); // Message came from us
265
266
 
266
267
  let message = rawMessage.data;
@@ -276,7 +277,7 @@ export default class TeraFy {
276
277
  return Promise.resolve();
277
278
  } else if (message?.action == 'rpc') {
278
279
  return Promise.resolve()
279
- .then(()=> this[message.method].apply(this, message.args))
280
+ .then(()=> (this as any)[message.method].apply(this, message.args as any[]))
280
281
  .then(res => this.sendRaw({
281
282
  id: message.id,
282
283
  action: 'response',
@@ -293,7 +294,7 @@ export default class TeraFy {
293
294
  })
294
295
  } else if (message?.action == 'event') {
295
296
  return Promise.resolve()
296
- .then(()=> this.events.emit(message.event, ...message.payload))
297
+ .then(()=> this.events.emit(message.event, ...(message.payload as [])))
297
298
  .catch(e => {
298
299
  console.warn(`TERA-FY client threw while handling emitted event "${message.event}"`, {message});
299
300
  throw e;
@@ -311,7 +312,7 @@ export default class TeraFy {
311
312
  /**
312
313
  * Listening postboxes, these correspond to outgoing message IDs that expect a response
313
314
  */
314
- acceptPostboxes = {};
315
+ acceptPostboxes: { [key: string]: any } = {};
315
316
 
316
317
  // }}}
317
318
 
@@ -325,7 +326,7 @@ export default class TeraFy {
325
326
  *
326
327
  * @returns {Promise<Reactive>} A promise which resolves to the reactive object
327
328
  */
328
- mountNamespace(name) {
329
+ mountNamespace(name: any) {
329
330
  if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
330
331
  if (this.namespaces[name]) return Promise.resolve(this.namespaces[name]); // Already mounted
331
332
 
@@ -343,7 +344,7 @@ export default class TeraFy {
343
344
  *
344
345
  * @returns {Promise} A promise which resolves when the mount operation has completed
345
346
  */
346
- _mountNamespace(name) { // eslint-disable-line no-unused-vars
347
+ _mountNamespace(name: any) { // eslint-disable-line no-unused-vars
347
348
  console.warn('teraFy._mountNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
348
349
  throw new Error('teraFy._mountNamespace() is not supported');
349
350
  }
@@ -359,7 +360,7 @@ export default class TeraFy {
359
360
  *
360
361
  * @returns {Promise} A promise which resolves when the operation has completed
361
362
  */
362
- unmountNamespace(name) {
363
+ unmountNamespace(name: any) {
363
364
  if (!this.namespaces[name]) return Promise.resolve(); // Already unmounted
364
365
  return this._unmountNamespace(name);
365
366
  }
@@ -373,7 +374,7 @@ export default class TeraFy {
373
374
  *
374
375
  * @returns {Promise} A promise which resolves when the operation has completed
375
376
  */
376
- _unmountNamespace(name) { // eslint-disable-line no-unused-vars
377
+ _unmountNamespace(name: any) { // eslint-disable-line no-unused-vars
377
378
  console.warn('teraFy.unbindNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
378
379
  }
379
380
  // }}}
@@ -385,10 +386,11 @@ export default class TeraFy {
385
386
  *
386
387
  * @param {Object} [options] Additional options to merge into `settings` via `set`
387
388
  */
388
- constructor(options) {
389
+ constructor(options?: any) {
389
390
  if (options) this.set(options);
390
391
  }
391
392
 
393
+ private initPromise: Promise<TeraFy> | null = null;
392
394
 
393
395
  /**
394
396
  * Initalize the TERA client singleton
@@ -397,26 +399,27 @@ export default class TeraFy {
397
399
  * @param {Object} [options] Additional options to merge into `settings` via `set`
398
400
  * @returns {Promise<TeraFy>} An eventual promise which will resovle with this terafy instance
399
401
  */
400
- init(options) {
402
+ init(options?: any): Promise<TeraFy> {
401
403
  if (options) this.set(options);
402
- if (this.init.promise) return this.init.promise; // Aleady been called - return init promise
404
+ if (this.initPromise) return this.initPromise; // Aleady been called - return init promise
403
405
 
404
406
  window.addEventListener('message', this.acceptMessage.bind(this));
405
407
 
406
408
  const context = this;
407
- return this.init.promise = Promise.resolve()
409
+ this.initPromise = Promise.resolve()
408
410
  .then(()=> this.settings.session ||= 'tfy-' + this.getEntropicString(16))
409
411
  .then(()=> this.debug('INFO', 4, '[0/6] Init', 'Session', this.settings.session, 'against', this.settings.siteUrl))
410
412
  .then(()=> { // Init various options for optimized access
411
413
  if (!this.settings.devMode) return; // Not in dev mode
412
- this.settings.debugPaths =
413
- !this.settings.debugPaths ? null // Falsy - disable
414
- : Array.isArray(this.settings.debugPaths) ? this.settings.debugPaths.map(path =>
414
+ if (this.settings.debugPaths) {
415
+ this.settings.debugPaths = (this.settings.debugPaths as any[]).map((path: any) =>
415
416
  Array.isArray(path) ? path.join('.') // Transform arrays into dotted notation
416
417
  : typeof path == 'string' ? path // Assume already in dotted notation
417
418
  : (()=> { throw new Error('Unknown path type - should be an array or string in dotted notation') })()
418
419
  )
419
- : (()=> { throw new Error('Unknown terafyClient.settings.debugPaths type') })()
420
+ } else {
421
+ this.settings.debugPaths = null;
422
+ }
420
423
 
421
424
  this.debug('INFO', 0, 'Watching state paths', this.settings.debugPaths);
422
425
  })
@@ -459,12 +462,16 @@ export default class TeraFy {
459
462
  plugin.init.call(context, this.settings)
460
463
  )
461
464
  ))
462
- .then(()=> this.debug('INFO', 4, '[6/6] Init complete'))
463
- .then(()=> context)
465
+ .then(()=> {
466
+ this.debug('INFO', 4, '[6/6] Init complete');
467
+ return context; // Resolve with the instance
468
+ })
464
469
  .catch(e => {
465
470
  this.debug('WARN', 0, 'Init process fault', e);
466
- return context;
467
- })
471
+ throw e; // Re-throw
472
+ });
473
+
474
+ return this.initPromise;
468
475
  }
469
476
 
470
477
 
@@ -475,23 +482,24 @@ export default class TeraFy {
475
482
  *
476
483
  * @returns {Promise<String>} A promise which will resolve with the detected mode to use
477
484
  */
478
- detectMode() {
485
+ detectMode(): Promise<'parent' | 'child' | 'popup'> {
479
486
  if (this.settings.mode != 'detect') { // Dev has specified a forced mode to use
480
487
  return Promise.resolve(this.settings.mode);
481
488
  } else if (window.self === window.parent) { // This frame is already at the top
482
- return Promise.resolve(this.settings.modeFallback);
489
+ return Promise.resolve(this.settings.modeFallback as 'child' | 'popup');
483
490
  } else { // No idea - try messaging
484
491
  return Promise.resolve()
485
492
  .then(()=> this.settings.mode = 'parent') // Switch to parent mode...
486
- .then(()=> new Promise((resolve, reject) => { // And try messaging with a timeout
493
+ .then(()=> new Promise<void>((resolve, reject) => { // And try messaging with a timeout
487
494
  let timeoutHandle = setTimeout(()=> reject('TIMEOUT'), this.settings.modeTimeout);
488
495
 
489
496
  this.rpc('handshake')
490
497
  .then(()=> clearTimeout(timeoutHandle))
491
- .then(()=> resolve())
498
+ .then(()=> resolve(undefined))
499
+ .catch(reject); // Propagate RPC errors
492
500
  }))
493
- .then(()=> 'parent')
494
- .catch(()=> this.settings.modeFallback)
501
+ .then(()=> 'parent' as 'parent')
502
+ .catch(()=> this.settings.modeFallback as 'child' | 'popup')
495
503
  }
496
504
  }
497
505
 
@@ -504,29 +512,29 @@ export default class TeraFy {
504
512
  injectComms() {
505
513
  switch (this.settings.mode) {
506
514
  case 'child': return Promise.resolve()
507
- .then(()=> new Promise(resolve => {
515
+ .then(()=> new Promise<void>(resolve => {
508
516
  this.debug('INFO', 2, 'Injecting TERA site as iFrame child');
509
517
 
510
518
  this.dom.el = document.createElement('div')
511
519
  this.dom.el.id = 'tera-fy';
512
- this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
513
- this.dom.el.classList.add('minimized');
514
- document.body.append(this.dom.el);
520
+ this.dom.el!.classList.toggle('dev-mode', this.settings.devMode);
521
+ this.dom.el!.classList.add('minimized');
522
+ document.body.append(this.dom.el!);
515
523
 
516
- this.dom.el.addEventListener('click', ()=> this.dom.el.classList.toggle('minimized'));
524
+ this.dom.el!.addEventListener('click', ()=> this.dom.el!.classList.toggle('minimized'));
517
525
 
518
526
  this.dom.iframe = document.createElement('iframe')
519
527
 
520
528
  // Queue up event chain when document loads
521
- this.dom.iframe.setAttribute('sandbox', this.settings.frameSandbox.join(' '));
522
- this.dom.iframe.addEventListener('load', ()=> {
529
+ this.dom.iframe!.setAttribute('sandbox', this.settings.frameSandbox.join(' '));
530
+ this.dom.iframe!.addEventListener('load', ()=> {
523
531
  this.debug('INFO', 3, 'Embeded iframe ready');
524
- resolve();
532
+ resolve(undefined);
525
533
  });
526
534
 
527
535
  // Start document load sequence + append to DOM
528
- this.dom.iframe.src = this.settings.siteUrl;
529
- this.dom.el.append(this.dom.iframe);
536
+ this.dom.iframe!.src = this.settings.siteUrl;
537
+ this.dom.el!.append(this.dom.iframe!);
530
538
  }))
531
539
  .then(()=> this.handshakeLoop())
532
540
 
@@ -556,7 +564,7 @@ export default class TeraFy {
556
564
  *
557
565
  * @returns {Promise} A promise which will either resolve when the handshake is successful OR fail with 'TIMEOUT'
558
566
  */
559
- handshakeLoop(options) {
567
+ handshakeLoop(options?: any) {
560
568
  let settings = {
561
569
  handshakeInterval: this.settings.handshakeInterval,
562
570
  handshakeTimeout: this.settings.handshakeTimeout,
@@ -564,9 +572,9 @@ export default class TeraFy {
564
572
  };
565
573
 
566
574
  // Loop until the window context returns a handshake
567
- return new Promise((resolve, reject) => {
575
+ return new Promise<void>((resolve, reject) => {
568
576
  let handshakeCount = 0;
569
- let handshakeTimer;
577
+ let handshakeTimer: any;
570
578
 
571
579
  let handshakeTimeout = setTimeout(()=> {
572
580
  clearTimeout(handshakeTimer);
@@ -584,8 +592,8 @@ export default class TeraFy {
584
592
  clearTimeout(handshakeTimeout);
585
593
  clearTimeout(handshakeTimer);
586
594
  })
587
- .then(()=> resolve())
588
- .catch(reject)
595
+ .then(()=> resolve(undefined))
596
+ .catch(reject) // Let RPC errors propagate
589
597
  };
590
598
  tryHandshake(); // Kick off initial handshake
591
599
  });
@@ -601,7 +609,7 @@ export default class TeraFy {
601
609
  switch (this.settings.mode) {
602
610
  case 'child':
603
611
  this.dom.stylesheet = document.createElement('style');
604
- this.dom.stylesheet.innerHTML = [
612
+ this.dom.stylesheet!.innerHTML = [
605
613
  ':root {',
606
614
  '--TERA-accent: #4d659c;',
607
615
  '}',
@@ -714,7 +722,7 @@ export default class TeraFy {
714
722
  '}',
715
723
  // }}}
716
724
  ].join('\n');
717
- document.head.appendChild(this.dom.stylesheet);
725
+ document.head.appendChild(this.dom.stylesheet!);
718
726
  break;
719
727
  case 'parent':
720
728
  case 'popup':
@@ -732,7 +740,7 @@ export default class TeraFy {
732
740
  */
733
741
  injectMethods() {
734
742
  this.methods.forEach(method =>
735
- this[method] = this.rpc.bind(this, method)
743
+ (this as any)[method] = this.rpc.bind(this, method)
736
744
  );
737
745
  }
738
746
  // }}}
@@ -748,7 +756,7 @@ export default class TeraFy {
748
756
  * @param {Number} [verboseLevel=1] The verbosity level to trigger at. If `settings.verbosity` is lower than this, the message is ignored
749
757
  * @param {...*} [msg] Output to show
750
758
  */
751
- debug(...msg) {
759
+ debug(...msg: any[]) {
752
760
  if (!this.settings.devMode || this.settings.verbosity < 1) return; // Debugging is disabled
753
761
  let method = 'log';
754
762
  let verboseLevel = 1;
@@ -765,7 +773,7 @@ export default class TeraFy {
765
773
 
766
774
  if (this.settings.verbosity < verboseLevel) return; // Called but this output is too verbose for our settings - skip
767
775
 
768
- console[method](
776
+ (console as any)[method](
769
777
  '%c[TERA-FY CLIENT]',
770
778
  'font-weight: bold; color: #ff5722;',
771
779
  ...msg,
@@ -786,7 +794,7 @@ export default class TeraFy {
786
794
  *
787
795
  * @returns {TeraFy} This chainable terafy instance
788
796
  */
789
- set(key, value, options) {
797
+ set(key: string | object, value?: any, options?: { ignoreNullish?: boolean }) {
790
798
  let settings = {
791
799
  ignoreNullish: true,
792
800
  ...options,
@@ -794,12 +802,12 @@ export default class TeraFy {
794
802
 
795
803
  if (typeof key == 'string') {
796
804
  if (!settings.ignoreNullish || (value !== null && value !== undefined))
797
- this.settings[key] = value;
805
+ (this.settings as any)[key] = value;
798
806
  } else {
799
807
  Object.assign(this.settings, key);
800
808
  }
801
809
 
802
- return this.toggleDevMode(this.settings.devMode);
810
+ return this.toggleDevMode(this.settings.devMode as boolean);
803
811
  }
804
812
 
805
813
 
@@ -813,7 +821,7 @@ export default class TeraFy {
813
821
  *
814
822
  * @returns {TeraFy} This chainable terafy instance
815
823
  */
816
- setIfDev(key, value, options) {
824
+ setIfDev(key: any, value: any, options?: any) {
817
825
  if (!this.settings.devMode || value === undefined) return this;
818
826
  return this.set(key, value, options);
819
827
  }
@@ -827,8 +835,8 @@ export default class TeraFy {
827
835
  *
828
836
  * @returns {TeraFy} This chainable terafy instance
829
837
  */
830
- use(source, options) {
831
- let mod =
838
+ use(source: any, options?: any) {
839
+ let mod: any =
832
840
  typeof source == 'function' ? new source(this, options)
833
841
  : typeof source == 'object' ? source
834
842
  : typeof source == 'string' ? (()=> { throw new Error('use(String) is not yet supported') })()
@@ -847,7 +855,7 @@ export default class TeraFy {
847
855
  * @param {Object} target Initalied class instance to extend
848
856
  * @param {Object} source Initalized source object to extend from
849
857
  */
850
- mixin(target, source) {
858
+ mixin(target: any, source: any) {
851
859
  // Iterate through the source object upwards extracting each prototype
852
860
  let prototypeStack = [];
853
861
  let node = source;
@@ -863,17 +871,17 @@ export default class TeraFy {
863
871
  && !prop.startsWith('__') // Ignore double underscore meta properties
864
872
  )
865
873
  .forEach(prop => {
866
- if (typeof source[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
874
+ if (typeof (source as any)[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
867
875
  Object.defineProperty(
868
876
  target,
869
877
  prop,
870
878
  {
871
879
  enumerable: false,
872
- value: source[prop].bind(target), // Rebind functions
880
+ value: (source as any)[prop].bind(target), // Rebind functions
873
881
  },
874
882
  );
875
883
  } else { // Everything else, just glue onto the object
876
- target[prop] = source[prop];
884
+ target[prop] = (source as any)[prop];
877
885
  }
878
886
  })
879
887
  )
@@ -891,7 +899,7 @@ export default class TeraFy {
891
899
  *
892
900
  * @returns {TeraFy} This chainable terafy instance
893
901
  */
894
- toggleDevMode(devModeEnabled = 'toggle') {
902
+ toggleDevMode(devModeEnabled: 'toggle' | 'proxy' | boolean = 'toggle') {
895
903
  if (devModeEnabled === 'toggle') {
896
904
  this.settings.devMode = !this.settings.devMode;
897
905
  } else if (devModeEnabled === 'proxy') {
@@ -908,7 +916,7 @@ export default class TeraFy {
908
916
  this.settings.restrictOrigin = '*'; // Allow all upstream iframes
909
917
 
910
918
  if (this.dom?.el) // Have we actually set up yet?
911
- this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
919
+ this.dom.el!.classList.toggle('dev-mode', this.settings.devMode);
912
920
 
913
921
  return this;
914
922
  }
@@ -920,7 +928,7 @@ export default class TeraFy {
920
928
  *
921
929
  * @param {String|Boolean} [isFocused='toggle'] Whether to fullscreen the embedded component
922
930
  */
923
- toggleFocus(isFocused = 'toggle') {
931
+ toggleFocus(isFocused: boolean | 'toggle' = 'toggle') {
924
932
  this.debug('INFO', 2, 'Request focus', {isFocused});
925
933
  globalThis.document.body.classList.toggle('tera-fy-focus', isFocused === 'toggle' ? undefined : isFocused);
926
934
  }
@@ -1187,9 +1195,9 @@ export default class TeraFy {
1187
1195
  *
1188
1196
  * @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
1189
1197
  */
1190
- selectProjectFile(options) {
1198
+ selectProjectFile(options?: any) {
1191
1199
  return this.rpc('selectProjectFile', options)
1192
- .then(file => file
1200
+ .then((file: any) => file
1193
1201
  ? new ProjectFile({
1194
1202
  tera: this,
1195
1203
  ...file,
@@ -1210,9 +1218,9 @@ export default class TeraFy {
1210
1218
  *
1211
1219
  * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
1212
1220
  */
1213
- getProjectFiles(options) {
1221
+ getProjectFiles(options?: any) {
1214
1222
  return this.rpc('getProjectFiles', options)
1215
- .then(files => files.map(file =>
1223
+ .then((files: any) => files.map((file: any) =>
1216
1224
  new ProjectFile({
1217
1225
  tera: this,
1218
1226
  ...file,
@@ -1245,9 +1253,9 @@ export default class TeraFy {
1245
1253
  *
1246
1254
  * @returns {Promise<ProjectFile>} The eventual fetched ProjectFile (or requested subkey)
1247
1255
  */
1248
- getProjectFile(id, options) {
1256
+ getProjectFile(id: any, options?: any) {
1249
1257
  return this.rpc('getProjectFile', id, options)
1250
- .then(file => file
1258
+ .then((file: any) => file
1251
1259
  ? new ProjectFile({
1252
1260
  tera: this,
1253
1261
  ...file,
@@ -1265,9 +1273,9 @@ export default class TeraFy {
1265
1273
  * @param {String} name The name + relative directory path component
1266
1274
  * @returns {Promise<ProjectFile>} The eventual ProjectFile created
1267
1275
  */
1268
- createProjectFile(name) {
1276
+ createProjectFile(name: any) {
1269
1277
  return this.rpc('createProjectFile', name)
1270
- .then(file => file
1278
+ .then((file: any) => file
1271
1279
  ? new ProjectFile({
1272
1280
  tera: this,
1273
1281
  ...file,
@@ -1498,4 +1506,4 @@ export default class TeraFy {
1498
1506
  */
1499
1507
 
1500
1508
  // }}}
1501
- }
1509
+ }
@@ -2,6 +2,19 @@ import {create as createDomain} from 'node:domain';
2
2
  import detectPort from 'detect-port';
3
3
  import Proxy from 'http-proxy';
4
4
 
5
+ // Define options interface based on JSDoc and settings
6
+ interface TeraProxyOptions {
7
+ force?: boolean;
8
+ autoStart?: boolean;
9
+ host?: string;
10
+ port?: number;
11
+ targetProtocol?: string;
12
+ targetHost?: string;
13
+ targetPort?: number;
14
+ portConflict?: 'ignore' | 'throw';
15
+ onLog?: (level: 'INFO' | 'WARN', ...msg: any[]) => void;
16
+ }
17
+
5
18
  export class TeraProxy {
6
19
  /**
7
20
  * Setup a local loopback proxy for TERA-tools.com
@@ -27,8 +40,8 @@ export class TeraProxy {
27
40
  targetProtocol: 'https',
28
41
  targetHost: 'dev.tera-tools.com',
29
42
  targetPort: 443,
30
- portConflict: 'ignore',
31
- onLog: (level, ...msg) => console.log(...msg),
43
+ portConflict: 'ignore' as 'ignore' | 'throw', // Add type assertion for stricter checking if needed elsewhere
44
+ onLog: (level: 'INFO' | 'WARN', ...msg: any[]) => console.log(...msg),
32
45
  }
33
46
 
34
47
 
@@ -36,7 +49,7 @@ export class TeraProxy {
36
49
  * Eventual proxy server when the plugin has booted
37
50
  * @type {ProxyServer}
38
51
  */
39
- proxyServer;
52
+ proxyServer: Proxy | undefined;
40
53
 
41
54
 
42
55
  /**
@@ -49,7 +62,12 @@ export class TeraProxy {
49
62
 
50
63
  return Promise.resolve()
51
64
  .then(()=> detectPort(this.settings.port))
52
- .then(gotPort => gotPort != this.settings.port && Promise.reject('PORT-CONFLICT'))
65
+ .then(gotPort => {
66
+ if (gotPort != this.settings.port) {
67
+ return Promise.reject('PORT-CONFLICT');
68
+ }
69
+ // Only return void if the port is available
70
+ })
53
71
  .then(() => this.proxyServer = Proxy.createProxyServer({ // Create proxy pass-thru
54
72
  changeOrigin: true,
55
73
  target: {
@@ -58,20 +76,29 @@ export class TeraProxy {
58
76
  port: this.settings.targetPort,
59
77
  },
60
78
  }))
61
- .then(()=> new Promise((resolve, reject) => {
79
+ .then(()=> new Promise<void>((resolve, reject) => {
62
80
  // Wrap listener in a domain so we can correctly catch EADDRINUSE
63
81
  let domain = createDomain();
64
- domain.on('error', err => {
82
+ domain.on('error', (err: NodeJS.ErrnoException) => { // Add type to err
65
83
  if (err.code == 'EADDRINUSE') {
66
84
  reject('PORT-CONFLICT');
67
85
  } else {
68
86
  reject(err);
69
87
  }
70
88
  });
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
- })
89
+ // Run the server creation and listening within the domain
90
+ domain.run(() => {
91
+ if (!this.proxyServer) return reject(new Error('Proxy server not initialized')); // Guard against undefined proxyServer
92
+ this.proxyServer.listen(this.settings.port, this.settings.host)
93
+ // Handle errors on the proxy server itself, although domain should catch listen errors
94
+ this.proxyServer.on('error', (err: NodeJS.ErrnoException) => {
95
+ if (err.code == 'EADDRINUSE') {
96
+ reject('PORT-CONFLICT');
97
+ } else {
98
+ reject(err);
99
+ }
100
+ });
101
+ });
75
102
  }))
76
103
  .catch(e => {
77
104
  if (e === 'PORT-CONFLICT') {
@@ -79,7 +106,7 @@ export class TeraProxy {
79
106
  this.settings.onLog('WARN', 'Port', this.settings.port, 'is already allocated - assuming TERA is already running locally and skipping proxy');
80
107
  return false; // Do nothing
81
108
  } else {
82
- throw err;
109
+ throw e; // Throw the 'PORT-CONFLICT' string or a new Error
83
110
  }
84
111
  } else {
85
112
  throw e; // Re-throw everything else
@@ -94,15 +121,15 @@ export class TeraProxy {
94
121
  */
95
122
  stop() {
96
123
  return Promise.resolve()
97
- .then(()=> this.proxyServer && new Promise(resolve => this.proxyServer.close(()=> resolve())))
124
+ .then(()=> this.proxyServer && new Promise<void>(resolve => this.proxyServer!.close(()=> resolve()))) // Use non-null assertion if sure it exists here
98
125
  }
99
126
 
100
127
 
101
- constructor(options) {
128
+ constructor(options?: TeraProxyOptions) { // Add type annotation and make optional
102
129
  if (options) Object.assign(this.settings, options);
103
130
 
104
131
  // Auto start?
105
- if (options?.autoStart ?? true) this.start();
132
+ if (this.settings.autoStart) this.start(); // Use resolved settings.autoStart
106
133
  }
107
134
  }
108
135
 
@@ -113,6 +140,6 @@ export class TeraProxy {
113
140
  * @param {Object} [options] Options to pass to the Proxy module
114
141
  * @returns {TeraProxy} A TeraProxy instance
115
142
  */
116
- export default function(options) {
143
+ export default function(options?: TeraProxyOptions) { // Add type annotation and make optional
117
144
  return new TeraProxy(options);
118
- }
145
+ }