@plattar/plattar-ar-adapter 2.7.2 → 2.7.3

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.
@@ -30,6 +30,8 @@ class ConfiguratorAR extends launcher_ar_1.LauncherAR {
30
30
  this._ar = null;
31
31
  }
32
32
  _SetupAnalytics() {
33
+ if (this._analytics !== null)
34
+ return;
33
35
  const scene = this._options.state.scene;
34
36
  let analytics = null;
35
37
  // setup scene stuff (if any)
@@ -35,6 +35,8 @@ class ModelAR extends launcher_ar_1.LauncherAR {
35
35
  return this._options.modelID;
36
36
  }
37
37
  _SetupAnalytics(model) {
38
+ if (this._analytics !== null)
39
+ return;
38
40
  let analytics = null;
39
41
  const project = model.relationships.find(plattar_api_1.Project);
40
42
  // setup scene stuff (if any)
@@ -40,6 +40,8 @@ class ProductAR extends launcher_ar_1.LauncherAR {
40
40
  return this._options.variationSKU;
41
41
  }
42
42
  _SetupAnalytics(product, variation) {
43
+ if (this._analytics !== null)
44
+ return;
43
45
  let analytics = null;
44
46
  const scene = product.relationships.find(plattar_api_1.Scene);
45
47
  // setup scene stuff (if any)
@@ -89,9 +91,7 @@ class ProductAR extends launcher_ar_1.LauncherAR {
89
91
  return reject(new Error("ProductAR.init() - cannot proceed as AR not available in context"));
90
92
  }
91
93
  const product = new plattar_api_1.Product(this.productID);
92
- product.include(plattar_api_1.ProductVariation);
93
94
  product.include(plattar_api_1.ProductVariation.include(plattar_api_1.FileModel));
94
- product.include(plattar_api_1.Scene);
95
95
  product.include(plattar_api_1.Scene.include(plattar_api_1.Project));
96
96
  product.get().then((product) => {
97
97
  // find the required variation from our product
package/dist/ar/raw-ar.js CHANGED
@@ -42,6 +42,8 @@ class RawAR extends launcher_ar_1.LauncherAR {
42
42
  }
43
43
  _SetupAnalytics() {
44
44
  return new Promise((accept, _reject) => {
45
+ if (this._analytics !== null)
46
+ return accept();
45
47
  const sceneID = this._options.sceneID;
46
48
  if (!sceneID) {
47
49
  return accept();
@@ -34,6 +34,8 @@ class SceneAR extends launcher_ar_1.LauncherAR {
34
34
  return this._options.sceneID;
35
35
  }
36
36
  _SetupAnalytics(scene) {
37
+ if (this._analytics !== null)
38
+ return;
37
39
  let analytics = null;
38
40
  // setup scene stuff (if any)
39
41
  if (scene) {
@@ -159,7 +161,6 @@ class SceneAR extends launcher_ar_1.LauncherAR {
159
161
  }
160
162
  const scene = new plattar_api_1.Scene(this.sceneID);
161
163
  scene.include(plattar_api_1.Project);
162
- scene.include(plattar_api_1.SceneProduct);
163
164
  scene.include(plattar_api_1.SceneProduct.include(plattar_api_1.Product));
164
165
  scene.include(plattar_api_1.SceneModel);
165
166
  scene.get().then((scene) => {
@@ -9,6 +9,7 @@ export interface SceneGraphAROptions {
9
9
  */
10
10
  export declare class SceneGraphAR extends LauncherAR {
11
11
  private _analytics;
12
+ private _analyticsSetup;
12
13
  private _options;
13
14
  private _ar;
14
15
  constructor(options: SceneGraphAROptions);
@@ -17,6 +17,7 @@ const version_1 = __importDefault(require("../version"));
17
17
  class SceneGraphAR extends launcher_ar_1.LauncherAR {
18
18
  // analytics instance
19
19
  _analytics = null;
20
+ _analyticsSetup = null;
20
21
  _options;
21
22
  // this thing controls the actual AR view
22
23
  // this is setup via .init() function
@@ -26,32 +27,37 @@ class SceneGraphAR extends launcher_ar_1.LauncherAR {
26
27
  this._options = options;
27
28
  this._ar = null;
28
29
  }
29
- async _SetupAnalytics() {
30
- const scene = new plattar_api_1.Scene(this._options.sceneID);
31
- scene.include(plattar_api_1.Project);
32
- const fetchedScene = await scene.get();
33
- let analytics = null;
34
- analytics = new plattar_analytics_1.Analytics(fetchedScene.attributes.application_id);
35
- analytics.origin = plattar_api_1.Server.location().type;
36
- this._analytics = analytics;
37
- analytics.data.push("type", "scene-graph-ar");
38
- analytics.data.push("sdkVersion", version_1.default);
39
- analytics.data.push("sceneId", fetchedScene.id);
40
- analytics.data.push("sceneTitle", fetchedScene.attributes.title);
41
- const application = fetchedScene.relationships.find(plattar_api_1.Project);
42
- // setup application stuff (if any)
43
- if (application) {
44
- analytics.data.push("applicationId", application.id);
45
- analytics.data.push("applicationTitle", application.attributes.title);
46
- if (this._options.useARBanner) {
47
- this.options.banner = {
48
- title: application.attributes.title,
49
- subtitle: fetchedScene.attributes.title,
50
- button: 'Visit'
51
- };
30
+ _SetupAnalytics() {
31
+ if (this._analyticsSetup !== null)
32
+ return this._analyticsSetup;
33
+ this._analyticsSetup = (async () => {
34
+ const scene = new plattar_api_1.Scene(this._options.sceneID);
35
+ scene.include(plattar_api_1.Project);
36
+ const fetchedScene = await scene.get();
37
+ let analytics = null;
38
+ analytics = new plattar_analytics_1.Analytics(fetchedScene.attributes.application_id);
39
+ analytics.origin = plattar_api_1.Server.location().type;
40
+ this._analytics = analytics;
41
+ analytics.data.push("type", "scene-graph-ar");
42
+ analytics.data.push("sdkVersion", version_1.default);
43
+ analytics.data.push("sceneId", fetchedScene.id);
44
+ analytics.data.push("sceneTitle", fetchedScene.attributes.title);
45
+ const application = fetchedScene.relationships.find(plattar_api_1.Project);
46
+ // setup application stuff (if any)
47
+ if (application) {
48
+ analytics.data.push("applicationId", application.id);
49
+ analytics.data.push("applicationTitle", application.attributes.title);
50
+ if (this._options.useARBanner) {
51
+ this.options.banner = {
52
+ title: application.attributes.title,
53
+ subtitle: fetchedScene.attributes.title,
54
+ button: 'Visit'
55
+ };
56
+ }
52
57
  }
53
- }
54
- return fetchedScene;
58
+ return fetchedScene;
59
+ })();
60
+ return this._analyticsSetup;
55
61
  }
56
62
  /**
57
63
  * Composes a Scene into an AR Model (remote operation) that can be used to launch
@@ -11,6 +11,11 @@ export declare class ConfiguratorController extends PlattarController {
11
11
  startARQRCode(options: any): Promise<HTMLElement>;
12
12
  startViewerQRCode(options: QRCodeOptions): Promise<HTMLElement>;
13
13
  startRenderer(): Promise<HTMLElement>;
14
+ /**
15
+ * Legacy furniture scenes carry a "product" state entry — the embed is
16
+ * re-created with product attributes and rendering restarts there
17
+ */
18
+ private _transitionLegacyProduct;
14
19
  initAR(): Promise<LauncherAR>;
15
20
  /**
16
21
  * Private Function - This launches the Static/Inherited AR Mode
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConfiguratorController = void 0;
4
4
  const plattar_api_1 = require("@plattar/plattar-api");
5
5
  const scene_product_ar_1 = require("../../ar/scene-product-ar");
6
+ const configurator_state_1 = require("../../util/configurator-state");
6
7
  const util_1 = require("../../util/util");
7
8
  const plattar_controller_1 = require("./plattar-controller");
8
9
  const configurator_ar_1 = require("../../ar/configurator-ar");
@@ -81,6 +82,11 @@ class ConfiguratorController extends plattar_controller_1.PlattarController {
81
82
  }
82
83
  return Promise.reject(new Error("ConfiguratorController.startARQRCode() - legacy product transition failed"));
83
84
  }
85
+ // Kick off scene-graph encoding immediately while we proceed to the base
86
+ // implementation — the result is Promise-cached so no duplicate request is fired
87
+ if (!this.getAttribute("scene-graph-id")) {
88
+ dState.state.encodeSceneGraphID().catch(() => null);
89
+ }
84
90
  }
85
91
  catch (_err) {
86
92
  }
@@ -202,28 +208,38 @@ class ConfiguratorController extends plattar_controller_1.PlattarController {
202
208
  // optional attributes
203
209
  let configState = null;
204
210
  this._state = plattar_controller_1.ControllerState.Renderer;
205
- try {
206
- const dState = await this.getConfiguratorState();
211
+ const suppliedState = this.getAttribute("config-state");
212
+ if (suppliedState) {
213
+ // the state was supplied by the host page — decode it locally
214
+ // rather than paying for the scene fetch getConfiguratorState()
215
+ // performs before the renderer iframe can even be created. The
216
+ // renderer only needs the re-encoded state; the flows that do
217
+ // need the scene (QR/AR) decode on demand and reuse this same
218
+ // instance via _bootConfigState, keeping user mutations.
219
+ configState = configurator_state_1.ConfiguratorState.decode(suppliedState);
220
+ this._bootConfigState = configState;
207
221
  // if this is declared, we have a furniture scene that we need to re-create the embed
208
222
  // with new attributes
209
- const product = dState.state.firstOfType("product");
223
+ const product = configState.firstOfType("product");
210
224
  if (product) {
211
- this.parent.lockObserver();
212
- this.parent.destroy();
213
- this.setAttribute("product-id", product.scene_product_id);
214
- this.removeAttribute("scene-id");
215
- this.parent.unlockObserver();
216
- const controller = this.parent.create();
217
- if (controller) {
218
- return controller.startRenderer();
219
- }
220
- return Promise.reject(new Error("ConfiguratorController.startRenderer() - legacy product transition failed"));
225
+ return this._transitionLegacyProduct(product);
221
226
  }
222
- configState = dState;
223
227
  }
224
- catch (_err) {
225
- // config state is not available
226
- configState = null;
228
+ else {
229
+ try {
230
+ const dState = await this.getConfiguratorState();
231
+ // if this is declared, we have a furniture scene that we need to re-create the embed
232
+ // with new attributes
233
+ const product = dState.state.firstOfType("product");
234
+ if (product) {
235
+ return this._transitionLegacyProduct(product);
236
+ }
237
+ configState = dState.state;
238
+ }
239
+ catch (_err) {
240
+ // config state is not available
241
+ configState = null;
242
+ }
227
243
  }
228
244
  // required attributes with defaults for plattar-configurator node
229
245
  const width = this.getAttribute("width") || "500px";
@@ -238,7 +254,7 @@ class ConfiguratorController extends plattar_controller_1.PlattarController {
238
254
  const showAR = this.getAttribute("show-ar");
239
255
  const showUI = this.getAttribute("show-ui");
240
256
  if (configState) {
241
- const encodedState = configState.state.encode();
257
+ const encodedState = configState.encode();
242
258
  if (encodedState.length < 6000) {
243
259
  viewer.setAttribute("config-state", encodedState);
244
260
  }
@@ -257,6 +273,22 @@ class ConfiguratorController extends plattar_controller_1.PlattarController {
257
273
  return accept(viewer);
258
274
  });
259
275
  }
276
+ /**
277
+ * Legacy furniture scenes carry a "product" state entry — the embed is
278
+ * re-created with product attributes and rendering restarts there
279
+ */
280
+ async _transitionLegacyProduct(product) {
281
+ this.parent.lockObserver();
282
+ this.parent.destroy();
283
+ this.setAttribute("product-id", product.scene_product_id);
284
+ this.removeAttribute("scene-id");
285
+ this.parent.unlockObserver();
286
+ const controller = this.parent.create();
287
+ if (controller) {
288
+ return controller.startRenderer();
289
+ }
290
+ return Promise.reject(new Error("ConfiguratorController.startRenderer() - legacy product transition failed"));
291
+ }
260
292
  async initAR() {
261
293
  if (!util_1.Util.canAugment()) {
262
294
  throw new Error("ConfiguratorController.initAR() - cannot proceed as AR not available in context");
@@ -82,16 +82,10 @@ class GalleryController extends plattar_controller_1.PlattarController {
82
82
  this._prevQROpt = opt;
83
83
  if (!opt.detached) {
84
84
  this._state = plattar_controller_1.ControllerState.QRCode;
85
- return new Promise((accept, reject) => {
86
- viewer.onload = () => {
87
- return accept(viewer);
88
- };
89
- this.append(viewer);
90
- });
85
+ this.append(viewer);
86
+ return this._awaitLoad(viewer);
91
87
  }
92
- return new Promise((accept, reject) => {
93
- return accept(viewer);
94
- });
88
+ return Promise.resolve(viewer);
95
89
  }
96
90
  async startRenderer() {
97
91
  // remove the old renderer instance if any
@@ -60,6 +60,11 @@ class LauncherController extends plattar_controller_1.PlattarController {
60
60
  }
61
61
  return Promise.reject(new Error("LauncherController.startARQRCode() - legacy product transition failed"));
62
62
  }
63
+ // Kick off scene-graph encoding immediately while we proceed to the base
64
+ // implementation — the result is Promise-cached so no duplicate request is fired
65
+ if (!this.getAttribute("scene-graph-id")) {
66
+ dState.state.encodeSceneGraphID().catch(() => null);
67
+ }
63
68
  }
64
69
  catch (_err) {
65
70
  }
@@ -122,10 +127,10 @@ class LauncherController extends plattar_controller_1.PlattarController {
122
127
  if (sceneGraphID) {
123
128
  viewer.setAttribute("scene-graph-id", sceneGraphID);
124
129
  }
125
- else {
130
+ else if (configState) {
126
131
  try {
127
- const sceneGraphID = await (await this.getConfiguratorState()).state.encodeSceneGraphID();
128
- viewer.setAttribute("scene-graph-id", sceneGraphID);
132
+ const encodedID = await configState.state.encodeSceneGraphID();
133
+ viewer.setAttribute("scene-graph-id", encodedID);
129
134
  }
130
135
  catch (_err) {
131
136
  // scene graph ID not available for some reason
@@ -136,7 +141,7 @@ class LauncherController extends plattar_controller_1.PlattarController {
136
141
  return new Promise((accept, reject) => {
137
142
  this.append(viewer);
138
143
  if (configState) {
139
- this.setupMessengerObservers(viewer, configState);
144
+ this.setupMessengerObservers(viewer, configState.state);
140
145
  }
141
146
  return accept(viewer);
142
147
  });
@@ -1,5 +1,5 @@
1
1
  import { LauncherAR } from "../../ar/launcher-ar";
2
- import { DecodedConfiguratorState } from "../../util/configurator-state";
2
+ import { ConfiguratorState, DecodedConfiguratorState } from "../../util/configurator-state";
3
3
  import type PlattarEmbed from "../plattar-embed";
4
4
  export declare enum ControllerState {
5
5
  None = 0,
@@ -26,6 +26,7 @@ export declare abstract class PlattarController {
26
26
  protected _state: ControllerState;
27
27
  protected _element: HTMLElement | null;
28
28
  protected _prevQROpt: any;
29
+ protected _bootConfigState: ConfiguratorState | null;
29
30
  private _selectVariationObserver;
30
31
  private _selectVariationIDObserver;
31
32
  private _selectVariationSKUObserver;
@@ -51,7 +52,7 @@ export declare abstract class PlattarController {
51
52
  * Setup messenger observers to detect variation changes and apply to the internal
52
53
  * configuration state
53
54
  */
54
- protected setupMessengerObservers(viewer: any, configState: DecodedConfiguratorState): void;
55
+ protected setupMessengerObservers(viewer: any, configState: ConfiguratorState): void;
55
56
  /**
56
57
  * Remove all pre-existing observers
57
58
  */
@@ -81,6 +82,12 @@ export declare abstract class PlattarController {
81
82
  * Initialise and return a launcher that can be used to start AR
82
83
  */
83
84
  abstract initAR(): Promise<LauncherAR>;
85
+ /**
86
+ * Appends the viewer to the shadow DOM and waits for its onload event.
87
+ * Rejects if onload does not fire within timeoutMs (default 30 s) so
88
+ * callers are never left with a permanently-pending Promise.
89
+ */
90
+ protected _awaitLoad(viewer: HTMLElement, timeoutMs?: number): Promise<HTMLElement>;
84
91
  /**
85
92
  * Removes the currently active renderer view from the DOM
86
93
  */
@@ -32,6 +32,11 @@ class PlattarController {
32
32
  _state = ControllerState.None;
33
33
  _element = null;
34
34
  _prevQROpt = null;
35
+ // when the renderer was booted from a supplied config-state attribute the
36
+ // state is decoded locally without the scene fetch — this keeps that live
37
+ // instance (mutated by the messenger observers as the user configures) so
38
+ // a later full decode for QR/AR reuses it instead of losing those changes
39
+ _bootConfigState = null;
35
40
  _selectVariationObserver = null;
36
41
  _selectVariationIDObserver = null;
37
42
  _selectVariationSKUObserver = null;
@@ -53,7 +58,7 @@ class PlattarController {
53
58
  // get a list of variation SKU's to use for initialising
54
59
  const variationSKUs = this.getAttribute("variation-sku");
55
60
  // generate the decoded configurator state
56
- const decodedState = configState ? await configurator_state_1.ConfiguratorState.decodeState(sceneID, configState) : await configurator_state_1.ConfiguratorState.decodeScene(sceneID);
61
+ const decodedState = configState ? await configurator_state_1.ConfiguratorState.decodeState(sceneID, configState, this._bootConfigState) : await configurator_state_1.ConfiguratorState.decodeScene(sceneID);
57
62
  // change the ID's and SKU's (if any) of the default configuration state
58
63
  const variationIDList = variationIDs ? variationIDs.split(",") : [];
59
64
  const variationSKUList = variationSKUs ? variationSKUs.split(",") : [];
@@ -76,7 +81,7 @@ class PlattarController {
76
81
  const args = cd.data[0];
77
82
  const variations = args ? (Array.isArray(args) ? args : [args]) : [];
78
83
  variations.forEach((variationID) => {
79
- configState.state.setVariationID(variationID);
84
+ configState.setVariationID(variationID);
80
85
  });
81
86
  }
82
87
  });
@@ -85,7 +90,7 @@ class PlattarController {
85
90
  const args = cd.data[0];
86
91
  const variations = args ? (Array.isArray(args) ? args : [args]) : [];
87
92
  variations.forEach((variationID) => {
88
- configState.state.setVariationID(variationID);
93
+ configState.setVariationID(variationID);
89
94
  });
90
95
  }
91
96
  });
@@ -94,7 +99,7 @@ class PlattarController {
94
99
  const args = cd.data[0];
95
100
  const variations = args ? (Array.isArray(args) ? args : [args]) : [];
96
101
  variations.forEach((variationSKU) => {
97
- configState.state.setVariationSKU(variationSKU);
102
+ configState.setVariationSKU(variationSKU);
98
103
  });
99
104
  }
100
105
  });
@@ -216,8 +221,9 @@ class PlattarController {
216
221
  }
217
222
  else {
218
223
  try {
219
- const sceneGraphID = await (await this.getConfiguratorState()).state.encodeSceneGraphID();
220
- dst += "&scene_graph_id=" + sceneGraphID;
224
+ const configState = await this.getConfiguratorState();
225
+ const encodedID = await configState.state.encodeSceneGraphID();
226
+ dst += "&scene_graph_id=" + encodedID;
221
227
  }
222
228
  catch (_err) {
223
229
  // scene graph ID not available for some reason
@@ -229,17 +235,29 @@ class PlattarController {
229
235
  this._prevQROpt = opt;
230
236
  if (!opt.detached) {
231
237
  this._state = ControllerState.QRCode;
232
- return new Promise((accept, reject) => {
233
- this.append(viewer);
234
- viewer.onload = () => {
235
- return accept(viewer);
236
- };
237
- });
238
+ this.append(viewer);
239
+ return this._awaitLoad(viewer);
238
240
  }
239
241
  return new Promise((accept, reject) => {
240
242
  return accept(viewer);
241
243
  });
242
244
  }
245
+ /**
246
+ * Appends the viewer to the shadow DOM and waits for its onload event.
247
+ * Rejects if onload does not fire within timeoutMs (default 30 s) so
248
+ * callers are never left with a permanently-pending Promise.
249
+ */
250
+ _awaitLoad(viewer, timeoutMs = 30000) {
251
+ return new Promise((accept, reject) => {
252
+ const timeout = setTimeout(() => {
253
+ reject(new Error("PlattarController._awaitLoad() - element did not fire onload within " + timeoutMs + "ms"));
254
+ }, timeoutMs);
255
+ viewer.onload = () => {
256
+ clearTimeout(timeout);
257
+ accept(viewer);
258
+ };
259
+ });
260
+ }
243
261
  /**
244
262
  * Removes the currently active renderer view from the DOM
245
263
  */
@@ -133,16 +133,10 @@ class VTOController extends plattar_controller_1.PlattarController {
133
133
  this._prevQROpt = opt;
134
134
  if (!opt.detached) {
135
135
  this._state = plattar_controller_1.ControllerState.QRCode;
136
- return new Promise((accept, reject) => {
137
- viewer.onload = () => {
138
- return accept(viewer);
139
- };
140
- this.append(viewer);
141
- });
136
+ this.append(viewer);
137
+ return this._awaitLoad(viewer);
142
138
  }
143
- return new Promise((accept, reject) => {
144
- return accept(viewer);
145
- });
139
+ return Promise.resolve(viewer);
146
140
  }
147
141
  async startRenderer() {
148
142
  // remove the old renderer instance if any
@@ -193,7 +187,7 @@ class VTOController extends plattar_controller_1.PlattarController {
193
187
  return new Promise((accept, reject) => {
194
188
  this.append(viewer);
195
189
  if (configState) {
196
- this.setupMessengerObservers(viewer, configState);
190
+ this.setupMessengerObservers(viewer, configState.state);
197
191
  }
198
192
  return accept(viewer);
199
193
  });
@@ -96,12 +96,8 @@ class WebXRController extends plattar_controller_1.PlattarController {
96
96
  viewer.setAttribute("url", opt.url || dst);
97
97
  this._state = plattar_controller_1.ControllerState.QRCode;
98
98
  this._prevQROpt = opt;
99
- return new Promise((accept, reject) => {
100
- viewer.onload = () => {
101
- return accept(viewer);
102
- };
103
- this.append(viewer);
104
- });
99
+ this.append(viewer);
100
+ return this._awaitLoad(viewer);
105
101
  }
106
102
  async startRenderer() {
107
103
  // remove the old renderer instance if any
@@ -131,10 +127,8 @@ class WebXRController extends plattar_controller_1.PlattarController {
131
127
  if (showUI) {
132
128
  viewer.setAttribute("show-ui", showUI);
133
129
  }
134
- return new Promise((accept, reject) => {
135
- this.append(viewer);
136
- return accept(viewer);
137
- });
130
+ this.append(viewer);
131
+ return Promise.resolve(viewer);
138
132
  }
139
133
  async initAR() {
140
134
  throw new Error("WebXRController.initAR() - cannot proceed as AR not available in webxr");
@@ -17,6 +17,12 @@ export default class PlattarEmbed extends HTMLElement {
17
17
  * Begin observing all changes to this DOM element
18
18
  */
19
19
  connectedCallback(): void;
20
+ /**
21
+ * Clean up when the element is removed from the DOM.
22
+ * Disconnects the MutationObserver and removes all messenger subscriptions
23
+ * to prevent memory leaks from dangling closures.
24
+ */
25
+ disconnectedCallback(): void;
20
26
  /**
21
27
  * creates a brand new instance of this embed
22
28
  */
@@ -53,6 +53,18 @@ class PlattarEmbed extends HTMLElement {
53
53
  connectedCallback() {
54
54
  this.create();
55
55
  }
56
+ /**
57
+ * Clean up when the element is removed from the DOM.
58
+ * Disconnects the MutationObserver and removes all messenger subscriptions
59
+ * to prevent memory leaks from dangling closures.
60
+ */
61
+ disconnectedCallback() {
62
+ if (this._observer) {
63
+ this._observer.disconnect();
64
+ this._observer = null;
65
+ }
66
+ this.destroy();
67
+ }
56
68
  /**
57
69
  * creates a brand new instance of this embed
58
70
  */
@@ -21,6 +21,10 @@ export declare class ConfiguratorState {
21
21
  private readonly _state;
22
22
  private readonly _mappedVariationIDValues;
23
23
  private readonly _mappedVariationSKUValues;
24
+ private _cachedSceneGraphID;
25
+ private _cachedSceneGraphJSON;
26
+ private _cachedSceneGraph;
27
+ private static readonly _sceneAPICache;
24
28
  constructor(state?: string | null | undefined);
25
29
  /**
26
30
  * Modifyes the SceneProduct that this Variation SKU belongs to and changes for
@@ -100,13 +104,21 @@ export declare class ConfiguratorState {
100
104
  * @returns - ConfiguratorState instance
101
105
  */
102
106
  static decode(state: string): ConfiguratorState;
107
+ /**
108
+ * Returns a cached Promise<Scene> for the given sceneID, firing at most one
109
+ * API request per sceneID per page lifetime. Both decodeState() and
110
+ * decodeScene() share this cache so they never race each other.
111
+ */
112
+ private static _fetchScene;
103
113
  /**
104
114
  * Decodes a previously generated state
105
115
  * @param sceneID
106
116
  * @param state
117
+ * @param existingState - (optional) reuse this already-decoded instance instead of
118
+ * parsing a fresh one, so mutations applied to it before this call are preserved
107
119
  * @returns
108
120
  */
109
- static decodeState(sceneID?: string | null | undefined, state?: string | null | undefined): Promise<DecodedConfiguratorState>;
121
+ static decodeState(sceneID?: string | null | undefined, state?: string | null | undefined, existingState?: ConfiguratorState | null | undefined): Promise<DecodedConfiguratorState>;
110
122
  /**
111
123
  * Generates a new ConfiguratorState instance from all SceneProducts and default
112
124
  * variations from the provided Scene ID