@configura/web-api 1.6.0-iotest.4 → 1.6.1-alpha.2

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 (51) hide show
  1. package/dist/CfgProduct.d.ts +34 -17
  2. package/dist/CfgProduct.js +118 -54
  3. package/dist/index.d.ts +2 -6
  4. package/dist/index.js +2 -6
  5. package/dist/productConfiguration/CfgFeature.d.ts +2 -1
  6. package/dist/productConfiguration/CfgFeature.js +26 -20
  7. package/dist/productConfiguration/CfgOption.d.ts +30 -8
  8. package/dist/productConfiguration/CfgOption.js +50 -18
  9. package/dist/productConfiguration/CfgProductConfiguration.d.ts +1 -2
  10. package/dist/productConfiguration/CfgProductConfiguration.js +9 -8
  11. package/dist/productConfiguration/filters.d.ts +1 -1
  12. package/dist/productConfiguration/filters.js +6 -3
  13. package/dist/syncGroups/SyncGroupsApplier.d.ts +20 -0
  14. package/dist/syncGroups/SyncGroupsApplier.js +520 -0
  15. package/dist/syncGroups/SyncGroupsApplyMode.d.ts +21 -0
  16. package/dist/syncGroups/SyncGroupsApplyMode.js +21 -0
  17. package/dist/syncGroups/SyncGroupsHandler.d.ts +40 -0
  18. package/dist/syncGroups/SyncGroupsHandler.js +352 -0
  19. package/dist/syncGroups/SyncGroupsPathHelper.d.ts +27 -0
  20. package/dist/syncGroups/SyncGroupsPathHelper.js +89 -0
  21. package/dist/syncGroups/SyncGroupsState.d.ts +26 -0
  22. package/dist/syncGroups/SyncGroupsState.js +111 -0
  23. package/dist/syncGroups/SyncGroupsTransaction.d.ts +51 -0
  24. package/dist/syncGroups/SyncGroupsTransaction.js +100 -0
  25. package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +24 -95
  26. package/dist/tests/testData/testDataCachedGetProduct.js +8 -19
  27. package/dist/tests/testData/testDataOptions.d.ts +13 -0
  28. package/dist/tests/testData/testDataOptions.js +60 -0
  29. package/dist/tests/testData/testDataProductAggregatedPrice.js +12 -23
  30. package/dist/tests/testData/testDataUpcharge.js +16 -48
  31. package/dist/utilitiesCatalogueData.d.ts +7 -1
  32. package/dist/utilitiesCatalogueData.js +104 -1
  33. package/package.json +3 -3
  34. package/dist/io/CfgHistoryManager.d.ts +0 -26
  35. package/dist/io/CfgHistoryManager.js +0 -58
  36. package/dist/io/CfgHistoryToCameraConfConnector.d.ts +0 -15
  37. package/dist/io/CfgHistoryToCameraConfConnector.js +0 -58
  38. package/dist/io/CfgHistoryToProdConfConnector.d.ts +0 -14
  39. package/dist/io/CfgHistoryToProdConfConnector.js +0 -24
  40. package/dist/io/CfgIOCameraConfConnector.d.ts +0 -31
  41. package/dist/io/CfgIOCameraConfConnector.js +0 -72
  42. package/dist/io/CfgIOManager.d.ts +0 -29
  43. package/dist/io/CfgIOManager.js +0 -89
  44. package/dist/io/CfgIOProdConfConnector.d.ts +0 -36
  45. package/dist/io/CfgIOProdConfConnector.js +0 -102
  46. package/dist/io/CfgWindowMessageManager.d.ts +0 -13
  47. package/dist/io/CfgWindowMessageManager.js +0 -33
  48. package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +0 -6
  49. package/dist/io/CfgWindowMessageToCameraConfConnector.js +0 -6
  50. package/dist/io/CfgWindowMessageToProdConfConnector.d.ts +0 -14
  51. package/dist/io/CfgWindowMessageToProdConfConnector.js +0 -17
@@ -1,14 +1,15 @@
1
1
  import { AggregatedLoadingObservable, LengthUnit, Observable, SingleArgCallback } from "@configura/web-utilities";
2
- import { AdditionalProductConfiguration, CatalogueParams, MeasureParam, MtrlApplication, Prices, Transform } from "./CatalogueAPI.js";
2
+ import { AdditionalProductConfiguration, AdditionalProductRef, CatalogueParams, MeasureParam, MtrlApplication, Prices, Transform } from "./CatalogueAPI.js";
3
3
  import { CfgMeasureDefinition } from "./CfgMeasure.js";
4
4
  import { _CfgFeatureInternal } from "./productConfiguration/CfgFeature.js";
5
5
  import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
6
6
  import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
7
7
  import { ProductLoader } from "./productLoader.js";
8
+ import { SyncGroupsApplyMode } from "./syncGroups/SyncGroupsApplyMode.js";
9
+ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
8
10
  import { CfgProductData, RootNodeSource } from "./utilitiesCatalogueData.js";
