@duet3d/objectmodel 3.7.0-beta.5 → 3.7.0-beta.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.
package/README.md CHANGED
@@ -4,13 +4,7 @@ TypeScript implementation of the Duet3D Object Model.
4
4
 
5
5
  ## Installation
6
6
 
7
- Install via `npm install @duet3d/objectmodel`. Users of Vue 2 must also run this command after the first import:
8
-
9
- ```
10
- globalThis._duetModelSetArray = (array, index, value) => Vue.set(array, index, value);
11
- ```
12
-
13
- This is required to make sure that change events for arrays are correctly fired.
7
+ Install via `npm install @duet3d/objectmodel`.
14
8
 
15
9
  ## Bug reports
16
10
 
@@ -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
  /**
@@ -1,5 +1,4 @@
1
1
  import { isModelObject } from "./ModelObject";
2
- import { setArrayItem } from "./index";
3
2
  /**
4
3
  * Class for storing model object items in an array
5
4
  */
@@ -34,9 +33,10 @@ export class ModelCollection extends Array {
34
33
  /**
35
34
  * Update this instance from the given data
36
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
37
37
  * @returns Updated instance
38
38
  */
39
- update(jsonElement) {
39
+ update(jsonElement, authoritative = false) {
40
40
  if (jsonElement === null) {
41
41
  return null;
42
42
  }
@@ -52,23 +52,23 @@ export class ModelCollection extends Array {
52
52
  if (currentItem === null) {
53
53
  const newItem = jsonElement[i];
54
54
  if (newItem instanceof that.$itemConstructor) {
55
- setArrayItem(this, i, jsonElement[i]);
55
+ this[i] = jsonElement[i];
56
56
  }
57
57
  else {
58
58
  const refItem = new that.$itemConstructor();
59
- setArrayItem(this, i, refItem.update(newItem));
59
+ this[i] = refItem.update(newItem, authoritative);
60
60
  }
61
61
  }
62
62
  else if (isModelObject(currentItem)) {
63
- const newItem = currentItem.update(jsonElement[i]);
63
+ const newItem = currentItem.update(jsonElement[i], authoritative);
64
64
  if (currentItem !== newItem) {
65
- setArrayItem(this, i, newItem);
65
+ this[i] = newItem;
66
66
  }
67
67
  }
68
68
  else {
69
69
  const newItem = jsonElement[i];
70
70
  if (currentItem !== newItem) {
71
- setArrayItem(this, i, newItem);
71
+ this[i] = newItem;
72
72
  }
73
73
  }
74
74
  }
@@ -80,7 +80,7 @@ export class ModelCollection extends Array {
80
80
  }
81
81
  else {
82
82
  const newItem = new that.$itemConstructor();
83
- super.push(newItem.update(itemToAdd));
83
+ super.push(newItem.update(itemToAdd, authoritative));
84
84
  }
85
85
  }
86
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,19 @@ 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
+ * Reset the properties that may hold null and are missing from the given authoritative data.
26
+ * Overridden where an instance never receives a complete snapshot of itself
27
+ * @param jsonElement JSON data this instance is being updated from
28
+ */
29
+ protected resetMissingProperties(jsonElement: any): void;
21
30
  /**
22
31
  * Update this instance from the given data
23
32
  * @param jsonElement JSON data to upgrade this instance from
33
+ * @param authoritative Whether the given data is a complete snapshot of this instance and everything below it
24
34
  * @returns Updated instance (may not equal the original instance)
25
35
  */
26
- update(jsonElement: any): IModelObject | null;
36
+ update(jsonElement: any, authoritative?: boolean): IModelObject | null;
27
37
  /**
28
38
  * Wrap a nullable model object property so that type checks can be performed
29
39
  * @param key Property key of the derived class
@@ -1,4 +1,34 @@
1
- import { ModelCollection, setArrayItem } from "./index";
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
@@ -10,22 +40,38 @@ export function isModelObject(value) {
10
40
  * Base class for object model classes
11
41
  */
12
42
  export class ModelObject {
43
+ /**
44
+ * Reset the properties that may hold null and are missing from the given authoritative data.
45
+ * Overridden where an instance never receives a complete snapshot of itself
46
+ * @param jsonElement JSON data this instance is being updated from
47
+ */
48
+ resetMissingProperties(jsonElement) {
49
+ for (const key of getNullableProperties(this)) {
50
+ if (!(key in jsonElement)) {
51
+ this[key] = null;
52
+ }
53
+ }
54
+ }
13
55
  /**
14
56
  * Update this instance from the given data
15
57
  * @param jsonElement JSON data to upgrade this instance from
58
+ * @param authoritative Whether the given data is a complete snapshot of this instance and everything below it
16
59
  * @returns Updated instance (may not equal the original instance)
17
60
  */
18
- update(jsonElement) {
61
+ update(jsonElement, authoritative = false) {
19
62
  if (jsonElement === null) {
20
63
  return null;
21
64
  }
65
+ if (authoritative) {
66
+ this.resetMissingProperties(jsonElement);
67
+ }
22
68
  for (const [key, value] of Object.entries(jsonElement)) {
23
69
  if (key in this) {
24
70
  const ownKey = key;
25
71
  const prop = this[ownKey];
26
72
  if (isModelObject(prop)) {
27
73
  // Update model objects
28
- const updatedObject = prop.update(value);
74
+ const updatedObject = prop.update(value, authoritative);
29
75
  if (prop !== updatedObject) {
30
76
  const propDescriptor = Object.getOwnPropertyDescriptor(this, key);
31
77
  if (propDescriptor !== undefined) {
@@ -49,12 +95,12 @@ export class ModelObject {
49
95
  for (let i = 0; i < Math.min(prop.length, value.length); i++) {
50
96
  const propItem = prop[i];
51
97
  if (propItem === null) {
52
- setArrayItem(prop, i, value[i]);
98
+ prop[i] = value[i];
53
99
  }
54
100
  else {
55
101
  const newItem = value[i];
56
102
  if (propItem !== newItem) {
57
- setArrayItem(prop, i, newItem);
103
+ prop[i] = newItem;
58
104
  }
59
105
  }
60
106
  }
@@ -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
+ protected resetMissingProperties(): void;
27
32
  readonly boards: ModelCollection<Board>;
28
33
  readonly directories: Directories;
29
34
  readonly fans: ModelCollection<Fan | null>;
@@ -46,5 +46,10 @@ export class ObjectModel extends ModelObject {
46
46
  this.volumes = new ModelCollection(Volume);
47
47
  ModelObject.wrapModelProperty(this, "sbc", SBC);
48
48
  }
49
+ /**
50
+ * A payload handed to the root always covers a subset of the top-level keys rather than the whole model,
51
+ * so authoritative reconstruction has to start one level below it
52
+ */
53
+ resetMissingProperties() { }
49
54
  }
50
55
  export default ObjectModel;
package/dist/index.d.ts CHANGED
@@ -23,4 +23,3 @@ export * from "./volumes";
23
23
  export * from "./ObjectModel";
24
24
  import ObjectModel from "./ObjectModel";
25
25
  export default ObjectModel;
26
- export declare function setArrayItem(array: Array<any>, index: number, value: any): void;
package/dist/index.js CHANGED
@@ -25,13 +25,3 @@ export * from "./ObjectModel";
25
25
  // Expose ObjectModel as default export
26
26
  import ObjectModel from "./ObjectModel";
27
27
  export default ObjectModel;
28
- // Unfortunately we need to define a way to update arrays to remain compatible with Vue 2 (due to IE11).
29
- // This will become obsolete as soon as DWC is upgraded to Vue 3, but that isn't going to happen anytime soon.
30
- // Until then a Vue 2 user would have to call something like this on initialization to work around this limitation:
31
- // globalThis._duetModelSetArray = (array, index, value) => Vue.set(array, index, value);
32
- // or in TypeScript
33
- // (globalThis as any)._duetModelSetArray = (array: object, index: string | number, value: any) => Vue.set(array, index, value);
34
- globalThis._duetModelSetArray = (array, index, value) => array[index] = value;
35
- export function setArrayItem(array, index, value) {
36
- globalThis._duetModelSetArray(array, index, value);
37
- }
@@ -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.5",
3
+ "version": "3.7.0-beta.7",
4
4
  "description": "TypeScript implementation of the Duet3D Object Model",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",