@iebh/tera-fy 2.3.7 → 2.3.9

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 (41) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/lib/projectFile.js +118 -6
  3. package/dist/lib/projectFile.js.map +1 -1
  4. package/dist/lib/syncro/entities.d.ts +7 -1
  5. package/dist/lib/syncro/entities.js +79 -95
  6. package/dist/lib/syncro/entities.js.map +1 -1
  7. package/dist/lib/syncro/keyed.js +24 -23
  8. package/dist/lib/syncro/keyed.js.map +1 -1
  9. package/dist/lib/syncro/syncro.d.ts +15 -2
  10. package/dist/lib/syncro/syncro.js +124 -43
  11. package/dist/lib/syncro/syncro.js.map +1 -1
  12. package/dist/lib/terafy.bootstrapper.js +11 -11
  13. package/dist/lib/terafy.bootstrapper.js.map +1 -1
  14. package/dist/lib/terafy.client.js +157 -158
  15. package/dist/lib/terafy.client.js.map +1 -1
  16. package/dist/lib/terafy.proxy.js +32 -27
  17. package/dist/lib/terafy.proxy.js.map +1 -1
  18. package/dist/lib/terafy.server.js +52 -54
  19. package/dist/lib/terafy.server.js.map +1 -1
  20. package/dist/plugin.vue2.es2019.js +27 -27
  21. package/dist/plugins/firebase.js +22 -10
  22. package/dist/plugins/firebase.js.map +1 -1
  23. package/dist/plugins/vue2.d.ts +1 -2
  24. package/dist/plugins/vue2.js +24 -17
  25. package/dist/plugins/vue2.js.map +1 -1
  26. package/dist/plugins/vue3.d.ts +1 -2
  27. package/dist/plugins/vue3.js +7 -11
  28. package/dist/plugins/vue3.js.map +1 -1
  29. package/dist/terafy.bootstrapper.es2019.js +2 -2
  30. package/dist/terafy.bootstrapper.js +2 -2
  31. package/dist/terafy.es2019.js +2 -2
  32. package/dist/terafy.js +1 -1
  33. package/lib/syncro/entities.ts +94 -103
  34. package/lib/syncro/keyed.ts +1 -0
  35. package/lib/syncro/syncro.ts +44 -8
  36. package/lib/terafy.client.ts +0 -1
  37. package/lib/terafy.server.ts +6 -7
  38. package/package.json +4 -3
  39. package/plugins/vue2.ts +2 -3
  40. package/plugins/vue3.ts +2 -4
  41. package/tsconfig.json +3 -3