9
11
  export declare type CfgProductChangeNotification = {
10
12
  freshRef: CfgProduct;
11
- committed: boolean;
12
13
  };
13
14
  export declare type CfgProductSettings = {
14
15
  /**
@@ -19,12 +20,19 @@ export declare type CfgProductSettings = {
19
20
  */
20
21
  strictSelectOneSelectionCount: boolean;
21
22
  /**
22
- * Activating this will make setAPI throw an error if the number of actually selected options
23
- * on Features (excluding Group) are not exactly equal to the number of options passed in.
24
- * Note: This check is not always reliable for Options with multiple Features each, which we
25
- * believe is a rare use case.
23
+ * Controls if SyncGroups are applied Faster or Stricter.
24
+ *
25
+ * Fast - Tries to minimize the number of validates calls to the AI lowering the response times
26
+ * for selecting options. Might not always give the expected result for complex products.
27
+ *
28
+ * Strict - Apply the SyncGroups rules in a stricter fashion to be as close to CET as possible,
29
+ * which might result in longer response times and more validation calls to the API when
30
+ * selecting options.
31
+ *
32
+ * The SDK will default to Strict, but we recommend that you try out Fast since cases where the
33
+ * results differ should be rare in most real uses cases and the speedup can be quite large.
26
34
  */
27
- strictSetApiSelectionMatch: boolean;
35
+ syncGroupsApplyMode: SyncGroupsApplyMode | undefined;
28
36
  };
29
37
  /**
30
38
  * This enum is used internally in the SDK and is not expected by be used directly by integrators.
@@ -49,6 +57,10 @@ export declare type CfgPrice = {
49
57
  currency: string;
50
58
  fractionDigits: number;
51
59
  };
60
+ export declare type RevalidateResult = {
61
+ wasAborted: boolean;
62
+ requestDidValidate: boolean;
63
+ };
52
64
  /**
53
65
  * This class is meant to only be used through CfgProduct. It should never be instantiated on its
54
66
  * own. Normally the internal state of this class should never be directly modified. CfgProduct is
@@ -64,12 +76,10 @@ export declare class _CfgProductInternal {
64
76
  private readonly _rawUnit;
65
77
  private _rawProductData;
66
78
  readonly loadingObservable: AggregatedLoadingObservable;
67
- readonly refKey: string | undefined;
68
- readonly refDescription: string | undefined;
69
79
  readonly parent: _CfgProductInternal | undefined;
70
- readonly transform: Transform | undefined;
71
- anchor: MeasureParam | undefined;
72
- static make: (productLoaderRaw: ProductLoader, productLoaderForGroupedLoad: ProductLoader | undefined, lang: string, catId: CatalogueParams, partNumber: string, settings: CfgProductSettings, optional: boolean, loadingObservable: AggregatedLoadingObservable, refKey: string | undefined, refDescription: string | undefined, parent: _CfgProductInternal | undefined, root: _CfgProductInternal | undefined, transform: Transform | undefined, anchor: MeasureParam | undefined) => Promise<_CfgProductInternal>;
80
+ private _additionalProductRef;
81
+ private readonly _syncGroupHandler;
82
+ static make: (productLoaderRaw: ProductLoader, productLoaderForGroupedLoad: ProductLoader | undefined, lang: string, catId: CatalogueParams, partNumber: string, settings: CfgProductSettings, optional: boolean, loadingObservable: AggregatedLoadingObservable, parent: _CfgProductInternal | undefined, root: _CfgProductInternal | undefined, additionalProductRef: AdditionalProductRef | undefined) => Promise<_CfgProductInternal>;
73
83
  private constructor();
74
84
  readonly root: _CfgProductInternal;
75
85
  private _destroyed;
@@ -82,6 +92,7 @@ export declare class _CfgProductInternal {
82
92
  readonly isAdditionalProduct: boolean;
83
93
  clone(parent?: _CfgProductInternal, root?: _CfgProductInternal): Promise<_CfgProductInternal>;
84
94
  destroy: () => void;
95
+ _updateAdditionalProdRef(p: AdditionalProductRef): void;
85
96
  get description(): string | undefined;
86
97
  get rootNodeSources(): RootNodeSource[] | undefined;
87
98
  get mtrlApplications(): MtrlApplication[] | undefined;
@@ -91,6 +102,9 @@ export declare class _CfgProductInternal {
91
102
  private _measureDefinitions;
92
103
  get measureDefinitions(): CfgMeasureDefinition[];
93
104
  private _unit;
105
+ get refKey(): string | undefined;
106
+ get transform(): Transform | undefined;
107
+ get anchor(): MeasureParam | undefined;
94
108
  /** @throws an error if the actual unit sent by the server was not a LengthUnit */
95
109
  get unit(): LengthUnit;
96
110
  get aggregatedPrice(): CfgPrice;
