@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
@@ -1,19 +1,36 @@
1
+ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */
1
2
  // Global 'app' declaration
2
- declare var app: any;
3
+ declare let app: any;
3
4
 
4
5
  // Global window augmentation
5
6
  declare global {
6
7
  interface Window {
8
+ // eslint-disable-next-line no-unused-vars
7
9
  panic(text: any): void;
8
10
  }
9
11
  }
10
12
 
13
+ interface UiWindowPermissions {
14
+ popup?: boolean;
15
+ location?: boolean;
16
+ menubar?: boolean;
17
+ status?: boolean;
18
+ scrollbars?: boolean;
19
+ }
20
+
21
+ interface UiWindowOptions {
22
+ width?: number;
23
+ height?: number;
24
+ center?: boolean;
25
+ permissions?: UiWindowPermissions;
26
+ }
27
+
11
28
  import {cloneDeep} from 'lodash-es';
12
29
  import mixin from '#utils/mixin';
13
30
  import {nanoid} from 'nanoid';
14
31
  import pathTools from '#utils/pathTools';
15
32
  import promiseDefer from '#utils/pDefer';
16
- // @ts-ignore
33
+ // @ts-expect-error TODO: Remove when reflib gets declaration file
17
34
  import Reflib from '@iebh/reflib';
18
35
  import {reactive} from 'vue';
19
36
 
@@ -23,7 +40,6 @@ import {reactive} from 'vue';
23
40
  * @class TeraFyServer
24
41
  */
25
42
 
