@dra2020/baseclient 1.0.12 → 1.0.13

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 (120) hide show
  1. package/package.json +16 -15
  2. package/LICENSE +0 -21
  3. package/README.md +0 -26
  4. package/dist/all/all.d.ts +0 -20
  5. package/dist/baseclient.js +0 -10113
  6. package/dist/baseclient.js.map +0 -1
  7. package/dist/context/all.d.ts +0 -1
  8. package/dist/context/context.d.ts +0 -13
  9. package/dist/filterexpr/all.d.ts +0 -1
  10. package/dist/filterexpr/filterexpr.d.ts +0 -67
  11. package/dist/fsm/all.d.ts +0 -1
  12. package/dist/fsm/fsm.d.ts +0 -119
  13. package/dist/geo/all.d.ts +0 -2
  14. package/dist/geo/geo.d.ts +0 -67
  15. package/dist/geo/vfeature.d.ts +0 -4
  16. package/dist/logabstract/all.d.ts +0 -1
  17. package/dist/logabstract/log.d.ts +0 -26
  18. package/dist/logclient/all.d.ts +0 -1
  19. package/dist/logclient/log.d.ts +0 -6
  20. package/dist/ot-editutil/all.d.ts +0 -2
  21. package/dist/ot-editutil/oteditutil.d.ts +0 -14
  22. package/dist/ot-editutil/otmaputil.d.ts +0 -21
  23. package/dist/ot-js/all.d.ts +0 -9
  24. package/dist/ot-js/otarray.d.ts +0 -111
  25. package/dist/ot-js/otclientengine.d.ts +0 -38
  26. package/dist/ot-js/otcomposite.d.ts +0 -37
  27. package/dist/ot-js/otcounter.d.ts +0 -17
  28. package/dist/ot-js/otengine.d.ts +0 -22
  29. package/dist/ot-js/otmap.d.ts +0 -19
  30. package/dist/ot-js/otserverengine.d.ts +0 -38
  31. package/dist/ot-js/otsession.d.ts +0 -114
  32. package/dist/ot-js/ottypes.d.ts +0 -29
  33. package/dist/poly/all.d.ts +0 -15
  34. package/dist/poly/blend.d.ts +0 -1
  35. package/dist/poly/boundbox.d.ts +0 -16
  36. package/dist/poly/cartesian.d.ts +0 -5
  37. package/dist/poly/graham-scan.d.ts +0 -8
  38. package/dist/poly/hash.d.ts +0 -1
  39. package/dist/poly/matrix.d.ts +0 -24
  40. package/dist/poly/minbound.d.ts +0 -1
  41. package/dist/poly/poly.d.ts +0 -52
  42. package/dist/poly/polybin.d.ts +0 -5
  43. package/dist/poly/polylabel.d.ts +0 -7
  44. package/dist/poly/polypack.d.ts +0 -30
  45. package/dist/poly/polyround.d.ts +0 -1
  46. package/dist/poly/polysimplify.d.ts +0 -1
  47. package/dist/poly/quad.d.ts +0 -48
  48. package/dist/poly/selfintersect.d.ts +0 -1
  49. package/dist/poly/shamos.d.ts +0 -1
  50. package/dist/poly/simplify.d.ts +0 -2
  51. package/dist/poly/topo.d.ts +0 -46
  52. package/dist/poly/union.d.ts +0 -49
  53. package/dist/util/all.d.ts +0 -5
  54. package/dist/util/bintrie.d.ts +0 -93
  55. package/dist/util/countedhash.d.ts +0 -19
  56. package/dist/util/gradient.d.ts +0 -15
  57. package/dist/util/indexedarray.d.ts +0 -15
  58. package/dist/util/util.d.ts +0 -68
  59. package/docs/context.md +0 -2
  60. package/docs/filterexpr.md +0 -22
  61. package/docs/fsm.md +0 -243
  62. package/docs/logabstract.md +0 -2
  63. package/docs/logclient.md +0 -2
  64. package/docs/ot-editutil.md +0 -2
  65. package/docs/ot-js.md +0 -95
  66. package/docs/poly.md +0 -103
  67. package/docs/util.md +0 -2
  68. package/lib/all/all.ts +0 -21
  69. package/lib/context/all.ts +0 -1
  70. package/lib/context/context.ts +0 -82
  71. package/lib/filterexpr/all.ts +0 -1
  72. package/lib/filterexpr/filterexpr.ts +0 -699
  73. package/lib/fsm/all.ts +0 -1
  74. package/lib/fsm/fsm.ts +0 -559
  75. package/lib/geo/all.ts +0 -2
  76. package/lib/geo/geo.ts +0 -452
  77. package/lib/geo/vfeature.ts +0 -34
  78. package/lib/logabstract/all.ts +0 -1
  79. package/lib/logabstract/log.ts +0 -55
  80. package/lib/logclient/all.ts +0 -1
  81. package/lib/logclient/log.ts +0 -105
  82. package/lib/ot-editutil/all.ts +0 -2
  83. package/lib/ot-editutil/oteditutil.ts +0 -180
  84. package/lib/ot-editutil/otmaputil.ts +0 -209
  85. package/lib/ot-js/all.ts +0 -9
  86. package/lib/ot-js/otarray.ts +0 -1168
  87. package/lib/ot-js/otclientengine.ts +0 -327
  88. package/lib/ot-js/otcomposite.ts +0 -247
  89. package/lib/ot-js/otcounter.ts +0 -145
  90. package/lib/ot-js/otengine.ts +0 -71
  91. package/lib/ot-js/otmap.ts +0 -144
  92. package/lib/ot-js/otserverengine.ts +0 -329
  93. package/lib/ot-js/otsession.ts +0 -202
  94. package/lib/ot-js/ottypes.ts +0 -98
  95. package/lib/poly/all.ts +0 -15
  96. package/lib/poly/blend.ts +0 -27
  97. package/lib/poly/boundbox.ts +0 -102
  98. package/lib/poly/cartesian.ts +0 -130
  99. package/lib/poly/graham-scan.ts +0 -401
  100. package/lib/poly/hash.ts +0 -15
  101. package/lib/poly/matrix.ts +0 -309
  102. package/lib/poly/minbound.ts +0 -211
  103. package/lib/poly/poly.ts +0 -767
  104. package/lib/poly/polybin.ts +0 -218
  105. package/lib/poly/polylabel.ts +0 -204
  106. package/lib/poly/polypack.ts +0 -468
  107. package/lib/poly/polyround.ts +0 -30
  108. package/lib/poly/polysimplify.ts +0 -24
  109. package/lib/poly/quad.ts +0 -272
  110. package/lib/poly/selfintersect.ts +0 -87
  111. package/lib/poly/shamos.ts +0 -297
  112. package/lib/poly/simplify.ts +0 -119
  113. package/lib/poly/topo.ts +0 -499
  114. package/lib/poly/union.ts +0 -388
  115. package/lib/util/all.ts +0 -5
  116. package/lib/util/bintrie.ts +0 -603
  117. package/lib/util/countedhash.ts +0 -83
  118. package/lib/util/gradient.ts +0 -108
  119. package/lib/util/indexedarray.ts +0 -80
  120. package/lib/util/util.ts +0 -695