@@ -113,15 +127,18 @@ export declare class _CfgProductInternal {
113
127
  * It does not affect the visibility of anything in the 3D view at all.
114
128
  */
115
129
  get visible(): boolean;
116
- _notifyAllOfChange: (bubbleMode: CfgProductBubbleMode, committed: boolean) => Promise<void>;
130
+ _notifyAllOfChange: (bubbleMode: CfgProductBubbleMode) => Promise<void>;
117
131
  /** Called when a child (additional product or the configuration) has changed. */
118
132
  private _childHasChanged;
119
133
  /** Called by child to tell its parent that it has changed. */
120
- _additionalProductHasChanged: (freshRef: CfgProduct, bubbleMode: CfgProductBubbleMode, committed: boolean) => Promise<void>;
134
+ _additionalProductHasChanged: (freshRef: CfgProduct, bubbleMode: CfgProductBubbleMode) => Promise<void>;
121
135
  /** Called by the configuration to tell its parent that it has changed. */
122
- _configurationHasChanged: (freshRef: CfgProductConfiguration, bubbleMode: ProductConfigurationBubbleMode, committed: boolean) => Promise<void>;
136
+ _configurationHasChanged: (freshRef: CfgProductConfiguration, bubbleMode: ProductConfigurationBubbleMode) => Promise<void>;
123
137
  getApiSelection: () => AdditionalProductConfiguration;
124
138
  setApiSelection: (s: AdditionalProductConfiguration, doValidate: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
139
+ copyFrom: (source: _CfgProductInternal, doValidate: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
140
+ private _setApiSelectionWithOtherProduct;
141
+ get syncGroupHandler(): SyncGroupsHandler | undefined;
125
142
  structureCompare: (other: _CfgProductInternal, strictOrder?: boolean, descriptionMatch?: boolean) => boolean;
126
143
  tryMatchSelection: (other: _CfgProductInternal, descriptionMatch?: boolean, productLoaderForGroupedLoad?: ProductLoader | undefined) => Promise<boolean>;
127
144
  /** Only features in selected options and selected additional products. */
@@ -132,7 +149,7 @@ export declare class _CfgProductInternal {
132
149
  * product in isolation. The validation result is applied on the configuration. Then additional
133
150
  * products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
134
151
  */
135
- _revalidate: (bubbleMode: CfgProductBubbleMode, productLoader: ProductLoader, committed: boolean) => Promise<boolean>;
152
+ _revalidate: (bubbleMode: CfgProductBubbleMode, productLoader: ProductLoader) => Promise<RevalidateResult>;
136
153
  /**
137
154
  * Based on this configuration find what additional products should be shown and not, unload
138
155
  * (i.e. destroy) those that should no longer be shown, load the new ones.
@@ -144,7 +161,7 @@ export declare class CfgProduct {
144
161
  static make(productLoader: ProductLoader, lang: string, catId: CatalogueParams, partNumber: string, settings?: Partial<CfgProductSettings>): Promise<CfgProduct>;
145
162
  /**
146
163
  * Makes an object wrapping the passed object. This is not a clone method, it is a method to
147
- * make a new outer reference. Like a shallow copy./ We use this to help frameworks that are
164
+ * make a new outer reference. Like a shallow copy. We use this to help frameworks that are
148
165
  * build around using equals to detect change.
149
166
  */
150
167
  static _makeNewRefFrom(source: _CfgProductInternal): CfgProduct;
@@ -7,18 +7,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { AggregatedLoadingObservable, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
10
+ import { AggregatedLoadingObservable, assert, assertDefined, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
11
11
  import { CfgMeasureDefinition } from "./CfgMeasure.js";
12
12
  import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
13
13
  import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
14
14
  import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
15
15
  import { wrapWithCache } from "./productLoader.js";
16
- import { comparePricesObjects, correctDefaultsOnCatalogueParams, makeProductKey, } from "./utilitiesCatalogueData.js";
16
+ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
17
+ import { comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
17
18
  function completeSettings(incompleteSettings) {
18
- var _a, _b;
19
+ var _a;
19
20
  return {
20
21
  strictSelectOneSelectionCount: (_a = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSelectOneSelectionCount) !== null && _a !== void 0 ? _a : false,
21
- strictSetApiSelectionMatch: (_b = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSetApiSelectionMatch) !== null && _b !== void 0 ? _b : false,
22
+ syncGroupsApplyMode: incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.syncGroupsApplyMode,
22
23
  };
23
24
  }
24
25
  /**
@@ -50,7 +51,8 @@ function isDescriptionMatch(l, r) {
50
51
  * the class that should be used and interacted with.
51
52
  */
52
53
  export class _CfgProductInternal {
53
- constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, refKey, refDescription, parent, root, transform, anchor) {
54
+ constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, parent, root, _additionalProductRef, _syncGroupHandler) {
55
+ var _a;
54
56
  this._productLoaderRaw = _productLoaderRaw;
55
57
  this.lang = lang;
56
58
  this.catId = catId;
@@ -60,11 +62,9 @@ export class _CfgProductInternal {
60
62
  this._rawUnit = _rawUnit;
61
63
  this._rawProductData = _rawProductData;
62
64
  this.loadingObservable = loadingObservable;
63
- this.refKey = refKey;
64
- this.refDescription = refDescription;
65
65
  this.parent = parent;
66
- this.transform = transform;
67
- this.anchor = anchor;
66
+ this._additionalProductRef = _additionalProductRef;
67
+ this._syncGroupHandler = _syncGroupHandler;
68
68
  this._destroyed = false;
69
69
  this.additionalProducts = [];
70
70
  this.changeObservable = new Observable();
@@ -76,31 +76,31 @@ export class _CfgProductInternal {
76
76
  additionalProduct.destroy();
77
77
  }
78
78
  };
79
- this._notifyAllOfChange = (bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
79
+ this._notifyAllOfChange = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
80
80
  if (bubbleMode === CfgProductBubbleMode.Stop) {
81
81
  return;
82
82
  }
83
83
  const parent = this.parent;
84
84
  const freshRef = CfgProduct._makeNewRefFrom(this);
85
- this.changeObservable.notifyAll({ freshRef, committed });
85
+ this.changeObservable.notifyAll({ freshRef });
86
86
  if (parent !== undefined) {
87
87
  yield parent._additionalProductHasChanged(freshRef, bubbleMode === CfgProductBubbleMode.OneLevel
88
88
  ? CfgProductBubbleMode.Stop
89
- : bubbleMode, committed);
89
+ : bubbleMode);
90
90
  }
91
91
  });
92
92
  /** Called when a child (additional product or the configuration) has changed. */
93
- this._childHasChanged = (bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
93
+ this._childHasChanged = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
94
94
  if (bubbleMode === CfgProductBubbleMode.ToRootAndBubbleSelected &&
95
95
  this.optional &&
96
96
  !this.selected) {
97
97
  yield this.setSelected(true, bubbleMode);
98
98
  return;
99
99
  }
100
- yield this._notifyAllOfChange(bubbleMode, committed);
100
+ yield this._notifyAllOfChange(bubbleMode);
101
101
  });
102
102
  /** Called by child to tell its parent that it has changed. */
103
- this._additionalProductHasChanged = (freshRef, bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
103
+ this._additionalProductHasChanged = (freshRef, bubbleMode) => __awaiter(this, void 0, void 0, function* () {
104
104
  const i = this.additionalProducts.findIndex((a) => a.isBackedBySame(freshRef));
105
105
  if (i !== -1) {
106
106
  // Child additional product might not be found. This probably means that propagate
@@ -110,31 +110,34 @@ export class _CfgProductInternal {
110
110
  // C no longer is part of the tree. Odd, but fully permitted.
111
111
  this.additionalProducts[i] = freshRef;
112
112
  }
113
- yield this._childHasChanged(bubbleMode, committed);
113
+ yield this._childHasChanged(bubbleMode);
114
114
  });
115
115
  /** Called by the configuration to tell its parent that it has changed. */
116
- this._configurationHasChanged = (freshRef, bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
116
+ this._configurationHasChanged = (freshRef, bubbleMode) => __awaiter(this, void 0, void 0, function* () {
117
117
  this._configuration = freshRef;
118
118
  switch (bubbleMode) {
119
119
  case ProductConfigurationBubbleMode.ValidateAndBubbleSelected:
120
120
  // The revalidate call will continue the bubble
121
- yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw, committed);
121
+ yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw);
122
+ return;
123
+ case ProductConfigurationBubbleMode.BubbleSelected:
124
+ yield this._childHasChanged(CfgProductBubbleMode.ToRootAndBubbleSelected);
122
125
  return;
123
126
  case ProductConfigurationBubbleMode.Validate:
124
127
  // The revalidate call will continue the bubble
125
- yield this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw, committed);
128
+ yield this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw);
126
129
  return;
127
130
  case ProductConfigurationBubbleMode.ToParentProduct:
128
131
  // Do not continue bubble as we have reached the parent CfgProduct
129
132
  return;
130
133
  case ProductConfigurationBubbleMode.OneLevel:
131
- yield this._childHasChanged(CfgProductBubbleMode.OneLevel, committed);
134
+ yield this._childHasChanged(CfgProductBubbleMode.OneLevel);
132
135
  return;
133
136
  case ProductConfigurationBubbleMode.Stop:
134
- yield this._childHasChanged(CfgProductBubbleMode.Stop, committed);
137
+ yield this._childHasChanged(CfgProductBubbleMode.Stop);
135
138
  return;
136
139
  case ProductConfigurationBubbleMode.ToRoot:
137
- yield this._childHasChanged(CfgProductBubbleMode.ToRoot, committed);
140
+ yield this._childHasChanged(CfgProductBubbleMode.ToRoot);
138
141
  return;
139
142
  }
140
143
  });
@@ -145,13 +148,27 @@ export class _CfgProductInternal {
145
148
  additionalProducts: this.additionalProducts.map((additionalProduct) => additionalProduct.getApiSelection()),
146
149
  });
147
150
  this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
151
+ return this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
152
+ });
153
+ this.copyFrom = (source, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
154
+ return yield this._setApiSelectionWithOtherProduct(source.getApiSelection(), doValidate, productLoaderForGroupedLoad, source);
155
+ });
156
+ this._setApiSelectionWithOtherProduct = (s, doValidate, productLoaderForGroupedLoad, sourceProduct) => __awaiter(this, void 0, void 0, function* () {
148
157
  // Wrap with cache will make getProduct for this function call use the same server call
149
158
  // for the same product with the same params. Used for getProduct (when a new additional
150
159
  // product is loaded) and postValidate.
151
160
  productLoaderForGroupedLoad =
152
161
  productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
162
+ let change = false;
163
+ if (sourceProduct !== undefined) {
164
+ this._rawProductData = sourceProduct.rawProductData;
165
+ this.configuration._internal.populateFeatures(sourceProduct.configuration.rootFeatureRefs);
166
+ change = true; // We can not know if this is an actual change, so we assume it is
167
+ }
153
168
  const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
154
- let change = configurationChange;
169
+ if (configurationChange) {
170
+ change = true;
171
+ }
155
172
  if (this.optional) {
156
173
  if (yield this.setSelected(s.selected !== false, CfgProductBubbleMode.Stop)) {
157
174
  change = true;
@@ -165,28 +182,34 @@ export class _CfgProductInternal {
165
182
  if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
166
183
  throw new Error(`Additional products are not same length. This product: "${this.key}". This product additional products count: ${additionalProductsCount}. Passed apiSelection additional products count: ${apiSelectionAdditionalProductsCount}`);
167
184
  }
168
- if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionChild) => {
169
- const refKey = apiSelectionChild.refKey;
170
- if (refKey === undefined) {
171
- throw new Error("Additional product api configurations must have refKey.");
172
- }
185
+ const sourceProductAdditionalProducts = sourceProduct === null || sourceProduct === void 0 ? void 0 : sourceProduct.additionalProducts;
186
+ assert(!sourceProductAdditionalProducts ||
187
+ additionalProductsCount === sourceProductAdditionalProducts.length, `Passed sourceProduct does not have the same number of additional products as this.`);
188
+ if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionAdditionalProduct, index) => {
189
+ var _a;
190
+ const refKey = apiSelectionAdditionalProduct.refKey;
191
+ assertDefined(refKey, "Additional product api configurations must have refKey.");
173
192
  const i = additionalProducts.findIndex((a) => refKey === a.refKey);
174
- if (i === -1) {
175
- throw new Error(`Additional product not found. This product: "${this.key}". refKey of not found additional product: "${refKey}"`);
193
+ assert(i !== -1, `Additional product not found. This product: "${this.key}". refKey of not found additional product: "${refKey}"`);
194
+ let sourceProductAdditionalProduct = undefined;
195
+ if (sourceProductAdditionalProducts !== undefined) {
196
+ sourceProductAdditionalProduct =
197
+ (_a = sourceProductAdditionalProducts.find((a) => refKey === a.refKey)) === null || _a === void 0 ? void 0 : _a._internal;
198
+ assertDefined(sourceProductAdditionalProduct, "Additional product not found in sourceProduct");
176
199
  }
177
200
  const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
178
- return additionalProduct._internal.setApiSelection(apiSelectionChild, doValidate, productLoaderForGroupedLoad);
201
+ return additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
179
202
  }))).some((b) => b)) {
180
203
  change = true;
181
204
  }
