kitchen-simulator 3.3.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 +183 -76
  2. package/lib/index.js +183 -76
  3. package/package.json +1 -1
package/es/index.js CHANGED
@@ -14,6 +14,8 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
14
14
  import React from 'react';
15
15
  import LiteRenderer from "./LiteRenderer";
16
16
  import { createRoot } from 'react-dom/client';
17
+ var ROOT_KEY = '__kitchenSimulatorRoot__';
18
+ var API_KEY = '__kitchenSimulatorApi__';
17
19
  function nextFrame() {
18
20
  return new Promise(function (resolve) {
19
21
  return requestAnimationFrame(resolve);
@@ -164,6 +166,7 @@ function installGltfTracker() {
164
166
  }
165
167
  var subscribe = function subscribe(fn) {
166
168
  listeners.add(fn);
169
+ // IMPORTANT: emits synchronously
167
170
  fn(inFlight);
168
171
  return function () {
169
172
  return listeners["delete"](fn);
@@ -171,8 +174,8 @@ function installGltfTracker() {
171
174
  };
172
175
 
173
176
  /**
174
- * Wait until GLTF network is idle (inFlight===0).
175
- * 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.
176
179
  */
177
180
  var waitForIdle = /*#__PURE__*/function () {
178
181
  var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
@@ -187,7 +190,7 @@ function installGltfTracker() {
187
190
  while (1) switch (_context3.prev = _context3.next) {
188
191
  case 0:
189
192
  _ref3 = _args3.length > 0 && _args3[0] !== undefined ? _args3[0] : {}, _ref3$timeoutMs = _ref3.timeoutMs, timeoutMs = _ref3$timeoutMs === void 0 ? 30000 : _ref3$timeoutMs, _ref3$graceMs = _ref3.graceMs, graceMs = _ref3$graceMs === void 0 ? 50 : _ref3$graceMs;
190
- start = Date.now(); // Grace: give async pipelines a chance to start requests
193
+ start = Date.now();
191
194
  if (!(graceMs > 0)) {
192
195
  _context3.next = 1;
193
196
  break;
@@ -196,17 +199,19 @@ function installGltfTracker() {
196
199
  return sleep(graceMs);
197
200
  case 1:
198
201
  return _context3.abrupt("return", new Promise(function (resolve, reject) {
199
- var unsub = subscribe(function (count) {
202
+ var unsub = null;
203
+ var onChange = function onChange(count) {
200
204
  if (count === 0) {
201
- unsub();
205
+ if (unsub) unsub();
202
206
  resolve(true);
203
207
  return;
204
208
  }
205
209
  if (Date.now() - start > timeoutMs) {
206
- unsub();
210
+ if (unsub) unsub();
207
211
  reject(new Error('GLTF did not become idle within timeout'));
208
212
  }
209
- });
213
+ };
214
+ unsub = subscribe(onChange);
210
215
  }));
211
216
  case 2:
212
217
  case "end":
@@ -235,91 +240,168 @@ function installGltfTracker() {
235
240
  };
236
241
  }
237
242
  export function renderKitchenSimulator(container) {
238
- var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
243
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs, _props$syncGltfGraceM, _props$syncExtraFrame;
239
244
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
240
- var root = createRoot(container);
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
256
+ var root = container[ROOT_KEY];
257
+ if (!root) {
258
+ root = createRoot(container);
259
+ container[ROOT_KEY] = root;
260
+ }
241
261
  var setExternalEventFn = null;
242
- var queue = [];
243
- var pendingMarkers = new Set();
244
- var draining = false;
245
262
  var destroyed = false;
246
- var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
247
263
 
248
- // assume all events may load gltfs
249
- var WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT = (_props$waitForGltfIdl = props.waitForGltfIdleAfterEachEvent) !== null && _props$waitForGltfIdl !== void 0 ? _props$waitForGltfIdl : true;
250
- var GLTF_TIMEOUT_MS = (_props$gltfTimeoutMs = props.gltfTimeoutMs) !== null && _props$gltfTimeoutMs !== void 0 ? _props$gltfTimeoutMs : 30000;
251
- 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');
252
268
  var gltfTracker = installGltfTracker();
253
- function drain() {
254
- 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;
255
283
  }
256
- function _drain() {
257
- _drain = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
258
- 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;
259
291
  return _regeneratorRuntime.wrap(function (_context5) {
260
292
  while (1) switch (_context5.prev = _context5.next) {
261
293
  case 0:
262
- if (!(draining || destroyed)) {
263
- _context5.next = 1;
264
- break;
265
- }
266
- 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;
267
296
  case 1:
268
- draining = true;
269
- _context5.prev = 2;
270
- case 3:
271
- if (!(queue.length && !destroyed)) {
272
- _context5.next = 10;
297
+ if (!(i < frames)) {
298
+ _context5.next = 3;
273
299
  break;
274
300
  }
275
- ev = queue.shift(); // skip internal markers
276
- 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) {
277
309
  _context5.next = 4;
278
310
  break;
279
311
  }
280
- pendingMarkers["delete"](ev.__batchMarker);
281
- return _context5.abrupt("continue", 3);
312
+ return _context5.abrupt("return");
282
313
  case 4:
283
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
284
-
285
- // baseline spacing for react + legacy lifecycle
286
- i = 0;
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;
317
+ _context5.next = 5;
318
+ return gltfTracker.waitForIdle({
319
+ graceMs: graceMs,
320
+ timeoutMs: timeoutMs
321
+ });
287
322
  case 5:
288
- if (!(i < FRAMES_PER_EVENT)) {
289
- _context5.next = 7;
290
- break;
291
- }
292
323
  _context5.next = 6;
293
324
  return nextFrame();
294
325
  case 6:
295
- i += 1;
296
- _context5.next = 5;
297
- break;
326
+ if (!isSyncScene(ev)) {
327
+ _context5.next = 9;
328
+ break;
329
+ }
330
+ _i = 0;
298
331
  case 7:
299
- if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
332
+ if (!(_i < syncExtraFrames)) {
300
333
  _context5.next = 9;
301
334
  break;
302
335
  }
303
336
  _context5.next = 8;
304
- return gltfTracker.waitForIdle({
305
- timeoutMs: (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : GLTF_TIMEOUT_MS,
306
- graceMs: (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : GLTF_GRACE_MS
307
- });
308
- case 8:
309
- _context5.next = 9;
310
337
  return nextFrame();
338
+ case 8:
339
+ _i += 1;
340
+ _context5.next = 7;
341
+ break;
311
342
  case 9:
312
- _context5.next = 3;
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;
361
+ break;
362
+ }
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;
385
+ return nextFrame();
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;
313
395
  break;
314
- case 10:
315
- _context5.prev = 10;
396
+ case 8:
397
+ _context6.prev = 8;
316
398
  draining = false;
317
- return _context5.finish(10);
318
- case 11:
399
+ return _context6.finish(8);
400
+ case 9:
319
401
  case "end":
320
- return _context5.stop();
402
+ return _context6.stop();
321
403
  }
322
- }, _callee5, null, [[2,, 10, 11]]);
404
+ }, _callee6, null, [[2,, 8, 9]]);
323
405
  }));
324
406
  return _drain.apply(this, arguments);
325
407
  }
@@ -331,16 +413,27 @@ export function renderKitchenSimulator(container) {
331
413
  _this2.state = {
332
414
  externalEvent: p.externalEvent || null
333
415
  };
334
- setExternalEventFn = _this2.setExternalEvent.bind(_this2);
416
+ _this2._mounted = false;
335
417
  return _this2;
336
418
  }
337
419
  _inherits(Wrapper, _React$Component);
338
420
  return _createClass(Wrapper, [{
339
- key: "setExternalEvent",
340
- value: function setExternalEvent(newEvent) {
341
- this.setState({
342
- externalEvent: newEvent
343
- });
421
+ key: "componentDidMount",
422
+ value: function componentDidMount() {
423
+ var _this3 = this;
424
+ this._mounted = true;
425
+ setExternalEventFn = function setExternalEventFn(newEvent) {
426
+ if (!_this3._mounted) return;
427
+ _this3.setState({
428
+ externalEvent: newEvent
429
+ });
430
+ };
431
+ }
432
+ }, {
433
+ key: "componentWillUnmount",
434
+ value: function componentWillUnmount() {
435
+ this._mounted = false;
436
+ setExternalEventFn = null;
344
437
  }
345
438
  }, {
346
439
  key: "render",
@@ -351,8 +444,15 @@ export function renderKitchenSimulator(container) {
351
444
  }
352
445
  }]);
353
446
  }(React.Component);
354
- root.render(/*#__PURE__*/React.createElement(Wrapper, props));
355
- 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
+ */
356
456
  sendExternalEvents: function sendExternalEvents(eventOrEvents) {
357
457
  var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
358
458
  var _iterator2 = _createForOfIteratorHelper(events),
@@ -362,8 +462,6 @@ export function renderKitchenSimulator(container) {
362
462
  var e = _step2.value;
363
463
  queue.push(e);
364
464
  }
365
-
366
- // marker for this batch
367
465
  } catch (err) {
368
466
  _iterator2.e(err);
369
467
  } finally {
@@ -372,7 +470,8 @@ export function renderKitchenSimulator(container) {
372
470
  var token = Symbol('batch');
373
471
  pendingMarkers.add(token);
374
472
  queue.push({
375
- __batchMarker: token
473
+ __marker: MARKER,
474
+ token: token
376
475
  });
377
476
  drain();
378
477
  return new Promise(function (resolve) {
@@ -411,20 +510,28 @@ export function renderKitchenSimulator(container) {
411
510
  check();
412
511
  });
413
512
  },
414
- updateExternalEvent: function updateExternalEvent(newExternalEvent) {
415
- return this.sendExternalEvents(newExternalEvent);
513
+ updateExternalEvent: function updateExternalEvent(e) {
514
+ return api.sendExternalEvents(e);
416
515
  },
417
516
  clearQueue: function clearQueue() {
418
517
  queue.length = 0;
419
518
  pendingMarkers.clear();
420
519
  },
421
520
  unmount: function unmount() {
422
- var _this$clearQueue;
423
521
  destroyed = true;
424
- (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
522
+ api.clearQueue();
425
523
  gltfTracker.uninstall();
426
524
  root.unmount();
525
+ container[ROOT_KEY] = null;
526
+ container[API_KEY] = null;
427
527
  }
428
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;
429
536
  }
430
537
  export default renderKitchenSimulator;
package/lib/index.js CHANGED
@@ -22,6 +22,8 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
22
22
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
23
23
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
24
24
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
25
+ var ROOT_KEY = '__kitchenSimulatorRoot__';
26
+ var API_KEY = '__kitchenSimulatorApi__';
25
27
  function nextFrame() {
26
28
  return new Promise(function (resolve) {
27
29
  return requestAnimationFrame(resolve);
@@ -172,6 +174,7 @@ function installGltfTracker() {
172
174
  }
173
175
  var subscribe = function subscribe(fn) {
174
176
  listeners.add(fn);
177
+ // IMPORTANT: emits synchronously
175
178
  fn(inFlight);
176
179
  return function () {
177
180
  return listeners["delete"](fn);
@@ -179,8 +182,8 @@ function installGltfTracker() {
179
182
  };
180
183
 
181
184
  /**
182
- * Wait until GLTF network is idle (inFlight===0).
183
- * 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.
184
187
  */
185
188
  var waitForIdle = /*#__PURE__*/function () {
186
189
  var _ref2 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3() {
@@ -195,7 +198,7 @@ function installGltfTracker() {
195
198
  while (1) switch (_context3.prev = _context3.next) {
196
199
  case 0:
197
200
  _ref3 = _args3.length > 0 && _args3[0] !== undefined ? _args3[0] : {}, _ref3$timeoutMs = _ref3.timeoutMs, timeoutMs = _ref3$timeoutMs === void 0 ? 30000 : _ref3$timeoutMs, _ref3$graceMs = _ref3.graceMs, graceMs = _ref3$graceMs === void 0 ? 50 : _ref3$graceMs;
198
- start = Date.now(); // Grace: give async pipelines a chance to start requests
201
+ start = Date.now();
199
202
  if (!(graceMs > 0)) {
200
203
  _context3.next = 1;
201
204
  break;
@@ -204,17 +207,19 @@ function installGltfTracker() {
204
207
  return sleep(graceMs);
205
208
  case 1:
206
209
  return _context3.abrupt("return", new Promise(function (resolve, reject) {
207
- var unsub = subscribe(function (count) {
210
+ var unsub = null;
211
+ var onChange = function onChange(count) {
208
212
  if (count === 0) {
209
- unsub();
213
+ if (unsub) unsub();
210
214
  resolve(true);
211
215
  return;
212
216
  }
213
217
  if (Date.now() - start > timeoutMs) {
214
- unsub();
218
+ if (unsub) unsub();
215
219
  reject(new Error('GLTF did not become idle within timeout'));
216
220
  }
217
- });
221
+ };
222
+ unsub = subscribe(onChange);
218
223
  }));
219
224
  case 2:
220
225
  case "end":
@@ -243,91 +248,168 @@ function installGltfTracker() {
243
248
  };
244
249
  }
245
250
  function renderKitchenSimulator(container) {
246
- var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
251
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs, _props$syncGltfGraceM, _props$syncExtraFrame;
247
252
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
248
- var root = (0, _client.createRoot)(container);
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
264
+ var root = container[ROOT_KEY];
265
+ if (!root) {
266
+ root = (0, _client.createRoot)(container);
267
+ container[ROOT_KEY] = root;
268
+ }
249
269
  var setExternalEventFn = null;
250
- var queue = [];
251
- var pendingMarkers = new Set();
252
- var draining = false;
253
270
  var destroyed = false;
254
- var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
255
271
 
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;
272
+ // queue + marker handling
273
+ var queue = [];
274
+ var pendingMarkers = new Set();
275
+ var MARKER = Symbol('marker');
260
276
  var gltfTracker = installGltfTracker();
261
- function drain() {
262
- 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;
263
291
  }
264
- function _drain() {
265
- _drain = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5() {
266
- 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;
267
299
  return _regenerator["default"].wrap(function (_context5) {
268
300
  while (1) switch (_context5.prev = _context5.next) {
269
301
  case 0:
270
- if (!(draining || destroyed)) {
271
- _context5.next = 1;
272
- break;
273
- }
274
- 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;
275
304
  case 1:
276
- draining = true;
277
- _context5.prev = 2;
278
- case 3:
279
- if (!(queue.length && !destroyed)) {
280
- _context5.next = 10;
305
+ if (!(i < frames)) {
306
+ _context5.next = 3;
281
307
  break;
282
308
  }
283
- ev = queue.shift(); // skip internal markers
284
- 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) {
285
317
  _context5.next = 4;
286
318
  break;
287
319
  }
288
- pendingMarkers["delete"](ev.__batchMarker);
289
- return _context5.abrupt("continue", 3);
320
+ return _context5.abrupt("return");
290
321
  case 4:
291
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
292
-
293
- // baseline spacing for react + legacy lifecycle
294
- i = 0;
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;
325
+ _context5.next = 5;
326
+ return gltfTracker.waitForIdle({
327
+ graceMs: graceMs,
328
+ timeoutMs: timeoutMs
329
+ });
295
330
  case 5:
296
- if (!(i < FRAMES_PER_EVENT)) {
297
- _context5.next = 7;
298
- break;
299
- }
300
331
  _context5.next = 6;
301
332
  return nextFrame();
302
333
  case 6:
303
- i += 1;
304
- _context5.next = 5;
305
- break;
334
+ if (!isSyncScene(ev)) {
335
+ _context5.next = 9;
336
+ break;
337
+ }
338
+ _i = 0;
306
339
  case 7:
307
- if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
340
+ if (!(_i < syncExtraFrames)) {
308
341
  _context5.next = 9;
309
342
  break;
310
343
  }
311
344
  _context5.next = 8;
312
- return gltfTracker.waitForIdle({
313
- timeoutMs: (_ev$gltfTimeoutMs = ev === null || ev === void 0 ? void 0 : ev.gltfTimeoutMs) !== null && _ev$gltfTimeoutMs !== void 0 ? _ev$gltfTimeoutMs : GLTF_TIMEOUT_MS,
314
- graceMs: (_ev$gltfGraceMs = ev === null || ev === void 0 ? void 0 : ev.gltfGraceMs) !== null && _ev$gltfGraceMs !== void 0 ? _ev$gltfGraceMs : GLTF_GRACE_MS
315
- });
316
- case 8:
317
- _context5.next = 9;
318
345
  return nextFrame();
346
+ case 8:
347
+ _i += 1;
348
+ _context5.next = 7;
349
+ break;
319
350
  case 9:
320
- _context5.next = 3;
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;
369
+ break;
370
+ }
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;
393
+ return nextFrame();
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;
321
403
  break;
322
- case 10:
323
- _context5.prev = 10;
404
+ case 8:
405
+ _context6.prev = 8;
324
406
  draining = false;
325
- return _context5.finish(10);
326
- case 11:
407
+ return _context6.finish(8);
408
+ case 9:
327
409
  case "end":
328
- return _context5.stop();
410
+ return _context6.stop();
329
411
  }
330
- }, _callee5, null, [[2,, 10, 11]]);
412
+ }, _callee6, null, [[2,, 8, 9]]);
331
413
  }));
332
414
  return _drain.apply(this, arguments);
333
415
  }
@@ -339,16 +421,27 @@ function renderKitchenSimulator(container) {
339
421
  _this2.state = {
340
422
  externalEvent: p.externalEvent || null
341
423
  };
342
- setExternalEventFn = _this2.setExternalEvent.bind(_this2);
424
+ _this2._mounted = false;
343
425
  return _this2;
344
426
  }
345
427
  (0, _inherits2["default"])(Wrapper, _React$Component);
346
428
  return (0, _createClass2["default"])(Wrapper, [{
347
- key: "setExternalEvent",
348
- value: function setExternalEvent(newEvent) {
349
- this.setState({
350
- externalEvent: newEvent
351
- });
429
+ key: "componentDidMount",
430
+ value: function componentDidMount() {
431
+ var _this3 = this;
432
+ this._mounted = true;
433
+ setExternalEventFn = function setExternalEventFn(newEvent) {
434
+ if (!_this3._mounted) return;
435
+ _this3.setState({
436
+ externalEvent: newEvent
437
+ });
438
+ };
439
+ }
440
+ }, {
441
+ key: "componentWillUnmount",
442
+ value: function componentWillUnmount() {
443
+ this._mounted = false;
444
+ setExternalEventFn = null;
352
445
  }
353
446
  }, {
354
447
  key: "render",
@@ -359,8 +452,15 @@ function renderKitchenSimulator(container) {
359
452
  }
360
453
  }]);
361
454
  }(_react["default"].Component);
362
- root.render(/*#__PURE__*/_react["default"].createElement(Wrapper, props));
363
- 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
+ */
364
464
  sendExternalEvents: function sendExternalEvents(eventOrEvents) {
365
465
  var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
366
466
  var _iterator2 = _createForOfIteratorHelper(events),
@@ -370,8 +470,6 @@ function renderKitchenSimulator(container) {
370
470
  var e = _step2.value;
371
471
  queue.push(e);
372
472
  }
373
-
374
- // marker for this batch
375
473
  } catch (err) {
376
474
  _iterator2.e(err);
377
475
  } finally {
@@ -380,7 +478,8 @@ function renderKitchenSimulator(container) {
380
478
  var token = Symbol('batch');
381
479
  pendingMarkers.add(token);
382
480
  queue.push({
383
- __batchMarker: token
481
+ __marker: MARKER,
482
+ token: token
384
483
  });
385
484
  drain();
386
485
  return new Promise(function (resolve) {
@@ -419,20 +518,28 @@ function renderKitchenSimulator(container) {
419
518
  check();
420
519
  });
421
520
  },
422
- updateExternalEvent: function updateExternalEvent(newExternalEvent) {
423
- return this.sendExternalEvents(newExternalEvent);
521
+ updateExternalEvent: function updateExternalEvent(e) {
522
+ return api.sendExternalEvents(e);
424
523
  },
425
524
  clearQueue: function clearQueue() {
426
525
  queue.length = 0;
427
526
  pendingMarkers.clear();
428
527
  },
429
528
  unmount: function unmount() {
430
- var _this$clearQueue;
431
529
  destroyed = true;
432
- (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
530
+ api.clearQueue();
433
531
  gltfTracker.uninstall();
434
532
  root.unmount();
533
+ container[ROOT_KEY] = null;
534
+ container[API_KEY] = null;
435
535
  }
436
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;
437
544
  }
438
545
  var _default = exports["default"] = renderKitchenSimulator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitchen-simulator",
3
- "version": "3.3.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",