@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,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":"AAAA,OAAO,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,GAAG,IAAI,eAAe,EACtB,MAAM,IAAI,eAAe,EACzB,UAAU,IAAI,mBAAmB,EACjC,MAAM,IAAI,eAAe,EACzB,SAAS,IAAI,kBAAkB,GAI/B,MAAM,oBAAoB,CAAC;AAC5B,aAAa;AACb,OAAO,OAAO,MAAM,4BAA4B,CAAC;AACjD,OAAO,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAC;AAC9B,OAAO,YAAY,MAAM,SAAS,CAAC;AAqBnC;;;;;EAKE;AACF,MAAM,CAAC,OAAO,OAAO,MAAM;IAuG1B;;;;;;MAME;IACF,KAAK,CAAC,GAAG,GAAU,IAAG,CAAC,CAAC,qCAAqC;IAG7D;;;;;;MAME;IACF,UAAU,CAAC,GAAG,GAAU;QACvB,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IAC9C,CAAC;IAGD;;;;;MAKE;IACF,YAAY,IAAY,EAAE,OAAa;QA9DvC;;;;UAIE;QACF,aAAQ,GAAG,GAAG,CAAC;QAGf;;;;;;;UAOE;QACF,WAAM,GAAG;YACR,iBAAiB,EAAE,KAAgB,EAAE,QAAQ;YAC7C,iBAAiB,EAAE,iCAA2C;YAC9D,OAAO,EAAE,EAAyB;SAClC,CAAC;QAGF;;;;;;;UAOE;QACF,YAAO,GAAG,KAAK,CAAC;QA6DhB;;;;;UAKE;QACF,oBAAe,GAAsB,EAAE,CAAC;QAnCvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YACnB,GAAG,OAAO;YACV,MAAM,EAAE;gBACP,GAAG,IAAI,CAAC,MAAM;gBACd,GAAG,OAAO,EAAE,MAAM;aAClB;SACD,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,0DAA0D;YAC9E,MAAM,CAAC,OAAO,GAAG,UAAU,MAAM,EAAE,EAAE,CAAC;IACxC,CAAC;IAGD;;;;;MAKE;IACF,OAAO;QACN,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACvB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe;aACrC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAChB;aACC,IAAI,CAAC,GAAE,EAAE,CAAC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC,CAAA,CAAC,oDAAoD;IAC5F,CAAC;IAYD;;;;;;;;;;;MAWE;IACF,WAAW,CAAC,KAAU;QACrB,OAAO,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACrF,IAAI,GAAG,GAAwB,EAAC,GAAG,KAAK,EAAC,CAAC;QAC1C,OAAO;YACN,GAAG;YACH,QAAQ,CAAC,KAAU;gBAClB,8EAA8E;gBAC9E,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;qBACzB,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAClC,CAAC;YACD,QAAQ;gBACP,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YACD,KAAK,CAAC,EAA2B;gBAChC,OAAO;YACR,CAAC;SACD,CAAC;IACH,CAAC;IAAA,CAAC;IAGF;;;;;;;;;;;;;MAaE;IACF,MAAM,CAAC,SAAS,CAAC,IAAY,EAAE,OAAa;QAC3C,IAAI,QAAQ,GAAG;YACd,aAAa,EAAE,KAAK;YACpB,GAAG,OAAO;SACV,CAAC;QAEF,IAAI,WAAW,GAAG,IAAI,MAAM;QAC3B,qHAAqH;QACrH,GAAG;cACD,kBAAkB,CAAC,qEAAqE;cACxF,IAAI,CAAC,mBAAmB;cACxB,qEAAqE,CAAC,sCAAsC;cAC5G,oBAAoB,CAAC,wCAAwC;cAC5D,KAAK,CAAC,yCAAyC;cAC/C,KAAK,CAAC,gCAAgC;cACtC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,mCAAmC;cAC1E,QAAQ;cACR,GAAG,CACL,CAAC;QAEF,IAAI,SAAS,GAAG,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAyD,CAAC;QAE7G,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAC;QAC/G,IAAI,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,gBAAgB,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAE3J,OAAO;YACN,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,EAAE,EAAE,SAAS,CAAC,EAAE;YAChB,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,YAAY,EAAE,SAAS,CAAC,MAAM;YAC9B,IAAI,EAAE,SAAS,CAAC,QAAQ;gBACvB,CAAC,CAAC,GAAG,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,QAAQ,EAAE;gBAC1C,CAAC,CAAC,SAAS,CAAC,EAAE;SACf,CAAC;IACH,CAAC;IAGD;;;;;;;;;MASE;IACF,MAAM,CAAC,WAAW,CAAC,WAAgB,EAAE;QACpC,OAAO,OAAO,CAAC,SAAS,CAAC,QAAQ,EAAE;YAClC,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,IAAI,EAAE,kFAAkF;YAC/F,OAAO,EAAE;gBACR,oBAAoB;gBACpB,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO;aAC3B;YACD,SAAS,EAAE,KAAK;SAChB,CAAC,CAAC;IACJ,CAAC;IAGD;;;;;;MAME;IACF,MAAM,CAAC,aAAa,CAAC,WAAgB,EAAE;QACtC,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE;YACpC,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,IAAI,EAAE,sEAAsE;YACnF,OAAO,EAAE;gBACR,oBAAoB;gBACpB,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO;aAC3B;YACD,WAAW,EAAE,KAAK;SAClB,CAAC,CAAC;IACJ,CAAC;IAGD;;;;;;;;MAQE;IACF,MAAM,CAAC,iBAAiB,CAAC,IAAS;QACjC,MAAM,MAAM,GAAwB,EAAE,CAAC;QAEvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC;YAE1B,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC,CAAC,4CAA4C;gBACpE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;YACtC,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;YACtC,CAAC;iBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;YACvC,CAAC;iBAAM,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YACnC,CAAC;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,oDAAoD;gBACpD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;4BACtD,MAAM,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;4BACjD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,mEAAmE;wBACvF,CAAC,CAAC,EAAE,EAAE,CAAC;YACR,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACzE,CAAC;QACF,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;IAGD;;;;;;;;MAQE;IACF,MAAM,CAAC,mBAAmB,CAAC,SAAc,EAAE;QAC1C,IAAI,MAAM,GAAwB,EAAE,CAAC;QACrC,KAAK,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;YACxB,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,cAAc,GAAG;gBACpB,aAAa,EAAE,cAAc,EAAE,aAAa;gBAC5C,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB;aAC3F,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,0CAA0C;YAEpF,IAAI,cAAc,EAAE,CAAC;gBACpB,IAAI,cAAc,KAAK,UAAU,EAAE,CAAC;oBACnC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;gBACvE,CAAC;qBAAM,IAAI,cAAc,KAAK,YAAY,EAAE,CAAC;oBAC5C,IAAI,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;oBACnC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,CAAC;qBAAM,IAAI,cAAc,KAAK,WAAW,EAAE,CAAC;oBAC3C,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;gBACrC,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,oFAAoF;gBACpF,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAA;YAChD,CAAC;QACF,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAGD;;;;;;MAME;IACF,MAAM,CAAC,WAAW,CAAC,IAAY;QAC9B,IAAI,EAAC,YAAY,EAAE,IAAI,EAAC,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAElD,OAAO,OAAO,CAAC,OAAO,EAAE;aACtB,IAAI,CAAC,KAAK,IAAG,EAAE,CAAC,eAAe,CAAE,+CAA+C;QAChF,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,CACrD,CAAC;aACD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,sBAAsB;YAC/C,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAClC,CAAC,CAAC,IAAI,CACN,CAAA;IACH,CAAC;IAGD;;;;;;;;;;MAUE;IACF,MAAM,CAAC,WAAW,CAAC,IAAY,EAAE,KAAU,EAAE,OAAsC;QAClF,IAAI,QAAQ,GAAG;YACd,MAAM,EAAE,OAAO;YACf,GAAG,OAAO;SACV,CAAC;QACF,IAAI,EAAC,YAAY,EAAE,IAAI,EAAC,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACrE,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEhD,OAAO,OAAO,CAAC,OAAO,EAAE;aACtB,IAAI,CAAC,GAAE,EAAE;YACT,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACjC,OAAO,kBAAkB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC,CAAC,mBAAmB;gBAC3B,OAAO,eAAe,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,yBAAyB;YACzE,CAAC;QACF,CAAC,CAAC;aACD,IAAI,CAAC,GAAE,EAAE,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC;IAGD;;;;;;;MAOE;IACF,KAAK,CAAC,OAAa;QAClB,IAAI,QAAQ,GAAG;YACd,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,CAAC;YACV,YAAY,EAAE,GAAG;YACjB,GAAG,OAAO;SACV,CAAC;QAEF,IAAI,EAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAC,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,QAAyB,CAAC,CAAC,4DAA4D;QAC3F,IAAI,GAAQ,CAAC,CAAC,6BAA6B;QAE3C,OAAO,YAAY,CAClB,KAAK,IAAqB,EAAE;YAC3B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,mGAAmG;YAEnI,+CAA+C;YAC/C,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;YAEpE,kBAAkB;YAClB,IAAI,YAAY,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAElD,iCAAiC;YACjC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;YAC5L,IAAI,CAAC,KAAK,GAAG,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC;YAEhC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,EAAC,GAAG,EAAC,CAAC,CAAC;YAEnC,8BAA8B;YAC9B,MAAM,WAAW,GAAgB,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;gBAC5E,IAAI,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;gBACzD,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAC,YAAY,EAAC,CAAC,CAAC;gBAChD,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,0FAA0F;YAElI,iDAAiD;YACjD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,iCAAiC;gBACrD,aAAa;YACd,CAAC;iBAAM,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,yEAAyE;gBAC5G,IAAI,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;gBACzE,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAC,MAAM,EAAE,KAAK,EAAC,CAAC,CAAC;YACtE,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,KAAK,CAAC,wCAAwC,MAAM,kBAAkB,CAAC,CAAC;gBAC7E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC9E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAClB,MAAM,IAAI,KAAK,CAAC,2BAA2B,YAAY,KAAK,IAAI,cAAc,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;gBACtG,CAAC;gBACD,4FAA4F;YAC7F,CAAC;YAED,4BAA4B;YAC5B,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,QAAa,EAAE,EAAE;gBACzC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,EAAC,QAAQ,EAAC,CAAC,CAAC;gBACvC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAC,MAAM,EAAE,OAAO,EAAC,CAAC,CAAC;YACrD,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAEnB,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;gBAC7B,SAAS,EAAE,IAAI;aACf,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QACb,CAAC,EACD;YACC,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,UAAU,EAAE,QAAQ,CAAC,YAAY;YACjC,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,CAAC;YACT,eAAe,EAAE,KAAK,EAAE,CAAM,EAAE,EAAE;gBACjC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,WAAW,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;gBAC1G,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,gDAAgD;YACvE,CAAC;SACD,CACD,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;MAcE;IACF,KAAK,CAAC,GAAG,CACR,GAAoB,EACpB,KAAU,EACV,OAA2F;QAE3F,wEAAwE;QACxE,IAAI,OAAY,CAAC;QACjB,IAAI,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC,mDAAmD;YAChF,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,IAAI,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC,CAAC,8CAA8C;YAClF,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACxF,CAAC;QACD,MAAM;QAEN,IAAI,QAAQ,GAAG;YACd,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,KAAK;YACjB,YAAY,EAAE,KAAK;YACnB,GAAG,OAAO;SACV,CAAC;QAEF,gBAAgB;QAChB,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,uBAAuB;YAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;iBACrB,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;iBAC9C,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;gBACnB,UAAU,GAAG,IAAI,CAAC;gBAClB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACP,UAAU,GAAG,IAAI,CAAC;YAClB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;QAED,2BAA2B;QAC3B,IACC,CAAC,QAAQ,CAAC,UAAU,IAAI,UAAU,CAAC;eAChC,QAAQ,CAAC,KAAK,EAChB,CAAC;YACF,MAAM,IAAI,CAAC,KAAK,CAAC,EAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,EAAC,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;MAQE;IACF,YAAY,CAAC,SAAkB,IAAI,EAAE,OAAa;QACjD,IAAI,QAAQ,GAAG;YACd,SAAS,EAAE,IAAI;YACf,GAAG,OAAO;SACV,CAAC;QAEF,kDAAkD;QAClD,YAAY,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAEnC,IAAI,MAAM,EAAE,CAAC;YACZ,MAAM,eAAe,GAAG,KAAK,IAAI,EAAE;gBAClC,wBAAwB;gBACxB,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;gBAEvB,uDAAuD;gBACvD,IAAI,MAAM;oBAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;YACnD,CAAC,CAAC;YAEF,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;YAElF,IAAI,QAAQ,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,8CAA8C;QAChG,CAAC;IACF,CAAC;IAGD;;;;;MAKE;IACF,KAAK,CAAC,SAAS;QACd,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAEzB,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,YAAY,EAAE;gBACvF,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACR,cAAc,EAAE,kBAAkB;iBAClC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACpB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;iBAClC,CAAC;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,QAAQ,CAAC,UAAU,EAAE,EAAE,EAAC,QAAQ,EAAC,CAAC,CAAC;YAClF,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,oCAAoC;QAC3D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,EAAE,KAAK,CAAC,CAAC;YACxD,qDAAqD;QACtD,CAAC;IACF,CAAC;IAGD;;;;;;;;MAQE;IACF,iBAAiB,CAAC,KAAU,EAAE,OAAsC;QACnE,IAAI,QAAQ,GAAG;YACd,MAAM,EAAE,OAAO;YACf,GAAG,OAAO;SACV,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAE3F,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEhD,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YACjC,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC,CAAC,mBAAmB;YAC3B,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACpD,CAAC;IACF,CAAC;IAGD;;;;;MAKE;IACF,iBAAiB;QAChB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAE3F,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;aACjC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,wCAAwC;IAChG,CAAC;IAGD;;;;;MAKE;IACF,SAAS;QACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,IAAI,CAAC;IACb,CAAC;IAGD;;;;;;;;MAQE;IACF,KAAK,CAAC,OAAa;QAClB,IAAI,QAAQ,GAAG;YACd,OAAO,EAAE,KAAK;YACd,GAAG,OAAO;SACV,CAAC;QAEF,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aAC1G,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE;YAC5B,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,kBAAkB,CAAC,CAC3D,CAAC;IACJ,CAAC;CASD;AAGD;;;;;;;EAOE;AACF,MAAM,UAAU,YAAY,CAAC,QAAgB,CAAC;IAC7C,IAAI,IAAI,GAAG,kCAAkC;KAC5C,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,4BAA4B;QAC5C,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,sEAAsE;IAEvG,OAAO,CACN,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK;QACjB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;YAClB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAM,CAAC;gBAC/B,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,aAAiB,EAAE,aAAiB,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;oBACpF,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAC,EAAE,GAAW,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,yBAAyB;wBACxG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;4BAClB,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAC,EAAE,GAAQ,EAAE,CAAC,YAAY,CAAC,KAAK,GAAC,CAAC,CAAC,CAAC,CAAC,yBAAyB;gCAC5G,CAAC,CAAC,MAAM,CAAC,WAAW,CACnB,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAC,CAAC;qCAChC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAiB,EAAE,CAAC;oCAC7B,MAAM,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;0CAC1G,IAAI,CAAC,EAAE;oCACT,YAAY,CAAC,KAAK,GAAC,CAAC,CAAC;iCACrB,CAAC,CACH,CACD,CAAA;AACF,CAAC;AAGD;;;EAGE;AACF,MAAM,oBAAoB,GAAG;IAC5B,EAAE,EAAE,QAAQ;IACZ,SAAS,EAAE,IAAI;IACf,IAAI,EAAE,CAAC,CAAM,EAAW,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3C,SAAS,EAAE,CAAC,CAAM,EAAO,EAAE,CAAC,CAAC,EAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAC,CAAC;IACjD,WAAW,EAAE,CAAC,CAAM,EAAS,EAAE;QAC9B,IAAI,GAAG,GAAU,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAC,CAAC,CAAC;QAEjE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;aACf,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC;aAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,sCAAsC;QAE9E,OAAO,GAAG,CAAC;IACZ,CAAC;CACD,CAAC"}