@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.
- package/CHANGELOG.md +38 -0
- package/api.md +68 -66
- package/dist/lib/projectFile.d.ts +182 -0
- package/dist/lib/projectFile.js +157 -0
- package/dist/lib/projectFile.js.map +1 -0
- package/dist/lib/syncro/entities.d.ts +28 -0
- package/dist/lib/syncro/entities.js +203 -0
- package/dist/lib/syncro/entities.js.map +1 -0
- package/dist/lib/syncro/keyed.d.ts +95 -0
- package/dist/lib/syncro/keyed.js +286 -0
- package/dist/lib/syncro/keyed.js.map +1 -0
- package/dist/lib/syncro/syncro.d.ts +328 -0
- package/dist/lib/syncro/syncro.js +633 -0
- package/dist/lib/syncro/syncro.js.map +1 -0
- package/dist/lib/terafy.bootstrapper.d.ts +42 -0
- package/dist/lib/terafy.bootstrapper.js +130 -0
- package/dist/lib/terafy.bootstrapper.js.map +1 -0
- package/dist/lib/terafy.client.d.ts +532 -0
- package/dist/lib/terafy.client.js +1110 -0
- package/dist/lib/terafy.client.js.map +1 -0
- package/dist/lib/terafy.proxy.d.ts +66 -0
- package/dist/lib/terafy.proxy.js +123 -0
- package/dist/lib/terafy.proxy.js.map +1 -0
- package/dist/lib/terafy.server.d.ts +607 -0
- package/dist/lib/terafy.server.js +1774 -0
- package/dist/lib/terafy.server.js.map +1 -0
- package/dist/plugin.vue2.es2019.js +30 -13
- package/dist/plugins/base.d.ts +20 -0
- package/dist/plugins/base.js +21 -0
- package/dist/plugins/base.js.map +1 -0
- package/dist/plugins/firebase.d.ts +62 -0
- package/dist/plugins/firebase.js +111 -0
- package/dist/plugins/firebase.js.map +1 -0
- package/dist/plugins/vite.d.ts +12 -0
- package/dist/plugins/vite.js +22 -0
- package/dist/plugins/vite.js.map +1 -0
- package/dist/plugins/vue2.d.ts +68 -0
- package/dist/plugins/vue2.js +96 -0
- package/dist/plugins/vue2.js.map +1 -0
- package/dist/plugins/vue3.d.ts +64 -0
- package/dist/plugins/vue3.js +96 -0
- package/dist/plugins/vue3.js.map +1 -0
- package/dist/terafy.bootstrapper.es2019.js +2 -2
- package/dist/terafy.bootstrapper.js +2 -2
- package/dist/terafy.es2019.js +2 -2
- package/dist/terafy.js +1 -1
- package/dist/utils/mixin.d.ts +11 -0
- package/dist/utils/mixin.js +15 -0
- package/dist/utils/mixin.js.map +1 -0
- package/dist/utils/pDefer.d.ts +12 -0
- package/dist/utils/pDefer.js +14 -0
- package/dist/utils/pDefer.js.map +1 -0
- package/dist/utils/pathTools.d.ts +70 -0
- package/dist/utils/pathTools.js +120 -0
- package/dist/utils/pathTools.js.map +1 -0
- package/eslint.config.js +44 -8
- package/lib/{projectFile.js → projectFile.ts} +83 -40
- package/lib/syncro/entities.ts +288 -0
- package/lib/syncro/{keyed.js → keyed.ts} +114 -57
- package/lib/syncro/{syncro.js → syncro.ts} +204 -169
- package/lib/{terafy.bootstrapper.js → terafy.bootstrapper.ts} +49 -31
- package/lib/{terafy.client.js → terafy.client.ts} +94 -86
- package/lib/{terafy.proxy.js → terafy.proxy.ts} +43 -16
- package/lib/{terafy.server.js → terafy.server.ts} +364 -223
- package/package.json +65 -26
- package/plugins/{base.js → base.ts} +3 -1
- package/plugins/{firebase.js → firebase.ts} +34 -16
- package/plugins/{vite.js → vite.ts} +3 -3
- package/plugins/{vue2.js → vue2.ts} +17 -10
- package/plugins/{vue3.js → vue3.ts} +11 -9
- package/tsconfig.json +30 -0
- package/utils/{mixin.js → mixin.ts} +1 -1
- package/utils/{pDefer.js → pDefer.ts} +10 -3
- package/utils/{pathTools.js → pathTools.ts} +11 -9
- package/lib/syncro/entities.js +0 -232
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
import { isEmpty, cloneDeep, random, sample, throttle, isEqual } from 'lodash-es';
|
|
2
|
+
import { doc as FirestoreDocRef, getDoc as FirestoreGetDoc, onSnapshot as FirestoreOnSnapshot, setDoc as FirestoreSetDoc, updateDoc as FirestoreUpdateDoc, } from 'firebase/firestore';
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import marshal from '@momsfriendlydevco/marshal';
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
6
|
+
import PromiseRetry from 'p-retry';
|
|
7
|
+
/**
|
|
8
|
+
* @class Syncro
|
|
9
|
+
* TERA Isomorphic Syncro class
|
|
10
|
+
* 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
|
|
11
|
+
* This class tries to be as independent as possible to help with adapting it to various front-end TERA-fy plugin frameworks
|
|
12
|
+
*/
|
|
13
|
+
export default class Syncro {
|
|
14
|
+
/**
|
|
15
|
+
* @interface
|
|
16
|
+
* Debugging printer for this instance
|
|
17
|
+
* Defaults to doing nothing
|
|
18
|
+
*
|
|
19
|
+
* @param {*...} [msg] The message to output
|
|
20
|
+
*/
|
|
21
|
+
debug(...msg) { } // eslint-disable-line no-unused-vars
|
|
22
|
+
/**
|
|
23
|
+
* @interface
|
|
24
|
+
* Debugging printer specifically for error messages
|
|
25
|
+
* Defaults to using console.log()
|
|
26
|
+
*
|
|
27
|
+
* @param {*...} [msg] The message to output
|
|
28
|
+
*/
|
|
29
|
+
debugError(...msg) {
|
|
30
|
+
console.log(`[Syncro ${this.path}]`, ...msg);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Instance constructor
|
|
34
|
+
*
|
|
35
|
+
* @param {String} path Mount path for the Syncro. Should be in the form `${ENTITY}::${ID}(::${RELATION})?`
|
|
36
|
+
* @param {Object} [options] Additional instance setters (mutates instance directly), note that the `config` subkey is merged with the existing config rather than assigned
|
|
37
|
+
*/
|
|
38
|
+
constructor(path, options) {
|
|
39
|
+
/**
|
|
40
|
+
* Default throttle to apply for writes
|
|
41
|
+
*
|
|
42
|
+
* @type {Number} Throttle time in milliseconds
|
|
43
|
+
*/
|
|
44
|
+
this.throttle = 250;
|
|
45
|
+
/**
|
|
46
|
+
* Various Misc config for the Syncro instance
|
|
47
|
+
*
|
|
48
|
+
* @type {Object}
|
|
49
|
+
* @property {Number} heartbeatinterval Time in milliseconds between heartbeat beacons
|
|
50
|
+
* @property {String} syncroRegistryUrl The prefix Sync worker URL, used to populate Syncros and determine their active status
|
|
51
|
+
* @property {Object} context Additional named parameters to pass to callbacks like initState
|
|
52
|
+
*/
|
|
53
|
+
this.config = {
|
|
54
|
+
heartbeatInterval: 50000, //~= 50s
|
|
55
|
+
syncroRegistryUrl: 'https://tera-tools.com/api/sync',
|
|
56
|
+
context: {},
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Whether the next heartbeat should be marked as 'dirty'
|
|
60
|
+
* This indicates that at least one change has occured since the last hearbeat and the server should perform a flush (but not a clean)
|
|
61
|
+
* This flag is only transmitted once in the next heartbeat before being reset
|
|
62
|
+
*
|
|
63
|
+
* @see markDirty()
|
|
64
|
+
* @type {Boolean}
|
|
65
|
+
*/
|
|
66
|
+
this.isDirty = false;
|
|
67
|
+
/**
|
|
68
|
+
* Actions to preform when we are destroying this instance
|
|
69
|
+
* This is an array of function callbacks to execute in parallel when `destroy()` is called
|
|
70
|
+
*
|
|
71
|
+
* @type {Array<() => void>}
|
|
72
|
+
*/
|
|
73
|
+
this._destroyActions = [];
|
|
74
|
+
this.path = path;
|
|
75
|
+
Object.assign(this, {
|
|
76
|
+
...options,
|
|
77
|
+
config: {
|
|
78
|
+
...this.config,
|
|
79
|
+
...options?.config,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (!Syncro.session) // Assign a random session ID if we don't already have one
|
|
83
|
+
Syncro.session = `syncro_${nanoid()}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Instance destruction trigger
|
|
87
|
+
* This will unsubscribe from various facilities and release the object for cleanup
|
|
88
|
+
*
|
|
89
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
90
|
+
*/
|
|
91
|
+
destroy() {
|
|
92
|
+
this.debug('Destroy!');
|
|
93
|
+
return Promise.all(this._destroyActions
|
|
94
|
+
.map(fn => fn()))
|
|
95
|
+
.then(() => this._destroyActions = []); // Reset list of actions to perform when terminating
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Function to return whatever the local framework uses as a reactive object
|
|
99
|
+
* This should respond with an object of mandatory functions to watch for changes and remerge them
|
|
100
|
+
*
|
|
101
|
+
* @param {Object} value Initial value of the reactive
|
|
102
|
+
*
|
|
103
|
+
* @returns {ReactiveWrapper} A reactive object prototype
|
|
104
|
+
* @property {Object} doc The reactive object
|
|
105
|
+
* @property {Function} setState Function used to overwrite the default state, called as `(newState:Object)`
|
|
106
|
+
* @property {Function} getState Function used to fetch the current snapshot state, called as `()`
|
|
107
|
+
* @property {Function} watch Function used to set up state watchers, should call its callback when a change is detected, called as `(cb:Function)`
|
|
108
|
+
*/
|
|
109
|
+
getReactive(value) {
|
|
110
|
+
console.warn('Syncro.getReactive has not been subclassed, assuming a POJO response');
|
|
111
|
+
let doc = { ...value };
|
|
112
|
+
return {
|
|
113
|
+
doc,
|
|
114
|
+
setState(state) {
|
|
115
|
+
// Shallow copy all sub keys into existing object (keeping the object pointer)
|
|
116
|
+
Object.entries(state || {})
|
|
117
|
+
.forEach(([k, v]) => doc[k] = v);
|
|
118
|
+
},
|
|
119
|
+
getState() {
|
|
120
|
+
return cloneDeep(doc);
|
|
121
|
+
},
|
|
122
|
+
watch(cb) {
|
|
123
|
+
// Stub
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
;
|
|
128
|
+
/**
|
|
129
|
+
* Returns the split entity + ID relationship from a given session path
|
|
130
|
+
* This funciton checks for valid UUID format strings + that the entity is a known/supported entity (see `knownEntities`)
|
|
131
|
+
* NOTE: When used by itself (i.e. ignoring response) this function can also act as a guard that a path is valid
|
|
132
|
+
*
|
|
133
|
+
* INPUT: `widgets::UUID` -> `{entity:'widgets', id:UUID}`
|
|
134
|
+
* INPUT: `widgets::UUID::thing` -> `{entity:'widgets', id:UUID, relation:'thing'}`
|
|
135
|
+
*
|
|
136
|
+
* @param {String} path The input session path of the form `${ENTITY}::${ID}`
|
|
137
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
138
|
+
* @param {Boolean} [options.allowAsterisk=false] Whether to allow the meta asterisk character when recognising paths, this is used by the SyncroKeyed class
|
|
139
|
+
*
|
|
140
|
+
* @returns {PathSplitResult} An object composed of the session path components
|
|
141
|
+
*/
|
|
142
|
+
static pathSplit(path, options) {
|
|
143
|
+
let settings = {
|
|
144
|
+
allowAsterisk: false,
|
|
145
|
+
...options,
|
|
146
|
+
};
|
|
147
|
+
let pathMatcher = new RegExp(
|
|
148
|
+
// Compose the patch matching expression - note double escapes for backslashes to avoid encoding as raw string values
|
|
149
|
+
'^'
|
|
150
|
+
+ '(?<entity>\\w+?)' // Any alpha-numeric sequence as the entity name (non-greedy capture)
|
|
151
|
+
+ '::' // Followed by '::'
|
|
152
|
+
+ '(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})' // Followed by a valid lower-case UUID
|
|
153
|
+
+ '(?:::(?<relation>[' // Followed by an optional ansi relation
|
|
154
|
+
+ '\\w' // Which is any alpha-numeric sequence...
|
|
155
|
+
+ '\\-' // ... Including UUID characters
|
|
156
|
+
+ (settings.allowAsterisk ? '\\*' : '') // ... and (optionally) an asterisk
|
|
157
|
+
+ ']+?))?'
|
|
158
|
+
+ '$');
|
|
159
|
+
let extracted = { ...pathMatcher.exec(path)?.groups };
|
|
160
|
+
if (!extracted || !extracted.entity || !extracted.id)
|
|
161
|
+
throw new Error(`Invalid session path syntax "${path}"`);
|
|
162
|
+
if (Syncro.SyncroEntities && !(extracted.entity in Syncro.SyncroEntities))
|
|
163
|
+
throw new Error(`Unsupported entity "${path}" -> Entity="${extracted.entity}"`);
|
|
164
|
+
return {
|
|
165
|
+
entity: extracted.entity,
|
|
166
|
+
id: extracted.id,
|
|
167
|
+
relation: extracted.relation,
|
|
168
|
+
fsCollection: extracted.entity,
|
|
169
|
+
fsId: extracted.relation
|
|
170
|
+
? `${extracted.id}::${extracted.relation}`
|
|
171
|
+
: extracted.id,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Convert local POJO -> Firestore compatible object
|
|
176
|
+
* This applies the following mutations to the incoming object:
|
|
177
|
+
*
|
|
178
|
+
* 1. Arrays are converted to Objects (Firestore cannot store nested arrays)
|
|
179
|
+
* 2. All non-POJO objects (e.g. Dates) to a symetric object
|
|
180
|
+
*
|
|
181
|
+
* @param {Object} snapshot The current state to convert
|
|
182
|
+
* @returns {Object} A Firebase compatible object
|
|
183
|
+
*/
|
|
184
|
+
static toFirestore(snapshot = {}) {
|
|
185
|
+
return marshal.serialize(snapshot, {
|
|
186
|
+
circular: false,
|
|
187
|
+
clone: true, // Clone away from the original Vue Reactive so we dont mangle it while traversing
|
|
188
|
+
modules: [
|
|
189
|
+
marshalFlattenArrays,
|
|
190
|
+
...marshal.settings.modules,
|
|
191
|
+
],
|
|
192
|
+
stringify: false,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Convert local Firestore compatible object -> local POJO
|
|
197
|
+
* This reverses the mutations listed in `toFirestore()`
|
|
198
|
+
*
|
|
199
|
+
* @param {Object} snapshot The raw Firebase state to convert
|
|
200
|
+
* @returns {Object} A JavaScript POJO representing the converted state
|
|
201
|
+
*/
|
|
202
|
+
static fromFirestore(snapshot = {}) {
|
|
203
|
+
return marshal.deserialize(snapshot, {
|
|
204
|
+
circular: false,
|
|
205
|
+
clone: true, // Clone away from original so we don't trigger a loop within Firebase
|
|
206
|
+
modules: [
|
|
207
|
+
marshalFlattenArrays,
|
|
208
|
+
...marshal.settings.modules,
|
|
209
|
+
],
|
|
210
|
+
destringify: false,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Convert a raw POJO into Firestore field layout
|
|
215
|
+
* Field structures are usually consumed by the Firestore ReST API and need converting before being used
|
|
216
|
+
* NOTE: This does not serialize the incoming data so you likely want to use this as `toFirestoreFields(toFirestore(data))`
|
|
217
|
+
*
|
|
218
|
+
* @see https://stackoverflow.com/a/62304377
|
|
219
|
+
* @param {Object} data The raw value to convert
|
|
220
|
+
* @returns {Object} A Firestore compatible, typed data structure
|
|
221
|
+
*/
|
|
222
|
+
static toFirestoreFields(data) {
|
|
223
|
+
const result = {};
|
|
224
|
+
for (const [key, value] of Object.entries(data)) {
|
|
225
|
+
const type = typeof value;
|
|
226
|
+
if (type === 'string') { // eslint-disable-line unicorn/prefer-switch
|
|
227
|
+
result[key] = { stringValue: value };
|
|
228
|
+
}
|
|
229
|
+
else if (type === 'number') {
|
|
230
|
+
result[key] = { doubleValue: value };
|
|
231
|
+
}
|
|
232
|
+
else if (type === 'boolean') {
|
|
233
|
+
result[key] = { booleanValue: value };
|
|
234
|
+
}
|
|
235
|
+
else if (value === null) {
|
|
236
|
+
result[key] = { nullValue: null };
|
|
237
|
+
}
|
|
238
|
+
else if (Array.isArray(value)) {
|
|
239
|
+
// Need to handle the inner item structure correctly
|
|
240
|
+
result[key] = { arrayValue: { values: value.map(item => {
|
|
241
|
+
const field = Syncro.toFirestoreFields({ item });
|
|
242
|
+
return field.item; // Extract the typed value from the temporary {item: ...} structure
|
|
243
|
+
}) } };
|
|
244
|
+
}
|
|
245
|
+
else if (type === 'object') {
|
|
246
|
+
result[key] = { mapValue: { fields: Syncro.toFirestoreFields(value) } };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Convert a Firestore field dump into a native POJO
|
|
253
|
+
* Field structures are usually provided by the Firestore ReST API and need de-typing back into a native document
|
|
254
|
+
* NOTE: This does not deserialize the result so you likely want to use this as `fromFirestore(fromFirestoreFields(response.fields))`
|
|
255
|
+
*
|
|
256
|
+
* @see https://stackoverflow.com/a/62304377
|
|
257
|
+
* @param {Object} fields The raw Snapshot to convert
|
|
258
|
+
* @returns {Object} A JavaScript POJO representing the converted state
|
|
259
|
+
*/
|
|
260
|
+
static fromFirestoreFields(fields = {}) {
|
|
261
|
+
let result = {};
|
|
262
|
+
for (let key in fields) {
|
|
263
|
+
let value = fields[key];
|
|
264
|
+
let isDocumentType = [
|
|
265
|
+
'stringValue', 'booleanValue', 'doubleValue',
|
|
266
|
+
'integerValue', 'timestampValue', 'mapValue', 'arrayValue', 'nullValue', // Added nullValue
|
|
267
|
+
].find(t => t === Object.keys(value)[0]); // Check the first key of the value object
|
|
268
|
+
if (isDocumentType) {
|
|
269
|
+
if (isDocumentType === 'mapValue') {
|
|
270
|
+
result[key] = Syncro.fromFirestoreFields(value.mapValue.fields || {});
|
|
271
|
+
}
|
|
272
|
+
else if (isDocumentType === 'arrayValue') {
|
|
273
|
+
let list = value.arrayValue.values;
|
|
274
|
+
result[key] = !!list ? list.map((l) => Syncro.fromFirestoreFields(l)) : [];
|
|
275
|
+
}
|
|
276
|
+
else if (isDocumentType === 'nullValue') {
|
|
277
|
+
result[key] = null;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
result[key] = value[isDocumentType];
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// This case might not be standard Firestore field structure, but handle recursively
|
|
285
|
+
result[key] = Syncro.fromFirestoreFields(value);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Perform a one-off fetch of a given Syncro path
|
|
292
|
+
*
|
|
293
|
+
* @param {String} path The Syncro entity + ID path. Takes the form `ENTITY::ID`
|
|
294
|
+
*
|
|
295
|
+
* @returns {Promise<Object|Null>} An eventual snapshot of the given path, if the entity doesn't exist null is returned
|
|
296
|
+
*/
|
|
297
|
+
static getSnapshot(path) {
|
|
298
|
+
let { fsCollection, fsId } = Syncro.pathSplit(path);
|
|
299
|
+
return Promise.resolve()
|
|
300
|
+
.then(async () => FirestoreGetDoc(// Set up binding and wait for it to come ready
|
|
301
|
+
FirestoreDocRef(Syncro.firestore, fsCollection, fsId)))
|
|
302
|
+
.then(doc => doc.exists() // Use exists() method
|
|
303
|
+
? Syncro.fromFirestore(doc.data())
|
|
304
|
+
: null);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Perform a one-off set/merge operation against
|
|
308
|
+
*
|
|
309
|
+
* @param {String} path The Syncro entity + ID path. Takes the form `ENTITY::ID`
|
|
310
|
+
* @param {Object} state The new state to set/merge
|
|
311
|
+
*
|
|
312
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
313
|
+
* @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)
|
|
314
|
+
*
|
|
315
|
+
* @returns {Promise<*>} The state object after it has been applied
|
|
316
|
+
*/
|
|
317
|
+
static setSnapshot(path, state, options) {
|
|
318
|
+
let settings = {
|
|
319
|
+
method: 'merge',
|
|
320
|
+
...options,
|
|
321
|
+
};
|
|
322
|
+
let { fsCollection, fsId } = Syncro.pathSplit(path);
|
|
323
|
+
const docRef = FirestoreDocRef(Syncro.firestore, fsCollection, fsId);
|
|
324
|
+
const firestoreData = Syncro.toFirestore(state);
|
|
325
|
+
return Promise.resolve()
|
|
326
|
+
.then(() => {
|
|
327
|
+
if (settings.method === 'merge') {
|
|
328
|
+
return FirestoreUpdateDoc(docRef, firestoreData);
|
|
329
|
+
}
|
|
330
|
+
else { // method === 'set'
|
|
331
|
+
return FirestoreSetDoc(docRef, firestoreData); // Default set overwrites
|
|
332
|
+
}
|
|
333
|
+
})
|
|
334
|
+
.then(() => state);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Mount the remote Firestore document against this Syncro instance
|
|
338
|
+
*
|
|
339
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
340
|
+
* @param {Object} [options.initalState] State to use if no state is already loaded, overrides the entities own `initState` function fetcher
|
|
341
|
+
* @param {Number} [options.retries=3] Number of times to retry if a mounted Syncro fails its sanity checks
|
|
342
|
+
* @returns {Promise<Syncro>} A promise which resolves as this syncro instance when completed
|
|
343
|
+
*/
|
|
344
|
+
mount(options) {
|
|
345
|
+
let settings = {
|
|
346
|
+
initialState: null,
|
|
347
|
+
retries: 5,
|
|
348
|
+
retryMinTime: 250,
|
|
349
|
+
...options,
|
|
350
|
+
};
|
|
351
|
+
let { fsCollection, fsId, entity } = Syncro.pathSplit(this.path);
|
|
352
|
+
let reactive; // Eventual response from reactive() with the intitial value
|
|
353
|
+
let doc; // Eventual Firebase document
|
|
354
|
+
return PromiseRetry(async () => {
|
|
355
|
+
await this.setHeartbeat(false); // Disable any existing heartbeat - this only really applies if we're changing path for some reason
|
|
356
|
+
// Set up binding and wait for it to come ready
|
|
357
|
+
this.docRef = FirestoreDocRef(Syncro.firestore, fsCollection, fsId);
|
|
358
|
+
// Initalize state
|
|
359
|
+
let initialState = await this.getFirestoreState();
|
|
360
|
+
// Construct a reactive component
|
|
361
|
+
reactive = this.getReactive(initialState);
|
|
362
|
+
if (!reactive.doc || !reactive.setState || !reactive.getState || !reactive.watch)
|
|
363
|
+
throw new Error('Syncro.getReactive() requires a returned `doc`, `setState()`, `getState()` + `watch()`');
|
|
364
|
+
this.value = doc = reactive.doc;
|
|
365
|
+
this.debug('Initial state', { doc });
|
|
366
|
+
// Subscribe to remote updates
|
|
367
|
+
const unsubscribe = FirestoreOnSnapshot(this.docRef, snapshot => {
|
|
368
|
+
let snapshotData = Syncro.fromFirestore(snapshot.data());
|
|
369
|
+
this.debug('Incoming snapshot', { snapshotData });
|
|
370
|
+
reactive.setState(snapshotData);
|
|
371
|
+
});
|
|
372
|
+
this._destroyActions.push(unsubscribe); // Add the unsubscribe handle to the list of destroyAction promises we call on `destroy()`
|
|
373
|
+
// Optionally create the doc if it has no content
|
|
374
|
+
if (!isEmpty(doc)) { // Doc already has content - skip
|
|
375
|
+
// Do nothing
|
|
376
|
+
}
|
|
377
|
+
else if (settings.initialState) { // Provided an intiailState - use that instead of the entities own method
|
|
378
|
+
this.debug('Populate initial Syncro state (from provided initialState)');
|
|
379
|
+
await this.setFirestoreState(settings.initialState, { method: 'set' });
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
this.debug(`Populate initial Syncro state (from "${entity}" Syncro worker)`);
|
|
383
|
+
const response = await fetch(`${this.config.syncroRegistryUrl}/${this.path}`);
|
|
384
|
+
if (!response.ok) {
|
|
385
|
+
throw new Error(`Failed to check Syncro "${fsCollection}::${fsId}" status - ${response.statusText}`);
|
|
386
|
+
}
|
|
387
|
+
// Assuming the fetch populates the syncro state server-side, no local state set needed here
|
|
388
|
+
}
|
|
389
|
+
// Setup local state watcher
|
|
390
|
+
reactive.watch(throttle((newState) => {
|
|
391
|
+
this.debug('Local change', { newState });
|
|
392
|
+
this.markDirty();
|
|
393
|
+
this.setFirestoreState(newState, { method: 'merge' });
|
|
394
|
+
}, this.throttle));
|
|
395
|
+
await this.setHeartbeat(true, {
|
|
396
|
+
immediate: true,
|
|
397
|
+
});
|
|
398
|
+
return this;
|
|
399
|
+
}, {
|
|
400
|
+
retries: settings.retries,
|
|
401
|
+
minTimeout: settings.retryMinTime,
|
|
402
|
+
randomize: true,
|
|
403
|
+
factor: 3,
|
|
404
|
+
onFailedAttempt: async (e) => {
|
|
405
|
+
this.debugError(`[Attempt ${e.attemptNumber}/${e.attemptNumber + e.retriesLeft - 1}] to mount syncro`, e);
|
|
406
|
+
await this.destroy(); // Ensure cleanup on failed attempt before retry
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Merge a single or multiple values into a Syncro data object
|
|
412
|
+
* NOTE: Default behaviour is to flush (if any changes apply), use direct object mutation or disable with `flush:false` to disable
|
|
413
|
+
*
|
|
414
|
+
* @param {String|Object} key Either the single named key to set OR the object to merge
|
|
415
|
+
* @param {*} [value] The value to set if `key` is a string
|
|
416
|
+
*
|
|
417
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
418
|
+
* @param {Boolean} [options.delta=true] Only merge keys that differ, skipping flush if no changes are made
|
|
419
|
+
* @param {Boolean} [options.flush=true] Send a flush signal that Firebase should sync to Supabase on changes
|
|
420
|
+
* @param {Boolean} [options.forceFlush=false] Flush even if no changes were made
|
|
421
|
+
* @param {Boolean} [options.flushDestroy=false] Destroy the Syncro after flushing
|
|
422
|
+
*
|
|
423
|
+
* @returns {Promise<Syncro>} A promise which resolves with this Syncro instance on completion
|
|
424
|
+
*/
|
|
425
|
+
async set(key, value, options) {
|
|
426
|
+
// Argument mangling - [key, value, settings] -> changes{}, settings {{{
|
|
427
|
+
let changes;
|
|
428
|
+
if (typeof key == 'string') { // Called as (key:String, value:*, options?:Object)
|
|
429
|
+
changes[key] = value;
|
|
430
|
+
}
|
|
431
|
+
else if (typeof key == 'object') { // Called as (changes:Object, options?:Object)
|
|
432
|
+
[changes, options] = [key, value];
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
throw new Error('Unknown call signature for set() - call with string+value or object');
|
|
436
|
+
}
|
|
437
|
+
// }}}
|
|
438
|
+
let settings = {
|
|
439
|
+
delta: true,
|
|
440
|
+
flush: true,
|
|
441
|
+
forceFlush: false,
|
|
442
|
+
flushDestroy: false,
|
|
443
|
+
...options,
|
|
444
|
+
};
|
|
445
|
+
// Perform merge
|
|
446
|
+
let hasChanges = false;
|
|
447
|
+
if (settings.delta) { // Merge changes lazily
|
|
448
|
+
Object.entries(changes)
|
|
449
|
+
.filter(([k, v]) => !isEqual(this.value[k], v))
|
|
450
|
+
.forEach(([k, v]) => {
|
|
451
|
+
hasChanges = true;
|
|
452
|
+
this.value[k] = v;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
hasChanges = true;
|
|
457
|
+
Object.assign(this.value, changes);
|
|
458
|
+
}
|
|
459
|
+
// Optionally perform flush
|
|
460
|
+
if ((settings.forceFlush || hasChanges)
|
|
461
|
+
&& settings.flush) {
|
|
462
|
+
await this.flush({ destroy: settings.flushDestroy });
|
|
463
|
+
}
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Schedule Syncro heartbeats
|
|
468
|
+
* This populates the `sync` presence meta-information
|
|
469
|
+
*
|
|
470
|
+
* @param {Boolean} [enable=true] Whether to enable heartbeating
|
|
471
|
+
*
|
|
472
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
473
|
+
* @param {Boolean} [options.immediate=false] Fire a heartbeat as soon as this function is called, this is only really useful on mount
|
|
474
|
+
*/
|
|
475
|
+
setHeartbeat(enable = true, options) {
|
|
476
|
+
let settings = {
|
|
477
|
+
immediate: true,
|
|
478
|
+
...options,
|
|
479
|
+
};
|
|
480
|
+
// Clear existing heartbeat timer, if there is one
|
|
481
|
+
clearTimeout(this._heartbeatTimer);
|
|
482
|
+
if (enable) {
|
|
483
|
+
const heartbeatAction = async () => {
|
|
484
|
+
// Perform the heartbeat
|
|
485
|
+
await this.heartbeat();
|
|
486
|
+
// If we're enabled - schedule the next heartbeat timer
|
|
487
|
+
if (enable)
|
|
488
|
+
this.setHeartbeat(true); // Reschedule
|
|
489
|
+
};
|
|
490
|
+
this._heartbeatTimer = setTimeout(heartbeatAction, this.config.heartbeatInterval);
|
|
491
|
+
if (settings.immediate)
|
|
492
|
+
return this.heartbeat(); // Return the promise from immediate heartbeat
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Perform one heartbeat pulse to the server to indicate presense within this Syncro
|
|
497
|
+
* This function is automatically called by a timer if `setHeartbeat(true)` (the default behaviour)
|
|
498
|
+
*
|
|
499
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
500
|
+
*/
|
|
501
|
+
async heartbeat() {
|
|
502
|
+
this.debug('heartbeat!');
|
|
503
|
+
try {
|
|
504
|
+
const response = await fetch(`${this.config.syncroRegistryUrl}/${this.path}/heartbeat`, {
|
|
505
|
+
method: 'post',
|
|
506
|
+
headers: {
|
|
507
|
+
'Content-Type': 'application/json'
|
|
508
|
+
},
|
|
509
|
+
body: JSON.stringify({
|
|
510
|
+
session: Syncro.session,
|
|
511
|
+
...(this.isDirty && { dirty: true }),
|
|
512
|
+
}),
|
|
513
|
+
});
|
|
514
|
+
if (!response.ok) {
|
|
515
|
+
console.warn(this.path, `Heartbeat failed - ${response.statusText}`, { response });
|
|
516
|
+
}
|
|
517
|
+
this.isDirty = false; // Reset the dirty flag if it is set
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
console.warn(this.path, 'Heartbeat fetch error', error);
|
|
521
|
+
// Decide if isDirty should be reset on network error
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Utility function to directly set this documents firestore state
|
|
526
|
+
*
|
|
527
|
+
* @param {Object} state The state to set / merge
|
|
528
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
529
|
+
* @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)
|
|
530
|
+
*
|
|
531
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
532
|
+
*/
|
|
533
|
+
setFirestoreState(state, options) {
|
|
534
|
+
let settings = {
|
|
535
|
+
method: 'merge',
|
|
536
|
+
...options,
|
|
537
|
+
};
|
|
538
|
+
if (!this.docRef)
|
|
539
|
+
throw new Error('mount() must be called before setting Firestore state');
|
|
540
|
+
const firestoreData = Syncro.toFirestore(state);
|
|
541
|
+
if (settings.method === 'merge') {
|
|
542
|
+
return FirestoreUpdateDoc(this.docRef, firestoreData);
|
|
543
|
+
}
|
|
544
|
+
else { // method === 'set'
|
|
545
|
+
return FirestoreSetDoc(this.docRef, firestoreData);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Utility method to fetch the Firestore state for this Syncro
|
|
550
|
+
* NOTE: This directly extracts the state of the Firestore, not its wrapping doc object returned by `FirestoreGetDoc`
|
|
551
|
+
*
|
|
552
|
+
* @returns {Promise<Object>} A promise which resolves to the Firestore state
|
|
553
|
+
*/
|
|
554
|
+
getFirestoreState() {
|
|
555
|
+
if (!this.docRef)
|
|
556
|
+
throw new Error('mount() must be called before getting Firestore state');
|
|
557
|
+
return FirestoreGetDoc(this.docRef)
|
|
558
|
+
.then(doc => Syncro.fromFirestore(doc.data() ?? {})); // Handle undefined data with default {}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Set the Syncro dirty flag which gets passed to the server on the next heartbeat
|
|
562
|
+
*
|
|
563
|
+
* @see isDirty
|
|
564
|
+
* @returns {Syncro} This chainable Syncro instance
|
|
565
|
+
*/
|
|
566
|
+
markDirty() {
|
|
567
|
+
this.isDirty = true;
|
|
568
|
+
return this;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Force the server to flush state
|
|
572
|
+
* This is only really useful for debugging as this happens automatically anyway
|
|
573
|
+
*
|
|
574
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
575
|
+
* @param {Boolean} [options.destroy=false] Instruct the server to also dispose of the Syncro state
|
|
576
|
+
*
|
|
577
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
578
|
+
*/
|
|
579
|
+
flush(options) {
|
|
580
|
+
let settings = {
|
|
581
|
+
destroy: false,
|
|
582
|
+
...options,
|
|
583
|
+
};
|
|
584
|
+
return fetch(`${this.config.syncroRegistryUrl}/${this.path}/flush` + (settings.destroy ? '?destroy=1' : ''))
|
|
585
|
+
.then(response => response.ok
|
|
586
|
+
? null
|
|
587
|
+
: Promise.reject(response.statusText || 'An error occured'));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Build a chaotic random tree structure based on dice rolls
|
|
592
|
+
* This funciton is mainly used for sync testing
|
|
593
|
+
*
|
|
594
|
+
* @param {Number} [depth=0] The current depth we are starting at, changes the nature of branches based on probability
|
|
595
|
+
*
|
|
596
|
+
* @returns {*} The current branch conotents
|
|
597
|
+
*/
|
|
598
|
+
export function randomBranch(depth = 0) {
|
|
599
|
+
let dice = // Roll a dice to pick the content
|
|
600
|
+
depth == 0 ? 10 // first roll is always '10'
|
|
601
|
+
: random(0, 11 - depth, false); // Subsequent rolls bias downwards based on depth (to avoid recursion)
|
|
602
|
+
return (dice == 0 ? false
|
|
603
|
+
: dice == 1 ? true
|
|
604
|
+
: dice == 2 ? random(1, 10000)
|
|
605
|
+
: dice == 3 ? (new Date(random(1000000000000, 1777777777777))).toISOString()
|
|
606
|
+
: dice == 5 ? Array.from({ length: random(1, 10) }, () => random(1, 10)) // Added return type hint
|
|
607
|
+
: dice == 6 ? null
|
|
608
|
+
: dice < 8 ? Array.from({ length: random(1, 10) }, () => randomBranch(depth + 1)) // Added return type hint
|
|
609
|
+
: Object.fromEntries(Array.from({ length: random(1, 5) })
|
|
610
|
+
.map((v, k) => [
|
|
611
|
+
sample(['foo', 'bar', 'baz', 'quz', 'flarp', 'corge', 'grault', 'garply', 'waldo', 'fred', 'plugh', 'thud'])
|
|
612
|
+
+ `_${k}`,
|
|
613
|
+
randomBranch(depth + 1),
|
|
614
|
+
])));
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* NPM:@momsfriendlydevco/marshal Compatible module for flattening arrays
|
|
618
|
+
* @type {MarshalModule}
|
|
619
|
+
*/
|
|
620
|
+
const marshalFlattenArrays = {
|
|
621
|
+
id: `~array`,
|
|
622
|
+
recursive: true,
|
|
623
|
+
test: (v) => Array.isArray(v),
|
|
624
|
+
serialize: (v) => ({ _: '~array', ...v }),
|
|
625
|
+
deserialize: (v) => {
|
|
626
|
+
let arr = Array.from({ length: Object.keys(v).length - 1 });
|
|
627
|
+
Object.entries(v)
|
|
628
|
+
.filter(([k]) => k !== '_')
|
|
629
|
+
.forEach(([k, val]) => arr[+k] = val); // Changed v to val to avoid shadowing
|
|
630
|
+
return arr;
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
//# sourceMappingURL=syncro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncro.js","sourceRoot":"","sources":["../../../lib/syncro/syncro.ts"],"names":[],"mappings":"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"}
|