182
205
  if (doValidate && configurationChange) {
183
- yield this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad, true);
206
+ yield this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad);
184
207
  }
185
208
  else if (change) {
186
209
  // As setApiSelection is done recursively each level takes care of its own notifications
187
210
  // so we only need to bubble one level to notify this and swap out the reference in the
188
211
  // parent.
189
- yield this._notifyAllOfChange(CfgProductBubbleMode.OneLevel, true);
212
+ yield this._notifyAllOfChange(CfgProductBubbleMode.OneLevel);
190
213
  }
191
214
  return change;
192
215
  });
@@ -212,10 +235,10 @@ export class _CfgProductInternal {
212
235
  const configurationChange = yield this.configuration._internal.tryMatchSelection(other.configuration._internal, descriptionMatch, false);
213
236
  if (configurationChange) {
214
237
  change = true;
215
- yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad, true);
238
+ yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad);
216
239
  }
217
240
  else if (change) {
218
- yield this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected, true);
241
+ yield this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected);
219
242
  }
220
243
  const thisAdditionalProducts = this.additionalProducts;
221
244
  const otherAdditionalProducts = other.additionalProducts;
@@ -264,12 +287,13 @@ export class _CfgProductInternal {
264
287
  * product in isolation. The validation result is applied on the configuration. Then additional
265
288
  * products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
266
289
  */
267
- this._revalidate = (bubbleMode, productLoader, committed) => __awaiter(this, void 0, void 0, function* () {
290
+ this._revalidate = (bubbleMode, productLoader) => __awaiter(this, void 0, void 0, function* () {
268
291
  const { _configuration: configuration } = this;
269
292
  const token = this.loadingObservable.startChildLoading();
270
293
  this._revalidateInProgressToken = token;
271
294
  try {
272
295
  const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), { selOptions: configuration.getApiSelection() });
296
+ const requestDidValidate = response.validated;
273
297
  // The revalidateInProgressToken is used to know if some other revalidate
274
298
  // call has happened after this call, thereby making this call obsolete.
275
299
  // This is a bit crude in that it does not actually cancel previous validate
@@ -278,7 +302,7 @@ export class _CfgProductInternal {
278
302
  // of all, the heavy work happens on the server, and that work will not be
279
303
  // cancelled even if we would cancel the call.
280
304
  if (this._revalidateInProgressToken !== token) {
281
- return false;
305
+ return { wasAborted: true, requestDidValidate };
282
306
  }
283
307
  // After a successful validate-call we will always assume there
284
308
  // is a change. It would be possible to compare relevant parts
@@ -286,7 +310,7 @@ export class _CfgProductInternal {
286
310
  // syndAndLoad, however the code comparing productData would be fragile
287
311
  // and likely to break if new data-fields were added.
288
312
  if (this._destroyed) {
289
- return false;
313
+ return { wasAborted: true, requestDidValidate };
290
314
  }
291
315
  const { productData, rootFeatureRefs } = response;
292
316
  const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
@@ -301,10 +325,10 @@ export class _CfgProductInternal {
301
325
  yield configuration._internal.setApiSelection(productData.partsData.selOptions || [], false);
302
326
  yield this._syncAndLoadAdditionalProducts(productLoader);
303
327
  if (this._destroyed) {
304
- return false;
328
+ return { wasAborted: true, requestDidValidate };
305
329
  }
306
- yield this._notifyAllOfChange(bubbleMode, committed);
307
- return true;
330
+ yield this._notifyAllOfChange(bubbleMode);
331
+ return { wasAborted: false, requestDidValidate };
308
332
  }
309
333
  catch (e) {
310
334
  throw e;
@@ -322,7 +346,14 @@ export class _CfgProductInternal {
322
346
  const additionalProductRefs = [
323
347
  ...(productData.additionalProductRefs || []),
324
348
  ...collectAdditionalProductRefs(configuration),
325
- ].map((prodRef, originalIndex) => ({
349
+ ]
350
+ .reduce((a, c) => {
351
+ if (a.every((p) => !isSameProductRef(p, c))) {
352
+ a.push(c);
353
+ }
354
+ return a;
355
+ }, [])
356
+ .map((prodRef, originalIndex) => ({
326
357
  prodRef,
327
358
  originalIndex,
328
359
  }));
@@ -330,14 +361,19 @@ export class _CfgProductInternal {
330
361
  let i = currentAdditionalProducts.length;
331
362
  while (i--) {
332
363
  const currentAdditionalProduct = currentAdditionalProducts[i];
333
- const refKey = currentAdditionalProduct.refKey;
334
- const j = additionalProductRefs.findIndex((p) => p.prodRef.refKey === refKey);
364
+ const j = additionalProductRefs.findIndex((p) => {
365
+ const prodRef = p.prodRef;
366
+ return (prodRef.refKey === currentAdditionalProduct.refKey &&
367
+ prodRef.partNumber === currentAdditionalProduct.partNumber &&
368
+ isSameCatalogueParams(prodRef.catId, currentAdditionalProduct.catId));
369
+ });
335
370
  if (j === -1) {
336
371
  currentAdditionalProduct.destroy();
337
372
  currentAdditionalProducts.splice(i, 1);
338
373
  change = true;
339
374
  }
340
375
  else {
376
+ currentAdditionalProduct._internal._updateAdditionalProdRef(additionalProductRefs[j].prodRef);
341
377
  additionalProductRefs.splice(j, 1);
342
378
  }
343
379
  }
@@ -350,7 +386,7 @@ export class _CfgProductInternal {
350
386
  const additionalProductRef = p.prodRef;
351
387
  return {
352
388
  originalIndex: p.originalIndex,
353
- product: CfgProduct._makeNewRefFrom(yield _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, lang, additionalProductRef.catId, additionalProductRef.partNumber, this.settings, additionalProductRef.optional === true, this.loadingObservable, additionalProductRef.refKey, additionalProductRef.refDescription, this, this.root, additionalProductRef.transform, additionalProductRef.anchor)),
389
+ product: CfgProduct._makeNewRefFrom(yield _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, lang, additionalProductRef.catId, additionalProductRef.partNumber, this.settings, additionalProductRef.optional === true, this.loadingObservable, this, this.root, additionalProductRef)),
354
390
  };
355
391
  }))()));
356
392
  if (this._destroyed) {
@@ -368,8 +404,8 @@ export class _CfgProductInternal {
368
404
  this.loadingObservable.stopChildLoading(token);
369
405
  }
370
406
  });
371
- this.root = root || this;
372
- this.key = makeProductKey(catId, refKey || partNumber);
407
+ this.root = root !== null && root !== void 0 ? root : this;
408
+ this.key = makeProductKey(catId, (_a = _additionalProductRef === null || _additionalProductRef === void 0 ? void 0 : _additionalProductRef.refKey) !== null && _a !== void 0 ? _a : partNumber);
373
409
  this._selected = optional ? selected : undefined;
374
410
  this.isAdditionalProduct = parent !== undefined;
375
411
  this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs, allRawFeatures, apiSelection, this, this.root);
@@ -380,9 +416,10 @@ export class _CfgProductInternal {
380
416
  clone(parent, root) {
381
417
  return __awaiter(this, void 0, void 0, function* () {
382
418
  const product = yield new Promise((initSuccess, initFail) => {
419
+ var _a;
383
420
  const p = new _CfgProductInternal(() => {
384
421
  initSuccess(p);
385
- }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), this.refKey, this.refDescription, parent, root, this.transform, this.anchor);
422
+ }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), parent, root, this._additionalProductRef, (_a = this._syncGroupHandler) === null || _a === void 0 ? void 0 : _a.clone());
386
423
  });
387
424
  for (const additionalProduct of this.additionalProducts) {
388
425
  product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
@@ -390,8 +427,15 @@ export class _CfgProductInternal {
390
427
  return product;
391
428
  });
392
429
  }
430
+ _updateAdditionalProdRef(p) {
431
+ this._additionalProductRef = p;
432
+ if (p.optional !== this.optional) {
433
+ this._selected = p.optional ? false : undefined;
434
+ }
435
+ }
393
436
  get description() {
394
- return this.refDescription || this._rawProductData.description;
437
+ var _a, _b;
438
+ return (_b = (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.refDescription) !== null && _b !== void 0 ? _b : this._rawProductData.description;
395
439
  }
396
440
  get rootNodeSources() {
397
441
  return this._rawProductData.models;
@@ -417,6 +461,18 @@ export class _CfgProductInternal {
417
461
  }
418
462
  return this._measureDefinitions;
419
463
  }
464
+ get refKey() {
465
+ var _a;
466
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.refKey;
467
+ }
468
+ get transform() {
469
+ var _a;
470
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.transform;
471
+ }
472
+ get anchor() {
473
+ var _a;
474
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.anchor;
475
+ }
420
476
  /** @throws an error if the actual unit sent by the server was not a LengthUnit */
421
477
  get unit() {
422
478
  if (this._unit === undefined) {
@@ -461,7 +517,7 @@ export class _CfgProductInternal {
461
517
  return false;
462
518
  }
463
519
  this._selected = v;
464
- yield this._notifyAllOfChange(bubbleMode, true);
520
+ yield this._notifyAllOfChange(bubbleMode);
465
521
  return true;
466
522
  });
467
523
  }
@@ -494,14 +550,20 @@ export class _CfgProductInternal {
494
550
  ? this.visibleIfAdditionalProduct
495
551
  : this.visibleIfMainProduct;
496
552
  }
553
+ get syncGroupHandler() {
554
+ return this.root._syncGroupHandler;
555
+ }
497
556
  }
498
557
  _CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
499
- lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescription, parent, root, transform, anchor) => __awaiter(void 0, void 0, void 0, function* () {
558
+ lang, catId, partNumber, settings, optional, loadingObservable, parent, root, additionalProductRef) => __awaiter(void 0, void 0, void 0, function* () {
500
559
  // Wrap with cache will make getProduct for this function call use the same server call
501
560
  // for the same product with the same params. Not retained for future calls, only used
502
561
  // at this initial load.
503
562
  productLoaderForGroupedLoad =
504
563
  productLoaderForGroupedLoad || wrapWithCache(productLoaderRaw);
564
+ const syncGroupHandler = root === undefined
565
+ ? SyncGroupsHandler.make(settings.syncGroupsApplyMode, loadingObservable)
566
+ : undefined;
505
567
  const productResponse = yield productLoaderForGroupedLoad.getProduct(Object.assign(Object.assign({ lang }, correctDefaultsOnCatalogueParams(catId)), { partNumber }));
506
568
  const { productData, rootFeatureRefs, features: allRawFeatures, uuid, unit, } = productResponse;
507
569
  const product = yield new Promise((initSuccess, initFail) => {
@@ -510,9 +572,11 @@ lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescr
510
572
  // But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
511
573
  // Feel free to find a nicer more readable solution :)
512
574
  initSuccess(p);
513
- }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, refKey, refDescription, parent, root, transform, anchor);
575
+ }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, parent, root, additionalProductRef, syncGroupHandler);
514
576
  });
