multyx-client 0.1.5 → 0.1.7

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.
@@ -19,7 +19,7 @@ class Controller {
19
19
  };
20
20
  document.addEventListener('keydown', e => {
21
21
  if (this.preventDefault)
22
- e.preventDefault;
22
+ e.preventDefault();
23
23
  const key = e.key.toLowerCase();
24
24
  // When holding down key
25
25
  if (this.keys[key] && this.listening.has('keyhold')) {
@@ -40,7 +40,7 @@ class Controller {
40
40
  });
41
41
  document.addEventListener('keyup', e => {
42
42
  if (this.preventDefault)
43
- e.preventDefault;
43
+ e.preventDefault();
44
44
  const key = e.key.toLowerCase();
45
45
  delete this.keys[key];
46
46
  delete this.keys[e.code];
@@ -52,7 +52,7 @@ class Controller {
52
52
  // Mouse input events
53
53
  document.addEventListener('mousedown', e => {
54
54
  if (this.preventDefault)
55
- e.preventDefault;
55
+ e.preventDefault();
56
56
  if (this.mouseGetter) {
57
57
  const mouse = this.mouseGetter();
58
58
  this.mouse.x = mouse.x;
@@ -70,7 +70,7 @@ class Controller {
70
70
  });
71
71
  document.addEventListener('mouseup', e => {
72
72
  if (this.preventDefault)
73
- e.preventDefault;
73
+ e.preventDefault();
74
74
  if (this.mouseGetter) {
75
75
  const mouse = this.mouseGetter();
76
76
  this.mouse.x = mouse.x;
@@ -88,7 +88,7 @@ class Controller {
88
88
  });
89
89
  document.addEventListener('mousemove', e => {
90
90
  if (this.preventDefault)
91
- e.preventDefault;
91
+ e.preventDefault();
92
92
  if (this.mouseGetter) {
93
93
  const mouse = this.mouseGetter();
94
94
  this.mouse.x = mouse.x;
@@ -115,7 +115,6 @@ class Controller {
115
115
  * @param anchor Anchor the origin at a specific spot on the canvas
116
116
  */
117
117
  mapCanvasPosition(canvas, position) {
118
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
119
118
  const t = 'top' in position;
120
119
  const b = 'bottom' in position;
121
120
  const l = 'left' in position;
@@ -180,12 +179,12 @@ class Controller {
180
179
  return error(true, 'bottom');
181
180
  position.bottom = 0;
182
181
  if (l) {
183
- (_a = position.top) !== null && _a !== void 0 ? _a : (position.top = Math.abs(hToW * position.left * 2));
184
- (_b = position.right) !== null && _b !== void 0 ? _b : (position.right = -position.left);
182
+ position.top = Math.abs(hToW * position.left * 2);
183
+ position.right = -position.left;
185
184
  }
186
185
  else if (r) {
187
- (_c = position.top) !== null && _c !== void 0 ? _c : (position.top = Math.abs(hToW * position.right * 2));
188
- (_d = position.left) !== null && _d !== void 0 ? _d : (position.left = -position.right);
186
+ position.top = Math.abs(hToW * position.right * 2);
187
+ position.left = -position.right;
189
188
  }
190
189
  else {
191
190
  position.left = -Math.abs(wToH * position.top / 2);
@@ -199,12 +198,12 @@ class Controller {
199
198
  return error(true, 'top');
200
199
  position.top = 0;
201
200
  if (l) {
202
- (_e = position.bottom) !== null && _e !== void 0 ? _e : (position.bottom = Math.abs(hToW * position.left * 2));
203
- (_f = position.right) !== null && _f !== void 0 ? _f : (position.right = -position.left);
201
+ position.bottom = Math.abs(hToW * position.left * 2);
202
+ position.right = -position.left;
204
203
  }
205
204
  else if (r) {
206
- (_g = position.bottom) !== null && _g !== void 0 ? _g : (position.bottom = Math.abs(hToW * position.right * 2));
207
- (_h = position.left) !== null && _h !== void 0 ? _h : (position.left = -position.right);
205
+ position.bottom = Math.abs(hToW * position.right * 2);
206
+ position.left = -position.right;
208
207
  }
209
208
  else {
210
209
  position.left = -Math.abs(wToH * position.bottom / 2);
@@ -218,12 +217,12 @@ class Controller {
218
217
  return error(true, 'left');
219
218
  position.left = 0;
220
219
  if (t) {
221
- (_j = position.right) !== null && _j !== void 0 ? _j : (position.right = -Math.abs(wToH * position.top * 2));
222
- (_k = position.bottom) !== null && _k !== void 0 ? _k : (position.bottom = -position.top);
220
+ position.right = -Math.abs(wToH * position.top * 2);
221
+ position.bottom = -position.top;
223
222
  }
224
223
  else if (b) {
225
- (_l = position.right) !== null && _l !== void 0 ? _l : (position.right = Math.abs(wToH * position.bottom * 2));
226
- (_m = position.top) !== null && _m !== void 0 ? _m : (position.top = -position.bottom);
224
+ position.right = Math.abs(wToH * position.bottom * 2);
225
+ position.top = -position.bottom;
227
226
  }
228
227
  else {
229
228
  position.top = -Math.abs(hToW * position.right / 2);
@@ -237,12 +236,12 @@ class Controller {
237
236
  return error(true, 'right');
238
237
  position.right = 0;
239
238
  if (t) {
240
- (_o = position.left) !== null && _o !== void 0 ? _o : (position.left = -Math.abs(wToH * position.top * 2));
241
- (_p = position.bottom) !== null && _p !== void 0 ? _p : (position.bottom = -position.top);
239
+ position.left = -Math.abs(wToH * position.top * 2);
240
+ position.bottom = -position.top;
242
241
  }
243
242
  else if (b) {
244
- (_q = position.left) !== null && _q !== void 0 ? _q : (position.left = Math.abs(wToH * position.bottom * 2));
245
- (_r = position.top) !== null && _r !== void 0 ? _r : (position.top = -position.bottom);
243
+ position.left = Math.abs(wToH * position.bottom * 2);
244
+ position.top = -position.bottom;
246
245
  }
247
246
  else {
248
247
  position.top = -Math.abs(hToW * position.right / 2);
@@ -294,14 +293,14 @@ class Controller {
294
293
  position.left = Math.abs(wToH * position.top);
295
294
  }
296
295
  const ctx = canvas.getContext("2d");
297
- ctx.setTransform(1, 0, 0, 1, 0, 0);
296
+ ctx === null || ctx === void 0 ? void 0 : ctx.setTransform(1, 0, 0, 1, 0, 0);
298
297
  canvas.width = Math.floor(Math.abs(position.right - position.left));
299
298
  canvas.height = Math.floor(Math.abs(position.bottom - position.top));
300
299
  if (position.right < position.left)
301
- ctx.scale(-1, 1);
300
+ ctx === null || ctx === void 0 ? void 0 : ctx.scale(-1, 1);
302
301
  if (position.top > position.bottom)
303
- ctx.scale(1, -1);
304
- ctx.translate(-position.left, -position.top);
302
+ ctx === null || ctx === void 0 ? void 0 : ctx.scale(1, -1);
303
+ ctx === null || ctx === void 0 ? void 0 : ctx.translate(-position.left, -position.top);
305
304
  }
306
305
  /**
307
306
  * @param centerX Anchor x-value corresponding to mouse position x-value of 0
@@ -329,15 +328,15 @@ class Controller {
329
328
  */
330
329
  mapMouseToCanvas(canvas) {
331
330
  const ctx = canvas.getContext("2d");
332
- const transform = ctx.getTransform();
331
+ const transform = ctx === null || ctx === void 0 ? void 0 : ctx.getTransform();
333
332
  const bounding = canvas.getBoundingClientRect();
334
333
  // Ratio between canvas scale to unit pixels
335
334
  const canvasRatioX = bounding.width / canvas.width;
336
335
  const canvasRatioY = bounding.height / canvas.height;
337
- this.mouse.centerX = bounding.left + transform.e * canvasRatioX;
338
- this.mouse.centerY = bounding.top + transform.f * canvasRatioY;
339
- this.mouse.scaleX = canvasRatioX * transform.a;
340
- this.mouse.scaleY = canvasRatioY * transform.d;
336
+ this.mouse.centerX = bounding.left + (transform === null || transform === void 0 ? void 0 : transform.e) * canvasRatioX;
337
+ this.mouse.centerY = bounding.top + (transform === null || transform === void 0 ? void 0 : transform.f) * canvasRatioY;
338
+ this.mouse.scaleX = canvasRatioX * (transform === null || transform === void 0 ? void 0 : transform.a);
339
+ this.mouse.scaleY = canvasRatioY * (transform === null || transform === void 0 ? void 0 : transform.d);
341
340
  }
342
341
  /**
343
342
  * Utilize mouse coordinates of another object
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Add, Done } from './utils';
1
+ import { Add, Done, Interpolate } from './utils';
2
2
  import { RawObject } from "./types";
3
3
  import { Controller } from "./controller";
4
4
  import { MultyxClientObject } from "./items";
@@ -27,6 +27,7 @@ export default class Multyx {
27
27
  static Native: symbol;
28
28
  static Custom: symbol;
29
29
  static Any: symbol;
30
+ static Interpolate: typeof Interpolate;
30
31
  constructor(options?: Options, callback?: () => void);
31
32
  /**
32
33
  * Listen for a message from the server
package/dist/index.js CHANGED
@@ -12,8 +12,10 @@ class Multyx {
12
12
  // Queue of functions to be called after each frame
13
13
  this[_a] = [];
14
14
  this.options = Object.assign(Object.assign({}, options_1.DefaultOptions), options);
15
- const url = `ws${this.options.secure ? 's' : ''}://${this.options.uri.split('/')[0]}:${this.options.port}/${(_b = this.options.uri.split('/')[1]) !== null && _b !== void 0 ? _b : ''}`;
16
- this.ws = new WebSocket(url);
15
+ if (!this.options.uri)
16
+ throw new Error('URI is required');
17
+ const uri = `ws${this.options.secure ? 's' : ''}://${this.options.uri.split('/')[0]}:${this.options.port}/${(_b = this.options.uri.split('/')[1]) !== null && _b !== void 0 ? _b : ''}`;
18
+ this.ws = new WebSocket(uri);
17
19
  this.ping = 0;
18
20
  this.space = 'default';
19
21
  this.events = new Map();
@@ -108,7 +110,7 @@ class Multyx {
108
110
  * @param msg Message to parse
109
111
  */
110
112
  parseNativeEvent(msg) {
111
- var _b, _c, _d, _e;
113
+ var _b, _c, _d, _e, _f;
112
114
  msg.data = msg.data.map(message_1.UncompressUpdate);
113
115
  if (this.options.logUpdateFrame)
114
116
  console.log(msg.data);
@@ -172,9 +174,10 @@ class Multyx {
172
174
  }
173
175
  // Response to client
174
176
  case 'resp': {
175
- const promiseResolve = this.events.get(Symbol.for("_" + update.name))[0];
177
+ const promiseResolve = (_f = this.events.get(Symbol.for("_" + update.name))) === null || _f === void 0 ? void 0 : _f[0];
176
178
  this.events.delete(Symbol.for("_" + update.name));
177
- this[utils_1.Done].push(() => promiseResolve(update.response));
179
+ if (promiseResolve)
180
+ this[utils_1.Done].push(() => promiseResolve(update.response));
178
181
  break;
179
182
  }
180
183
  default: {
@@ -256,4 +259,5 @@ Multyx.Edit = Symbol('edit');
256
259
  Multyx.Native = Symbol('native');
257
260
  Multyx.Custom = Symbol('custom');
258
261
  Multyx.Any = Symbol('any');
262
+ Multyx.Interpolate = utils_1.Interpolate;
259
263
  exports.default = Multyx;
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.MultyxClientValue = exports.MultyxClientObject = exports.MultyxClientList = void 0;
4
7
  exports.IsMultyxClientItem = IsMultyxClientItem;
5
- const list_1 = require("./list");
8
+ const list_1 = __importDefault(require("./list"));
6
9
  exports.MultyxClientList = list_1.default;
7
- const object_1 = require("./object");
10
+ const object_1 = __importDefault(require("./object"));
8
11
  exports.MultyxClientObject = object_1.default;
9
- const value_1 = require("./value");
12
+ const value_1 = __importDefault(require("./value"));
10
13
  exports.MultyxClientValue = value_1.default;
11
14
  function IsMultyxClientItem(value) {
12
15
  return value instanceof list_1.default || value instanceof object_1.default || value instanceof value_1.default;
@@ -1,7 +1,8 @@
1
1
  import Multyx from '../';
2
2
  import { type MultyxClientItem, type MultyxClientObject, MultyxClientValue } from '.';
3
- import { EditWrapper, Unpack } from '../utils';
3
+ import { Edit, EditWrapper, Unpack } from '../utils';
4
4
  export default class MultyxClientList {
5
+ readonly type = "list";
5
6
  protected list: MultyxClientItem[];
6
7
  private multyx;
7
8
  propertyPath: string[];
@@ -21,9 +22,10 @@ export default class MultyxClientList {
21
22
  * @param shift Shift amount, positive for right, negative for left
22
23
  */
23
24
  private handleShiftOperation;
24
- constructor(multyx: Multyx, list: any[] | EditWrapper<any[]>, propertyPath: string[], editable: boolean);
25
+ [index: number]: MultyxClientItem | undefined;
26
+ constructor(multyx: Multyx, list: any[] | EditWrapper<any[]>, propertyPath: string[] | undefined, editable: boolean);
25
27
  has(index: number): boolean;
26
- get(index: number | string[]): MultyxClientItem;
28
+ get(index: number | string[]): MultyxClientItem | undefined;
27
29
  private recursiveSet;
28
30
  set(index: number | string[], value: any): boolean;
29
31
  delete(index: number, native?: boolean): boolean;
@@ -34,9 +36,9 @@ export default class MultyxClientList {
34
36
  */
35
37
  await(index: number): Promise<unknown>;
36
38
  push(...items: any): number;
37
- pop(): MultyxClientItem | null;
39
+ pop(): MultyxClientItem | undefined;
38
40
  unshift(...items: any[]): number;
39
- shift(): MultyxClientList | MultyxClientObject | MultyxClientValue;
41
+ shift(): MultyxClientList | MultyxClientObject | MultyxClientValue | undefined;
40
42
  slice(start?: number, end?: number): (MultyxClientList | MultyxClientObject | MultyxClientValue)[];
41
43
  splice(start: number, deleteCount?: number, ...items: any[]): (MultyxClientList | MultyxClientObject | MultyxClientValue)[];
42
44
  setSplice(start: number, deleteCount?: number, ...items: any[]): void;
@@ -51,13 +53,18 @@ export default class MultyxClientList {
51
53
  forEach(callbackfn: (value: any, index: number, array: MultyxClientList) => void): void;
52
54
  every(predicate: (value: any, index: number, array: MultyxClientList) => boolean): boolean;
53
55
  some(predicate: (value: any, index: number, array: MultyxClientList) => boolean): boolean;
54
- find(predicate: (value: any, index: number, array: MultyxClientList) => boolean): MultyxClientList | MultyxClientObject | MultyxClientValue;
56
+ find(predicate: (value: any, index: number, array: MultyxClientList) => boolean): MultyxClientList | MultyxClientObject | MultyxClientValue | undefined;
55
57
  findIndex(predicate: (value: any, index: number, array: MultyxClientList) => boolean): number;
56
58
  entries(): [any, number][];
57
59
  keys(): number[];
60
+ [Edit](): void;
58
61
  [Unpack](constraints: any[]): void;
59
62
  [Symbol.iterator](): Iterator<MultyxClientItem>;
60
63
  toString: () => string;
61
64
  valueOf: () => any[];
62
65
  [Symbol.toPrimitive]: () => any[];
66
+ hydrateFromServer(values: any[]): void;
67
+ private tryApplyServerValue;
68
+ private notifyIndexWaiters;
69
+ private enqueueEditCallbacks;
63
70
  }
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  var _a;
3
6
  Object.defineProperty(exports, "__esModule", { value: true });
4
7
  const _1 = require(".");
5
8
  const utils_1 = require("../utils");
6
- const router_1 = require("./router");
9
+ const router_1 = __importDefault(require("./router"));
7
10
  const message_1 = require("../message");
11
+ const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
8
12
  class MultyxClientList {
9
13
  addEditCallback(callback) {
10
14
  this.editCallbacks.push(callback);
@@ -70,6 +74,7 @@ class MultyxClientList {
70
74
  }
71
75
  }
72
76
  constructor(multyx, list, propertyPath = [], editable) {
77
+ this.type = 'list';
73
78
  this.editCallbacks = [];
74
79
  this.toString = () => this.value.toString();
75
80
  this.valueOf = () => this.value;
@@ -148,6 +153,9 @@ class MultyxClientList {
148
153
  this.set(parseInt(path[0]), new utils_1.EditWrapper({}));
149
154
  next = this.get(parseInt(path[0]));
150
155
  }
156
+ if (!next || next instanceof _1.MultyxClientValue) {
157
+ return false;
158
+ }
151
159
  return next.set(path.slice(1), value);
152
160
  }
153
161
  set(index, value) {
@@ -156,31 +164,28 @@ class MultyxClientList {
156
164
  const oldValue = this.get(index);
157
165
  const serverSet = value instanceof utils_1.EditWrapper;
158
166
  const allowed = serverSet || this.editable;
159
- if (serverSet || (0, _1.IsMultyxClientItem)(value))
160
- value = value.value;
161
- if (value === undefined)
167
+ const incoming = (serverSet || (0, _1.IsMultyxClientItem)(value)) ? value.value : value;
168
+ if (incoming === undefined)
162
169
  return this.delete(index, serverSet);
163
- // If value is a MultyxClientValue, set the value
164
- if (this.list[index] instanceof _1.MultyxClientValue && typeof value != 'object') {
165
- return this.list[index].set(serverSet ? new utils_1.EditWrapper(value) : value);
170
+ if (serverSet && this.tryApplyServerValue(index, incoming, oldValue)) {
171
+ return true;
172
+ }
173
+ // If value is a MultyxClientValue, set the value directly
174
+ if (this.list[index] instanceof _1.MultyxClientValue && (typeof incoming !== 'object' || incoming === null)) {
175
+ const result = this.list[index].set(serverSet ? new utils_1.EditWrapper(incoming) : incoming);
176
+ this.enqueueEditCallbacks(index, oldValue);
177
+ return result;
166
178
  }
167
179
  // Attempting to edit property not editable to client
168
180
  if (!allowed) {
169
181
  if (this.multyx.options.verbose) {
170
- console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join('.') + '.' + index}' to ${value}`);
182
+ console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join('.') + '.' + index}' to ${incoming}`);
171
183
  }
172
184
  return false;
173
185
  }
174
- this.list[index] = new ((0, router_1.default)(value))(this.multyx, serverSet ? new utils_1.EditWrapper(value) : value, [...this.propertyPath, index.toString()], this.editable);
175
- const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + index);
176
- if (this.multyx.events.has(propSymbol)) {
177
- this.multyx[utils_1.Done].push(...this.multyx.events.get(propSymbol).map(e => () => e(this.list[index])));
178
- }
179
- // We have to push into queue, since object may not be fully created
180
- // and there may still be more updates to parse
181
- for (const listener of this.editCallbacks) {
182
- this.multyx[utils_1.Add](() => listener(index, this.get(index), oldValue));
183
- }
186
+ this.list[index] = new ((0, router_1.default)(incoming))(this.multyx, serverSet ? new utils_1.EditWrapper(incoming) : incoming, [...this.propertyPath, index.toString()], this.editable);
187
+ this.notifyIndexWaiters(index);
188
+ this.enqueueEditCallbacks(index, oldValue);
184
189
  return true;
185
190
  }
186
191
  delete(index, native = false) {
@@ -225,7 +230,7 @@ class MultyxClientList {
225
230
  }
226
231
  pop() {
227
232
  if (this.length === 0)
228
- return null;
233
+ return undefined;
229
234
  const res = this.get(this.length);
230
235
  this.delete(this.length);
231
236
  return res;
@@ -255,7 +260,7 @@ class MultyxClientList {
255
260
  return this.list.slice(start, end);
256
261
  }
257
262
  splice(start, deleteCount, ...items) {
258
- return this.list.splice(start, deleteCount, ...items);
263
+ return this.list.splice(start, deleteCount !== null && deleteCount !== void 0 ? deleteCount : 0, ...items);
259
264
  }
260
265
  setSplice(start, deleteCount, ...items) {
261
266
  if (deleteCount === undefined) {
@@ -385,6 +390,7 @@ class MultyxClientList {
385
390
  keys() {
386
391
  return Array(this.length).fill(0).map((_, i) => i);
387
392
  }
393
+ [utils_1.Edit]() { }
388
394
  [utils_1.Unpack](constraints) {
389
395
  var _b;
390
396
  for (let i = 0; i < this.length; i++) {
@@ -398,6 +404,51 @@ class MultyxClientList {
398
404
  values[i] = this.get(i);
399
405
  return values[Symbol.iterator]();
400
406
  }
407
+ hydrateFromServer(values) {
408
+ if (!Array.isArray(values))
409
+ return;
410
+ for (let i = 0; i < values.length; i++) {
411
+ this.set(i, new utils_1.EditWrapper(values[i]));
412
+ }
413
+ for (let i = values.length; i < this.length; i++) {
414
+ this.delete(i, true);
415
+ }
416
+ this.length = values.length;
417
+ }
418
+ tryApplyServerValue(index, incoming, oldValue) {
419
+ const current = this.list[index];
420
+ if (!current)
421
+ return false;
422
+ if (current instanceof _1.MultyxClientValue && (typeof incoming !== 'object' || incoming === null)) {
423
+ current.set(new utils_1.EditWrapper(incoming));
424
+ this.enqueueEditCallbacks(index, oldValue);
425
+ return true;
426
+ }
427
+ const canHydrate = typeof (current === null || current === void 0 ? void 0 : current.hydrateFromServer) === 'function';
428
+ if (Array.isArray(incoming) && canHydrate && current.type === 'list') {
429
+ current.hydrateFromServer(incoming);
430
+ this.enqueueEditCallbacks(index, oldValue);
431
+ return true;
432
+ }
433
+ if (isPlainObject(incoming) && canHydrate && current.type === 'object') {
434
+ current.hydrateFromServer(incoming);
435
+ this.enqueueEditCallbacks(index, oldValue);
436
+ return true;
437
+ }
438
+ return false;
439
+ }
440
+ notifyIndexWaiters(index) {
441
+ var _b, _c;
442
+ const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + index);
443
+ if (this.multyx.events.has(propSymbol)) {
444
+ this.multyx[utils_1.Done].push(...((_c = (_b = this.multyx.events.get(propSymbol)) === null || _b === void 0 ? void 0 : _b.map(e => () => e(this.list[index]))) !== null && _c !== void 0 ? _c : []));
445
+ }
446
+ }
447
+ enqueueEditCallbacks(index, oldValue) {
448
+ for (const listener of this.editCallbacks) {
449
+ this.multyx[utils_1.Add](() => listener(index, this.get(index), oldValue));
450
+ }
451
+ }
401
452
  }
402
453
  _a = Symbol.toPrimitive;
403
454
  exports.default = MultyxClientList;
@@ -1,19 +1,22 @@
1
1
  import { RawObject } from '../types';
2
2
  import { Edit, EditWrapper, Unpack } from "../utils";
3
3
  import type Multyx from '../index';
4
- import { type MultyxClientItem } from ".";
4
+ import { type MultyxClientList, type MultyxClientItem } from ".";
5
+ import MultyxClientValue from "./value";
5
6
  export default class MultyxClientObject {
7
+ readonly type = "object";
6
8
  protected object: RawObject<MultyxClientItem>;
7
9
  private multyx;
8
10
  propertyPath: string[];
9
11
  editable: boolean;
10
12
  private editCallbacks;
11
- get value(): {};
13
+ get value(): RawObject<MultyxClientList | MultyxClientObject | MultyxClientValue>;
12
14
  addEditCallback(callback: (key: any, value: any) => void): void;
13
15
  [Edit](updatePath: string[], value: any): void;
14
- constructor(multyx: Multyx, object: RawObject | EditWrapper<RawObject>, propertyPath: string[], editable: boolean);
16
+ [key: string]: any;
17
+ constructor(multyx: Multyx, object: RawObject | EditWrapper<RawObject>, propertyPath: string[] | undefined, editable: boolean);
15
18
  has(property: any): boolean;
16
- get(property: string | string[]): MultyxClientItem;
19
+ get(property: string | string[]): MultyxClientItem | undefined;
17
20
  private recursiveSet;
18
21
  set(property: string | string[], value: any): boolean;
19
22
  delete(property: any, native?: boolean): boolean;
@@ -31,4 +34,7 @@ export default class MultyxClientObject {
31
34
  * @param constraints Packed constraints object mirroring MultyxClientObject shape
32
35
  */
33
36
  [Unpack](constraints: RawObject): void;
37
+ hydrateFromServer(value: RawObject): void;
38
+ private applyServerValue;
39
+ private notifyPropertyWaiters;
34
40
  }
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const message_1 = require("../message");
4
7
  const utils_1 = require("../utils");
5
8
  const _1 = require(".");
6
- const router_1 = require("./router");
7
- const value_1 = require("./value");
9
+ const router_1 = __importDefault(require("./router"));
10
+ const value_1 = __importDefault(require("./value"));
11
+ const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
8
12
  class MultyxClientObject {
9
13
  get value() {
10
14
  const parsed = {};
@@ -16,6 +20,7 @@ class MultyxClientObject {
16
20
  this.editCallbacks.push(callback);
17
21
  }
18
22
  [utils_1.Edit](updatePath, value) {
23
+ var _a;
19
24
  if (updatePath.length == 1) {
20
25
  this.set(updatePath[0], new utils_1.EditWrapper(value));
21
26
  return;
@@ -26,9 +31,10 @@ class MultyxClientObject {
26
31
  if (!this.has(updatePath[0])) {
27
32
  this.set(updatePath[0], new utils_1.EditWrapper({}));
28
33
  }
29
- this.get(updatePath[0])[utils_1.Edit](updatePath.slice(1), value);
34
+ (_a = this.get(updatePath[0])) === null || _a === void 0 ? void 0 : _a[utils_1.Edit](updatePath.slice(1), value);
30
35
  }
31
36
  constructor(multyx, object, propertyPath = [], editable) {
37
+ this.type = 'object';
32
38
  this.editCallbacks = [];
33
39
  this.object = {};
34
40
  this.propertyPath = propertyPath;
@@ -109,27 +115,26 @@ class MultyxClientObject {
109
115
  return this.recursiveSet(property, value);
110
116
  const serverSet = value instanceof utils_1.EditWrapper;
111
117
  const allowed = serverSet || this.editable;
112
- if (serverSet || (0, _1.IsMultyxClientItem)(value))
113
- value = value.value;
114
- if (value === undefined)
118
+ const incoming = (serverSet || (0, _1.IsMultyxClientItem)(value)) ? value.value : value;
119
+ if (incoming === undefined)
115
120
  return this.delete(property, serverSet);
121
+ if (serverSet && this.applyServerValue(property, incoming)) {
122
+ return true;
123
+ }
116
124
  // Only create new MultyxClientItem when needed
117
- if (this.object[property] instanceof value_1.default && typeof value != 'object') {
118
- return this.object[property].set(serverSet ? new utils_1.EditWrapper(value) : value);
125
+ if (this.object[property] instanceof value_1.default && (typeof incoming !== 'object' || incoming === null)) {
126
+ return this.object[property].set(serverSet ? new utils_1.EditWrapper(incoming) : incoming);
119
127
  }
120
128
  // Attempting to edit property not editable to client
121
129
  if (!allowed) {
122
130
  if (this.multyx.options.verbose) {
123
- console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join('.') + '.' + property}' to ${value}`);
131
+ console.error(`Attempting to set property that is not editable. Setting '${this.propertyPath.join('.') + '.' + property}' to ${incoming}`);
124
132
  }
125
133
  return false;
126
134
  }
127
135
  // Creating a new value
128
- this.object[property] = new ((0, router_1.default)(value))(this.multyx, serverSet ? new utils_1.EditWrapper(value) : value, [...this.propertyPath, property], this.editable);
129
- const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + property);
130
- if (this.multyx.events.has(propSymbol)) {
131
- this.multyx[utils_1.Done].push(...this.multyx.events.get(propSymbol).map(e => () => e(this.object[property])));
132
- }
136
+ this.object[property] = new ((0, router_1.default)(incoming))(this.multyx, serverSet ? new utils_1.EditWrapper(incoming) : incoming, [...this.propertyPath, property], this.editable);
137
+ this.notifyPropertyWaiters(property);
133
138
  return true;
134
139
  }
135
140
  delete(property, native = false) {
@@ -184,5 +189,43 @@ class MultyxClientObject {
184
189
  (_a = this.object[prop]) === null || _a === void 0 ? void 0 : _a[utils_1.Unpack](constraints[prop]);
185
190
  }
186
191
  }
192
+ hydrateFromServer(value) {
193
+ if (!isPlainObject(value))
194
+ return;
195
+ const remaining = new Set(Object.keys(this.object));
196
+ for (const [key, entry] of Object.entries(value)) {
197
+ remaining.delete(key);
198
+ this.set(key, new utils_1.EditWrapper(entry));
199
+ }
200
+ for (const key of remaining) {
201
+ this.delete(key, true);
202
+ }
203
+ }
204
+ applyServerValue(property, incoming) {
205
+ const current = this.object[property];
206
+ if (!current)
207
+ return false;
208
+ if (current instanceof value_1.default && (typeof incoming !== 'object' || incoming === null)) {
209
+ current.set(new utils_1.EditWrapper(incoming));
210
+ return true;
211
+ }
212
+ const canHydrate = typeof (current === null || current === void 0 ? void 0 : current.hydrateFromServer) === 'function';
213
+ if (Array.isArray(incoming) && canHydrate && current.type === 'list') {
214
+ current.hydrateFromServer(incoming);
215
+ return true;
216
+ }
217
+ if (isPlainObject(incoming) && canHydrate && current.type === 'object') {
218
+ current.hydrateFromServer(incoming);
219
+ return true;
220
+ }
221
+ return false;
222
+ }
223
+ notifyPropertyWaiters(property) {
224
+ var _a, _b;
225
+ const propSymbol = Symbol.for("_" + this.propertyPath.join('.') + '.' + property);
226
+ if (this.multyx.events.has(propSymbol)) {
227
+ this.multyx[utils_1.Done].push(...((_b = (_a = this.multyx.events.get(propSymbol)) === null || _a === void 0 ? void 0 : _a.map(e => () => e(this.object[property]))) !== null && _b !== void 0 ? _b : []));
228
+ }
229
+ }
187
230
  }
188
231
  exports.default = MultyxClientObject;
@@ -16,7 +16,7 @@ export default class MultyxClientValue {
16
16
  addReadModifier(modifier: (value: Value) => Value): void;
17
17
  addEditCallback(callback: (value: Value, previousValue: Value) => void): void;
18
18
  [Edit](updatePath: string[], value: any): void;
19
- constructor(multyx: Multyx, value: Value | EditWrapper<Value>, propertyPath: string[], editable: boolean);
19
+ constructor(multyx: Multyx, value: Value | EditWrapper<Value>, propertyPath: string[] | undefined, editable: boolean);
20
20
  set(value: Value | EditWrapper<Value>): boolean;
21
21
  bindElement(element: HTMLElement): void;
22
22
  /**
@@ -22,6 +22,7 @@ class MultyxClientValue {
22
22
  this.set(new utils_1.EditWrapper(value));
23
23
  }
24
24
  constructor(multyx, value, propertyPath = [], editable) {
25
+ var _b, _c;
25
26
  this.readModifiers = [];
26
27
  this.editCallbacks = [];
27
28
  /* Native methods to allow MultyxValue to be treated as primitive */
@@ -35,7 +36,7 @@ class MultyxClientValue {
35
36
  this.set(value);
36
37
  const propSymbol = Symbol.for("_" + this.propertyPath.join('.'));
37
38
  if (this.multyx.events.has(propSymbol)) {
38
- this.multyx[utils_1.Done].push(...this.multyx.events.get(propSymbol).map(e => () => e(this.value)));
39
+ this.multyx[utils_1.Done].push(...((_c = (_b = this.multyx.events.get(propSymbol)) === null || _b === void 0 ? void 0 : _b.map(e => () => e(this.value))) !== null && _c !== void 0 ? _c : []));
39
40
  }
40
41
  }
41
42
  set(value) {