@duet3d/objectmodel 3.7.0-beta.6 → 3.7.0-beta.8

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.
@@ -18,9 +18,10 @@ export declare class ModelCollection<T extends IModelObject | null> extends Arra
18
18
  /**
19
19
  * Update this instance from the given data
20
20
  * @param jsonElement JSON data to upgrade this instance from
21
+ * @param authoritative Whether the given data is a complete snapshot of the items and everything below them
21
22
  * @returns Updated instance
22
23
  */
23
- update(jsonElement: any): IModelObject | null;
24
+ update(jsonElement: any, authoritative?: boolean): IModelObject | null;
24
25
  }
25
26
  export default ModelCollection;
26
27
  /**
@@ -33,9 +33,10 @@ export class ModelCollection extends Array {
33
33
  /**
34
34
  * Update this instance from the given data
35
35
  * @param jsonElement JSON data to upgrade this instance from
36
+ * @param authoritative Whether the given data is a complete snapshot of the items and everything below them
36
37
  * @returns Updated instance
37
38
  */
38
- update(jsonElement) {
39
+ update(jsonElement, authoritative = false) {
39
40
  if (jsonElement === null) {
40
41
  return null;
41
42
  }
@@ -55,11 +56,11 @@ export class ModelCollection extends Array {
55
56
  }
56
57
  else {
57
58
  const refItem = new that.$itemConstructor();
58
- this[i] = refItem.update(newItem);
59
+ this[i] = refItem.update(newItem, authoritative);
59
60
  }
60
61
  }
61
62
  else if (isModelObject(currentItem)) {
62
- const newItem = currentItem.update(jsonElement[i]);
63
+ const newItem = currentItem.update(jsonElement[i], authoritative);
63
64
  if (currentItem !== newItem) {
64
65
  this[i] = newItem;
65
66
  }
@@ -79,7 +80,7 @@ export class ModelCollection extends Array {
79
80
  }
80
81
  else {
81
82
  const newItem = new that.$itemConstructor();
82
- super.push(newItem.update(itemToAdd));
83
+ super.push(newItem.update(itemToAdd, authoritative));
83
84
  }
84
85
  }
85
86
  return this;
@@ -15,14 +15,16 @@ export declare class ModelDictionary<T> extends Map<string, T | null> implements
15
15
  * Overridden set method to perform type-checks and update
16
16
  * @param key Key to set
17
17
  * @param value Value to set
18
+ * @param authoritative Whether the given value is a complete snapshot of the item and everything below it
18
19
  */
19
- set(key: string, value: T | null): this;
20
+ set(key: string, value: T | null, authoritative?: boolean): this;
20
21
  /**
21
22
  * Update this instance from the given data
22
23
  * @param jsonElement JSON data to upgrade this instance from
24
+ * @param authoritative Whether the given data is a complete snapshot of the items and everything below them
23
25
  * @returns Updated instance
24
26
  */
25
- update(jsonElement: any): IModelObject | null;
27
+ update(jsonElement: any, authoritative?: boolean): IModelObject | null;
26
28
  /**
27
29
  * Convert this object to JSON
28
30
  * @returns JSON object
@@ -18,8 +18,9 @@ export class ModelDictionary extends Map {
18
18
  * Overridden set method to perform type-checks and update
19
19
  * @param key Key to set
20
20
  * @param value Value to set
21
+ * @param authoritative Whether the given value is a complete snapshot of the item and everything below it
21
22
  */
22
- set(key, value) {
23
+ set(key, value, authoritative = false) {
23
24
  const that = this;
24
25
  if (value === null) {
25
26
  if (that.$nullDeletesKeys) {
@@ -33,13 +34,13 @@ export class ModelDictionary extends Map {
33
34
  if (that.$itemConstructor !== null && !(value instanceof that.$itemConstructor)) {
34
35
  const newItem = new that.$itemConstructor();
35
36
  if (isModelObject(newItem)) {
36
- const updatedItem = newItem.update(value);
37
+ const updatedItem = newItem.update(value, authoritative);
37
38
  return super.set(key, updatedItem);
38
39
  }
39
40
  }
40
41
  }
41
42
  else if (isModelObject(currentItem)) {
42
- const newItem = currentItem.update(value);
43
+ const newItem = currentItem.update(value, authoritative);
43
44
  if (currentItem !== newItem) {
44
45
  return super.set(key, value);
45
46
  }
@@ -50,20 +51,21 @@ export class ModelDictionary extends Map {
50
51
  /**
51
52
  * Update this instance from the given data
52
53
  * @param jsonElement JSON data to upgrade this instance from
54
+ * @param authoritative Whether the given data is a complete snapshot of the items and everything below them
53
55
  * @returns Updated instance
54
56
  */
55
- update(jsonElement) {
57
+ update(jsonElement, authoritative = false) {
56
58
  if (jsonElement === null) {
57
59
  this.clear();
58
60
  }
59
61
  else if (jsonElement instanceof Map) {
60
62
  for (const [key, value] of jsonElement.entries()) {
61
- this.set(key, value);
63
+ this.set(key, value, authoritative);
62
64
  }
63
65
  }
64
66
  else {
65
67
  for (const [key, value] of Object.entries(jsonElement)) {
66
- this.set(key, value);
68
+ this.set(key, value, authoritative);
67
69
  }
68
70
  }
69
71
  return this;
@@ -5,9 +5,12 @@ export interface IModelObject {
5
5
  /**
6
6
  * Update this instance from the given data
7
7
  * @param jsonElement JSON data to upgrade this instance from
8
+ * @param authoritative Whether the given data is a complete snapshot of this instance and everything below it,
9
+ * so that properties missing from it are known to be null. Set this only for responses that were not filtered
10
+ * by the sender, i.e. full object model key queries but never live/patch updates
8
11
  * @returns Updated instance (may not equal the original instance)
9
12
  */
10
- update(jsonElement: any): IModelObject | null;
13
+ update(jsonElement: any, authoritative?: boolean): IModelObject | null;
11
14
  }
12
15
  /**
13
16
  * Check whether a given value provides model update functionality
@@ -18,12 +21,20 @@ export declare function isModelObject(value: any): value is IModelObject;
18
21
  * Base class for object model classes
19
22
  */
20
23
  export declare abstract class ModelObject implements IModelObject {
24
+ /**
25
+ * Whether an authoritative update may reset the properties of this class that are missing from it.
26
+ * Set to false where an instance never receives a complete snapshot of itself. Deliberately static
27
+ * because an instance member would become part of the structural type and break consumers that hold
28
+ * a mapped version of the model, such as a Pinia store
29
+ */
30
+ static readonly resetsMissingProperties: boolean;
21
31
  /**
22
32
  * Update this instance from the given data
23
33
  * @param jsonElement JSON data to upgrade this instance from
34
+ * @param authoritative Whether the given data is a complete snapshot of this instance and everything below it
24
35
  * @returns Updated instance (may not equal the original instance)
25
36
  */
26
- update(jsonElement: any): IModelObject | null;
37
+ update(jsonElement: any, authoritative?: boolean): IModelObject | null;
27
38
  /**
28
39
  * Wrap a nullable model object property so that type checks can be performed
29
40
  * @param key Property key of the derived class
@@ -1,4 +1,34 @@
1
1
  import { ModelCollection } from "./index";
2
+ /**
3
+ * Names of the properties that default to null, per model object class. Queried from a pristine instance because
4
+ * TS types are gone at runtime, so a null default is the only remaining evidence that a property may hold null
5
+ */
6
+ const nullableProperties = new WeakMap();
7
+ /**
8
+ * Get the names of the properties of the given instance's class that may hold null
9
+ * @param instance Instance to inspect the class of
10
+ */
11
+ function getNullableProperties(instance) {
12
+ let result = nullableProperties.get(instance.constructor);
13
+ if (result === undefined) {
14
+ result = new Set();
15
+ try {
16
+ for (const [key, value] of Object.entries(new instance.constructor())) {
17
+ if (value === null) {
18
+ result.add(key);
19
+ }
20
+ }
21
+ }
22
+ catch (e) {
23
+ // A class that cannot be constructed without arguments simply opts out of null reconstruction
24
+ if (process.env.NODE_ENV !== "production") {
25
+ console.warn(`Failed to determine nullable properties of ${instance.constructor.name}`, e);
26
+ }
27
+ }
28
+ nullableProperties.set(instance.constructor, result);
29
+ }
30
+ return result;
31
+ }
2
32
  /**
3
33
  * Check whether a given value provides model update functionality
4
34
  * @param value Value to check
@@ -13,19 +43,27 @@ export class ModelObject {
13
43
  /**
14
44
  * Update this instance from the given data
15
45
  * @param jsonElement JSON data to upgrade this instance from
46
+ * @param authoritative Whether the given data is a complete snapshot of this instance and everything below it
16
47
  * @returns Updated instance (may not equal the original instance)
17
48
  */
18
- update(jsonElement) {
49
+ update(jsonElement, authoritative = false) {
19
50
  if (jsonElement === null) {
20
51
  return null;
21
52
  }
53
+ if (authoritative && this.constructor.resetsMissingProperties) {
54
+ for (const key of getNullableProperties(this)) {
55
+ if (!(key in jsonElement)) {
56
+ this[key] = null;
57
+ }
58
+ }
59
+ }
22
60
  for (const [key, value] of Object.entries(jsonElement)) {
23
61
  if (key in this) {
24
62
  const ownKey = key;
25
63
  const prop = this[ownKey];
26
64
  if (isModelObject(prop)) {
27
65
  // Update model objects
28
- const updatedObject = prop.update(value);
66
+ const updatedObject = prop.update(value, authoritative);
29
67
  if (prop !== updatedObject) {
30
68
  const propDescriptor = Object.getOwnPropertyDescriptor(this, key);
31
69
  if (propDescriptor !== undefined) {
@@ -219,6 +257,13 @@ export class ModelObject {
219
257
  });
220
258
  }
221
259
  }
260
+ /**
261
+ * Whether an authoritative update may reset the properties of this class that are missing from it.
262
+ * Set to false where an instance never receives a complete snapshot of itself. Deliberately static
263
+ * because an instance member would become part of the structural type and break consumers that hold
264
+ * a mapped version of the model, such as a Pinia store
265
+ */
266
+ ModelObject.resetsMissingProperties = true;
222
267
  export default ModelObject;
223
268
  /**
224
269
  * Initialize a model object from the given data
@@ -24,6 +24,11 @@ import LedStrip from "./ledStrips";
24
24
  */
25
25
  export declare class ObjectModel extends ModelObject {
26
26
  constructor();
27
+ /**
28
+ * A payload handed to the root always covers a subset of the top-level keys rather than the whole model,
29
+ * so authoritative reconstruction has to start one level below it
30
+ */
31
+ static readonly resetsMissingProperties: boolean;
27
32
  readonly boards: ModelCollection<Board>;
28
33
  readonly directories: Directories;
29
34
  readonly fans: ModelCollection<Fan | null>;
@@ -47,4 +47,9 @@ export class ObjectModel extends ModelObject {
47
47
  ModelObject.wrapModelProperty(this, "sbc", SBC);
48
48
  }
49
49
  }
50
+ /**
51
+ * A payload handed to the root always covers a subset of the top-level keys rather than the whole model,
52
+ * so authoritative reconstruction has to start one level below it
53
+ */
54
+ ObjectModel.resetsMissingProperties = false;
50
55
  export default ObjectModel;
@@ -16,6 +16,7 @@ export declare class RotatingMagnetFilamentMonitorConfigured extends ModelObject
16
16
  }
17
17
  export declare class RotatingMagnetFilamentMonitor extends Duet3DFilamentMonitor {
18
18
  constructor();
19
+ agc: number | null;
19
20
  calibrated: RotatingMagnetFilamentMonitorCalibrated | null;
20
21
  readonly configured: RotatingMagnetFilamentMonitorConfigured;
21
22
  update(jsonElement: any): IModelObject | null;
@@ -24,6 +24,7 @@ export class RotatingMagnetFilamentMonitorConfigured extends ModelObject {
24
24
  export class RotatingMagnetFilamentMonitor extends Duet3DFilamentMonitor {
25
25
  constructor() {
26
26
  super(FilamentMonitorType.rotatingMagnet);
27
+ this.agc = null;
27
28
  this.calibrated = new RotatingMagnetFilamentMonitorCalibrated();
28
29
  this.configured = new RotatingMagnetFilamentMonitorConfigured();
29
30
  ModelObject.wrapModelProperty(this, "calibrated", RotatingMagnetFilamentMonitorCalibrated);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duet3d/objectmodel",
3
- "version": "3.7.0-beta.6",
3
+ "version": "3.7.0-beta.8",
4
4
  "description": "TypeScript implementation of the Duet3D Object Model",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",