jsynchronous 0.9.5 → 0.9.6

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/README.md CHANGED
@@ -257,7 +257,7 @@ $orientation.i = 0; // Will synchronize
257
257
  $orientation.j = 1; // Will synchronize
258
258
  ```
259
259
 
260
- We recommended you use the prefix ‘$’ or some other convention when you reference a synchronized variable to indicate that assignments to that variable will be sent over the network.
260
+ We recommended you use the prefix ‘$’ or some other convention when you reference a synchronized variable to indicate that the variable is reactive and assignments to that variable will be sent over the network.
261
261
 
262
262
  # Documentation Reference
263
263
 
@@ -0,0 +1,40 @@
1
+ type Stand_In = 'object' | 'array';
2
+ type Event = 'changes' | 'snapshot';
3
+ type Event_Changes_Callback = (data: any) => void;
4
+
5
+ export type Synchronized_Variable<Variable_Type> = Variable_Type & {
6
+ $on: (event: Event, callback: Event_Changes_Callback) => void;
7
+ $info: () => {
8
+ client_history: boolean
9
+ counter: number
10
+ handshake: true
11
+ history_length: number
12
+ name: string
13
+ one_way: boolean
14
+ resyncs : number
15
+ rewind : boolean
16
+ rewound : boolean
17
+ snapshots : string[]
18
+ standIn : boolean
19
+ };
20
+ $tart: () => void;
21
+ $listeners: () => any[];
22
+ $napshot: (name: string) => void;
23
+ $rewind: (name: string, counter?: number) => void;
24
+ list: () => string[];
25
+ variables: () => { [key]: string, value: Synchronized_Variable<Variable_Type> };
26
+ }
27
+
28
+ declare function jsynchronous<Variable_Type>(standInType: Stand_In, name?: string): Synchronized_Variable<Variable_Type>;
29
+
30
+ declare namespace jsynchronous {
31
+ export let send: (websocket: any, data: any) => void;
32
+ export const onmessage: (data: any) => void;
33
+ }
34
+
35
+ export default jsynchronous;
36
+
37
+ // export interface jsynchronous {
38
+ // <Synchronized_Variable>(standInType: Stand_In, name: string) => Synchronized_Type;
39
+ // send: (data: any) => void;
40
+ // }
@@ -1,6 +1,8 @@
1
1
  var jsynchronous;
2
2
 
3
3
  function jsynchronousSetup() {
4
+ var ENCODE = false;
5
+
4
6
  var TYPE_ENCODINGS = [
5
7
  'array',
6
8
  'object',
@@ -11,7 +13,8 @@ function jsynchronousSetup() {
11
13
  'null',
12
14
  'empty',
13
15
  'bigint',
14
- 'function'
16
+ 'function',
17
+ 'date'
15
18
  ]
16
19
 
17
20
  var OP_ENCODINGS = [
@@ -40,7 +43,7 @@ function jsynchronousSetup() {
40
43
 
41
44
  function onmessage(data) {
42
45
  var json = JSON.parse(data);
43
- var op = OP_ENCODINGS[json[0]];
46
+ var op = decodeOp(json[0]);
44
47
  var name = json[1];
45
48
 
46
49
  if (op === 'initial') {
@@ -62,7 +65,7 @@ function jsynchronousSetup() {
62
65
  if (jsynchronous.send) {
63
66
  var uniqueId = 'initial' + name;
64
67
  communicateWithBackoff(uniqueId, ['initial', name], function () {
65
- console.log('Unknown jsynchronous variable, requesting initial' + name);
68
+ console.warn('Unknown jsynchronous variable, requesting initial' + name);
66
69
  return jsyncs[name] === undefined;
67
70
  }, 2000);
68
71
  } else {
@@ -140,7 +143,7 @@ function jsynchronousSetup() {
140
143
  }
141
144
 
142
145
  function newJsynchronous(name, counter, settings, data) {
143
- var rootType = TYPE_ENCODINGS[data[0][1]];
146
+ var rootType = decodeType(data[0][1]);
144
147
  var jsync = jsyncObject(name, counter, settings);
145
148
  var reserved;
146
149
  if (settings) reserved = settings.reserved;
@@ -152,7 +155,7 @@ function jsynchronousSetup() {
152
155
  for (var i=0; i<data.length; i++) {
153
156
  var d = data[i];
154
157
  var hash = d[0];
155
- var type = TYPE_ENCODINGS[d[1]];
158
+ var type = decodeType(d[1]);
156
159
  var each = d[2];
157
160
  var description = createSyncedVariable(hash, type, each, jsync, (i === 0));
158
161
 
@@ -249,9 +252,6 @@ function jsynchronousSetup() {
249
252
  hash: hash,
250
253
  type: type,
251
254
  variable: undefined,
252
- descendants: {}, // key->value corresponds to descendant.hash->[properties]. Follow properties to find descendant
253
- parents: {}, // key->value corresponds to parentHash->{details: parent, props: []}
254
- children: {} // key->value corresponds to childHash->{details: child, props: []}
255
255
  }
256
256
 
257
257
  if (existing && detailedType(existing.variable) === type) {
@@ -266,7 +266,7 @@ function jsynchronousSetup() {
266
266
  jsync.objects[hash] = details;
267
267
 
268
268
  enumerate(each, type, function (prop, encoded) {
269
- var t = TYPE_ENCODINGS[encoded[0]]
269
+ var t = decodeType(encoded[0])
270
270
  var v = encoded[1];
271
271
 
272
272
  if (isPrimitive(t)) {
@@ -320,7 +320,7 @@ function jsynchronousSetup() {
320
320
 
321
321
  if (change === null) { continue }
322
322
 
323
- var op = OP_ENCODINGS[change[0]];
323
+ var op = decodeOp(change[0]);
324
324
  var hash = change[1];
325
325
  var details;
326
326
  var pt;
@@ -347,7 +347,7 @@ function jsynchronousSetup() {
347
347
  } else if (op === 'new') {
348
348
  var type = change[2];
349
349
  var each = change[3];
350
- createSyncedVariable(hash, TYPE_ENCODINGS[type], each, jsync);
350
+ createSyncedVariable(hash, decodeType(type), each, jsync);
351
351
  } else if (op === 'end') {
352
352
  endObject(details, jsync);
353
353
  } else if (op === 'snapshot') {
@@ -389,7 +389,7 @@ function jsynchronousSetup() {
389
389
  }
390
390
  function set(details, prop, newDetails, oldDetails, jsync) {
391
391
  var object = details.variable;
392
- var type = TYPE_ENCODINGS[newDetails[0]];
392
+ var type = decodeType(newDetails[0]);
393
393
  var value;
394
394
 
395
395
  if (isPrimitive(type)) {
@@ -400,7 +400,7 @@ function jsynchronousSetup() {
400
400
  value = childDetails.variable;
401
401
  }
402
402
 
403
- var oldType = TYPE_ENCODINGS[oldDetails[0]];
403
+ var oldType = decodeType(oldDetails[0]);
404
404
  var oldValue;
405
405
 
406
406
  if (isPrimitive(oldType)) {
@@ -414,7 +414,7 @@ function jsynchronousSetup() {
414
414
  }
415
415
  function del(details, prop, oldDetails, jsync) {
416
416
  var object = details.variable;
417
- var oldType = TYPE_ENCODINGS[oldDetails[0]];
417
+ var oldType = decodeType(oldDetails[0]);
418
418
  var oldValue = oldDetails[1];
419
419
 
420
420
  if (isPrimitive(oldType)) {
@@ -557,6 +557,8 @@ function jsynchronousSetup() {
557
557
  return null;
558
558
  } else if (type === 'function') {
559
559
  return undefined; // Functions are, for now, read-only
560
+ } else if (type === 'date') {
561
+ return new Date(value);
560
562
  }
561
563
  }
562
564
  function detailedType(value) {
@@ -596,7 +598,8 @@ function jsynchronousSetup() {
596
598
  type === 'null' ||
597
599
  type === 'empty' ||
598
600
  type === 'bigint' ||
599
- type === 'function') { // Functions are, for now, read-only
601
+ type === 'function' || // Functions are, for now, read-only
602
+ type === 'date') {
600
603
  return true
601
604
  } else {
602
605
  return false
@@ -662,6 +665,21 @@ function jsynchronousSetup() {
662
665
  return mirror;
663
666
  }
664
667
  }
668
+ function decodeOp(number) {
669
+ if (ENCODE) {
670
+ return OP_ENCODINGS[number]
671
+ } else {
672
+ return number
673
+ }
674
+ }
675
+ function decodeType(number) {
676
+ if (ENCODE) {
677
+ return TYPE_ENCODINGS[number];
678
+ } else {
679
+ return number;
680
+ }
681
+ }
682
+
665
683
 
666
684
 
667
685
  function addSynchronizedVariableMethods(jsync, targetVariable, reservedWords) {
@@ -749,7 +767,7 @@ function jsynchronousSetup() {
749
767
  });
750
768
  }
751
769
  }
752
- function triggerChanges(jsync, callback) {
770
+ function triggerChanges(jsync) {
753
771
  for (var j=0; j<jsync.changesEvents.length; j++) {
754
772
  var e = jsync.changesEvents[j];
755
773
  var variable = jsync.root.variable;
@@ -762,7 +780,7 @@ function jsynchronousSetup() {
762
780
  // ----------------------------------------------------------------
763
781
  function communicate(op, a, b, c, d) {
764
782
  var payload = []
765
- payload.push(OP_ENCODINGS.indexOf(op));
783
+ payload.push(ENCODE ? OP_ENCODINGS.indexOf(op) : op);
766
784
  if (a !== undefined) payload.push(a);
767
785
  if (b !== undefined) payload.push(b);
768
786
  if (c !== undefined) payload.push(c);
@@ -0,0 +1,41 @@
1
+ type Event_Changes_Callback = (data: any) => void;
2
+
3
+ export type Synchronized_Variable<Variable_Type> = Variable_Type & {
4
+ $on: (event: Event, callback: Event_Changes_Callback) => void;
5
+ $ync: (websocket: any) => void;
6
+ $unsync: (websocket: any) => void;
7
+ }
8
+
9
+ export type JysnchronousOptions = {
10
+ rewind?: boolean;
11
+ one_way?: boolean;
12
+ send?: (websocket: any, data: any) => void;
13
+ buffer_time?: number;
14
+ client_history?: number;
15
+ history_limit?: number;
16
+ wait?: boolean;
17
+ $ync?: string;
18
+ $unsync?: string;
19
+ $on?: string;
20
+ $tart?: string;
21
+ $listeners?: string;
22
+ $info?: string;
23
+ $napshot?: string;
24
+ $rewind?: string;
25
+ $copy?: string;
26
+ }
27
+
28
+ export type Jsynchronous = {
29
+ <Variable_Type>(initialVariable: Variable_Type, name?: string, options?: JsynchronousOptions): Synchronized_Variable<Variable_Type>;
30
+ send: (websocket: any, data: any) => void;
31
+ onmessage: (websocket: any, data: any) => void;
32
+ list: () => string[];
33
+ variables: () => { [key: string]: Synchronized_Variable<Variable_Type> };
34
+ pausegc: () => void;
35
+ resumegc: () => void;
36
+ rungc: () => void;
37
+ }
38
+
39
+ declare const jsynchronous: Jsynchronous;
40
+
41
+ export default jsynchronous;
package/jsynchronous.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const ENCODE = false;
4
+
3
5
  const TYPE_ENCODINGS = {
4
6
  'array': 0,
5
7
  'object': 1,
@@ -10,7 +12,8 @@ const TYPE_ENCODINGS = {
10
12
  'null': 6,
11
13
  'empty': 7,
12
14
  'bigint': 8,
13
- 'function': 9
15
+ 'function': 9,
16
+ 'date': 10
14
17
  }
15
18
 
16
19
  const OP_ENCODINGS = {
@@ -60,7 +63,7 @@ class Change {
60
63
  }
61
64
  encode() {
62
65
  const change = [
63
- OP_ENCODINGS[this.operation],
66
+ encodeOp(this.operation),
64
67
  this.hash,
65
68
  this.prop,
66
69
  encode(this.value, this.type),
@@ -82,7 +85,7 @@ class Creation {
82
85
  }
83
86
  encode() {
84
87
  const creation = [
85
- OP_ENCODINGS[this.operation],
88
+ encodeOp(this.operation),
86
89
  this.description[0],
87
90
  this.description[1],
88
91
  this.description[2]
@@ -106,7 +109,7 @@ class Deletion {
106
109
  }
107
110
  encode() {
108
111
  const deletion = [
109
- OP_ENCODINGS[this.operation],
112
+ encodeOp(this.operation),
110
113
  this.hash
111
114
  ]
112
115
  return deletion;
@@ -132,7 +135,7 @@ class Snapshot {
132
135
  }
133
136
  encode() {
134
137
  const snapshot = [
135
- OP_ENCODINGS[this.operation],
138
+ encodeOp(this.operation),
136
139
  this.id,
137
140
  this.name
138
141
  ]
@@ -272,7 +275,7 @@ class SyncedObject {
272
275
  describe() {
273
276
  const state = [
274
277
  this.hash,
275
- TYPE_ENCODINGS[this.type],
278
+ encodeOp(this.type),
276
279
  newCollection(this.type, this.proxy)
277
280
  ]
278
281
 
@@ -435,7 +438,7 @@ class JSynchronous {
435
438
  }
436
439
 
437
440
  for (let [websocket, listener] of this.listeners) {
438
- this.send(websocket, JSON.stringify([OP_ENCODINGS['changes'], this.name, min, max, changes]));
441
+ this.send(websocket, JSON.stringify([encodeOp('changes'), this.name, min, max, changes]));
439
442
  }
440
443
 
441
444
  // Now that it's sent, the rest is history
@@ -470,7 +473,7 @@ class JSynchronous {
470
473
  return c.encode();
471
474
  });
472
475
 
473
- this.send(websocket, JSON.stringify([OP_ENCODINGS['changes'], this.name, min, max, changes]));
476
+ this.send(websocket, JSON.stringify([encodeOp('changes'), this.name, min, max, changes]));
474
477
  }
475
478
  }
476
479
  }
@@ -557,7 +560,7 @@ class JSynchronous {
557
560
  } catch (e) {
558
561
  listener.penalty += 5; // Penalize clients heavily if they trigger errors
559
562
  console.error("Jsynchronous client->server onmessage error", e);
560
- jsynchronous.send(websocket, JSON.stringify([OP_ENCODINGS['error'], e.toString()]));
563
+ jsynchronous.send(websocket, JSON.stringify([encodeOp('error'), e.toString()]));
561
564
  }
562
565
  }
563
566
  handshake(websocket, listener, rootHash) {
@@ -567,7 +570,7 @@ class JSynchronous {
567
570
 
568
571
  let secret = listener.secret || randomHash(); // no need to worry about collisions. Secrets aren't shared.
569
572
  listener.secret = secret;
570
- this.send(websocket, JSON.stringify([OP_ENCODINGS['handshake'], this.name, secret]));
573
+ this.send(websocket, JSON.stringify([encodeOp('handshake'), this.name, secret]));
571
574
  }
572
575
  resync(websocket, listener, json) {
573
576
  let secret = json[2];
@@ -578,17 +581,17 @@ class JSynchronous {
578
581
  let historyMax = historyMin + (max-min);
579
582
  if (historyMin === -1 || historyMax >= this.history.length) {
580
583
  // TODO: Hard reset on client
581
- let payload = [OP_ENCODINGS['error'], 'Unable to resync'];
584
+ let payload = [encodeOp('error'), 'Unable to resync'];
582
585
  jsynchronous.send(websocket, JSON.stringify(payload));
583
586
  } else {
584
587
  let slice = this.history.slice(historyMin, historyMax);
585
588
  let encoded = slice.map((h) => h.encode());
586
- let payload = [OP_ENCODINGS['changes'], this.name, min, max-1, encoded];
589
+ let payload = [encodeOp('changes'), this.name, min, max-1, encoded];
587
590
  this.send(websocket, JSON.stringify(payload));
588
591
  }
589
592
  } else {
590
593
  // TODO: Tear this out, we don't need to be polite to clients that fail the secret check
591
- let payload = [OP_ENCODINGS['error'], 'Secret check failed'];
594
+ let payload = [encodeOp('error'), 'Secret check failed'];
592
595
  jsynchronous.send(websocket, JSON.stringify(payload));
593
596
  }
594
597
  }
@@ -712,7 +715,7 @@ class JSynchronous {
712
715
  }
713
716
 
714
717
  const fullState = [
715
- OP_ENCODINGS['initial'],
718
+ encodeOp('initial'),
716
719
  this.name,
717
720
  this.counter,
718
721
  settings
@@ -880,7 +883,8 @@ function isPrimitive(detailed) {
880
883
  detailed === 'bigint' ||
881
884
  detailed === 'null' || // Although null is in ECMA an object, we'll consider it a primitive for simplicity
882
885
  detailed === 'undefined' ||
883
- detailed === 'function') { // For now, functions are just values as far as jsynchronous is concerned
886
+ detailed === 'function' || // For now, functions are just values as far as jsynchronous is concerned
887
+ detailed === 'date') {
884
888
  return true;
885
889
  } else {
886
890
  return false;
@@ -1020,15 +1024,15 @@ function labelEmpty(source, target) {
1020
1024
  if (value === undefined) {
1021
1025
  allKeys = allKeys || Object.keys(source).map(k => Number(k));
1022
1026
  if (binarySearch(allKeys, ((a) => a - i)) === -1) {
1023
- target[i] = [TYPE_ENCODINGS['empty']];
1027
+ target[i] = [encodeOp('empty')];
1024
1028
  }
1025
1029
  }
1026
1030
  }
1027
1031
  }
1028
1032
 
1029
1033
  function encode(value, type) {
1030
- // Expects value to be either be a primitive or a syncedObject Proxy
1031
- const encoded = [TYPE_ENCODINGS[type]];
1034
+ // Expects value to be either be a primitive, a date or a syncedObject Proxy
1035
+ const encoded = [encodeOp(type)];
1032
1036
 
1033
1037
  if (isPrimitive(type)) {
1034
1038
  const p = encodePrimitive(value, type)
@@ -1051,6 +1055,7 @@ function encodePrimitive(value, type) {
1051
1055
  if (type === 'boolean') return !!value ? 1 : 0;
1052
1056
  if (type === 'bigint') return String(value);
1053
1057
  if (type === 'function') return undefined;
1058
+ if (type === 'date') return value.getTime();
1054
1059
  throw `Jsynchronous sanity error - Primitive is unserializable ${type}, ${value}`;
1055
1060
  }
1056
1061
  function encodeEnumerable(value) {
@@ -1061,8 +1066,26 @@ function encodeEnumerable(value) {
1061
1066
  return syncedObject.hash;
1062
1067
  }
1063
1068
  function getOp(number) {
1064
- for (const [key, value] of Object.entries(OP_ENCODINGS)) {
1065
- if (value === number) return key;
1069
+ if (ENCODE) {
1070
+ for (const [key, value] of Object.entries(OP_ENCODINGS)) {
1071
+ if (value === number) return key;
1072
+ }
1073
+ return false;
1074
+ } else {
1075
+ return number;
1076
+ }
1077
+ }
1078
+ function encodeType(type) {
1079
+ if (encode) {
1080
+ return TYPE_ENCODINGS[type];
1081
+ } else {
1082
+ return type;
1083
+ }
1084
+ }
1085
+ function encodeOp(op) {
1086
+ if (ENCODE) {
1087
+ return OP_ENCODINGS[op];
1088
+ } else {
1089
+ return op;
1066
1090
  }
1067
- return false;
1068
1091
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsynchronous",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "author": "Sirius Strebe <siriusastrebe@gmail.com> (http://asksiri.us)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -67,7 +67,7 @@ app.get('/jsynchronous-client.js', (req, res) => {
67
67
  // ----------------------------------------------------------------
68
68
  // Selenium Test
69
69
  // ----------------------------------------------------------------
70
- const browsers = ['firefox', 'chrome'];
70
+ const browsers = ['firefox'];
71
71
  let drivers = [];
72
72
  const runTests = (async () => {
73
73
  drivers = await Promise.all(browsers.map((browser) => new Builder().forBrowser(browser).build()));