@iebh/tera-fy 2.3.10 → 2.3.12
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 +32 -0
- package/api.md +8 -11
- package/dist/plugin.vue2.es2019.js +28 -27
- package/dist/terafy.bootstrapper.es2019.js +2 -2
- package/dist/terafy.bootstrapper.js +2 -2
- package/dist/terafy.es2019.js +5 -5
- package/dist/terafy.js +5 -5
- package/eslint.config.js +4 -66
- package/lib/{projectFile.ts → projectFile.js} +52 -76
- package/lib/syncro/{entities.ts → entities.js} +14 -83
- package/lib/syncro/{keyed.ts → keyed.js} +46 -68
- package/lib/syncro/{syncro.ts → syncro.js} +104 -136
- package/lib/{terafy.bootstrapper.ts → terafy.bootstrapper.js} +20 -30
- package/lib/{terafy.client.ts → terafy.client.js} +85 -84
- package/lib/{terafy.proxy.ts → terafy.proxy.js} +12 -26
- package/lib/{terafy.server.ts → terafy.server.js} +204 -229
- package/package.json +20 -39
- package/plugins/{base.ts → base.js} +3 -5
- package/plugins/{firebase.ts → firebase.js} +22 -21
- package/plugins/{vite.ts → vite.js} +3 -3
- package/plugins/{vue2.ts → vue2.js} +8 -10
- package/plugins/{vue3.ts → vue3.js} +7 -10
- package/utils/mixin.js +15 -0
- package/utils/{pathTools.ts → pathTools.js} +8 -9
- package/widgets/tera-file-select.vue +1 -1
- package/dist/lib/projectFile.d.ts +0 -190
- package/dist/lib/projectFile.js +0 -305
- package/dist/lib/projectFile.js.map +0 -1
- package/dist/lib/syncro/entities.d.ts +0 -34
- package/dist/lib/syncro/entities.js +0 -212
- package/dist/lib/syncro/entities.js.map +0 -1
- package/dist/lib/syncro/keyed (Rhino's conflicted copy 2026-05-10).js +0 -287
- package/dist/lib/syncro/keyed.d.ts +0 -95
- package/dist/lib/syncro/keyed.js +0 -288
- package/dist/lib/syncro/keyed.js (Rhino's conflicted copy 2026-05-10).map +0 -1
- package/dist/lib/syncro/keyed.js.map +0 -1
- package/dist/lib/syncro/syncro (Rhino's conflicted copy 2026-05-10).js +0 -765
- package/dist/lib/syncro/syncro.d (Rhino's conflicted copy 2026-05-10).ts +0 -336
- package/dist/lib/syncro/syncro.d.ts +0 -343
- package/dist/lib/syncro/syncro.js +0 -771
- package/dist/lib/syncro/syncro.js (Rhino's conflicted copy 2026-05-10).map +0 -1
- package/dist/lib/syncro/syncro.js.map +0 -1
- package/dist/lib/terafy.bootstrapper.d.ts +0 -42
- package/dist/lib/terafy.bootstrapper.js +0 -129
- package/dist/lib/terafy.bootstrapper.js.map +0 -1
- package/dist/lib/terafy.client.d.ts +0 -555
- package/dist/lib/terafy.client.js +0 -1153
- package/dist/lib/terafy.client.js.map +0 -1
- package/dist/lib/terafy.proxy.d.ts +0 -66
- package/dist/lib/terafy.proxy.js +0 -130
- package/dist/lib/terafy.proxy.js.map +0 -1
- package/dist/lib/terafy.server.d.ts +0 -697
- package/dist/lib/terafy.server.js +0 -2062
- package/dist/lib/terafy.server.js.map +0 -1
- package/dist/plugin.vue2.es2019 (Rhino's conflicted copy 2026-05-10).js +0 -1271
- package/dist/plugins/base.d.ts +0 -20
- package/dist/plugins/base.js +0 -22
- package/dist/plugins/base.js.map +0 -1
- package/dist/plugins/firebase.d.ts +0 -62
- package/dist/plugins/firebase.js +0 -123
- package/dist/plugins/firebase.js.map +0 -1
- package/dist/plugins/vite.d.ts +0 -12
- package/dist/plugins/vite.js +0 -22
- package/dist/plugins/vite.js.map +0 -1
- package/dist/plugins/vue2.d.ts +0 -67
- package/dist/plugins/vue2.js +0 -104
- package/dist/plugins/vue2.js.map +0 -1
- package/dist/plugins/vue3.d.ts +0 -63
- package/dist/plugins/vue3.js +0 -93
- package/dist/plugins/vue3.js.map +0 -1
- package/dist/utils/mixin.d.ts +0 -11
- package/dist/utils/mixin.js +0 -15
- package/dist/utils/mixin.js.map +0 -1
- package/dist/utils/pDefer.d.ts +0 -16
- package/dist/utils/pDefer.js +0 -19
- package/dist/utils/pDefer.js.map +0 -1
- package/dist/utils/pathTools.d.ts +0 -70
- package/dist/utils/pathTools.js +0 -120
- package/dist/utils/pathTools.js.map +0 -1
- package/tsconfig.json +0 -30
- package/utils/mixin.ts +0 -18
- package/utils/pDefer.ts +0 -27
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
1
|
/* eslint-disable no-unused-vars */
|
|
3
2
|
import {
|
|
4
3
|
isEmpty,
|
|
@@ -14,41 +13,12 @@ import {
|
|
|
14
13
|
onSnapshot as FirestoreOnSnapshot,
|
|
15
14
|
setDoc as FirestoreSetDoc,
|
|
16
15
|
updateDoc as FirestoreUpdateDoc,
|
|
17
|
-
DocumentReference,
|
|
18
|
-
Firestore,
|
|
19
|
-
Unsubscribe,
|
|
20
16
|
} from 'firebase/firestore';
|
|
21
|
-
// @ts-expect-error No declaration file for marshal
|
|
22
17
|
import marshal from '@momsfriendlydevco/marshal';
|
|
23
18
|
import {nanoid} from 'nanoid';
|
|
24
19
|
import PromiseThrottle from 'p-throttle';
|
|
25
20
|
import PromiseRetry from 'p-retry';
|
|
26
|
-
import {
|
|
27
|
-
import { BoundSupabaseyFunction } from '@iebh/supabasey';
|
|
28
|
-
import type { PostgresSql } from './entities.js';
|
|
29
|
-
|
|
30
|
-
interface ThrottleOptions<T = any> {
|
|
31
|
-
limit: number,
|
|
32
|
-
interval: number,
|
|
33
|
-
strict: boolean,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface ReactiveWrapper<T = any> {
|
|
37
|
-
doc: T;
|
|
38
|
-
setState: (newState: T) => void;
|
|
39
|
-
getState: () => T;
|
|
40
|
-
watch: (cb: (newState: T) => void) => void;
|
|
41
|
-
throttle?: ThrottleOptions | true,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface PathSplitResult {
|
|
45
|
-
fsCollection: string;
|
|
46
|
-
fsId: string;
|
|
47
|
-
entity: string;
|
|
48
|
-
id: string;
|
|
49
|
-
relation?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
21
|
+
import {FirebaseError} from 'firebase/app';
|
|
52
22
|
|
|
53
23
|
/**
|
|
54
24
|
* @class Syncro
|
|
@@ -63,7 +33,7 @@ export default class Syncro {
|
|
|
63
33
|
*
|
|
64
34
|
* @type {FirebaseApp}
|
|
65
35
|
*/
|
|
66
|
-
static firebase
|
|
36
|
+
static firebase;
|
|
67
37
|
|
|
68
38
|
|
|
69
39
|
/**
|
|
@@ -71,7 +41,7 @@ export default class Syncro {
|
|
|
71
41
|
*
|
|
72
42
|
* @type {Firestore}
|
|
73
43
|
*/
|
|
74
|
-
static firestore
|
|
44
|
+
static firestore;
|
|
75
45
|
|
|
76
46
|
|
|
77
47
|
/**
|
|
@@ -79,15 +49,15 @@ export default class Syncro {
|
|
|
79
49
|
*
|
|
80
50
|
* @type {Supabasey}
|
|
81
51
|
*/
|
|
82
|
-
static supabasey
|
|
52
|
+
static supabasey;
|
|
83
53
|
|
|
84
54
|
|
|
85
55
|
/**
|
|
86
56
|
* Postgres SQL instance in use (injected by the Cloudflare Worker runtime via Hyperdrive)
|
|
87
57
|
*
|
|
88
|
-
* @type {
|
|
58
|
+
* @type {Object}
|
|
89
59
|
*/
|
|
90
|
-
static db
|
|
60
|
+
static db;
|
|
91
61
|
|
|
92
62
|
|
|
93
63
|
/**
|
|
@@ -96,16 +66,16 @@ export default class Syncro {
|
|
|
96
66
|
*
|
|
97
67
|
* @type {String}
|
|
98
68
|
*/
|
|
99
|
-
static session
|
|
69
|
+
static session;
|
|
100
70
|
|
|
101
71
|
|
|
102
72
|
/**
|
|
103
|
-
* OPTIONAL SyncroEntries from './entities.
|
|
73
|
+
* OPTIONAL SyncroEntries from './entities.js' if its required
|
|
104
74
|
* This only gets populated if `config.forceLocalInit` is truthy and we've mounted at least one Syncro
|
|
105
75
|
*
|
|
106
|
-
* @type {
|
|
76
|
+
* @type {Object}
|
|
107
77
|
*/
|
|
108
|
-
static SyncroEntities
|
|
78
|
+
static SyncroEntities;
|
|
109
79
|
|
|
110
80
|
|
|
111
81
|
/**
|
|
@@ -113,15 +83,15 @@ export default class Syncro {
|
|
|
113
83
|
*
|
|
114
84
|
* @type {String}
|
|
115
85
|
*/
|
|
116
|
-
path
|
|
86
|
+
path;
|
|
117
87
|
|
|
118
88
|
|
|
119
89
|
/**
|
|
120
90
|
* The Firestore docHandle when calling various Firestore functions
|
|
121
91
|
*
|
|
122
|
-
* @type {DocumentReference
|
|
92
|
+
* @type {DocumentReference}
|
|
123
93
|
*/
|
|
124
|
-
docRef
|
|
94
|
+
docRef;
|
|
125
95
|
|
|
126
96
|
|
|
127
97
|
/**
|
|
@@ -130,7 +100,7 @@ export default class Syncro {
|
|
|
130
100
|
*
|
|
131
101
|
* @type {any}
|
|
132
102
|
*/
|
|
133
|
-
value
|
|
103
|
+
value;
|
|
134
104
|
|
|
135
105
|
|
|
136
106
|
/**
|
|
@@ -150,9 +120,9 @@ export default class Syncro {
|
|
|
150
120
|
* @property {Object} context Additional named parameters to pass to callbacks like initState
|
|
151
121
|
*/
|
|
152
122
|
config = {
|
|
153
|
-
heartbeatInterval:
|
|
154
|
-
syncroRegistryUrl: 'https://tera-tools.com/api/sync'
|
|
155
|
-
context: {}
|
|
123
|
+
heartbeatInterval: 300_000, //~= 300s / 5m
|
|
124
|
+
syncroRegistryUrl: 'https://tera-tools.com/api/sync',
|
|
125
|
+
context: {},
|
|
156
126
|
};
|
|
157
127
|
|
|
158
128
|
|
|
@@ -172,9 +142,9 @@ export default class Syncro {
|
|
|
172
142
|
* Debugging printer for this instance
|
|
173
143
|
* Defaults to doing nothing
|
|
174
144
|
*
|
|
175
|
-
* @param {
|
|
145
|
+
* @param {...*} [msg] The message to output
|
|
176
146
|
*/
|
|
177
|
-
debug(...msg
|
|
147
|
+
debug(...msg) {}
|
|
178
148
|
|
|
179
149
|
|
|
180
150
|
/**
|
|
@@ -182,9 +152,9 @@ export default class Syncro {
|
|
|
182
152
|
* Debugging printer specifically for error messages
|
|
183
153
|
* Defaults to using console.log()
|
|
184
154
|
*
|
|
185
|
-
* @param {
|
|
155
|
+
* @param {...*} [msg] The message to output
|
|
186
156
|
*/
|
|
187
|
-
debugError(...msg
|
|
157
|
+
debugError(...msg) {
|
|
188
158
|
console.warn(`[Syncro ${this.path}]`, ...msg);
|
|
189
159
|
}
|
|
190
160
|
|
|
@@ -195,7 +165,7 @@ export default class Syncro {
|
|
|
195
165
|
* @param {String} path Mount path for the Syncro. Should be in the form `${ENTITY}::${ID}(::${RELATION})?`
|
|
196
166
|
* @param {Object} [options] Additional instance setters (mutates instance directly), note that the `config` subkey is merged with the existing config rather than assigned
|
|
197
167
|
*/
|
|
198
|
-
constructor(path
|
|
168
|
+
constructor(path, options) {
|
|
199
169
|
this.path = path;
|
|
200
170
|
Object.assign(this, {
|
|
201
171
|
...options,
|
|
@@ -216,7 +186,7 @@ export default class Syncro {
|
|
|
216
186
|
*
|
|
217
187
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
218
188
|
*/
|
|
219
|
-
destroy()
|
|
189
|
+
destroy() {
|
|
220
190
|
this.debug('Destroy!');
|
|
221
191
|
return Promise.resolve()
|
|
222
192
|
.then(()=> Promise.all(this._destroyActions // eslint-disable @typescript-eslint/await-thenable
|
|
@@ -230,9 +200,9 @@ export default class Syncro {
|
|
|
230
200
|
* Actions to preform when we are destroying this instance
|
|
231
201
|
* This is an array of function callbacks to execute in parallel when `destroy()` is called
|
|
232
202
|
*
|
|
233
|
-
* @type {Array<Function
|
|
203
|
+
* @type {Array<Function>}
|
|
234
204
|
*/
|
|
235
|
-
_destroyActions
|
|
205
|
+
_destroyActions = [];
|
|
236
206
|
|
|
237
207
|
|
|
238
208
|
/**
|
|
@@ -241,18 +211,18 @@ export default class Syncro {
|
|
|
241
211
|
*
|
|
242
212
|
* @param {Object} value Initial value of the reactive
|
|
243
213
|
*
|
|
244
|
-
* @returns {
|
|
214
|
+
* @returns {Object} A reactive object prototype
|
|
245
215
|
* @property {Object} doc The reactive object
|
|
246
216
|
* @property {Function} setState Function used to overwrite the default state, called as `(newState:Object)`
|
|
247
217
|
* @property {Function} getState Function used to fetch the current snapshot state, called as `()`
|
|
248
218
|
* @property {Function} watch Function used to set up state watchers, should call its callback when a change is detected, called as `(cb:Function)`
|
|
249
219
|
*/
|
|
250
|
-
getReactive(value
|
|
220
|
+
getReactive(value) {
|
|
251
221
|
console.warn('Syncro.getReactive has not been subclassed, assuming a POJO response');
|
|
252
|
-
const doc
|
|
222
|
+
const doc = {...value};
|
|
253
223
|
return {
|
|
254
224
|
doc,
|
|
255
|
-
setState(state
|
|
225
|
+
setState(state) {
|
|
256
226
|
// Shallow copy all sub keys into existing object (keeping the object pointer)
|
|
257
227
|
Object.entries(state || {})
|
|
258
228
|
.forEach(([k, v]) => doc[k] = v)
|
|
@@ -260,7 +230,7 @@ export default class Syncro {
|
|
|
260
230
|
getState() {
|
|
261
231
|
return cloneDeep(doc);
|
|
262
232
|
},
|
|
263
|
-
watch(cb
|
|
233
|
+
watch(cb) {
|
|
264
234
|
// Stub
|
|
265
235
|
},
|
|
266
236
|
};
|
|
@@ -279,9 +249,10 @@ export default class Syncro {
|
|
|
279
249
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
280
250
|
* @param {Boolean} [options.allowAsterisk=false] Whether to allow the meta asterisk character when recognising paths, this is used by the SyncroKeyed class
|
|
281
251
|
*
|
|
282
|
-
* @returns {
|
|
252
|
+
* @returns {Object} An object composed of the session path components
|
|
283
253
|
*/
|
|
284
|
-
static pathSplit(path
|
|
254
|
+
static pathSplit(path, options) {
|
|
255
|
+
/* eslint-disable @stylistic/indent-binary-ops */
|
|
285
256
|
const settings = {
|
|
286
257
|
allowAsterisk: false,
|
|
287
258
|
...options,
|
|
@@ -301,7 +272,7 @@ export default class Syncro {
|
|
|
301
272
|
+ '$'
|
|
302
273
|
);
|
|
303
274
|
|
|
304
|
-
const extracted = {
|
|
275
|
+
const extracted = {...pathMatcher.exec(path)?.groups};
|
|
305
276
|
|
|
306
277
|
if (!extracted || !extracted.entity || !extracted.id) throw new Error(`Invalid session path syntax "${path}"`);
|
|
307
278
|
if (Syncro.SyncroEntities && !(extracted.entity in Syncro.SyncroEntities)) throw new Error(`Unsupported entity "${path}" -> Entity="${extracted.entity}"`);
|
|
@@ -325,10 +296,10 @@ export default class Syncro {
|
|
|
325
296
|
* 1. Arrays are converted to Objects (Firestore cannot store nested arrays)
|
|
326
297
|
* 2. All non-POJO objects (e.g. Dates) to a symmetric object
|
|
327
298
|
*
|
|
328
|
-
* @param {Object} snapshot The current state to convert
|
|
299
|
+
* @param {Object} [snapshot={}] The current state to convert
|
|
329
300
|
* @returns {Object} A Firebase compatible object
|
|
330
301
|
*/
|
|
331
|
-
static toFirestore(snapshot
|
|
302
|
+
static toFirestore(snapshot = {}) {
|
|
332
303
|
return marshal.serialize(snapshot, {
|
|
333
304
|
...marshalBaseConfig,
|
|
334
305
|
stringify: false,
|
|
@@ -340,10 +311,10 @@ export default class Syncro {
|
|
|
340
311
|
* Convert local Firestore compatible object -> local POJO
|
|
341
312
|
* This reverses the mutations listed in `toFirestore()`
|
|
342
313
|
*
|
|
343
|
-
* @param {Object} snapshot The raw Firebase state to convert
|
|
314
|
+
* @param {Object} [snapshot={}] The raw Firebase state to convert
|
|
344
315
|
* @returns {Object} A JavaScript POJO representing the converted state
|
|
345
316
|
*/
|
|
346
|
-
static fromFirestore(snapshot
|
|
317
|
+
static fromFirestore(snapshot = {}) {
|
|
347
318
|
return marshal.deserialize(snapshot, {
|
|
348
319
|
...marshalBaseConfig,
|
|
349
320
|
destringify: false,
|
|
@@ -360,28 +331,28 @@ export default class Syncro {
|
|
|
360
331
|
* @param {Object} data The raw value to convert
|
|
361
332
|
* @returns {Object} A Firestore compatible, typed data structure
|
|
362
333
|
*/
|
|
363
|
-
static toFirestoreFields(data
|
|
364
|
-
const result
|
|
334
|
+
static toFirestoreFields(data) {
|
|
335
|
+
const result = {};
|
|
365
336
|
|
|
366
337
|
for (const [key, value] of Object.entries(data)) {
|
|
367
338
|
const type = typeof value;
|
|
368
339
|
|
|
369
340
|
if (type === 'string') { // eslint-disable-line unicorn/prefer-switch
|
|
370
|
-
result[key] = {
|
|
341
|
+
result[key] = {stringValue: value};
|
|
371
342
|
} else if (type === 'number') {
|
|
372
|
-
result[key] = {
|
|
343
|
+
result[key] = {doubleValue: value};
|
|
373
344
|
} else if (type === 'boolean') {
|
|
374
|
-
result[key] = {
|
|
345
|
+
result[key] = {booleanValue: value};
|
|
375
346
|
} else if (value === null) {
|
|
376
|
-
result[key] = {
|
|
347
|
+
result[key] = {nullValue: null};
|
|
377
348
|
} else if (Array.isArray(value)) {
|
|
378
349
|
// Need to handle the inner item structure correctly
|
|
379
|
-
result[key] = {
|
|
380
|
-
const field = Syncro.toFirestoreFields({
|
|
350
|
+
result[key] = {arrayValue: {values: value.map(item => {
|
|
351
|
+
const field = Syncro.toFirestoreFields({item});
|
|
381
352
|
return field.item; // Extract the typed value from the temporary {item: ...} structure
|
|
382
|
-
})
|
|
353
|
+
})}};
|
|
383
354
|
} else if (type === 'object') {
|
|
384
|
-
result[key] = {
|
|
355
|
+
result[key] = {mapValue: {fields: Syncro.toFirestoreFields(value)}};
|
|
385
356
|
}
|
|
386
357
|
}
|
|
387
358
|
|
|
@@ -395,11 +366,11 @@ export default class Syncro {
|
|
|
395
366
|
* NOTE: This does not deserialize the result so you likely want to use this as `fromFirestore(fromFirestoreFields(response.fields))`
|
|
396
367
|
*
|
|
397
368
|
* @see https://stackoverflow.com/a/62304377
|
|
398
|
-
* @param {Object} fields The raw Snapshot to convert
|
|
369
|
+
* @param {Object} [fields={}] The raw Snapshot to convert
|
|
399
370
|
* @returns {Object} A JavaScript POJO representing the converted state
|
|
400
371
|
*/
|
|
401
|
-
static fromFirestoreFields(fields
|
|
402
|
-
const result
|
|
372
|
+
static fromFirestoreFields(fields = {}) {
|
|
373
|
+
const result = {};
|
|
403
374
|
for (const key in fields) {
|
|
404
375
|
const value = fields[key];
|
|
405
376
|
const isDocumentType = [
|
|
@@ -409,25 +380,22 @@ export default class Syncro {
|
|
|
409
380
|
|
|
410
381
|
if (isDocumentType) {
|
|
411
382
|
switch (isDocumentType) {
|
|
412
|
-
|
|
413
|
-
|
|
383
|
+
case 'mapValue':
|
|
384
|
+
result[key] = Syncro.fromFirestoreFields(value.mapValue.fields || {});
|
|
385
|
+
break;
|
|
414
386
|
|
|
415
|
-
|
|
387
|
+
case 'arrayValue': {
|
|
388
|
+
const list = value.arrayValue.values;
|
|
389
|
+
result[key] = !!list ? list.map(l => Syncro.fromFirestoreFields(l)) : [];
|
|
416
390
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
case 'nullValue':
|
|
424
|
-
result[key] = null;
|
|
425
|
-
|
|
426
|
-
break;
|
|
427
|
-
|
|
428
|
-
default:
|
|
429
|
-
result[key] = value[isDocumentType];
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
case 'nullValue':
|
|
394
|
+
result[key] = null;
|
|
395
|
+
break;
|
|
430
396
|
|
|
397
|
+
default:
|
|
398
|
+
result[key] = value[isDocumentType];
|
|
431
399
|
}
|
|
432
400
|
} else {
|
|
433
401
|
// This case might not be standard Firestore field structure, but handle recursively
|
|
@@ -445,7 +413,7 @@ export default class Syncro {
|
|
|
445
413
|
*
|
|
446
414
|
* @returns {Promise<Object|Null>} An eventual snapshot of the given path, if the entity doesn't exist null is returned
|
|
447
415
|
*/
|
|
448
|
-
static getSnapshot(path
|
|
416
|
+
static getSnapshot(path) {
|
|
449
417
|
const {fsCollection, fsId} = Syncro.pathSplit(path);
|
|
450
418
|
|
|
451
419
|
return Promise.resolve()
|
|
@@ -470,7 +438,7 @@ export default class Syncro {
|
|
|
470
438
|
*
|
|
471
439
|
* @returns {Promise<*>} The state object after it has been applied
|
|
472
440
|
*/
|
|
473
|
-
static setSnapshot(path
|
|
441
|
+
static setSnapshot(path, state, options) {
|
|
474
442
|
const settings = {
|
|
475
443
|
method: 'merge',
|
|
476
444
|
...options,
|
|
@@ -481,7 +449,7 @@ export default class Syncro {
|
|
|
481
449
|
|
|
482
450
|
return Promise.resolve()
|
|
483
451
|
.then(()=> {
|
|
484
|
-
if (settings.method === 'merge') {
|
|
452
|
+
if (settings.method === 'merge') { // eslint-disable-line unicorn/prefer-ternary
|
|
485
453
|
return FirestoreUpdateDoc(docRef, firestoreData);
|
|
486
454
|
} else { // method === 'set'
|
|
487
455
|
return FirestoreSetDoc(docRef, firestoreData); // Default set overwrites
|
|
@@ -499,7 +467,7 @@ export default class Syncro {
|
|
|
499
467
|
* @param {Number} [options.retries=3] Number of times to retry if a mounted Syncro fails its sanity checks
|
|
500
468
|
* @returns {Promise<Syncro>} A promise which resolves as this syncro instance when completed
|
|
501
469
|
*/
|
|
502
|
-
mount(options
|
|
470
|
+
mount(options) {
|
|
503
471
|
const settings = {
|
|
504
472
|
initialState: null,
|
|
505
473
|
retries: 5,
|
|
@@ -508,11 +476,11 @@ export default class Syncro {
|
|
|
508
476
|
};
|
|
509
477
|
|
|
510
478
|
const {fsCollection, fsId, entity} = Syncro.pathSplit(this.path);
|
|
511
|
-
let reactive
|
|
512
|
-
let doc
|
|
479
|
+
let reactive; // Eventual response from reactive() with the initial value
|
|
480
|
+
let doc; // Eventual Firebase document
|
|
513
481
|
|
|
514
482
|
return PromiseRetry(
|
|
515
|
-
async ()
|
|
483
|
+
async () => {
|
|
516
484
|
await this.setHeartbeat(false); // Disable any existing heartbeat
|
|
517
485
|
|
|
518
486
|
// Set up binding and wait for it to come ready
|
|
@@ -572,7 +540,7 @@ export default class Syncro {
|
|
|
572
540
|
this.debug('Initial state', {doc});
|
|
573
541
|
|
|
574
542
|
// Subscribe to remote updates
|
|
575
|
-
const unsubscribe
|
|
543
|
+
const unsubscribe = FirestoreOnSnapshot(this.docRef, snapshot => {
|
|
576
544
|
const snapshotData = Syncro.fromFirestore(snapshot.data());
|
|
577
545
|
this.debug('Incoming snapshot', {snapshotData});
|
|
578
546
|
reactive.setState(snapshotData);
|
|
@@ -595,10 +563,10 @@ export default class Syncro {
|
|
|
595
563
|
}
|
|
596
564
|
|
|
597
565
|
// Setup local state watcher
|
|
598
|
-
reactive.watch(throttle(
|
|
566
|
+
reactive.watch(throttle(newState => {
|
|
599
567
|
this.debug('Local change', {newState});
|
|
600
568
|
this.markDirty();
|
|
601
|
-
this.setFirestoreState(newState, {method: 'merge'});
|
|
569
|
+
this.setFirestoreState(newState, {method: 'merge'});
|
|
602
570
|
}, this.throttle));
|
|
603
571
|
|
|
604
572
|
await this.setHeartbeat(true, {
|
|
@@ -612,11 +580,12 @@ export default class Syncro {
|
|
|
612
580
|
minTimeout: settings.retryMinTime,
|
|
613
581
|
randomize: true,
|
|
614
582
|
factor: 3,
|
|
615
|
-
onFailedAttempt: async
|
|
583
|
+
onFailedAttempt: async e => {
|
|
616
584
|
this.debugError(`[Attempt ${e.attemptNumber}/${e.attemptNumber + e.retriesLeft - 1}] to mount syncro`, e);
|
|
617
585
|
await this.destroy();
|
|
618
586
|
},
|
|
619
587
|
},
|
|
588
|
+
|
|
620
589
|
);
|
|
621
590
|
}
|
|
622
591
|
|
|
@@ -635,13 +604,9 @@ export default class Syncro {
|
|
|
635
604
|
*
|
|
636
605
|
* @returns {Promise<Syncro>} A promise which resolves with this Syncro instance on completion
|
|
637
606
|
*/
|
|
638
|
-
async set(
|
|
639
|
-
key: string | object,
|
|
640
|
-
value: any,
|
|
641
|
-
options: { delta?: boolean, flush?: boolean, forceFlush?: boolean, flushDestroy?: boolean }
|
|
642
|
-
) {
|
|
607
|
+
async set(key, value, options) {
|
|
643
608
|
// Argument mangling - [key, value, settings] -> changes{}, settings {{{
|
|
644
|
-
let changes
|
|
609
|
+
let changes;
|
|
645
610
|
if (typeof key == 'string') { // Called as (key:String, value:*, options?:Object)
|
|
646
611
|
changes[key] = value;
|
|
647
612
|
} else if (typeof key == 'object') { // Called as (changes:Object, options?:Object)
|
|
@@ -684,6 +649,7 @@ export default class Syncro {
|
|
|
684
649
|
return this;
|
|
685
650
|
}
|
|
686
651
|
|
|
652
|
+
|
|
687
653
|
/**
|
|
688
654
|
* Schedule Syncro heartbeats
|
|
689
655
|
* This populates the `sync` presence meta-information
|
|
@@ -692,9 +658,9 @@ export default class Syncro {
|
|
|
692
658
|
*
|
|
693
659
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
694
660
|
* @param {Boolean} [options.immediate=false] Fire a heartbeat as soon as this function is called, this is only really useful on mount
|
|
695
|
-
* @returns {Promise
|
|
661
|
+
* @returns {Promise} A promise that resolves when completed (if `{immediate:true}`) or just returns void
|
|
696
662
|
*/
|
|
697
|
-
setHeartbeat(enable
|
|
663
|
+
setHeartbeat(enable = true, options) {
|
|
698
664
|
const settings = {
|
|
699
665
|
immediate: true,
|
|
700
666
|
...options,
|
|
@@ -709,13 +675,14 @@ export default class Syncro {
|
|
|
709
675
|
await this.heartbeat();
|
|
710
676
|
|
|
711
677
|
// If we're enabled - schedule the next heartbeat timer
|
|
712
|
-
if (enable) this.setHeartbeat(true);
|
|
678
|
+
if (enable) this.setHeartbeat(true);
|
|
713
679
|
};
|
|
714
680
|
|
|
715
681
|
this._heartbeatTimer = setTimeout(heartbeatAction, this.config.heartbeatInterval);
|
|
716
682
|
|
|
717
683
|
if (settings.immediate) return this.heartbeat(); // Return the promise from immediate heartbeat
|
|
718
684
|
}
|
|
685
|
+
return Promise.resolve();
|
|
719
686
|
}
|
|
720
687
|
|
|
721
688
|
|
|
@@ -725,7 +692,7 @@ export default class Syncro {
|
|
|
725
692
|
*
|
|
726
693
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
727
694
|
*/
|
|
728
|
-
async heartbeat()
|
|
695
|
+
async heartbeat() {
|
|
729
696
|
this.debug('heartbeat!');
|
|
730
697
|
|
|
731
698
|
try {
|
|
@@ -761,7 +728,7 @@ export default class Syncro {
|
|
|
761
728
|
* @param {number} retries How many tries to take before erroring
|
|
762
729
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
763
730
|
*/
|
|
764
|
-
async setFirestoreState(state
|
|
731
|
+
async setFirestoreState(state, options, retries = 0) {
|
|
765
732
|
const settings = {
|
|
766
733
|
method: 'merge',
|
|
767
734
|
...options,
|
|
@@ -771,7 +738,7 @@ export default class Syncro {
|
|
|
771
738
|
const firestoreData = Syncro.toFirestore(state);
|
|
772
739
|
|
|
773
740
|
try {
|
|
774
|
-
if (settings.method === 'merge') {
|
|
741
|
+
if (settings.method === 'merge') { // eslint-disable-line unicorn/prefer-ternary
|
|
775
742
|
return await FirestoreUpdateDoc(this.docRef, firestoreData);
|
|
776
743
|
} else { // method === 'set'
|
|
777
744
|
return await FirestoreSetDoc(this.docRef, firestoreData);
|
|
@@ -803,7 +770,7 @@ export default class Syncro {
|
|
|
803
770
|
*
|
|
804
771
|
* @returns {Promise<Object>} A promise which resolves to the Firestore state
|
|
805
772
|
*/
|
|
806
|
-
getFirestoreState()
|
|
773
|
+
getFirestoreState() {
|
|
807
774
|
if (!this.docRef) throw new Error('mount() must be called before getting Firestore state');
|
|
808
775
|
|
|
809
776
|
return FirestoreGetDoc(this.docRef)
|
|
@@ -817,7 +784,7 @@ export default class Syncro {
|
|
|
817
784
|
* @see isDirty
|
|
818
785
|
* @returns {Syncro} This chainable Syncro instance
|
|
819
786
|
*/
|
|
820
|
-
markDirty()
|
|
787
|
+
markDirty() {
|
|
821
788
|
this.isDirty = true;
|
|
822
789
|
return this;
|
|
823
790
|
}
|
|
@@ -832,7 +799,7 @@ export default class Syncro {
|
|
|
832
799
|
*
|
|
833
800
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
834
801
|
*/
|
|
835
|
-
flush(options
|
|
802
|
+
flush(options) {
|
|
836
803
|
const settings = {
|
|
837
804
|
destroy: false,
|
|
838
805
|
...options,
|
|
@@ -849,9 +816,9 @@ export default class Syncro {
|
|
|
849
816
|
/**
|
|
850
817
|
* Timer handle for heartbeats
|
|
851
818
|
*
|
|
852
|
-
* @type {
|
|
819
|
+
* @type {*}
|
|
853
820
|
*/
|
|
854
|
-
_heartbeatTimer
|
|
821
|
+
_heartbeatTimer;
|
|
855
822
|
}
|
|
856
823
|
|
|
857
824
|
|
|
@@ -863,7 +830,8 @@ export default class Syncro {
|
|
|
863
830
|
*
|
|
864
831
|
* @returns {*} The current branch contents
|
|
865
832
|
*/
|
|
866
|
-
export function randomBranch(depth
|
|
833
|
+
export function randomBranch(depth = 0) {
|
|
834
|
+
/* eslint-disable @stylistic/indent */
|
|
867
835
|
const dice = // Roll a dice to pick the content
|
|
868
836
|
depth == 0 ? 10 // first roll is always '10'
|
|
869
837
|
: random(0, 11 - depth, false); // Subsequent rolls bias downwards based on depth (to avoid recursion)
|
|
@@ -873,12 +841,12 @@ export function randomBranch(depth: number = 0): any {
|
|
|
873
841
|
: dice == 1 ? true
|
|
874
842
|
: dice == 2 ? random(1, 10_000)
|
|
875
843
|
: dice == 3 ? (new Date(random(1_000_000_000_000, 1_777_777_777_777))).toISOString()
|
|
876
|
-
: dice == 5 ? Array.from({length: random(1, 10)}, ()
|
|
844
|
+
: dice == 5 ? Array.from({length: random(1, 10)}, () => random(1, 10))
|
|
877
845
|
: dice == 6 ? null
|
|
878
|
-
: dice < 8 ? Array.from({length: random(1, 10)}, ()
|
|
846
|
+
: dice < 8 ? Array.from({length: random(1, 10)}, () => randomBranch(depth+1))
|
|
879
847
|
: Object.fromEntries(
|
|
880
848
|
Array.from({length: random(1, 5)})
|
|
881
|
-
.map((v, k)
|
|
849
|
+
.map((v, k) => [
|
|
882
850
|
sample(['foo', 'bar', 'baz', 'quz', 'flarp', 'corge', 'grault', 'garply', 'waldo', 'fred', 'plugh', 'thud'])
|
|
883
851
|
+ `_${k}`,
|
|
884
852
|
randomBranch(depth+1),
|
|
@@ -899,30 +867,30 @@ const marshalBaseConfig = {
|
|
|
899
867
|
{ // Flatten arrays into something Firebase can handle
|
|
900
868
|
id: `~array`,
|
|
901
869
|
recursive: true,
|
|
902
|
-
test:
|
|
903
|
-
serialize:
|
|
904
|
-
deserialize:
|
|
870
|
+
test: v => Array.isArray(v),
|
|
871
|
+
serialize: v => ({_: '~array', ...v}),
|
|
872
|
+
deserialize: v => {
|
|
905
873
|
const arr = Array.from({length: Object.keys(v).length - 1});
|
|
906
874
|
|
|
907
875
|
Object.entries(v)
|
|
908
876
|
.filter(([k]) => k !== '_')
|
|
909
|
-
.forEach(([k, val]) => arr[+k] = val);
|
|
877
|
+
.forEach(([k, val]) => arr[+k] = val);
|
|
910
878
|
|
|
911
879
|
return arr;
|
|
912
880
|
},
|
|
913
881
|
},
|
|
914
882
|
{ // Strip Functions during {,de-}serialization
|
|
915
883
|
id: '~function',
|
|
916
|
-
test:
|
|
917
|
-
serialize: (v
|
|
884
|
+
test: v => typeof v == 'function',
|
|
885
|
+
serialize: (v, path) => {
|
|
918
886
|
console.warn('Marshal Warning: Stripping function from path', path.join('.'));
|
|
919
887
|
throw new Error('Function serializing is forbidden');
|
|
920
888
|
},
|
|
921
|
-
deserialize: (v
|
|
889
|
+
deserialize: (v, path) => {
|
|
922
890
|
console.warn('Marshal Warning: Stripping function from path', path.join('.'));
|
|
923
891
|
},
|
|
924
892
|
},
|
|
925
893
|
...marshal.settings.modules // Use default Marshal modules excepting...
|
|
926
|
-
.filter(
|
|
894
|
+
.filter(mod => mod.id != '~function') // Remove the Marshal function module as this upsets Cloudflare workers by using the `eval` built-in
|
|
927
895
|
],
|
|
928
896
|
};
|