@@ -21,16 +21,24 @@ import {
21
21
  // @ts-expect-error No declaration file for marshal
22
22
  import marshal from '@momsfriendlydevco/marshal';
23
23
  import {nanoid} from 'nanoid';
24
+ import PromiseThrottle from 'p-throttle';
24
25
  import PromiseRetry from 'p-retry';
25
26
  import {FirebaseApp, FirebaseError} from 'firebase/app';
26
27
  import { BoundSupabaseyFunction } from '@iebh/supabasey';
28
+ import type { PostgresSql } from './entities.js';
27
29
 
30
+ interface ThrottleOptions<T = any> {
31
+ limit: number,
32
+ interval: number,
33
+ strict: boolean,
34
+ }
28
35
 
29
36
  interface ReactiveWrapper<T = any> {
30
37
  doc: T;
31
38
  setState: (newState: T) => void;
32
39
  getState: () => T;
33
40
  watch: (cb: (newState: T) => void) => void;
41
+ throttle?: ThrottleOptions | true,
34
42
  }
35
43
 
36
44
  interface PathSplitResult {
@@ -74,6 +82,14 @@ export default class Syncro {
74
82
  static supabasey: BoundSupabaseyFunction;
75
83
 
76
84
 
85
+ /**
86
+ * Postgres SQL instance in use (injected by the Cloudflare Worker runtime via Hyperdrive)
87
+ *
88
+ * @type {PostgresSql}
89
+ */
90
+ static db: PostgresSql;
91
+
92
+
77
93
  /**
78
94
  * The current user session, should be unique for the user + browser tab
79
95
  * Used by the heartbeat system
@@ -169,7 +185,7 @@ export default class Syncro {
169
185
  * @param {*...} [msg] The message to output
170
186
  */
171
187
  debugError(...msg: any[]) {
172
- console.log(`[Syncro ${this.path}]`, ...msg);
188
+ console.warn(`[Syncro ${this.path}]`, ...msg);
173
189
  }
174
190
 
175
191
 
@@ -202,9 +218,10 @@ export default class Syncro {
202
218
  */
203
219
  destroy(): Promise<any[]> {
204
220
  this.debug('Destroy!');
205
- return Promise.all(this._destroyActions
206
- .map(fn => fn())
207
- )
221
+ return Promise.resolve()
222
+ .then(()=> Promise.all(this._destroyActions // eslint-disable @typescript-eslint/await-thenable
223
+ .map(fn => fn())
224
+ ))
208
225
  .then(()=> this._destroyActions = []) // Reset list of actions to perform when terminating
209
226
  }
210
227
 
@@ -213,7 +230,7 @@ export default class Syncro {
213
230
  * Actions to preform when we are destroying this instance
214
231
  * This is an array of function callbacks to execute in parallel when `destroy()` is called
215
232
  *
216
- * @type {Array<function>}
233
+ * @type {Array<Function<Promise>>}
217
234
  */
218
235
  _destroyActions: Array<() => void> = [];
219
236
 
@@ -531,6 +548,25 @@ export default class Syncro {
531
548
  // Construct a reactive component
532
549
  reactive = this.getReactive(initialState);
533
550
  if (!reactive.doc || !reactive.setState || !reactive.getState || !reactive.watch) throw new Error('Syncro.getReactive() requires a returned `doc`, `setState()`, `getState()` + `watch()`');
551
+
552
+ // Accept throttling for reactiveWrapper if present
553
+ if (reactive.throttle) { // Wanting to throttle - handed either truthy or an object of throttle settings
554
+ let throttleSettings = {
555
+ limit: 2,
556
+ interval: 100, // i.e. 2 calls within 100ms, otherwise throttle
557
+ strict: false,
558
+ ...(typeof reactive.throttle == 'object' && reactive.throttle), // Import throttle settings if any
559
+ };
560
+
561
+ // Wrap original reactive.setState() in a throttle function
562
+ let originalSetState = reactive.setState;
563
+ reactive.setState = PromiseThrottle({
564
+ limit: throttleSettings.limit,
565
+ interval: throttleSettings.interval,
566
+ onDelay: ()=> this.debug('Throttling excessive Syncro.setState() writes'),
567
+ })(originalSetState);
568
+ }
569
+
534
570
  this.value = doc = reactive.doc;
535
571
 
536
572
  this.debug('Initial state', {doc});
@@ -562,7 +598,7 @@ export default class Syncro {
562
598
  reactive.watch(throttle((newState: any) => {
563
599
  this.debug('Local change', {newState});
564
600
  this.markDirty();
565
- this.setFirestoreState(newState, {method: 'merge'});
601
+ this.setFirestoreState(newState, {method: 'merge'}); // eslint-disable-line @typescript-eslint/no-floating-promises
566
602
  }, this.throttle));
567
603
 
568
604
  await this.setHeartbeat(true, {
@@ -656,7 +692,7 @@ export default class Syncro {
656
692
  *
657
693
  * @param {Object} [options] Additional options to mutate behaviour
658
694
  * @param {Boolean} [options.immediate=false] Fire a heartbeat as soon as this function is called, this is only really useful on mount
659
- * @returns Promise that resolves to void or void
695
+ * @returns {Promise|Void} A promise that resolves when completed (if `{immediate:true}`) or void
660
696
  */
661
697
  setHeartbeat(enable: boolean = true, options?: any): Promise<void> | void {
662
698
  const settings = {
@@ -673,7 +709,7 @@ export default class Syncro {
673
709
  await this.heartbeat();
674
710
 
675
711
  // If we're enabled - schedule the next heartbeat timer
676
- if (enable) this.setHeartbeat(true); // Reschedule
712
+ if (enable) this.setHeartbeat(true); // eslint-disable-line @typescript-eslint/no-floating-promises
677
713
  };
678
714
 
679
715
  this._heartbeatTimer = setTimeout(heartbeatAction, this.config.heartbeatInterval);
@@ -68,7 +68,6 @@ export default class TeraFy {
68
68
  * Event emitter subscription endpoint
69
69
  * @type {Mitt}
70
70
  */
71
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
72
71
  // @ts-ignore - Because mitt is exported as cjs typescript has trouble resolving default export
73
72
  events = Mitt();
74
73
 
@@ -619,7 +619,7 @@ export default class TeraFyServer {
619
619
  await app.service('$projects').refresh();
620
620
  return;
621
621
  } catch (e: any) {
622
- throw new Error(`Failed to decode local dev state - ${e.toString()}`);
622
+ throw new Error(`Failed to decode local dev state - ${e.toString()}`, {cause: e});
623
623
  }
624
624
  }
625
625
 
@@ -1000,7 +1000,6 @@ export default class TeraFyServer {
1000
1000
  .then(()=> pathTools.get(target, path));
1001
1001
  } else { // Called as (value, options?) - Populate entire project layout
1002
1002
  actualValue = path; // The first argument is the value
1003
- settings = { ...value }; // The second argument holds the options
1004
1003
  pathTools.defaults(target, actualValue);
1005
1004
  this.debug('INFO', 1, 'setProjectStateDefaults', {
1006
1005
  defaults: actualValue,
@@ -1360,7 +1359,7 @@ export default class TeraFyServer {
1360
1359
  } catch (error: any) {
1361
1360
  this.debug('ERROR', 1, `Error during project file move from "${sourceStoragePath}" to "${targetStoragePath}":`, error);
1362
1361
  // Re-throw a more specific error or the original error for the caller to handle.
1363
- throw new Error(`Failed to move project file "${sourceId}" to "${newName}": ${error.message || String(error)}`);
1362
+ throw new Error(`Failed to move project file "${sourceId}" to "${newName}": ${error.message || String(error)}`, {cause: error});
1364
1363
  }
1365
1364
  }
1366
1365
 
@@ -1397,7 +1396,7 @@ export default class TeraFyServer {
1397
1396
  */
1398
1397
  setProjectFileContents(id: string | any | null, contents: any, options?: any): Promise<null> {
1399
1398
  // Argument Mangling Logic (Simplified)
1400
- let fileId: string | null = null;
1399
+ let fileId: string | null = null; // eslint-disable-line no-useless-assignment
1401
1400
  let fileContents: any;
1402
1401
  let mergedOptions: any;
1403
1402
 
@@ -1527,7 +1526,7 @@ export default class TeraFyServer {
1527
1526
  this.debug('INFO', 2, `Project folder "${cleanFolderPath}" ensured/created via placeholder at "${relativePlaceholderPath}".`);
1528
1527
  } catch (error: any) {
1529
1528
  this.debug('ERROR', 1, `Failed to create/ensure project folder "${cleanFolderPath}" via placeholder "${relativePlaceholderPath}"`, error);
1530
- throw new Error(`Failed to create project folder "${cleanFolderPath}": ${error.message || String(error)}`);
1529
+ throw new Error(`Failed to create project folder "${cleanFolderPath}": ${error.message || String(error)}`, {cause: error});
1531
1530
  }
1532
1531
  }
1533
1532
 
@@ -1602,7 +1601,7 @@ export default class TeraFyServer {
1602
1601
  this.debug('INFO', 2, `Project folder "${cleanFolderPath}" (prefix "${pathPrefix}") and its ${filesToDelete.length} contents deleted.`);
1603
1602
  } catch (error: any) {
1604
1603
  this.debug('ERROR', 1, `Failed to delete contents of project folder "${cleanFolderPath}" (prefix "${pathPrefix}")`, error);
1605
- throw new Error(`Failed to delete project folder "${cleanFolderPath}": ${error.message || String(error)}`);
1604
+ throw new Error(`Failed to delete project folder "${cleanFolderPath}": ${error.message || String(error)}`, {cause: error});
1606
1605
  }
1607
1606
  return null;
1608
1607
  }
@@ -1728,7 +1727,7 @@ export default class TeraFyServer {
1728
1727
  */
1729
1728
  setProjectLibrary(id: string | any | null, refs?: any, options?: any): Promise<null> {
1730
1729
  // Argument Mangling Logic (Simplified)
1731
- let fileId: string | null = null;
1730
+ let fileId: string | null = null; // eslint-disable-line no-useless-assignment
1732
1731
  let libraryRefs: any;
1733
1732
  let mergedOptions: any;
1734
1733
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "2.3.7",
3
+ "version": "2.3.9",
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=.",
@@ -15,7 +15,7 @@
15
15
  "build:docs:markdown": "documentation build lib/terafy.client.ts lib/projectFile.ts --format md --markdown-toc --output api.md",
16
16
  "lint": "eslint",
17
17
  "release": "release-it",
18
- "watch": "nodemon --watch lib --watch plugins --exec \"npm run build\""
18
+ "watch": "nodemon --watch lib --watch plugins --ext ts --exec \"npm run build\""
19
19
  },
20
20
  "type": "module",
21
21
  "imports": {
@@ -113,11 +113,12 @@
113
113
  "mitt": "^3.0.1",
114
114
  "nanoid": "^5.1.6",
115
115
  "p-retry": "^7.1.1",
116
+ "p-throttle": "^8.1.0",
116
117
  "release-it": "^19.2.4",
117
118
  "uuid": "^13.0.0"
118
119
  },
119
120
  "devDependencies": {
120
- "@momsfriendlydevco/eslint-config": "^2.4.1",
121
+ "@momsfriendlydevco/eslint-config": "^2.4.2",
121
122
  "@release-it/conventional-changelog": "^10.0.5",
122
123
  "@types/detect-port": "^2.0.0",
123
124
  "@types/http-proxy": "^1.17.17",
package/plugins/vue2.ts CHANGED
@@ -59,11 +59,10 @@ export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
59
59
  /**
60
60
  * Install into Vue@2
61
61
  *
62
- * @param {Object} options Additional options to mutate behaviour, see TeraFyPluginFirebase
62
+ * @param {Object} options Additional options to mutate behaviour, see TeraFyPluginFirebase for additional options
63
63
  * @param {Object} options.app Root level Vue app to bind against
64
64
  * @param {Vue} options.Vue Vue@2 instance to bind against
65
65
  * @param {String} [options.globalName='$tera'] Global property to allocate this service as within Vue2
66
- * @param {*...} [options...] see TeraFyPluginFirebase
67
66
  *
68
67
  * @returns {Promise} A Promise which will resolve when the init process has completed
69
68
  */
@@ -117,4 +116,4 @@ export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
117
116
  };
118
117
  }
119
118
 
120
- }
119
+ }
package/plugins/vue3.ts CHANGED
@@ -39,8 +39,7 @@ export default class TeraFyPluginVue3 extends TeraFyPluginFirebase {
39
39
  /**
40
40
  * Init the project including create a reactive mount for the active project
41
41
  *
42
- * @param {Object} options Additional options to mutate behaviour
43
- * @param {*} [options...] see TeraFyPluginFirebase
42
+ * @param {Object} options Additional options to mutate behaviour, see TeraFyPluginFirebase
44
43
  */
45
44
  async init(options: Record<string, any>) {
46
45
  await super.init(options); // Initalize parent class Firebase functionality
@@ -103,5 +102,4 @@ export default class TeraFyPluginVue3 extends TeraFyPluginFirebase {
103
102
 
104
103
  };
105
104
  }
106
-
107
- }
105
+ }
package/tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
3
+ "target": "ES2022",
4
4
  "module": "NodeNext",
5
5
  "moduleResolution": "NodeNext",
6
6
  "declaration": true,
@@ -25,6 +25,6 @@
25
25
  ],
26
26
  "exclude": [
27
27
  "node_modules",
28
- "dist",
28
+ "dist"
29
29
  ]
30
- }
30
+ }