26
- /* globals globalThis, app */
27
43
  export default class TeraFyServer {
28
44
 
29
45
  /**
@@ -120,7 +136,7 @@ export default class TeraFyServer {
120
136
  case TeraFyServer.SERVERMODE_TERA:
121
137
  case TeraFyServer.SERVERMODE_FRAME: {
122
138
  // Server is the top-level window so we need to send messages to an embedded iFrame
123
- let iFrame = document.querySelector('iframe#external') as HTMLIFrameElement | null;
139
+ const iFrame = document.querySelector<HTMLIFrameElement>('iframe#external');
124
140
  if (!iFrame) {
125
141
  this.debug('INFO', 2, 'Cannot locate TERA-FY top-level->iFrame#external - maybe there is none');
126
142
  return mixin(this, {
@@ -211,7 +227,7 @@ export default class TeraFyServer {
211
227
  send(message: any): Promise<any> {
212
228
  if (!this.messageEvent?.source) throw new Error('send() requires a messageEvent with a source');
213
229
 
214
- let id = nanoid();
230
+ const id = nanoid();
215
231
 
216
232
  this.acceptPostboxes[id] = {}; // Stub for the deferred promise
217
233
  this.acceptPostboxes[id].promise = new Promise((resolve, reject) => {
@@ -289,7 +305,7 @@ export default class TeraFyServer {
289
305
  // Ignore messages from the same origin (potential loops)
290
306
  if (typeof window !== 'undefined' && rawMessage.origin === window.location.origin) return;
291
307
 
292
- let message = rawMessage.data;
308
+ const message = rawMessage.data;
293
309
  // Ensure message is an object and has TERA property
294
310
  if (typeof message !== 'object' || message === null || !message.TERA) return;
295
311
  this.debug('INFO', 3, 'Recieved message', message);
@@ -432,14 +448,14 @@ export default class TeraFyServer {
432
448
  * @returns {Promise<User>} The current logged in user or null if none
433
449
  */
434
450
  getUser(options?: any): Promise<any | null> {
435
- let settings = {
451
+ const settings = {
436
452
  forceRetry: false,
437
453
  waitPromises: true,
438
454
  ...options,
439
455
  };
440
456
 
441
- let $auth = app.service('$auth');
442
- let $subscriptions = app.service('$subscriptions');
457
+ const $auth = app.service('$auth');
458
+ const $subscriptions = app.service('$subscriptions');
443
459
 
444
460
  return Promise.resolve()
445
461
  .then(()=> settings.waitPromises && Promise.all([
@@ -584,7 +600,7 @@ export default class TeraFyServer {
584
600
  try {
585
601
  lsState = JSON.parse(lsState);
586
602
 
587
- let $auth = app.service('$auth');
603
+ const $auth = app.service('$auth');
588
604
  $auth.state = 'user';
589
605
  $auth.ready = true;
590
606
  $auth.isLoggedIn = true;
@@ -605,7 +621,7 @@ export default class TeraFyServer {
605
621
 
606
622
  this.debug('INFO', 4, 'localStorage failed - using popup auth instead');
607
623
 
608
- let focusContent = document.createElement('div');
624
+ const focusContent = document.createElement('div');
609
625
  focusContent.innerHTML = '<div>Authenticate with <a href="https://tera-tools.com" target="_blank">TERA-tools.com</a></div>'
610
626
  + '<div class="mt-2"><a class="btn btn-light">Open Popup...</a></div>';
611
627
 
@@ -615,13 +631,13 @@ export default class TeraFyServer {
615
631
  );
616
632
 
617
633
  // Create a deferred promise which will (eventually) resolve when the downstream window signals its ready
618
- let waitOnWindowAuth = promiseDefer();
634
+ const waitOnWindowAuth = promiseDefer();
619
635
 
620
636
  // Create a listener for the message from the downstream window to resolve the promise
621
- let listenMessages = ({data}: {data: any}) => {
637
+ const listenMessages = ({data}: {data: any}) => {
622
638
  this.debug('INFO', 3, 'Recieved message from popup window', {data});
623
639
  if (data.TERA && data.action == 'popupUserState' && data.user) { // Signal sent from landing page - we're logged in, yey!
624
- let $auth = app.service('$auth');
640
+ const $auth = app.service('$auth');
625
641
 
626
642
  // Accept user polyfill from opener
627
643
  $auth.state = 'user';
@@ -686,7 +702,7 @@ export default class TeraFyServer {
686
702
  * @returns {Promise<Project|null>} The currently active project, if any
687
703
  */
688
704
  getProject(): Promise<any | null> {
689
- let $projects = app.service('$projects');
705
+ const $projects = app.service('$projects');
690
706
 
691
707
  return $projects.promise()
692
708
  .then(()=> $projects.active
@@ -707,7 +723,7 @@ export default class TeraFyServer {
707
723
  * @returns {Promise<Array<Project>>} Collection of projects the user has access to
708
724
  */
709
725
  getProjects(): Promise<any[]> {
710
- let $projects = app.service('$projects');
726
+ const $projects = app.service('$projects');
711
727
 
712
728
  return $projects.promise()
713
729
  .then(()=> $projects.list.map((project: any) => ({
@@ -744,7 +760,7 @@ export default class TeraFyServer {
744
760
  * @returns {Promise<Project>} The active project
745
761
  */
746
762
  requireProject(options?: any): Promise<any> {
747
- let settings = {
763
+ const settings = {
748
764
  autoRequireUser: true,
749
765
  autoSetActiveProject: true,
750
766
  title: 'Select a project to work with',
@@ -760,7 +776,7 @@ export default class TeraFyServer {
760
776
  if (active) return active; // Use active project
761
777
 
762
778
  return new Promise((resolve, reject) => {
763
- let askProject = (): Promise<any> => Promise.resolve()
779
+ const askProject = (): Promise<any> => Promise.resolve()
764
780
  .then(()=> this.selectProject({
765
781
  allowCancel: false,
766
782
  }))
@@ -801,7 +817,7 @@ export default class TeraFyServer {
801
817
  * @returns {Promise<Project>} The active project
802
818
  */
803
819
  selectProject(options?: any): Promise<any> {
804
- let settings = {
820
+ const settings = {
805
821
  title: 'Select a project to work with',
806
822
  allowCancel: true,
807
823
  setActive: false,
@@ -886,7 +902,7 @@ export default class TeraFyServer {
886
902
  * @returns {Promise<Object>} The current project state snapshot
887
903
  */
888
904
  getProjectState(options?: any): Promise<any> {
889
- let settings = {
905
+ const settings = {
890
906
  autoRequire: true,
891
907
  paths: null,
892
908
  ...options,
@@ -919,7 +935,7 @@ export default class TeraFyServer {
919
935
  * @returns {Promise<*>} A promise which resolves to `value` when the operation has been dispatched to the server and saved
920
936
  */
921
937
  setProjectState(path: string | string[], value: any, options?: any): Promise<any> {
922
- let settings = {
938
+ const settings = {
923
939
  strategy: 'set',
924
940
  ...options,
925
941
  };
@@ -962,7 +978,7 @@ export default class TeraFyServer {
962
978
  let settings = { ...options }; // Initialize settings from the third argument if present
963
979
  if (!app.service('$projects').active) throw new Error('No active project');
964
980
 
965
- let target = app.service('$projects').active;
981
+ const target = app.service('$projects').active;
966
982
  let actualValue: any;
967
983
 
968
984
  if (typeof path == 'string' || Array.isArray(path)) { // Called as (path, value, options?) Set sub-object
@@ -1049,12 +1065,11 @@ export default class TeraFyServer {
1049
1065
  * @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if acationed
1050
1066
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
1051
1067
  * @param {Boolean} [options.showHiddenFiles=false] Whether hidden data.json files should be shown
1052
- * @param {FileFilters} [options.filter] Optional file filters
1053
1068
  *
1054
1069
  * @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
1055
1070
  */
1056
1071
  selectProjectFile(options?: any): Promise<any> {
1057
- let settings = {
1072
+ const settings = {
1058
1073
  title: 'Select a file',
1059
1074
  hint: null,
1060
1075
  save: false,
@@ -1114,7 +1129,7 @@ export default class TeraFyServer {
1114
1129
  * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
1115
1130
  */
1116
1131
  getProjectFiles(options?: any): Promise<any[]> {
1117
- let settings = {
1132
+ const settings = {
1118
1133
  autoRequire: true,
1119
1134
  lazy: true,
1120
1135
  meta: true,
@@ -1147,7 +1162,7 @@ export default class TeraFyServer {
1147
1162
  * @returns {Promise<ProjectFile>} The eventual fetched ProjectFile (or requested subkey)
1148
1163
  */
1149
1164
  getProjectFile(name: string, options?: any): Promise<any> {
1150
- let settings = {
1165
+ const settings = {
1151
1166
  subkey: null,
1152
1167
  cache: true,
1153
1168
  ...(typeof options == 'string' ? {subkey: options} : options),
@@ -1190,7 +1205,7 @@ export default class TeraFyServer {
1190
1205
  // Recursively search for the file to handle searching folders
1191
1206
  return findFileRecursively(files, name);
1192
1207
  })
1193
- .then((file: any) => file && settings.subkey ? (file as any)[settings.subkey] : file)
1208
+ .then((file: any) => file && settings.subkey ? (file)[settings.subkey] : file)
1194
1209
  }
1195
1210
 
1196
1211
  /**
@@ -1204,7 +1219,7 @@ export default class TeraFyServer {
1204
1219
  * @returns {*} The file contents in the requested format
1205
1220
  */
1206
1221
  getProjectFileContents(id: string, options?: any): Promise<any> {
1207
- let settings = {
1222
+ const settings = {
1208
1223
  format: 'blob',
1209
1224
  ...options,
1210
1225
  };
@@ -1397,7 +1412,7 @@ export default class TeraFyServer {
1397
1412
 
1398
1413
  if (fileContents === undefined) throw new Error('setProjectFileContents requires contents to save.');
1399
1414
 
1400
- let settings = {
1415
+ const settings = {
1401
1416
  id: fileId,
1402
1417
  autoRequire: true,
1403
1418
  hint: null,
@@ -1474,7 +1489,7 @@ export default class TeraFyServer {
1474
1489
 
1475
1490
  let cleanFolderPath = folderPath.trim();
1476
1491
  if (cleanFolderPath.startsWith('/')) {
1477
- cleanFolderPath = cleanFolderPath.substring(1);
1492
+ cleanFolderPath = cleanFolderPath.slice(1);
1478
1493
  }
1479
1494
  if (cleanFolderPath.endsWith('/')) {
1480
1495
  cleanFolderPath = cleanFolderPath.slice(0, -1);
@@ -1536,7 +1551,7 @@ export default class TeraFyServer {
1536
1551
 
1537
1552
  let cleanFolderPath = folderPath.trim();
1538
1553
  if (cleanFolderPath.startsWith('/')) {
1539
- cleanFolderPath = cleanFolderPath.substring(1);
1554
+ cleanFolderPath = cleanFolderPath.slice(1);
1540
1555
  }
1541
1556
  if (cleanFolderPath.endsWith('/')) {
1542
1557
  cleanFolderPath = cleanFolderPath.slice(0, -1);
@@ -1603,7 +1618,7 @@ export default class TeraFyServer {
1603
1618
  * @returns {Promise<Array<Ref>>} A collection of references from the selected file
1604
1619
  */
1605
1620
  selectProjectLibrary(options?: any): Promise<any[]> {
1606
- let settings = {
1621
+ const settings = {
1607
1622
  title: 'Select a citation library',
1608
1623
  hint: null,
1609
1624
  allowUpload: true,
@@ -1613,7 +1628,7 @@ export default class TeraFyServer {
1613
1628
  autoRequire: true,
1614
1629
  filters: {
1615
1630
  library: true,
1616
- ...(options?.filters ?? {}), // Use filters from options if provided
1631
+ ...options?.filters, // Use filters from options if provided
1617
1632
  },
1618
1633
  ...options,
1619
1634
  };
@@ -1643,16 +1658,16 @@ export default class TeraFyServer {
1643
1658
  * @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default bevahiour) or a whatever format was requested
1644
1659
  */
1645
1660
  getProjectLibrary(id: string, options?: any): Promise<any> {
1646
- let settings = {
1661
+ const settings = {
1647
1662
  format: 'pojo',
1648
1663
  autoRequire: true,
1664
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
1649
1665
  filter: (file: any) => true, // Default filter
1650
- // @ts-ignore
1651
- find: (files: any[]) => files.at(0), // Default find
1666
+ find: (files: any[]) => files[0], // Default find
1652
1667
  ...options,
1653
1668
  };
1654
1669
 
1655
- let filePath: string = app.service('$projects').decodeFilePath(id);
1670
+ const filePath: string = app.service('$projects').decodeFilePath(id);
1656
1671
 
1657
1672
  return Promise.resolve()
1658
1673
  .then(()=> settings.autoRequire && this.requireProject())
@@ -1727,7 +1742,7 @@ export default class TeraFyServer {
1727
1742
 
1728
1743
  if (libraryRefs === undefined) throw new Error('setProjectLibrary requires refs to save.');
1729
1744
 
1730
- let settings = {
1745
+ const settings = {
1731
1746
  id: fileId,
1732
1747
  refs: libraryRefs,
1733
1748
  format: 'auto',
@@ -1877,7 +1892,7 @@ export default class TeraFyServer {
1877
1892
  * @returns {Promise} A promise which resolves when the alert has been dismissed
1878
1893
  */
1879
1894
  uiAlert(text: string | any, options?: any): Promise<void> {
1880
- let settings = {
1895
+ const settings = {
1881
1896
  body: 'Alert!',
1882
1897
  isHtml: false,
1883
1898
  title: 'TERA',
@@ -1917,7 +1932,7 @@ export default class TeraFyServer {
1917
1932
  * @returns {Promise} A promise which resolves with `Promise.resolve('OK')` or rejects with `Promise.reject('CANCEL')`
1918
1933
  */
1919
1934
  uiConfirm(text: string | any, options?: any): Promise<'OK'> {
1920
- let settings = {
1935
+ const settings = {
1921
1936
  body: 'Confirm?',
1922
1937
  isHtml: false,
1923
1938
  title: 'TERA',
@@ -1946,7 +1961,7 @@ export default class TeraFyServer {
1946
1961
  },
1947
1962
  ],
1948
1963
  })
1949
- .then(()=> 'OK' as 'OK') // Resolve with 'OK' if OK button clicked
1964
+ .then(()=> 'OK' as const) // Resolve with 'OK' if OK button clicked
1950
1965
  .catch(()=> Promise.reject('CANCEL')) // Reject with 'CANCEL' if Cancel button clicked or closed
1951
1966
  );
1952
1967
  }
@@ -1956,7 +1971,7 @@ export default class TeraFyServer {
1956
1971
  * Present some JSON to the user
1957
1972
  *
1958
1973
  * @function uiJson
1959
- * @param {String|Object} [data] Data to display, if (and this doesn't contain 'body' or 'title') this populates `options.body`
1974
+ * @param {String|Object} data Data to display
1960
1975
  *
1961
1976
  * @param {Object} [options] Additional options to mutate behaviour
1962
1977
  * @param {String} [options.body=""] The body text to display above the JSON
@@ -1967,15 +1982,11 @@ export default class TeraFyServer {
1967
1982
  * @returns {Promise} A promise which resolves with `Promise.resolve('OK')`
1968
1983
  */
1969
1984
  uiJson(data: any, options?: any): Promise<'OK'> {
1970
- let settings = {
1985
+ const settings = {
1971
1986
  body: '',
1972
1987
  isHtml: false,
1973
1988
  title: 'TERA',
1974
- ...(
1975
- typeof data == 'string' ? {data: JSON.parse(data), ...options}
1976
- : typeof data == 'object' && !data.body && !data.title ? {data, ...options}
1977
- : options
1978
- ),
1989
+ ...options,
1979
1990
  };
1980
1991
 
1981
1992
  return this.requestFocus(()=>
@@ -1986,10 +1997,10 @@ export default class TeraFyServer {
1986
1997
  componentProps: {
1987
1998
  body: settings.body,
1988
1999
  isHtml: settings.isHtml,
1989
- data: settings.data,
2000
+ data: data,
1990
2001
  },
1991
2002
  })
1992
- .then(()=> 'OK' as 'OK') // Resolve with 'OK' if OK button clicked
2003
+ .then(()=> 'OK' as const) // Resolve with 'OK' if OK button clicked
1993
2004
  .catch(()=> Promise.reject('CANCEL')) // Reject with 'CANCEL' if Cancel button clicked or closed
1994
2005
  );
1995
2006
  }
@@ -2028,7 +2039,7 @@ export default class TeraFyServer {
2028
2039
  * @returns {Promise} A promise which resolves when the dialog has been updated
2029
2040
  */
2030
2041
  uiProgress(options?: any): Promise<void> {
2031
- let currentOptions = options === false ? {close: true} : options || {};
2042
+ const currentOptions = options === false ? {close: true} : options || {};
2032
2043
 
2033
2044
  if (currentOptions.close) { // Asked to close the dialog
2034
2045
  const closePromise = this._uiProgress.promise
@@ -2093,7 +2104,7 @@ export default class TeraFyServer {
2093
2104
  * @returns {Promise<*>} Either the eventual user value or a throw with `Promise.reject('CANCEL')`
2094
2105
  */
2095
2106
  uiPrompt(text: string | any, options?: any): Promise<any> {
2096
- let settings = {
2107
+ const settings = {
2097
2108
  body: '',
2098
2109
  isHtml: false,
2099
2110
  title: 'Input required',
@@ -2174,11 +2185,11 @@ export default class TeraFyServer {
2174
2185
  *
2175
2186
  * @returns {WindowProxy} The opened window object (if `noopener` is not set in permissions)
2176
2187
  */
2177
- uiWindow(url: string | URL, options?: any): WindowProxy | null {
2188
+ uiWindow(url: string | URL, options?: UiWindowOptions): WindowProxy | null {
2178
2189
  // Ensure this runs only in browser context
2179
2190
  if (typeof window === 'undefined' || typeof screen === 'undefined') return null;
2180
2191
 
2181
- let settings = {
2192
+ const settings = {
2182
2193
  width: 500,
2183
2194
  height: 600,
2184
2195
  center: true,
@@ -2223,7 +2234,7 @@ export default class TeraFyServer {
2223
2234
  // Ensure this runs only in browser context
2224
2235
  if (typeof window === 'undefined' || typeof document === 'undefined') return;
2225
2236
 
2226
- let settings = {
2237
+ const settings = {
2227
2238
  logo: false,
2228
2239
  ...options,
2229
2240
  };
@@ -2240,7 +2251,7 @@ export default class TeraFyServer {
2240
2251
 
2241
2252
  let compiledContent: Element;
2242
2253
  if (typeof content == 'string') {
2243
- let el = document.createElement('div')
2254
+ const el = document.createElement('div')
2244
2255
  el.innerHTML = content;
2245
2256
  // If the string contained multiple top-level elements, wrap them
2246
2257
  compiledContent = el.children.length === 1 ? el.firstElementChild! : el;
@@ -2252,7 +2263,7 @@ export default class TeraFyServer {
2252
2263
  compiledContent.classList.add('tera-fy-uiSplat');
2253
2264
 
2254
2265
  if (settings.logo) {
2255
- let logoEl = document.createElement('div');
2266
+ const logoEl = document.createElement('div');
2256
2267
  logoEl.innerHTML = `<img src="${typeof settings.logo == 'string' ? settings.logo : '/assets/logo/logo.svg'}" class="img-logo"/>`;
2257
2268
  // Prepend logo within the content element
2258
2269
  compiledContent.prepend(logoEl);
@@ -2264,14 +2275,14 @@ export default class TeraFyServer {
2264
2275
 
2265
2276
  // Utility - debug() {{{
2266
2277
 
2267
- /* eslint-disable jsdoc/check-param-names */
2268
2278
  /**
2269
- * Debugging output function
2270
- * This function will only act if `settings.devMode` is truthy
2279
+ * Debugging output function.
2280
+ * This function will only act if `settings.devMode` is truthy.
2271
2281
  *
2272
- * @param {'INFO'|'LOG'|'WARN'|'ERROR'} [method='LOG'] Logging method to use
2273
- * @param {Number} [verboseLevel=1] The verbosity level to trigger at. If `settings.verbosity` is lower than this, the message is ignored
2274
- * @param {...*} [msg] Output to show
2282
+ * @param {...any} inputArgs The arguments to process for debugging. The function signature is flexible: `debug([method], [verboseLevel], ...msg)`.
2283
+ * - The first argument can optionally be a logging method string: 'INFO', 'LOG', 'WARN', or 'ERROR'. Defaults to 'LOG'.
2284
+ * - The next argument can optionally be a numeric verbosity level. The message is ignored if `settings.verbosity` is lower than this level. Defaults to 1.
2285
+ * - All subsequent arguments are passed to the console as the log message.
2275
2286
  */
2276
2287
  debug(...inputArgs: any[]): void {
2277
2288
  // Ensure console exists
@@ -2280,7 +2291,7 @@ export default class TeraFyServer {
2280
2291
 
2281
2292
  let method: keyof Console = 'log'; // Default method
2282
2293
  let verboseLevel = 1;
2283
- let msgArgs = [...inputArgs]; // Copy args to modify
2294
+ const msgArgs = [...inputArgs]; // Copy args to modify
2284
2295
 
2285
2296
  // Argument mangling for prefix method + verbosity level {{{
2286
2297
  if (typeof msgArgs[0] == 'string' && ['INFO', 'LOG', 'WARN', 'ERROR'].includes(msgArgs[0].toUpperCase())) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "TERA website worker",
5
5
  "scripts": {
6
6
  "dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.ts --outfile=dist/terafy.js --minify --serve --servedir=.",
@@ -113,12 +113,12 @@
113
113
  "mitt": "^3.0.1",
114
114
  "nanoid": "^5.1.2",
115
115
  "p-retry": "^6.2.1",
116
- "release-it": "^18.1.2",
116
+ "release-it": "^19.2.4",
117
117
  "uuid": "^11.1.0"
118
118
  },
119
119
  "devDependencies": {
120
120
  "@momsfriendlydevco/eslint-config": "^2.3.1",
121
- "@release-it/conventional-changelog": "^10.0.0",
121
+ "@release-it/conventional-changelog": "^10.0.5",
122
122
  "@types/detect-port": "^2.0.0",
123
123
  "@types/http-proxy": "^1.17.16",
124
124
  "@types/lodash-es": "^4.17.12",
@@ -127,10 +127,12 @@
127
127
  "@typescript-eslint/eslint-plugin": "^8.41.0",
128
128
  "@typescript-eslint/parser": "^8.41.0",
129
129
  "concurrently": "^9.1.2",
130
+ "conventional-changelog-eslint": "^6.0.0",
130
131
  "documentation": "^14.0.3",
131
132
  "esbuild": "^0.25.0",
132
133
  "eslint": "^9.34.0",
133
134
  "eslint-plugin": "^1.0.1",
135
+ "globals": "^16.5.0",
134
136
  "nodemon": "^3.1.9",
135
137
  "typescript": "^5.9.2",
136
138
  "typescript-eslint": "^8.41.0"
package/plugins/base.ts CHANGED
@@ -11,7 +11,7 @@ export default class TeraFyPlugin {
11
11
  /**
12
12
  * Optional function to be included when the main TeraFyClient is initalized
13
13
  */
14
- init() {}
14
+ init(): any {}
15
15
 
16
16
 
17
17
  /**
@@ -20,6 +20,7 @@ export default class TeraFyPlugin {
20
20
  * @param {TeraFy} terafy The TeraFy client this plugin is being initalized against
21
21
  * @param {Object} [options] Additional options to mutate behaviour
22
22
  */
23
- constructor(terafy: TeraFy, options: Object) { // eslint-disable-line no-unused-vars
23
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
24
+ constructor(terafy: TeraFy, options: object) { // eslint-disable-line no-unused-vars
24
25
  }
25
26
  }
@@ -1,5 +1,5 @@
1
- import {initializeApp as Firebase, FirebaseApp} from 'firebase/app';
2
- import {getFirestore as Firestore, Firestore as FirestoreInstance} from 'firebase/firestore';
1
+ import {initializeApp as Firebase} from 'firebase/app';
2
+ import {getFirestore as Firestore} from 'firebase/firestore';
3
3
  import Supabasey from '@iebh/supabasey';
4
4
  import Syncro from '../lib/syncro/syncro.js';
5
5
  import TeraFyPluginBase from './base.js';
@@ -23,24 +23,25 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
23
23
  // Declare properties expected by the class methods or potentially inherited
24
24
  getCredentials!: () => Promise<Record<string, any>>;
25
25
  requireProject!: () => Promise<{ id: string }>;
26
+ // eslint-disable-next-line no-unused-vars
26
27
  debug!: (...args: any[]) => void;
27
28
 
28
29
 
29
30
  /**
30
31
  * @interface
31
32
  * The Syncro#reactive option to use when creating new Syncro instances
32
- * This is expected to be overriden by other plugins
33
+ * This is expected to be overridden by other plugins
33
34
  * If falsy the Syncro module will fall back to its internal (POJO only) getReactive() function
34
35
  *
35
36
  * @name getReactive
36
37
  * @type {Function} A reactive function as defined in Syncro
37
38
  */
38
- getReactive?: Function;
39
+ getReactive?: () => any;
39
40
 
40
41
 
41
42
  /**
42
43
  * Setup Firebase + Firestore + Supabase
43
- * Default credentials (Firebase + Supabase) will be retrieved from `getCredentials()` unless overriden here
44
+ * Default credentials (Firebase + Supabase) will be retrieved from `getCredentials()` unless overridden here
44
45
  *
45
46
  * @param {Object} options Additional options to mutate behaviour (defaults to the main teraFy settings)
46
47
  * @param {String} [options.firebaseApiKey] Firebase API key
@@ -53,7 +54,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
53
54
  * @returns {Promise} A Promise which will resolve when the init process has completed
54
55
  */
55
56
  async init(options?: any): Promise<void> { // Add optional '?' and type 'any', keep async Promise<void>
56
- let settings = {
57
+ const settings = {
57
58
  firebaseApiKey: null,
58
59
  firebaseAuthDomain: null,
59
60
  firebaseProjectId: null,
@@ -64,7 +65,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
64
65
  ...options,
65
66
  };
66
67
 
67
- let emptyValues = Object.keys(settings).filter(k => k === null);
68
+ const emptyValues = Object.keys(settings).filter(k => k === null);
68
69
  if (emptyValues.length > 0)
69
70
  throw new Error('Firebase plugin requires mandatory options: ' + emptyValues.join(', '));
70
71
 
@@ -88,7 +89,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
88
89
  /**
89
90
  * Mount the given namespace against `namespaces[name]`
90
91
  *
91
- * @param {'_PROJECT'|String} name The name/Syncro path of the namespace to mount (or '_PROJECT' for the project mountpoint)
92
+ * @param {'_PROJECT'|String} name The name/Syncro path of the namespace to mount (or '_PROJECT' for the project mount-point)
92
93
  *
93
94
  * @returns {Promise} A promise which resolves when the operation has completed
94
95
  */
@@ -98,7 +99,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
98
99
  return Promise.resolve()
99
100
  .then(()=> this.requireProject())
100
101
  .then(project => {
101
- let path = name == '_PROJECT'
102
+ const path = name == '_PROJECT'
102
103
  ? `projects::${project.id}`
103
104
  : `project_namespaces::${project.id}::${name}`;
104
105
 
@@ -124,7 +125,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
124
125
  * @returns {Promise} A promise which resolves when the operation has completed
125
126
  */
126
127
  _unmountNamespace(name: string): Promise<void | any[]> { // Add type 'string'
127
- let syncro = this.syncros[name]; // Create local alias for Syncro before we detach it
128
+ const syncro = this.syncros[name]; // Create local alias for Syncro before we detach it
128
129
 
129
130
  // Detach local state
130
131
  delete this.namespaces[name];
@@ -132,7 +133,7 @@ export default class TeraFyPluginFirebase extends TeraFyPluginBase {
132
133
 
133
134
  // Check if syncro exists before calling destroy
134
135
  if (syncro) {
135
- return syncro.destroy(); // Trigger Syncro distruction
136
+ return syncro.destroy(); // Trigger Syncro destruction
136
137
  } else {
137
138
  return Promise.resolve(); // Or handle the case where syncro doesn't exist
138
139
  }
package/plugins/vue2.ts CHANGED
@@ -27,7 +27,7 @@ import TeraFyPluginFirebase from './firebase.js';
27
27
  export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
28
28
 
29
29
  /**
30
- * Local Vue@2 library to use, set during constuctor
30
+ * Local Vue@2 library to use, set during constructor
31
31
  *
32
32
  * @type {Vue}
33
33
  */
@@ -68,7 +68,7 @@ export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
68
68
  * @returns {Promise} A Promise which will resolve when the init process has completed
69
69
  */
70
70
  async init(options: any) {
71
- let settings = {
71
+ const settings = {
72
72
  app: null,
73
73
  Vue: null,
74
74
  globalName: '$tera',
@@ -86,17 +86,17 @@ export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
86
86
  this.Vue.prototype[settings.globalName] = this;
87
87
 
88
88
  await super.init(settings); // Initalize parent class Firebase functionality
89
- // @ts-ignore
89
+ // @ts-expect-error TODO: Track down why eslint throws error
90
90
  this.project = await this.mountNamespace('_PROJECT');
91
91
  }
92
92
 
93
93
 
94
94
  /** @override */
95
- // @ts-ignore
95
+ // @ts-expect-error TODO: Work out why TS doesn't like overrides
96
96
  getReactive(value: any) {
97
- let doc = this.Vue.observable(value);
97
+ const doc = this.Vue.observable(value);
98
98
 
99
- let watcherPath = `_teraFy_${this.reactiveId++}`;
99
+ const watcherPath = `_teraFy_${this.reactiveId++}`;
100
100
  this.app[watcherPath] = doc; // Attach onto app so we can use $watch later on
101
101
 
102
102
  return {
@@ -110,6 +110,7 @@ export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
110
110
  getState: () => {
111
111
  return cloneDeep(doc);
112
112
  },
113
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
113
114
  watch: (cb: Function) => {
114
115
  this.app.$watch(watcherPath, cb, {deep: true});
115
116
  },