@@ -1,327 +0,0 @@
1
- // Shared libraries
2
- import * as LogAbstract from "../logabstract/all";
3
-
4
- import * as OT from "./ottypes";
5
- import * as OTC from "./otcomposite";
6
- import * as OTE from "./otengine";
7
-
8
- export class OTClientEngine extends OTE.OTEngine
9
- {
10
- // Data members
11
- clientID: string;
12
- resourceID: string;
13
- isNeedAck: boolean;
14
- isNeedResend: boolean;
15
- bReadOnly: boolean;
16
- clientSequenceNo: number;
17
- stateServer: OTC.OTCompositeResource;
18
- stateLocal: OTC.OTCompositeResource;
19
- valCache: any;
20
- prefailCache: any;
21
-
22
- actionAllClient: OTC.OTCompositeResource;
23
- actionAllPendingClient: OTC.OTCompositeResource;
24
- actionSentClient: OTC.OTCompositeResource;
25
- actionSentClientOriginal: OTC.OTCompositeResource;
26
- actionServerInterposedSentClient: OTC.OTCompositeResource;
27
-
28
- // Constructor
29
- constructor(ilog: LogAbstract.ILog, rid: string, cid: string)
30
- {
31
- super(ilog);
32
-
33
- this.resourceID = rid;
34
- this.clientID = cid;
35
- this.initialize();
36
- this.bReadOnly = false;
37
- this.valCache = {};
38
- }
39
-
40
- initialize(): void
41
- {
42
- if (this.prefailCache === undefined && this.clientSequenceNo > 0)
43
- this.prefailCache = this.valCache;
44
- this.clientSequenceNo = 0;
45
- this.isNeedAck = false;
46
- this.isNeedResend = false;
47
- this.actionAllClient = new OTC.OTCompositeResource(this.resourceID, this.clientID);
48
- this.actionAllPendingClient = new OTC.OTCompositeResource(this.resourceID, this.clientID);
49
- this.actionSentClient = new OTC.OTCompositeResource(this.resourceID, this.clientID);
50
- this.actionSentClientOriginal = new OTC.OTCompositeResource(this.resourceID, this.clientID);
51
- this.actionServerInterposedSentClient = new OTC.OTCompositeResource(this.resourceID, this.clientID);
52
- this.stateServer = new OTC.OTCompositeResource(this.resourceID, this.clientID);
53
- this.stateLocal = new OTC.OTCompositeResource(this.resourceID, this.clientID);
54
- }
55
-
56
- // Members
57
- serverClock(): number
58
- {
59
- return this.stateServer.clock;
60
- }
61
-
62
- rid(): string
63
- {
64
- return this.resourceID;
65
- }
66
-
67
- cid(): string
68
- {
69
- return this.resourceID;
70
- }
71
-
72
- toPartialValue(resourceName: string): any
73
- {
74
- return this.valCache[resourceName];
75
- }
76
-
77
- toValue(): any
78
- {
79
- return this.valCache;
80
- }
81
-
82
- toPrefailValue(): any
83
- {
84
- return this.prefailCache;
85
- }
86
-
87
- clearPrefail(): void
88
- {
89
- delete this.prefailCache;
90
- }
91
-
92
- setReadOnly(b: boolean): void
93
- {
94
- if (b != this.bReadOnly)
95
- {
96
- this.bReadOnly = b;
97
- if (this.bReadOnly)
98
- this.failbackToServerState();
99
- }
100
- }
101
-
102
- startLocalEdit(): OTC.OTCompositeResource
103
- {
104
- return new OTC.OTCompositeResource(this.resourceID, this.clientID);
105
- }
106
-
107
- isPending(): boolean
108
- {
109
- return this.isNeedResend || !this.actionAllPendingClient.isEmpty();
110
- }
111
-
112
- getPending(): OTC.OTCompositeResource
113
- {
114
- if (!this.isNeedResend && this.actionAllPendingClient.isEmpty())
115
- return null;
116
- else
117
- {
118
- // If "isNeedResend" I need to send the exact same event (instead of aggregating all pending)
119
- // because the server might have actually received and processed the event and I just didn't
120
- // receive acknowledgement. If I merge that event into others I'll lose ability to distinguish
121
- // that. Eventually when I re-establish communication with server I will get that event response
122
- // and can then move on.
123
- if (! this.isNeedResend)
124
- {
125
- this.actionSentClient = this.actionAllPendingClient.copy();
126
- //console.log(`ClientEngine:getPending: bump sequence count from ${this.clientSequenceNo}`);
127
- this.actionSentClient.clientSequenceNo = this.clientSequenceNo++;
128
- this.actionAllPendingClient.empty();
129
- }
130
- this.actionSentClient.clock = this.stateServer.clock;
131
- this.actionSentClientOriginal = this.actionSentClient.copy();
132
- this.actionServerInterposedSentClient.empty();
133
- this.isNeedAck = true;
134
- this.isNeedResend = false;
135
- return this.actionSentClient.copy();
136
- }
137
- }
138
-
139
- // When I fail to send, I need to reset to resend the event again
140
- resetPending(): void
141
- {
142
- if (this.isNeedAck)
143
- {
144
- this.isNeedAck = false;
145
- this.isNeedResend = true;
146
- //console.log('otclientengine: resetPending');
147
- }
148
- //else
149
- // console.log('otclientengine: resetPending ignored because isNeedAck false');
150
- }
151
-
152
- // When I don't accurately have server state - will then refresh from server
153
- failbackToInitialState(): void
154
- {
155
- console.log('otclientengine: failbackToInitialState');
156
- if (this.prefailCache === undefined)
157
- this.prefailCache = this.valCache;
158
- this.initialize();
159
- }
160
-
161
- // When I have server state but my state got mixed up
162
- failbackToServerState(): void
163
- {
164
- console.log('otclientengine: failbackToServerState');
165
- if (this.prefailCache === undefined)
166
- this.prefailCache = this.valCache;
167
- this.stateLocal = this.stateServer.copy();
168
- this.isNeedAck = false;
169
- this.actionSentClient.empty();
170
- this.actionSentClientOriginal.empty();
171
- this.actionServerInterposedSentClient.empty();
172
- this.actionAllPendingClient.empty();
173
- this.actionAllClient.empty();
174
- this.valCache = this.stateLocal.toValue();
175
- this.emit('state');
176
- }
177
-
178
- //
179
- // Function: OTClientEngine.addRemote
180
- //
181
- // Description:
182
- // This function is really where the action is in managing the dynamic logic of applying OT. This is run
183
- // on each end point and handles the events received from the server. This includes server acknowledgements
184
- // (both success and failure) of locally generated events as well as all the events generated from other
185
- // clients.
186
- //
187
- // The key things that happen here are:
188
- // 1. Track server state.
189
- // 2. Respond to server acknowledgement of locally generated events. This also includes validation
190
- // (with failback code) in case where server transformed my event in a way that was inconsistent
191
- // with what I expected (due to insert collision that arose due to multiple independent events).
192
- // 3. Transform the incoming event (by local events) so it can be applied to local state.
193
- // 4. Transform pending local events so they can be dispatched to the service once the service
194
- // is ready for another event.
195
- //
196
-
197
- addRemote(orig: OTC.OTCompositeResource): void
198
- {
199
- // Reset if server forces restart
200
- if (orig.clock == OTC.clockInitialValue)
201
- {
202
- this.failbackToInitialState();
203
- return;
204
- }
205
-
206
- // Reset if server restarted and we don't sync up
207
- if (orig.clock < 0)
208
- {
209
- // If server didn't lose anything I can just keep going...
210
- if (this.stateServer.clock+1 == -orig.clock)
211
- orig.clock = - orig.clock
212
- else
213
- {
214
- this.failbackToInitialState();
215
- return;
216
- }
217
- }
218
-
219
- // Ignore if I've seen this event already
220
- if (orig.clock <= this.serverClock())
221
- {
222
- return;
223
- }
224
-
225
- let bMine: boolean = orig.clientID == this.clientID;
226
- let bResend: boolean = bMine && orig.clock == OTC.clockFailureValue;
227
- let a: OTC.OTCompositeResource = orig.copy();
228
-
229
- if (bResend)
230
- {
231
- // Service failed my request. Retry with currently outstanding content.
232
- this.resetPending();
233
- return;
234
- }
235
-
236
- try
237
- {
238
- // Track server state and clock
239
- this.stateServer.compose(a);
240
-
241
- if (bMine)
242
- {
243
- // Validate that I didn't run into unresolvable conflict
244
- if (! this.actionServerInterposedSentClient.isEmpty())
245
- {
246
- this.actionSentClientOriginal.transform(this.actionServerInterposedSentClient, true);
247
- if (! this.actionSentClient.effectivelyEqual(this.actionSentClientOriginal))
248
- {
249
- this.failbackToServerState();
250
- }
251
- }
252
-
253
- // I don't need to apply to local state since it has already been applied - this is just an ack.
254
- this.isNeedAck = false;
255
- this.actionSentClient.empty();
256
- this.actionSentClientOriginal.empty();
257
- this.actionServerInterposedSentClient.empty();
258
- this.actionAllClient = this.actionAllPendingClient.copy();
259
- }
260
- else
261
- {
262
- // Transform server action to apply locally by transforming by all pending client actions
263
- a.transform(this.actionAllClient, false);
264
-
265
- // And then compose with local state
266
- this.stateLocal.compose(a);
267
-
268
- // Transform pending client by server action so it is rooted off the server state.
269
- // This ensures that I can convert the next server action I receive.
270
- this.actionAllClient.transform(orig, true);
271
-
272
- // Transform server action to be after previously sent client action and then
273
- // transform the unsent actions so they are ready to be sent.
274
- let aServerTransformed: OTC.OTCompositeResource = orig.copy();
275
- aServerTransformed.transform(this.actionSentClient, false);
276
- this.actionAllPendingClient.transform(aServerTransformed, true);
277
-
278
- // And then transform the sent client action so ready to be used for transforming next server event
279
- this.actionSentClient.transform(orig, true);
280
-
281
- // Track server operations interposed between a sent action
282
- if (this.isNeedAck)
283
- this.actionServerInterposedSentClient.compose(orig);
284
-
285
- // Let clients know
286
- this.valCache = this.stateLocal.toValue();
287
- this.emit('state');
288
- }
289
- }
290
- catch (err)
291
- {
292
- this.ilog.error("OTClientEngine.addRemote: unexpected exception: " + err);
293
- this.failbackToInitialState();
294
- }
295
- }
296
-
297
- //
298
- // Function: addLocalEdit
299
- //
300
- // Description:
301
- // This is the logic for adding an action to the local state. The logic is straight-forward
302
- // as we need to track:
303
- // 1. The composed set of unacknowledged locally generated events.
304
- // 2. The composed set of unsent locally generated events (queued until sent event is acknowledged).
305
- // 3. The local state.
306
- // 4. An undo operation.
307
- //
308
- addLocalEdit(orig: OTC.OTCompositeResource): void
309
- {
310
- if (! this.bReadOnly)
311
- {
312
- try
313
- {
314
- this.actionAllClient.compose(orig);
315
- this.actionAllPendingClient.compose(orig);
316
- this.stateLocal.compose(orig);
317
- this.valCache = this.stateLocal.toValue();
318
- this.emit('state');
319
- }
320
- catch (err)
321
- {
322
- this.ilog.error("OTClientEngine.addLocalEdit: unexpected exception: " + err);
323
- this.failbackToInitialState();
324
- }
325
- }
326
- }
327
- };
@@ -1,247 +0,0 @@
1
- import * as OT from "./ottypes";
2
- import * as OTA from "./otarray";
3
- import * as OTM from "./otmap";
4
- import * as OTC from "./otcounter";
5
-
6
- export const clockInitialValue: number = -1; // Initial value
7
- export const clockTerminateValue: number = -2; // Terminal action from client.
8
- export const clockRandomizeValue: number = -3; // Fill in with random data.
9
- export const clockFailureValue: number = -4; // Server failed to apply
10
- export const clockInitializeValue: number = -5; // Used to initialize client to a specific string value.
11
- export const clockUndoValue: number = -6; // Used to indicate we should generate an undo event.
12
- export const clockSeenValue: number = -7; // Server has already seen this event
13
-
14
- export class OTCompositeResource extends OT.OTResourceBase
15
- {
16
- resourceID: string;
17
- clientID: string;
18
- clock: number;
19
- clientSequenceNo: number;
20
- static typeRegistry: any;
21
-
22
- constructor(rid: string, cid: string)
23
- {
24
- super('root', 'composite');
25
- this.resourceID = rid;
26
- this.clientID = cid;
27
- this.clock = clockInitialValue;
28
- this.clientSequenceNo = 0;
29
- }
30
-
31
- static registerType(underlyingType: string, factory: (resourceName: string) => OT.OTResourceBase): void
32
- {
33
- if (OTCompositeResource.typeRegistry == null)
34
- OTCompositeResource.typeRegistry = { };
35
- OTCompositeResource.typeRegistry[underlyingType] = factory;
36
- }
37
-
38
- findResource(rname: string, utype: string = '', bConstruct: boolean = false): OT.IOTResource
39
- {
40
- for (let i: number = this.length-1; i >= 0; i--)
41
- if (this.edits[i].resourceName === rname)
42
- return this.edits[i];
43
- if (bConstruct)
44
- {
45
- let edit: OT.IOTResource = OTCompositeResource.constructResource(rname, utype);
46
- this.edits.push(edit);
47
- return edit;
48
- }
49
- else
50
- return null;
51
- }
52
-
53
- map(rid: string): OTM.OTMapResource
54
- {
55
- return this.findResource(rid, 'map', true) as OTM.OTMapResource;
56
- }
57
-
58
- array(rid: string): OTA.OTArrayResource
59
- {
60
- return this.findResource(rid, 'array', true) as OTA.OTArrayResource;
61
- }
62
-
63
- counter(rid: string): OTC.OTCounterResource
64
- {
65
- return this.findResource(rid, 'counter', true) as OTC.OTCounterResource;
66
- }
67
-
68
- garbageCollect(map: any): boolean
69
- {
70
- if (map)
71
- {
72
- let bDirty: boolean = false;
73
- for (let i: number = this.length-1; i >= 0; i--)
74
- {
75
- if (map[this.edits[i].resourceName] === undefined)
76
- {
77
- this.edits.splice(i, 1);
78
- bDirty = true;
79
- }
80
- }
81
- return bDirty;
82
- }
83
- else
84
- return false; // If no resource map, we don't garbage collect
85
- }
86
-
87
- isEmpty(): boolean
88
- {
89
- // Canonical empty is an empty edits array, but an array of empty edits is always considered empty
90
- for (let i: number = 0; i < this.length; i++)
91
- if (! this.edits[i].isEmpty())
92
- return false;
93
- return true;
94
- }
95
-
96
- // Copy an instance
97
- copy(): OTCompositeResource
98
- {
99
- let c: OTCompositeResource = new OTCompositeResource(this.resourceID, this.clientID);
100
- c.clock = this.clock;
101
- c.clientSequenceNo = this.clientSequenceNo;
102
- for (let i: number = 0; i < this.length; i++)
103
- c.edits.push(this.edits[i].copy());
104
- return c;
105
- }
106
-
107
- // Test whether two operations are effectively equivalent
108
- effectivelyEqual(rhs: OTCompositeResource): boolean
109
- {
110
- // This should really be a structural error
111
- if (this.length != rhs.length)
112
- return false;
113
- for (let i: number = 0; i < this.length; i++)
114
- {
115
- let lhsEdit: OT.IOTResource = this.edits[i];
116
- let rhsEdit: OT.IOTResource = rhs.findResource(lhsEdit.resourceName);
117
-
118
- if ((rhsEdit == null && !lhsEdit.isEmpty()) || ! lhsEdit.effectivelyEqual(rhsEdit))
119
- return false;
120
- }
121
- return true;
122
- }
123
-
124
- // Core OT algorithm for this type
125
- transform(rhs: OTCompositeResource, bPriorIsService: boolean): void
126
- {
127
- for (let i: number = 0; i < rhs.length; i++)
128
- {
129
- let rhsEdit: OT.IOTResource = rhs.edits[i];
130
- let lhsEdit: OT.IOTResource = this.findResource(rhsEdit.resourceName, rhsEdit.underlyingType, false);
131
- if (lhsEdit)
132
- lhsEdit.transform(rhsEdit, bPriorIsService);
133
- }
134
- }
135
-
136
- // compose two edit actions
137
- compose(rhs: OTCompositeResource): void // throws on error
138
- {
139
- for (let i: number = 0; i < rhs.length; i++)
140
- {
141
- let rhsEdit: OT.IOTResource = rhs.edits[i];
142
-
143
- let lhsEdit: OT.IOTResource = this.findResource(rhsEdit.resourceName, rhsEdit.underlyingType, !rhsEdit.isEmpty());
144
- if (lhsEdit)
145
- lhsEdit.compose(rhsEdit);
146
- }
147
-
148
- this.clock = rhs.clock;
149
- this.clientSequenceNo = rhs.clientSequenceNo;
150
- }
151
-
152
- // apply this edit to an existing value, returning new value (if underlying type is mutable, may modify input)
153
- // For composite, takes array of values, returns array of results, one for each underlying resource.
154
- apply(runningValue: any): any
155
- {
156
- if (runningValue == null)
157
- runningValue = { };
158
- for (let i: number = 0; i < this.length; i++)
159
- {
160
- let e: OT.IOTResource = this.edits[i];
161
- runningValue[e.resourceName] = e.apply(runningValue[e.resourceName]);
162
- }
163
- return runningValue;
164
- }
165
-
166
- toPartialValue(resourceName: string): any
167
- {
168
- let e = this.edits.find(e => e.resourceName === resourceName);
169
- return e ? e.apply(null) : null;
170
- }
171
-
172
- toValue(): any
173
- {
174
- return this.apply(null);
175
- }
176
-
177
- minimize(): void
178
- {
179
- for (let i: number = 0; i < this.length; i++)
180
- this.edits[i].minimize();
181
- }
182
-
183
- static constructResource(rname: string, utype: string): OT.IOTResource
184
- {
185
- if (OTCompositeResource.typeRegistry == null)
186
- {
187
- //throw "OTCompositeResource.constructResource: no registered factories";
188
- // This is only place where Composite type knows of other types - could hoist to outer level
189
- OTCompositeResource.registerType('string', OTA.OTStringResource.factory);
190
- OTCompositeResource.registerType('array', OTA.OTArrayResource.factory);
191
- OTCompositeResource.registerType('map', OTM.OTMapResource.factory);
192
- OTCompositeResource.registerType('counter', OTC.OTCounterResource.factory);
193
- }
194
-
195
- let factory: (resourceName: string) => OT.OTResourceBase = OTCompositeResource.typeRegistry[utype];
196
- if (factory == null)
197
- throw "OTCompositeResource.constructResource: no registered factory for " + utype;
198
- return factory(rname);
199
- }
200
-
201
- // Deserialization
202
- static constructFromObject(o: any): OTCompositeResource
203
- {
204
- let cedit: OTCompositeResource = new OTCompositeResource("", "");
205
- if (o['resourceID'] !== undefined)
206
- cedit.resourceID = o['resourceID'];
207
- if (o['clientID'] !== undefined)
208
- cedit.clientID = o['clientID'];
209
- if (o['clock'] !== undefined)
210
- cedit.clock = Number(o['clock']);
211
- if (o['clientSequenceNo'] !== undefined)
212
- cedit.clientSequenceNo = Number(o['clientSequenceNo']);
213
- if (o['edits'] !== undefined)
214
- {
215
- let arrEdits: any = o['edits'];
216
- for (let i: number = 0; i < arrEdits.length; i++)
217
- {
218
- let a: any = arrEdits[i];
219
- let rname: string = a['resourceName'];
220
- let utype: string = a['underlyingType'];
221
- let edit: OT.IOTResource = this.constructResource(rname, utype);
222
- edit.edits = a['edits'];
223
- cedit.edits.push(edit);
224
- }
225
- }
226
- return cedit;
227
- }
228
-
229
- // Serialization
230
- toJSON(): any
231
- {
232
- let o: any = {
233
- "resourceID": this.resourceID,
234
- "clientID": this.clientID,
235
- "clock": this.clock,
236
- "clientSequenceNo": this.clientSequenceNo,
237
- "edits": [] };
238
- for (let i: number = 0; i < this.length; i++)
239
- {
240
- let edit: OT.IOTResource = this.edits[i];
241
- let oEdit: any = { "resourceName": edit.resourceName, "underlyingType": edit.underlyingType, "edits": edit.edits };
242
- o["edits"].push(oEdit);
243
- }
244
- return o;
245
- }
246
-
247
- }
@@ -1,145 +0,0 @@
1
- import * as OT from "./ottypes";
2
-
3
- // This implements OT for a simple map of counters. Instead of a new value replacing the
4
- // keyed value, values are added together. This allows a simple accumulating counter.
5
- // Possible future additions:
6
- // Add additional semantics for how the values accumulate. Examples from DropBox's datastore API
7
- // included "min" and "max" as alternate rules to "sum".
8
- //
9
-
10
- export const OpCounterAdd = 1;
11
- export const OpCounterDel = 2;
12
- export type CounterEdit = [ number, string, any ]; // Op, Key, Value
13
-
14
- export class OTCounterResource extends OT.OTResourceBase
15
- {
16
- constructor(rid: string)
17
- {
18
- super(rid, 'counter');
19
- }
20
- static factory(rid: string): OTCounterResource { return new OTCounterResource(rid); }
21
-
22
- // copy an instance
23
- copy(): OTCounterResource
24
- {
25
- let c: OTCounterResource = new OTCounterResource(this.resourceName);
26
- for (let i: number = 0; i < this.length; i++)
27
- {
28
- let e: CounterEdit = this.edits[i];
29
- c.edits.push([ e[0], e[1], e[2] ]);
30
- }
31
- return c;
32
- }
33
-
34
- // Test whether two operations are effectively equivalent
35
- effectivelyEqual(rhs: OTCounterResource): boolean
36
- {
37
- // This should really be a structural error
38
- if (this.length != rhs.length)
39
- return false;
40
-
41
- // This checks for exact structural equivalency. Really the ordering shouldn't matter for Counter so
42
- // an improvement to this algorithm would be to be more robust to ordering differences.
43
- for (let i: number = 0; i < this.length; i++)
44
- {
45
- let e1: CounterEdit = this.edits[i];
46
- let e2: CounterEdit = rhs.edits[i];
47
- if (e1[0] != e2[0] || e1[1] != e2[1] || e1[2] != e2[2])
48
- return false;
49
- }
50
- return true;
51
- }
52
-
53
- // Core OT algorithm for this type
54
- transform(prior: OTCounterResource, bPriorIsService: boolean): void
55
- {
56
- // Last wins - if I'm last, my adds and deletes are all preserved
57
- if (bPriorIsService)
58
- return;
59
-
60
- // Deletes in prior will delete mine. Implement by loading up properties rather than
61
- // using N^2 lookup through Edits array.
62
- let myEdits: any = this.toObject();
63
- let bEdited: boolean = false;
64
-
65
- // Now delete any that are deleted by prior.
66
- for (let i: number = 0; i < prior.length; i++)
67
- {
68
- let eP: CounterEdit = prior.edits[i];
69
- if (eP[0] == OpCounterDel)
70
- {
71
- delete myEdits[eP[1]];
72
- bEdited = true;
73
- }
74
- }
75
-
76
- // Now restore edit array from edited object
77
- if (bEdited)
78
- this.fromObject(myEdits);
79
- }
80
-
81
- // compose two edit actions
82
- compose(rhs: OTCounterResource): void // throws on error
83
- {
84
- let lhsKeys: any = this.toObject();
85
- let rhsKeys: any = rhs.toObject();
86
- for (let i: number = 0; i < rhs.length; i++)
87
- {
88
- let eR: CounterEdit = rhs.edits[i];
89
- let eL: CounterEdit = lhsKeys[eR[1]];
90
- if (eL === undefined)
91
- lhsKeys[eR[1]] = [ eR[0], eR[1], eR[2] ];
92
- else
93
- eL[2] += eR[2];
94
- }
95
- this.fromObject(lhsKeys);
96
- }
97
-
98
- apply(startValue: any): any
99
- {
100
- if (startValue == null)
101
- startValue = { };
102
- for (let i: number = 0; i < this.length; i++)
103
- {
104
- let e: CounterEdit = this.edits[i];
105
- switch (e[0])
106
- {
107
- case OpCounterAdd:
108
- if (startValue[e[1]] === undefined)
109
- startValue[e[1]] = e[2];
110
- else
111
- startValue[e[1]] += e[2];
112
- break;
113
- case OpCounterDel:
114
- delete startValue[e[1]];
115
- break;
116
- }
117
- }
118
- return startValue;
119
- }
120
-
121
- minimize(): any
122
- {
123
- // No-op
124
- }
125
-
126
- loadObject(o: any): any
127
- {
128
- for (let i: number = 0; i < this.length; i++)
129
- o[(this.edits[i])[1]] = this.edits[i];
130
- return o;
131
- }
132
-
133
- toObject(): any
134
- {
135
- return this.loadObject({ });
136
- }
137
-
138
- fromObject(o: any): void
139
- {
140
- this.edits = [];
141
- for (var p in o)
142
- if (o.hasOwnProperty(p))
143
- this.edits.push(o[p]);
144
- }
145
- }