@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
@@ -0,0 +1,328 @@
1
+ import { DocumentReference, Firestore } from 'firebase/firestore';
2
+ import { FirebaseApp } from 'firebase/app';
3
+ import { BoundSupabaseyFunction } from '@iebh/supabasey';
4
+ interface ReactiveWrapper<T = any> {
5
+ doc: T;
6
+ setState: (newState: T) => void;
7
+ getState: () => T;
8
+ watch: (cb: (newState: T) => void) => void;
9
+ }
10
+ interface PathSplitResult {
11
+ fsCollection: string;
12
+ fsId: string;
13
+ entity: string;
14
+ id: string;
15
+ relation?: string;
16
+ }
17
+ /**
18
+ * @class Syncro
19
+ * TERA Isomorphic Syncro class
20
+ * Slurp an entity from Supabase and hold it in Firebase/Firestore as a "floating" entity which periodically gets flushed back to Supabase and eventually completely cleaned up
21
+ * This class tries to be as independent as possible to help with adapting it to various front-end TERA-fy plugin frameworks
22
+ */
23
+ export default class Syncro {
24
+ /**
25
+ * Firebase instance in use
26
+ *
27
+ * @type {FirebaseApp}
28
+ */
29
+ static firebase: FirebaseApp;
30
+ /**
31
+ * Firestore instance in use
32
+ *
33
+ * @type {Firestore}
34
+ */
35
+ static firestore: Firestore;
36
+ /**
37
+ * Supabasey instance in use
38
+ *
39
+ * @type {Supabasey}
40
+ */
41
+ static supabasey: BoundSupabaseyFunction;
42
+ /**
43
+ * The current user session, should be unique for the user + browser tab
44
+ * Used by the heartbeat system
45
+ *
46
+ * @type {String}
47
+ */
48
+ static session: string | undefined;
49
+ /**
50
+ * OPTIONAL SyncroEntiries from './entiries.js' if its required
51
+ * This only gets populated if `config.forceLocalInit` is truthy and we've mounted at least one Syncro
52
+ *
53
+ * @type {Record<string, any>}
54
+ */
55
+ static SyncroEntities: Record<string, any>;
56
+ /**
57
+ * This instances fully formed string path
58
+ *
59
+ * @type {String}
60
+ */
61
+ path: string;
62
+ /**
63
+ * The Firestore docHandle when calling various Firestore functions
64
+ *
65
+ * @type {DocumentReference | undefined}
66
+ */
67
+ docRef: DocumentReference | undefined;
68
+ /**
69
+ * The reactive object managed by this Syncro instance
70
+ * The nature of this varies by framework and what `getReactive()` provides
71
+ *
72
+ * @type {any}
73
+ */
74
+ value: any;
75
+ /**
76
+ * Default throttle to apply for writes
77
+ *
78
+ * @type {Number} Throttle time in milliseconds
79
+ */
80
+ throttle: number;
81
+ /**
82
+ * Various Misc config for the Syncro instance
83
+ *
84
+ * @type {Object}
85
+ * @property {Number} heartbeatinterval Time in milliseconds between heartbeat beacons
86
+ * @property {String} syncroRegistryUrl The prefix Sync worker URL, used to populate Syncros and determine their active status
87
+ * @property {Object} context Additional named parameters to pass to callbacks like initState
88
+ */
89
+ config: {
90
+ heartbeatInterval: number;
91
+ syncroRegistryUrl: string;
92
+ context: Record<string, any>;
93
+ };
94
+ /**
95
+ * Whether the next heartbeat should be marked as 'dirty'
96
+ * This indicates that at least one change has occured since the last hearbeat and the server should perform a flush (but not a clean)
97
+ * This flag is only transmitted once in the next heartbeat before being reset
98
+ *
99
+ * @see markDirty()
100
+ * @type {Boolean}
101
+ */
102
+ isDirty: boolean;
103
+ /**
104
+ * @interface
105
+ * Debugging printer for this instance
106
+ * Defaults to doing nothing
107
+ *
108
+ * @param {*...} [msg] The message to output
109
+ */
110
+ debug(...msg: any[]): void;
111
+ /**
112
+ * @interface
113
+ * Debugging printer specifically for error messages
114
+ * Defaults to using console.log()
115
+ *
116
+ * @param {*...} [msg] The message to output
117
+ */
118
+ debugError(...msg: any[]): void;
119
+ /**
120
+ * Instance constructor
121
+ *
122
+ * @param {String} path Mount path for the Syncro. Should be in the form `${ENTITY}::${ID}(::${RELATION})?`
123
+ * @param {Object} [options] Additional instance setters (mutates instance directly), note that the `config` subkey is merged with the existing config rather than assigned
124
+ */
125
+ constructor(path: string, options?: any);
126
+ /**
127
+ * Instance destruction trigger
128
+ * This will unsubscribe from various facilities and release the object for cleanup
129
+ *
130
+ * @returns {Promise} A promise which resolves when the operation has completed
131
+ */
132
+ destroy(): Promise<any[]>;
133
+ /**
134
+ * Actions to preform when we are destroying this instance
135
+ * This is an array of function callbacks to execute in parallel when `destroy()` is called
136
+ *
137
+ * @type {Array<() => void>}
138
+ */
139
+ _destroyActions: Array<() => void>;
140
+ /**
141
+ * Function to return whatever the local framework uses as a reactive object
142
+ * This should respond with an object of mandatory functions to watch for changes and remerge them
143
+ *
144
+ * @param {Object} value Initial value of the reactive
145
+ *
146
+ * @returns {ReactiveWrapper} A reactive object prototype
147
+ * @property {Object} doc The reactive object
148
+ * @property {Function} setState Function used to overwrite the default state, called as `(newState:Object)`
149
+ * @property {Function} getState Function used to fetch the current snapshot state, called as `()`
150
+ * @property {Function} watch Function used to set up state watchers, should call its callback when a change is detected, called as `(cb:Function)`
151
+ */
152
+ getReactive(value: any): ReactiveWrapper;
153
+ /**
154
+ * Returns the split entity + ID relationship from a given session path
155
+ * This funciton checks for valid UUID format strings + that the entity is a known/supported entity (see `knownEntities`)
156
+ * NOTE: When used by itself (i.e. ignoring response) this function can also act as a guard that a path is valid
157
+ *
158
+ * INPUT: `widgets::UUID` -> `{entity:'widgets', id:UUID}`
159
+ * INPUT: `widgets::UUID::thing` -> `{entity:'widgets', id:UUID, relation:'thing'}`
160
+ *
161
+ * @param {String} path The input session path of the form `${ENTITY}::${ID}`
162
+ * @param {Object} [options] Additional options to mutate behaviour
163
+ * @param {Boolean} [options.allowAsterisk=false] Whether to allow the meta asterisk character when recognising paths, this is used by the SyncroKeyed class
164
+ *
165
+ * @returns {PathSplitResult} An object composed of the session path components
166
+ */
167
+ static pathSplit(path: string, options?: any): PathSplitResult;
168
+ /**
169
+ * Convert local POJO -> Firestore compatible object
170
+ * This applies the following mutations to the incoming object:
171
+ *
172
+ * 1. Arrays are converted to Objects (Firestore cannot store nested arrays)
173
+ * 2. All non-POJO objects (e.g. Dates) to a symetric object
174
+ *
175
+ * @param {Object} snapshot The current state to convert
176
+ * @returns {Object} A Firebase compatible object
177
+ */
178
+ static toFirestore(snapshot?: any): any;
179
+ /**
180
+ * Convert local Firestore compatible object -> local POJO
181
+ * This reverses the mutations listed in `toFirestore()`
182
+ *
183
+ * @param {Object} snapshot The raw Firebase state to convert
184
+ * @returns {Object} A JavaScript POJO representing the converted state
185
+ */
186
+ static fromFirestore(snapshot?: any): any;
187
+ /**
188
+ * Convert a raw POJO into Firestore field layout
189
+ * Field structures are usually consumed by the Firestore ReST API and need converting before being used
190
+ * NOTE: This does not serialize the incoming data so you likely want to use this as `toFirestoreFields(toFirestore(data))`
191
+ *
192
+ * @see https://stackoverflow.com/a/62304377
193
+ * @param {Object} data The raw value to convert
194
+ * @returns {Object} A Firestore compatible, typed data structure
195
+ */
196
+ static toFirestoreFields(data: any): Record<string, any>;
197
+ /**
198
+ * Convert a Firestore field dump into a native POJO
199
+ * Field structures are usually provided by the Firestore ReST API and need de-typing back into a native document
200
+ * NOTE: This does not deserialize the result so you likely want to use this as `fromFirestore(fromFirestoreFields(response.fields))`
201
+ *
202
+ * @see https://stackoverflow.com/a/62304377
203
+ * @param {Object} fields The raw Snapshot to convert
204
+ * @returns {Object} A JavaScript POJO representing the converted state
205
+ */
206
+ static fromFirestoreFields(fields?: any): any;
207
+ /**
208
+ * Perform a one-off fetch of a given Syncro path
209
+ *
210
+ * @param {String} path The Syncro entity + ID path. Takes the form `ENTITY::ID`
211
+ *
212
+ * @returns {Promise<Object|Null>} An eventual snapshot of the given path, if the entity doesn't exist null is returned
213
+ */
214
+ static getSnapshot(path: string): Promise<any | null>;
215
+ /**
216
+ * Perform a one-off set/merge operation against
217
+ *
218
+ * @param {String} path The Syncro entity + ID path. Takes the form `ENTITY::ID`
219
+ * @param {Object} state The new state to set/merge
220
+ *
221
+ * @param {Object} [options] Additional options to mutate behaviour
222
+ * @param {'merge'|'set'} [options.method='merge'] How to apply the new state. 'merge' (merge in partial data to an existing Syncro), 'set' (overwrite the entire Syncro state)
223
+ *
224
+ * @returns {Promise<*>} The state object after it has been applied
225
+ */
226
+ static setSnapshot(path: string, state: any, options?: {
227
+ method?: 'merge' | 'set';
228
+ }): Promise<any>;
229
+ /**
230
+ * Mount the remote Firestore document against this Syncro instance
231
+ *
232
+ * @param {Object} [options] Additional options to mutate behaviour
233
+ * @param {Object} [options.initalState] State to use if no state is already loaded, overrides the entities own `initState` function fetcher
234
+ * @param {Number} [options.retries=3] Number of times to retry if a mounted Syncro fails its sanity checks
235
+ * @returns {Promise<Syncro>} A promise which resolves as this syncro instance when completed
236
+ */
237
+ mount(options?: any): Promise<Syncro>;
238
+ /**
239
+ * Merge a single or multiple values into a Syncro data object
240
+ * NOTE: Default behaviour is to flush (if any changes apply), use direct object mutation or disable with `flush:false` to disable
241
+ *
242
+ * @param {String|Object} key Either the single named key to set OR the object to merge
243
+ * @param {*} [value] The value to set if `key` is a string
244
+ *
245
+ * @param {Object} [options] Additional options to mutate behaviour
246
+ * @param {Boolean} [options.delta=true] Only merge keys that differ, skipping flush if no changes are made
247
+ * @param {Boolean} [options.flush=true] Send a flush signal that Firebase should sync to Supabase on changes
248
+ * @param {Boolean} [options.forceFlush=false] Flush even if no changes were made
249
+ * @param {Boolean} [options.flushDestroy=false] Destroy the Syncro after flushing
250
+ *
251
+ * @returns {Promise<Syncro>} A promise which resolves with this Syncro instance on completion
252
+ */
253
+ set(key: string | object, value: any, options: {
254
+ delta?: boolean;
255
+ flush?: boolean;
256
+ forceFlush?: boolean;
257
+ flushDestroy?: boolean;
258
+ }): Promise<this>;
259
+ /**
260
+ * Schedule Syncro heartbeats
261
+ * This populates the `sync` presence meta-information
262
+ *
263
+ * @param {Boolean} [enable=true] Whether to enable heartbeating
264
+ *
265
+ * @param {Object} [options] Additional options to mutate behaviour
266
+ * @param {Boolean} [options.immediate=false] Fire a heartbeat as soon as this function is called, this is only really useful on mount
267
+ */
268
+ setHeartbeat(enable?: boolean, options?: any): Promise<void> | void;
269
+ /**
270
+ * Perform one heartbeat pulse to the server to indicate presense within this Syncro
271
+ * This function is automatically called by a timer if `setHeartbeat(true)` (the default behaviour)
272
+ *
273
+ * @returns {Promise} A promise which resolves when the operation has completed
274
+ */
275
+ heartbeat(): Promise<void>;
276
+ /**
277
+ * Utility function to directly set this documents firestore state
278
+ *
279
+ * @param {Object} state The state to set / merge
280
+ * @param {Object} [options] Additional options to mutate behaviour
281
+ * @param {'merge'|'set'} [options.method='merge'] How to apply the new state. 'merge' (merge in partial data to an existing Syncro), 'set' (overwrite the entire Syncro state)
282
+ *
283
+ * @returns {Promise} A promise which resolves when the operation has completed
284
+ */
285
+ setFirestoreState(state: any, options?: {
286
+ method?: 'merge' | 'set';
287
+ }): Promise<void>;
288
+ /**
289
+ * Utility method to fetch the Firestore state for this Syncro
290
+ * NOTE: This directly extracts the state of the Firestore, not its wrapping doc object returned by `FirestoreGetDoc`
291
+ *
292
+ * @returns {Promise<Object>} A promise which resolves to the Firestore state
293
+ */
294
+ getFirestoreState(): Promise<any>;
295
+ /**
296
+ * Set the Syncro dirty flag which gets passed to the server on the next heartbeat
297
+ *
298
+ * @see isDirty
299
+ * @returns {Syncro} This chainable Syncro instance
300
+ */
301
+ markDirty(): this;
302
+ /**
303
+ * Force the server to flush state
304
+ * This is only really useful for debugging as this happens automatically anyway
305
+ *
306
+ * @param {Object} [options] Additional options to mutate behaviour
307
+ * @param {Boolean} [options.destroy=false] Instruct the server to also dispose of the Syncro state
308
+ *
309
+ * @returns {Promise} A promise which resolves when the operation has completed
310
+ */
311
+ flush(options?: any): Promise<void | null>;
312
+ /**
313
+ * Timer handle for heartbeats
314
+ *
315
+ * @type {any}
316
+ */
317
+ _heartbeatTimer: any;
318
+ }
319
+ /**
320
+ * Build a chaotic random tree structure based on dice rolls
321
+ * This funciton is mainly used for sync testing
322
+ *
323
+ * @param {Number} [depth=0] The current depth we are starting at, changes the nature of branches based on probability
324
+ *
325
+ * @returns {*} The current branch conotents
326
+ */
327
+ export declare function randomBranch(depth?: number): any;
328
+ export {};