515
577
  yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
578
+ // Product is guaranteed to be root
579
+ yield (syncGroupHandler === null || syncGroupHandler === void 0 ? void 0 : syncGroupHandler.init(product, productLoaderForGroupedLoad));
516
580
  return product;
517
581
  });
518
582
  export class CfgProduct {
@@ -563,12 +627,12 @@ export class CfgProduct {
563
627
  }
564
628
  static make(productLoader, lang, catId, partNumber, settings) {
565
629
  return __awaiter(this, void 0, void 0, function* () {
566
- return this._makeNewRefFrom(yield _CfgProductInternal.make(productLoader, undefined, lang, catId, partNumber, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined, undefined, undefined, undefined));
630
+ return this._makeNewRefFrom(yield _CfgProductInternal.make(productLoader, undefined, lang, catId, partNumber, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined));
567
631
  });
568
632
  }
569
633
  /**
570
634
  * Makes an object wrapping the passed object. This is not a clone method, it is a method to
571
- * make a new outer reference. Like a shallow copy./ We use this to help frameworks that are
635
+ * make a new outer reference. Like a shallow copy. We use this to help frameworks that are
572
636
  * build around using equals to detect change.
573
637
  */
574
638
  static _makeNewRefFrom(source) {
package/dist/index.d.ts CHANGED
@@ -1,11 +1,5 @@
1
1
  export * from "./CatalogueAPI.js";
2
2
  export * from "./CfgProduct.js";
3
- export * from "./io/CfgHistoryManager.js";
4
- export * from "./io/CfgHistoryToCameraConfConnector.js";
5
- export * from "./io/CfgHistoryToProdConfConnector.js";
6
- export * from "./io/CfgWindowMessageManager.js";
7
- export * from "./io/CfgWindowMessageToCameraConfConnector.js";
8
- export * from "./io/CfgWindowMessageToProdConfConnector.js";
9
3
  export * from "./material/CfgMaterialMapping.js";
10
4
  export * from "./material/CfgMtrlApplication.js";
11
5
  export * from "./material/CfgMtrlApplicationSource.js";
@@ -17,6 +11,8 @@ export * from "./productConfiguration/CfgProductConfiguration.js";
17
11
  export * from "./productConfiguration/filters.js";
18
12
  export * from "./productConfiguration/productParamsGenerator.js";
19
13
  export * from "./productLoader.js";
14
+ export * from "./syncGroups/SyncGroupsApplyMode.js";
15
+ export * from "./syncGroups/SyncGroupsHandler.js";
20
16
  export * from "./tasks/formats.js";
21
17
  export * from "./tasks/TaskHandler.js";
22
18
  export * from "./utilitiesCatalogueData.js";
package/dist/index.js CHANGED
@@ -1,11 +1,5 @@
1
1
  export * from "./CatalogueAPI.js";
2
2
  export * from "./CfgProduct.js";
3
- export * from "./io/CfgHistoryManager.js";
4
- export * from "./io/CfgHistoryToCameraConfConnector.js";
5
- export * from "./io/CfgHistoryToProdConfConnector.js";
6
- export * from "./io/CfgWindowMessageManager.js";
7
- export * from "./io/CfgWindowMessageToCameraConfConnector.js";
8
- export * from "./io/CfgWindowMessageToProdConfConnector.js";
9
3
  export * from "./material/CfgMaterialMapping.js";
10
4
  export * from "./material/CfgMtrlApplication.js";
11
5
  export * from "./material/CfgMtrlApplicationSource.js";
@@ -17,6 +11,8 @@ export * from "./productConfiguration/CfgProductConfiguration.js";
17
11
  export * from "./productConfiguration/filters.js";
18
12
  export * from "./productConfiguration/productParamsGenerator.js";
19
13
  export * from "./productLoader.js";
14
+ export * from "./syncGroups/SyncGroupsApplyMode.js";
15
+ export * from "./syncGroups/SyncGroupsHandler.js";
20
16
  export * from "./tasks/formats.js";
21
17
  export * from "./tasks/TaskHandler.js";
22
18
  export * from "./utilitiesCatalogueData.js";
@@ -1,5 +1,5 @@
1
1
  import { LengthUnit, Observable, SingleArgCallback } from "@configura/web-utilities";
2
- import { Feature, SelectedOption } from "../CatalogueAPI.js";
2
+ import { Feature, SelectedOption, SyncGroup } from "../CatalogueAPI.js";
3
3
  import { CfgProduct, _CfgProductInternal } from "../CfgProduct.js";
4
4
  import { CfgMtrlApplication } from "../material/CfgMtrlApplication.js";
5
5
  import { CfgOption, ProductConfigurationBubbleMode, _CfgOptionInternal } from "./CfgOption.js";
@@ -49,6 +49,7 @@ export declare class _CfgFeatureInternal {
49
49
  get isUseNumericValue(): boolean;
50
50
  get numericValue(): number | undefined;
51
51
  setNumericValue: (val: number) => Promise<boolean>;
52
+ get syncGroup(): SyncGroup | undefined;
52
53
  get description(): string;
53
54
  /**
54
55
  * The MeasureParam class is re-used for different purposes. In Features it is used