kitchen-simulator 3.4.0 → 3.5.0

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 (3) hide show
  1. package/es/index.js +147 -70
  2. package/lib/index.js +147 -70
  3. package/package.json +1 -1
package/es/index.js CHANGED
@@ -166,6 +166,7 @@ function installGltfTracker() {
166
166
  }
167
167
  var subscribe = function subscribe(fn) {
168
168
  listeners.add(fn);
169
+ // IMPORTANT: emits synchronously
169
170
  fn(inFlight);
170
171
  return function () {
171
172
  return listeners["delete"](fn);
@@ -173,8 +174,8 @@ function installGltfTracker() {
173
174
  };
174
175
 
175
176
  /**
176
- * Wait until GLTF network is idle (inFlight===0).
177
- * Includes a grace window to catch loads that start slightly after the event.
177
+ * Wait until inFlight becomes 0 (with grace window).
178
+ * Safe even when already idle.
178
179
  */
179
180
  var waitForIdle = /*#__PURE__*/function () {
180
181
  var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
@@ -201,7 +202,7 @@ function installGltfTracker() {
201
202
  var unsub = null;
202
203
  var onChange = function onChange(count) {
203
204
  if (count === 0) {
204
- if (unsub) unsub(); // ✅ safe
205
+ if (unsub) unsub();
205
206
  resolve(true);
206
207
  return;
207
208
  }
@@ -239,104 +240,168 @@ function installGltfTracker() {
239
240
  };
240
241
  }
241
242
  export function renderKitchenSimulator(container) {
242
- var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
243
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs, _props$syncGltfGraceM, _props$syncExtraFrame;
243
244
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
245
+ if (!container) throw new Error('renderKitchenSimulator: container is required');
246
+
247
+ // ✅ Reuse existing API for same container
248
+ if (container[API_KEY]) {
249
+ var _container$API_KEY$__, _container$API_KEY;
250
+ // update render with latest props (safe)
251
+ (_container$API_KEY$__ = (_container$API_KEY = container[API_KEY]).__render) === null || _container$API_KEY$__ === void 0 || _container$API_KEY$__.call(_container$API_KEY, props);
252
+ return container[API_KEY];
253
+ }
254
+
255
+ // ✅ Reuse root for same container
244
256
  var root = container[ROOT_KEY];
245
257
  if (!root) {
246
258
  root = createRoot(container);
247
259
  container[ROOT_KEY] = root;
248
260
  }
249
261
  var setExternalEventFn = null;
250
- var queue = [];
251
- var pendingMarkers = new Set();
252
- var draining = false;
253
262
  var destroyed = false;
254
- var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
255
263
 
256
- // assume all events may load gltfs
257
- var WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT = (_props$waitForGltfIdl = props.waitForGltfIdleAfterEachEvent) !== null && _props$waitForGltfIdl !== void 0 ? _props$waitForGltfIdl : true;
258
- var GLTF_TIMEOUT_MS = (_props$gltfTimeoutMs = props.gltfTimeoutMs) !== null && _props$gltfTimeoutMs !== void 0 ? _props$gltfTimeoutMs : 30000;
259
- var GLTF_GRACE_MS = (_props$gltfGraceMs = props.gltfGraceMs) !== null && _props$gltfGraceMs !== void 0 ? _props$gltfGraceMs : 50;
264
+ // queue + marker handling
265
+ var queue = [];
266
+ var pendingMarkers = new Set();
267
+ var MARKER = Symbol('marker');
260
268
  var gltfTracker = installGltfTracker();
261
- function drain() {
262
- return _drain.apply(this, arguments);
269
+
270
+ // defaults
271
+ var defaultFramesPerEvent = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
272
+ var waitForGltf = (_props$waitForGltfIdl = props.waitForGltfIdleAfterEachEvent) !== null && _props$waitForGltfIdl !== void 0 ? _props$waitForGltfIdl : true;
273
+ var defaultTimeout = (_props$gltfTimeoutMs = props.gltfTimeoutMs) !== null && _props$gltfTimeoutMs !== void 0 ? _props$gltfTimeoutMs : 30000;
274
+ var defaultGrace = (_props$gltfGraceMs = props.gltfGraceMs) !== null && _props$gltfGraceMs !== void 0 ? _props$gltfGraceMs : 50;
275
+ var syncGrace = (_props$syncGltfGraceM = props.syncGltfGraceMs) !== null && _props$syncGltfGraceM !== void 0 ? _props$syncGltfGraceM : 250; // SyncScene uses larger grace
276
+ var syncExtraFrames = (_props$syncExtraFrame = props.syncExtraFrames) !== null && _props$syncExtraFrame !== void 0 ? _props$syncExtraFrame : 2;
277
+ var draining = false;
278
+ function isSyncScene(ev) {
279
+ var _ev$payload;
280
+ return (ev === null || ev === void 0 ? void 0 : ev.type) === 'EXTERNAL_EVENT_SYNC_SCENE' || (ev === null || ev === void 0 || (_ev$payload = ev.payload) === null || _ev$payload === void 0 ? void 0 : _ev$payload.mode) === 'sync-scene' ||
281
+ // if you pass mode sometimes
282
+ (ev === null || ev === void 0 ? void 0 : ev.__sync) === true;
263
283
  }
264
- function _drain() {
265
- _drain = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
266
- var _setExternalEventFn, ev, i, _ev$gltfTimeoutMs, _ev$gltfGraceMs;
284
+ function settle(_x) {
285
+ return _settle.apply(this, arguments);
286
+ }
287
+ function _settle() {
288
+ _settle = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5(ev) {
289
+ var _ev$framesPerEvent, _ev$gltfGraceMs, _ev$gltfTimeoutMs;
290
+ var frames, i, graceMs, timeoutMs, _i;
267
291
  return _regeneratorRuntime.wrap(function (_context5) {
268
292
  while (1) switch (_context5.prev = _context5.next) {
269
293
  case 0:
270
- if (!(draining || destroyed)) {
271
- _context5.next = 1;
272
- break;
273
- }
274
- return _context5.abrupt("return");
294
+ frames = (_ev$framesPerEvent = ev === null || ev === void 0 ? void 0 : ev.framesPerEvent) !== null && _ev$framesPerEvent !== void 0 ? _ev$framesPerEvent : defaultFramesPerEvent; // 1) allow React + reducers to run
295
+ i = 0;
275
296
  case 1:
276
- draining = true;
277
- _context5.prev = 2;
278
- case 3:
279
- if (!(queue.length && !destroyed)) {
280
- _context5.next = 12;
297
+ if (!(i < frames)) {
298
+ _context5.next = 3;
281
299
  break;
282
300
  }
283
- ev = queue.shift(); // skip internal markers
284
- if (!(ev && ev.__batchMarker)) {
301
+ _context5.next = 2;
302
+ return nextFrame();
303
+ case 2:
304
+ i += 1;
305
+ _context5.next = 1;
306
+ break;
307
+ case 3:
308
+ if (waitForGltf) {
285
309
  _context5.next = 4;
286
310
  break;
287
311
  }
288
- pendingMarkers["delete"](ev.__batchMarker);
289
- return _context5.abrupt("continue", 3);
312
+ return _context5.abrupt("return");
290
313
  case 4:
291
- if (setExternalEventFn) {
292
- _context5.next = 6;
293
- break;
294
- }
314
+ // 2) wait for gltf idle
315
+ graceMs = isSyncScene(ev) ? syncGrace : (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : defaultGrace;
316
+ timeoutMs = (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : defaultTimeout;
295
317
  _context5.next = 5;
296
- return nextFrame();
318
+ return gltfTracker.waitForIdle({
319
+ graceMs: graceMs,
320
+ timeoutMs: timeoutMs
321
+ });
297
322
  case 5:
298
- return _context5.abrupt("continue", 3);
323
+ _context5.next = 6;
324
+ return nextFrame();
299
325
  case 6:
300
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
301
-
302
- // baseline spacing for react + legacy lifecycle
303
- i = 0;
326
+ if (!isSyncScene(ev)) {
327
+ _context5.next = 9;
328
+ break;
329
+ }
330
+ _i = 0;
304
331
  case 7:
305
- if (!(i < FRAMES_PER_EVENT)) {
332
+ if (!(_i < syncExtraFrames)) {
306
333
  _context5.next = 9;
307
334
  break;
308
335
  }
309
336
  _context5.next = 8;
310
337
  return nextFrame();
311
338
  case 8:
312
- i += 1;
339
+ _i += 1;
313
340
  _context5.next = 7;
314
341
  break;
315
342
  case 9:
316
- if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
317
- _context5.next = 11;
343
+ case "end":
344
+ return _context5.stop();
345
+ }
346
+ }, _callee5);
347
+ }));
348
+ return _settle.apply(this, arguments);
349
+ }
350
+ function drain() {
351
+ return _drain.apply(this, arguments);
352
+ }
353
+ function _drain() {
354
+ _drain = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee6() {
355
+ var item;
356
+ return _regeneratorRuntime.wrap(function (_context6) {
357
+ while (1) switch (_context6.prev = _context6.next) {
358
+ case 0:
359
+ if (!(draining || destroyed)) {
360
+ _context6.next = 1;
318
361
  break;
319
362
  }
320
- _context5.next = 10;
321
- return gltfTracker.waitForIdle({
322
- timeoutMs: (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : GLTF_TIMEOUT_MS,
323
- graceMs: (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : GLTF_GRACE_MS
324
- });
325
- case 10:
326
- _context5.next = 11;
363
+ return _context6.abrupt("return");
364
+ case 1:
365
+ draining = true;
366
+ _context6.prev = 2;
367
+ case 3:
368
+ if (!(queue.length && !destroyed)) {
369
+ _context6.next = 8;
370
+ break;
371
+ }
372
+ item = queue.shift(); // marker?
373
+ if (!(item && item.__marker === MARKER)) {
374
+ _context6.next = 4;
375
+ break;
376
+ }
377
+ pendingMarkers["delete"](item.token);
378
+ return _context6.abrupt("continue", 3);
379
+ case 4:
380
+ if (setExternalEventFn) {
381
+ _context6.next = 6;
382
+ break;
383
+ }
384
+ _context6.next = 5;
327
385
  return nextFrame();
328
- case 11:
329
- _context5.next = 3;
386
+ case 5:
387
+ queue.unshift(item); // put back and retry
388
+ return _context6.abrupt("continue", 3);
389
+ case 6:
390
+ setExternalEventFn(item);
391
+ _context6.next = 7;
392
+ return settle(item);
393
+ case 7:
394
+ _context6.next = 3;
330
395
  break;
331
- case 12:
332
- _context5.prev = 12;
396
+ case 8:
397
+ _context6.prev = 8;
333
398
  draining = false;
334
- return _context5.finish(12);
335
- case 13:
399
+ return _context6.finish(8);
400
+ case 9:
336
401
  case "end":
337
- return _context5.stop();
402
+ return _context6.stop();
338
403
  }
339
- }, _callee5, null, [[2,, 12, 13]]);
404
+ }, _callee6, null, [[2,, 8, 9]]);
340
405
  }));
341
406
  return _drain.apply(this, arguments);
342
407
  }
@@ -358,7 +423,6 @@ export function renderKitchenSimulator(container) {
358
423
  var _this3 = this;
359
424
  this._mounted = true;
360
425
  setExternalEventFn = function setExternalEventFn(newEvent) {
361
- // ✅ ensure we never setState during render
362
426
  if (!_this3._mounted) return;
363
427
  _this3.setState({
364
428
  externalEvent: newEvent
@@ -380,8 +444,15 @@ export function renderKitchenSimulator(container) {
380
444
  }
381
445
  }]);
382
446
  }(React.Component);
383
- root.render(/*#__PURE__*/React.createElement(Wrapper, props));
384
- return {
447
+ var api = {
448
+ // internal: rerender wrapper with latest props if host calls renderKitchenSimulator again
449
+ __render: function __render(nextProps) {
450
+ root.render(/*#__PURE__*/React.createElement(Wrapper, nextProps));
451
+ },
452
+ /**
453
+ * Send one or many events (in order).
454
+ * Resolves when this batch has been delivered + settled.
455
+ */
385
456
  sendExternalEvents: function sendExternalEvents(eventOrEvents) {
386
457
  var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
387
458
  var _iterator2 = _createForOfIteratorHelper(events),
@@ -391,8 +462,6 @@ export function renderKitchenSimulator(container) {
391
462
  var e = _step2.value;
392
463
  queue.push(e);
393
464
  }
394
-
395
- // marker for this batch
396
465
  } catch (err) {
397
466
  _iterator2.e(err);
398
467
  } finally {
@@ -401,7 +470,8 @@ export function renderKitchenSimulator(container) {
401
470
  var token = Symbol('batch');
402
471
  pendingMarkers.add(token);
403
472
  queue.push({
404
- __batchMarker: token
473
+ __marker: MARKER,
474
+ token: token
405
475
  });
406
476
  drain();
407
477
  return new Promise(function (resolve) {
@@ -440,21 +510,28 @@ export function renderKitchenSimulator(container) {
440
510
  check();
441
511
  });
442
512
  },
443
- updateExternalEvent: function updateExternalEvent(newExternalEvent) {
444
- return this.sendExternalEvents(newExternalEvent);
513
+ updateExternalEvent: function updateExternalEvent(e) {
514
+ return api.sendExternalEvents(e);
445
515
  },
446
516
  clearQueue: function clearQueue() {
447
517
  queue.length = 0;
448
518
  pendingMarkers.clear();
449
519
  },
450
520
  unmount: function unmount() {
451
- var _this$clearQueue;
452
521
  destroyed = true;
453
- (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
522
+ api.clearQueue();
454
523
  gltfTracker.uninstall();
455
524
  root.unmount();
456
525
  container[ROOT_KEY] = null;
526
+ container[API_KEY] = null;
457
527
  }
458
528
  };
529
+
530
+ // first render
531
+ api.__render(props);
532
+
533
+ // store api on container so repeated calls reuse it
534
+ container[API_KEY] = api;
535
+ return api;
459
536
  }
460
537
  export default renderKitchenSimulator;
package/lib/index.js CHANGED
@@ -174,6 +174,7 @@ function installGltfTracker() {
174
174
  }
175
175
  var subscribe = function subscribe(fn) {
176
176
  listeners.add(fn);
177
+ // IMPORTANT: emits synchronously
177
178
  fn(inFlight);
178
179
  return function () {
179
180
  return listeners["delete"](fn);
@@ -181,8 +182,8 @@ function installGltfTracker() {
181
182
  };
182
183
 
183
184
  /**
184
- * Wait until GLTF network is idle (inFlight===0).
185
- * Includes a grace window to catch loads that start slightly after the event.
185
+ * Wait until inFlight becomes 0 (with grace window).
186
+ * Safe even when already idle.
186
187
  */
187
188
  var waitForIdle = /*#__PURE__*/function () {
188
189
  var _ref2 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3() {
@@ -209,7 +210,7 @@ function installGltfTracker() {
209
210
  var unsub = null;
210
211
  var onChange = function onChange(count) {
211
212
  if (count === 0) {
212
- if (unsub) unsub(); // ✅ safe
213
+ if (unsub) unsub();
213
214
  resolve(true);
214
215
  return;
215
216
  }
@@ -247,104 +248,168 @@ function installGltfTracker() {
247
248
  };
248
249
  }
249
250
  function renderKitchenSimulator(container) {
250
- var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
251
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs, _props$syncGltfGraceM, _props$syncExtraFrame;
251
252
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
253
+ if (!container) throw new Error('renderKitchenSimulator: container is required');
254
+
255
+ // ✅ Reuse existing API for same container
256
+ if (container[API_KEY]) {
257
+ var _container$API_KEY$__, _container$API_KEY;
258
+ // update render with latest props (safe)
259
+ (_container$API_KEY$__ = (_container$API_KEY = container[API_KEY]).__render) === null || _container$API_KEY$__ === void 0 || _container$API_KEY$__.call(_container$API_KEY, props);
260
+ return container[API_KEY];
261
+ }
262
+
263
+ // ✅ Reuse root for same container
252
264
  var root = container[ROOT_KEY];
253
265
  if (!root) {
254
266
  root = (0, _client.createRoot)(container);
255
267
  container[ROOT_KEY] = root;
256
268
  }
257
269
  var setExternalEventFn = null;
258
- var queue = [];
259
- var pendingMarkers = new Set();
260
- var draining = false;
261
270
  var destroyed = false;
262
- var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
263
271
 
264
- // assume all events may load gltfs
265
- var WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT = (_props$waitForGltfIdl = props.waitForGltfIdleAfterEachEvent) !== null && _props$waitForGltfIdl !== void 0 ? _props$waitForGltfIdl : true;
266
- var GLTF_TIMEOUT_MS = (_props$gltfTimeoutMs = props.gltfTimeoutMs) !== null && _props$gltfTimeoutMs !== void 0 ? _props$gltfTimeoutMs : 30000;
267
- var GLTF_GRACE_MS = (_props$gltfGraceMs = props.gltfGraceMs) !== null && _props$gltfGraceMs !== void 0 ? _props$gltfGraceMs : 50;
272
+ // queue + marker handling
273
+ var queue = [];
274
+ var pendingMarkers = new Set();
275
+ var MARKER = Symbol('marker');
268
276
  var gltfTracker = installGltfTracker();
269
- function drain() {
270
- return _drain.apply(this, arguments);
277
+
278
+ // defaults
279
+ var defaultFramesPerEvent = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
280
+ var waitForGltf = (_props$waitForGltfIdl = props.waitForGltfIdleAfterEachEvent) !== null && _props$waitForGltfIdl !== void 0 ? _props$waitForGltfIdl : true;
281
+ var defaultTimeout = (_props$gltfTimeoutMs = props.gltfTimeoutMs) !== null && _props$gltfTimeoutMs !== void 0 ? _props$gltfTimeoutMs : 30000;
282
+ var defaultGrace = (_props$gltfGraceMs = props.gltfGraceMs) !== null && _props$gltfGraceMs !== void 0 ? _props$gltfGraceMs : 50;
283
+ var syncGrace = (_props$syncGltfGraceM = props.syncGltfGraceMs) !== null && _props$syncGltfGraceM !== void 0 ? _props$syncGltfGraceM : 250; // SyncScene uses larger grace
284
+ var syncExtraFrames = (_props$syncExtraFrame = props.syncExtraFrames) !== null && _props$syncExtraFrame !== void 0 ? _props$syncExtraFrame : 2;
285
+ var draining = false;
286
+ function isSyncScene(ev) {
287
+ var _ev$payload;
288
+ return (ev === null || ev === void 0 ? void 0 : ev.type) === 'EXTERNAL_EVENT_SYNC_SCENE' || (ev === null || ev === void 0 || (_ev$payload = ev.payload) === null || _ev$payload === void 0 ? void 0 : _ev$payload.mode) === 'sync-scene' ||
289
+ // if you pass mode sometimes
290
+ (ev === null || ev === void 0 ? void 0 : ev.__sync) === true;
271
291
  }
272
- function _drain() {
273
- _drain = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5() {
274
- var _setExternalEventFn, ev, i, _ev$gltfTimeoutMs, _ev$gltfGraceMs;
292
+ function settle(_x) {
293
+ return _settle.apply(this, arguments);
294
+ }
295
+ function _settle() {
296
+ _settle = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5(ev) {
297
+ var _ev$framesPerEvent, _ev$gltfGraceMs, _ev$gltfTimeoutMs;
298
+ var frames, i, graceMs, timeoutMs, _i;
275
299
  return _regenerator["default"].wrap(function (_context5) {
276
300
  while (1) switch (_context5.prev = _context5.next) {
277
301
  case 0:
278
- if (!(draining || destroyed)) {
279
- _context5.next = 1;
280
- break;
281
- }
282
- return _context5.abrupt("return");
302
+ frames = (_ev$framesPerEvent = ev === null || ev === void 0 ? void 0 : ev.framesPerEvent) !== null && _ev$framesPerEvent !== void 0 ? _ev$framesPerEvent : defaultFramesPerEvent; // 1) allow React + reducers to run
303
+ i = 0;
283
304
  case 1:
284
- draining = true;
285
- _context5.prev = 2;
286
- case 3:
287
- if (!(queue.length && !destroyed)) {
288
- _context5.next = 12;
305
+ if (!(i < frames)) {
306
+ _context5.next = 3;
289
307
  break;
290
308
  }
291
- ev = queue.shift(); // skip internal markers
292
- if (!(ev && ev.__batchMarker)) {
309
+ _context5.next = 2;
310
+ return nextFrame();
311
+ case 2:
312
+ i += 1;
313
+ _context5.next = 1;
314
+ break;
315
+ case 3:
316
+ if (waitForGltf) {
293
317
  _context5.next = 4;
294
318
  break;
295
319
  }
296
- pendingMarkers["delete"](ev.__batchMarker);
297
- return _context5.abrupt("continue", 3);
320
+ return _context5.abrupt("return");
298
321
  case 4:
299
- if (setExternalEventFn) {
300
- _context5.next = 6;
301
- break;
302
- }
322
+ // 2) wait for gltf idle
323
+ graceMs = isSyncScene(ev) ? syncGrace : (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : defaultGrace;
324
+ timeoutMs = (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : defaultTimeout;
303
325
  _context5.next = 5;
304
- return nextFrame();
326
+ return gltfTracker.waitForIdle({
327
+ graceMs: graceMs,
328
+ timeoutMs: timeoutMs
329
+ });
305
330
  case 5:
306
- return _context5.abrupt("continue", 3);
331
+ _context5.next = 6;
332
+ return nextFrame();
307
333
  case 6:
308
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
309
-
310
- // baseline spacing for react + legacy lifecycle
311
- i = 0;
334
+ if (!isSyncScene(ev)) {
335
+ _context5.next = 9;
336
+ break;
337
+ }
338
+ _i = 0;
312
339
  case 7:
313
- if (!(i < FRAMES_PER_EVENT)) {
340
+ if (!(_i < syncExtraFrames)) {
314
341
  _context5.next = 9;
315
342
  break;
316
343
  }
317
344
  _context5.next = 8;
318
345
  return nextFrame();
319
346
  case 8:
320
- i += 1;
347
+ _i += 1;
321
348
  _context5.next = 7;
322
349
  break;
323
350
  case 9:
324
- if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
325
- _context5.next = 11;
351
+ case "end":
352
+ return _context5.stop();
353
+ }
354
+ }, _callee5);
355
+ }));
356
+ return _settle.apply(this, arguments);
357
+ }
358
+ function drain() {
359
+ return _drain.apply(this, arguments);
360
+ }
361
+ function _drain() {
362
+ _drain = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee6() {
363
+ var item;
364
+ return _regenerator["default"].wrap(function (_context6) {
365
+ while (1) switch (_context6.prev = _context6.next) {
366
+ case 0:
367
+ if (!(draining || destroyed)) {
368
+ _context6.next = 1;
326
369
  break;
327
370
  }
328
- _context5.next = 10;
329
- return gltfTracker.waitForIdle({
330
- timeoutMs: (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : GLTF_TIMEOUT_MS,
331
- graceMs: (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : GLTF_GRACE_MS
332
- });
333
- case 10:
334
- _context5.next = 11;
371
+ return _context6.abrupt("return");
372
+ case 1:
373
+ draining = true;
374
+ _context6.prev = 2;
375
+ case 3:
376
+ if (!(queue.length && !destroyed)) {
377
+ _context6.next = 8;
378
+ break;
379
+ }
380
+ item = queue.shift(); // marker?
381
+ if (!(item && item.__marker === MARKER)) {
382
+ _context6.next = 4;
383
+ break;
384
+ }
385
+ pendingMarkers["delete"](item.token);
386
+ return _context6.abrupt("continue", 3);
387
+ case 4:
388
+ if (setExternalEventFn) {
389
+ _context6.next = 6;
390
+ break;
391
+ }
392
+ _context6.next = 5;
335
393
  return nextFrame();
336
- case 11:
337
- _context5.next = 3;
394
+ case 5:
395
+ queue.unshift(item); // put back and retry
396
+ return _context6.abrupt("continue", 3);
397
+ case 6:
398
+ setExternalEventFn(item);
399
+ _context6.next = 7;
400
+ return settle(item);
401
+ case 7:
402
+ _context6.next = 3;
338
403
  break;
339
- case 12:
340
- _context5.prev = 12;
404
+ case 8:
405
+ _context6.prev = 8;
341
406
  draining = false;
342
- return _context5.finish(12);
343
- case 13:
407
+ return _context6.finish(8);
408
+ case 9:
344
409
  case "end":
345
- return _context5.stop();
410
+ return _context6.stop();
346
411
  }
347
- }, _callee5, null, [[2,, 12, 13]]);
412
+ }, _callee6, null, [[2,, 8, 9]]);
348
413
  }));
349
414
  return _drain.apply(this, arguments);
350
415
  }
@@ -366,7 +431,6 @@ function renderKitchenSimulator(container) {
366
431
  var _this3 = this;
367
432
  this._mounted = true;
368
433
  setExternalEventFn = function setExternalEventFn(newEvent) {
369
- // ✅ ensure we never setState during render
370
434
  if (!_this3._mounted) return;
371
435
  _this3.setState({
372
436
  externalEvent: newEvent
@@ -388,8 +452,15 @@ function renderKitchenSimulator(container) {
388
452
  }
389
453
  }]);
390
454
  }(_react["default"].Component);
391
- root.render(/*#__PURE__*/_react["default"].createElement(Wrapper, props));
392
- return {
455
+ var api = {
456
+ // internal: rerender wrapper with latest props if host calls renderKitchenSimulator again
457
+ __render: function __render(nextProps) {
458
+ root.render(/*#__PURE__*/_react["default"].createElement(Wrapper, nextProps));
459
+ },
460
+ /**
461
+ * Send one or many events (in order).
462
+ * Resolves when this batch has been delivered + settled.
463
+ */
393
464
  sendExternalEvents: function sendExternalEvents(eventOrEvents) {
394
465
  var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
395
466
  var _iterator2 = _createForOfIteratorHelper(events),
@@ -399,8 +470,6 @@ function renderKitchenSimulator(container) {
399
470
  var e = _step2.value;
400
471
  queue.push(e);
401
472
  }
402
-
403
- // marker for this batch
404
473
  } catch (err) {
405
474
  _iterator2.e(err);
406
475
  } finally {
@@ -409,7 +478,8 @@ function renderKitchenSimulator(container) {
409
478
  var token = Symbol('batch');
410
479
  pendingMarkers.add(token);
411
480
  queue.push({
412
- __batchMarker: token
481
+ __marker: MARKER,
482
+ token: token
413
483
  });
414
484
  drain();
415
485
  return new Promise(function (resolve) {
@@ -448,21 +518,28 @@ function renderKitchenSimulator(container) {
448
518
  check();
449
519
  });
450
520
  },
451
- updateExternalEvent: function updateExternalEvent(newExternalEvent) {
452
- return this.sendExternalEvents(newExternalEvent);
521
+ updateExternalEvent: function updateExternalEvent(e) {
522
+ return api.sendExternalEvents(e);
453
523
  },
454
524
  clearQueue: function clearQueue() {
455
525
  queue.length = 0;
456
526
  pendingMarkers.clear();
457
527
  },
458
528
  unmount: function unmount() {
459
- var _this$clearQueue;
460
529
  destroyed = true;
461
- (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
530
+ api.clearQueue();
462
531
  gltfTracker.uninstall();
463
532
  root.unmount();
464
533
  container[ROOT_KEY] = null;
534
+ container[API_KEY] = null;
465
535
  }
466
536
  };
537
+
538
+ // first render
539
+ api.__render(props);
540
+
541
+ // store api on container so repeated calls reuse it
542
+ container[API_KEY] = api;
543
+ return api;
467
544
  }
468
545
  var _default = exports["default"] = renderKitchenSimulator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitchen-simulator",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "It is a kitchen simulator (self-contained micro-frontend).",
5
5
  "license": "MIT",
6
6
  "main": "lib/index.js",