kitchen-simulator 3.2.0 → 3.4.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 +421 -17
  2. package/lib/index.js +421 -17
  3. package/package.json +1 -1
package/es/index.js CHANGED
@@ -4,34 +4,372 @@ import _createClass from "@babel/runtime/helpers/esm/createClass";
4
4
  import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn";
5
5
  import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf";
6
6
  import _inherits from "@babel/runtime/helpers/esm/inherits";
7
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
7
8
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
8
9
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
10
+ 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; } } }; }
11
+ 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; } }
12
+ 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; }
13
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
9
14
  import React from 'react';
10
- import ReactDOM from 'react-dom';
11
15
  import LiteRenderer from "./LiteRenderer";
16
+ import { createRoot } from 'react-dom/client';
17
+ var ROOT_KEY = '__kitchenSimulatorRoot__';
18
+ var API_KEY = '__kitchenSimulatorApi__';
19
+ function nextFrame() {
20
+ return new Promise(function (resolve) {
21
+ return requestAnimationFrame(resolve);
22
+ });
23
+ }
24
+ function sleep(ms) {
25
+ return new Promise(function (resolve) {
26
+ return setTimeout(resolve, ms);
27
+ });
28
+ }
29
+
30
+ /**
31
+ * Track GLTF/GLB network activity via XHR + fetch.
32
+ * Returns { getInFlight, waitForIdle, uninstall }.
33
+ */
34
+ function installGltfTracker() {
35
+ var _XHR$prototype, _XHR$prototype2;
36
+ if (typeof window === 'undefined') {
37
+ return {
38
+ getInFlight: function getInFlight() {
39
+ return 0;
40
+ },
41
+ waitForIdle: function () {
42
+ var _waitForIdle = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
43
+ return _regeneratorRuntime.wrap(function (_context) {
44
+ while (1) switch (_context.prev = _context.next) {
45
+ case 0:
46
+ return _context.abrupt("return", true);
47
+ case 1:
48
+ case "end":
49
+ return _context.stop();
50
+ }
51
+ }, _callee);
52
+ }));
53
+ function waitForIdle() {
54
+ return _waitForIdle.apply(this, arguments);
55
+ }
56
+ return waitForIdle;
57
+ }(),
58
+ uninstall: function uninstall() {}
59
+ };
60
+ }
61
+ var inFlight = 0;
62
+ var listeners = new Set();
63
+ var notify = function notify() {
64
+ var _iterator = _createForOfIteratorHelper(listeners),
65
+ _step;
66
+ try {
67
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
68
+ var fn = _step.value;
69
+ fn(inFlight);
70
+ }
71
+ } catch (err) {
72
+ _iterator.e(err);
73
+ } finally {
74
+ _iterator.f();
75
+ }
76
+ };
77
+ var isGltfUrl = function isGltfUrl(url) {
78
+ try {
79
+ var _url$toString, _url$toString2;
80
+ var s = typeof url === 'string' ? url : (_url$toString = url === null || url === void 0 || (_url$toString2 = url.toString) === null || _url$toString2 === void 0 ? void 0 : _url$toString2.call(url)) !== null && _url$toString !== void 0 ? _url$toString : '';
81
+ var u = s.toLowerCase();
82
+ return u.includes('.gltf') || u.includes('.glb');
83
+ } catch (_unused) {
84
+ return false;
85
+ }
86
+ };
87
+
88
+ // ---- XHR hook ----
89
+ var XHR = window.XMLHttpRequest;
90
+ var originalOpen = null;
91
+ var originalSend = null;
92
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype = XHR.prototype) !== null && _XHR$prototype !== void 0 && _XHR$prototype.open && XHR !== null && XHR !== void 0 && (_XHR$prototype2 = XHR.prototype) !== null && _XHR$prototype2 !== void 0 && _XHR$prototype2.send) {
93
+ originalOpen = XHR.prototype.open;
94
+ originalSend = XHR.prototype.send;
95
+ XHR.prototype.open = function (method, url) {
96
+ var _originalOpen;
97
+ this.__isGltfRequest = isGltfUrl(url);
98
+ for (var _len = arguments.length, rest = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
99
+ rest[_key - 2] = arguments[_key];
100
+ }
101
+ return (_originalOpen = originalOpen).call.apply(_originalOpen, [this, method, url].concat(rest));
102
+ };
103
+ XHR.prototype.send = function () {
104
+ var _this = this;
105
+ if (this.__isGltfRequest) {
106
+ inFlight += 1;
107
+ notify();
108
+ var _done = function done() {
109
+ inFlight -= 1;
110
+ if (inFlight < 0) inFlight = 0;
111
+ notify();
112
+ _this.removeEventListener('loadend', _done);
113
+ };
114
+ this.addEventListener('loadend', _done);
115
+ }
116
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
117
+ args[_key2] = arguments[_key2];
118
+ }
119
+ return originalSend.apply(this, args);
120
+ };
121
+ }
122
+
123
+ // ---- fetch hook ----
124
+ var originalFetch = window.fetch;
125
+ if (typeof originalFetch === 'function') {
126
+ window.fetch = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
127
+ var _len3,
128
+ args,
129
+ _key3,
130
+ url,
131
+ track,
132
+ _args2 = arguments;
133
+ return _regeneratorRuntime.wrap(function (_context2) {
134
+ while (1) switch (_context2.prev = _context2.next) {
135
+ case 0:
136
+ for (_len3 = _args2.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
137
+ args[_key3] = _args2[_key3];
138
+ }
139
+ url = args === null || args === void 0 ? void 0 : args[0];
140
+ track = isGltfUrl(url);
141
+ if (track) {
142
+ _context2.next = 1;
143
+ break;
144
+ }
145
+ return _context2.abrupt("return", originalFetch.apply(this, args));
146
+ case 1:
147
+ inFlight += 1;
148
+ notify();
149
+ _context2.prev = 2;
150
+ _context2.next = 3;
151
+ return originalFetch.apply(this, args);
152
+ case 3:
153
+ return _context2.abrupt("return", _context2.sent);
154
+ case 4:
155
+ _context2.prev = 4;
156
+ inFlight -= 1;
157
+ if (inFlight < 0) inFlight = 0;
158
+ notify();
159
+ return _context2.finish(4);
160
+ case 5:
161
+ case "end":
162
+ return _context2.stop();
163
+ }
164
+ }, _callee2, this, [[2,, 4, 5]]);
165
+ }));
166
+ }
167
+ var subscribe = function subscribe(fn) {
168
+ listeners.add(fn);
169
+ fn(inFlight);
170
+ return function () {
171
+ return listeners["delete"](fn);
172
+ };
173
+ };
174
+
175
+ /**
176
+ * Wait until GLTF network is idle (inFlight===0).
177
+ * Includes a grace window to catch loads that start slightly after the event.
178
+ */
179
+ var waitForIdle = /*#__PURE__*/function () {
180
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
181
+ var _ref3,
182
+ _ref3$timeoutMs,
183
+ timeoutMs,
184
+ _ref3$graceMs,
185
+ graceMs,
186
+ start,
187
+ _args3 = arguments;
188
+ return _regeneratorRuntime.wrap(function (_context3) {
189
+ while (1) switch (_context3.prev = _context3.next) {
190
+ case 0:
191
+ _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;
192
+ start = Date.now();
193
+ if (!(graceMs > 0)) {
194
+ _context3.next = 1;
195
+ break;
196
+ }
197
+ _context3.next = 1;
198
+ return sleep(graceMs);
199
+ case 1:
200
+ return _context3.abrupt("return", new Promise(function (resolve, reject) {
201
+ var unsub = null;
202
+ var onChange = function onChange(count) {
203
+ if (count === 0) {
204
+ if (unsub) unsub(); // ✅ safe
205
+ resolve(true);
206
+ return;
207
+ }
208
+ if (Date.now() - start > timeoutMs) {
209
+ if (unsub) unsub();
210
+ reject(new Error('GLTF did not become idle within timeout'));
211
+ }
212
+ };
213
+ unsub = subscribe(onChange);
214
+ }));
215
+ case 2:
216
+ case "end":
217
+ return _context3.stop();
218
+ }
219
+ }, _callee3);
220
+ }));
221
+ return function waitForIdle() {
222
+ return _ref2.apply(this, arguments);
223
+ };
224
+ }();
225
+ var uninstall = function uninstall() {
226
+ var _XHR$prototype3, _XHR$prototype4;
227
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype3 = XHR.prototype) !== null && _XHR$prototype3 !== void 0 && _XHR$prototype3.open && originalOpen) XHR.prototype.open = originalOpen;
228
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype4 = XHR.prototype) !== null && _XHR$prototype4 !== void 0 && _XHR$prototype4.send && originalSend) XHR.prototype.send = originalSend;
229
+ if (typeof originalFetch === 'function') window.fetch = originalFetch;
230
+ listeners.clear();
231
+ inFlight = 0;
232
+ };
233
+ return {
234
+ getInFlight: function getInFlight() {
235
+ return inFlight;
236
+ },
237
+ waitForIdle: waitForIdle,
238
+ uninstall: uninstall
239
+ };
240
+ }
12
241
  export function renderKitchenSimulator(container) {
242
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
13
243
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
244
+ var root = container[ROOT_KEY];
245
+ if (!root) {
246
+ root = createRoot(container);
247
+ container[ROOT_KEY] = root;
248
+ }
14
249
  var setExternalEventFn = null;
250
+ var queue = [];
251
+ var pendingMarkers = new Set();
252
+ var draining = false;
253
+ var destroyed = false;
254
+ var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
15
255
 
16
- // Internal wrapper that exposes a state setter
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;
260
+ var gltfTracker = installGltfTracker();
261
+ function drain() {
262
+ return _drain.apply(this, arguments);
263
+ }
264
+ function _drain() {
265
+ _drain = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee5() {
266
+ var _setExternalEventFn, ev, i, _ev$gltfTimeoutMs, _ev$gltfGraceMs;
267
+ return _regeneratorRuntime.wrap(function (_context5) {
268
+ while (1) switch (_context5.prev = _context5.next) {
269
+ case 0:
270
+ if (!(draining || destroyed)) {
271
+ _context5.next = 1;
272
+ break;
273
+ }
274
+ return _context5.abrupt("return");
275
+ case 1:
276
+ draining = true;
277
+ _context5.prev = 2;
278
+ case 3:
279
+ if (!(queue.length && !destroyed)) {
280
+ _context5.next = 12;
281
+ break;
282
+ }
283
+ ev = queue.shift(); // skip internal markers
284
+ if (!(ev && ev.__batchMarker)) {
285
+ _context5.next = 4;
286
+ break;
287
+ }
288
+ pendingMarkers["delete"](ev.__batchMarker);
289
+ return _context5.abrupt("continue", 3);
290
+ case 4:
291
+ if (setExternalEventFn) {
292
+ _context5.next = 6;
293
+ break;
294
+ }
295
+ _context5.next = 5;
296
+ return nextFrame();
297
+ case 5:
298
+ return _context5.abrupt("continue", 3);
299
+ case 6:
300
+ (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
301
+
302
+ // baseline spacing for react + legacy lifecycle
303
+ i = 0;
304
+ case 7:
305
+ if (!(i < FRAMES_PER_EVENT)) {
306
+ _context5.next = 9;
307
+ break;
308
+ }
309
+ _context5.next = 8;
310
+ return nextFrame();
311
+ case 8:
312
+ i += 1;
313
+ _context5.next = 7;
314
+ break;
315
+ case 9:
316
+ if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
317
+ _context5.next = 11;
318
+ break;
319
+ }
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;
327
+ return nextFrame();
328
+ case 11:
329
+ _context5.next = 3;
330
+ break;
331
+ case 12:
332
+ _context5.prev = 12;
333
+ draining = false;
334
+ return _context5.finish(12);
335
+ case 13:
336
+ case "end":
337
+ return _context5.stop();
338
+ }
339
+ }, _callee5, null, [[2,, 12, 13]]);
340
+ }));
341
+ return _drain.apply(this, arguments);
342
+ }
17
343
  var Wrapper = /*#__PURE__*/function (_React$Component) {
18
344
  function Wrapper(p) {
19
- var _this;
345
+ var _this2;
20
346
  _classCallCheck(this, Wrapper);
21
- _this = _callSuper(this, Wrapper, [p]);
22
- _this.state = {
347
+ _this2 = _callSuper(this, Wrapper, [p]);
348
+ _this2.state = {
23
349
  externalEvent: p.externalEvent || null
24
350
  };
25
- setExternalEventFn = _this.setExternalEvent.bind(_this);
26
- return _this;
351
+ _this2._mounted = false;
352
+ return _this2;
27
353
  }
28
354
  _inherits(Wrapper, _React$Component);
29
355
  return _createClass(Wrapper, [{
30
- key: "setExternalEvent",
31
- value: function setExternalEvent(newEvent) {
32
- this.setState({
33
- externalEvent: newEvent
34
- });
356
+ key: "componentDidMount",
357
+ value: function componentDidMount() {
358
+ var _this3 = this;
359
+ this._mounted = true;
360
+ setExternalEventFn = function setExternalEventFn(newEvent) {
361
+ // ✅ ensure we never setState during render
362
+ if (!_this3._mounted) return;
363
+ _this3.setState({
364
+ externalEvent: newEvent
365
+ });
366
+ };
367
+ }
368
+ }, {
369
+ key: "componentWillUnmount",
370
+ value: function componentWillUnmount() {
371
+ this._mounted = false;
372
+ setExternalEventFn = null;
35
373
  }
36
374
  }, {
37
375
  key: "render",
@@ -41,15 +379,81 @@ export function renderKitchenSimulator(container) {
41
379
  }));
42
380
  }
43
381
  }]);
44
- }(React.Component); // Mount once
45
- ReactDOM.render(/*#__PURE__*/React.createElement(Wrapper, props), container);
382
+ }(React.Component);
383
+ root.render(/*#__PURE__*/React.createElement(Wrapper, props));
46
384
  return {
385
+ sendExternalEvents: function sendExternalEvents(eventOrEvents) {
386
+ var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
387
+ var _iterator2 = _createForOfIteratorHelper(events),
388
+ _step2;
389
+ try {
390
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
391
+ var e = _step2.value;
392
+ queue.push(e);
393
+ }
394
+
395
+ // marker for this batch
396
+ } catch (err) {
397
+ _iterator2.e(err);
398
+ } finally {
399
+ _iterator2.f();
400
+ }
401
+ var token = Symbol('batch');
402
+ pendingMarkers.add(token);
403
+ queue.push({
404
+ __batchMarker: token
405
+ });
406
+ drain();
407
+ return new Promise(function (resolve) {
408
+ var check = /*#__PURE__*/function () {
409
+ var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
410
+ return _regeneratorRuntime.wrap(function (_context4) {
411
+ while (1) switch (_context4.prev = _context4.next) {
412
+ case 0:
413
+ if (destroyed) {
414
+ _context4.next = 3;
415
+ break;
416
+ }
417
+ if (!pendingMarkers.has(token)) {
418
+ _context4.next = 2;
419
+ break;
420
+ }
421
+ _context4.next = 1;
422
+ return nextFrame();
423
+ case 1:
424
+ return _context4.abrupt("continue", 0);
425
+ case 2:
426
+ resolve(true);
427
+ return _context4.abrupt("return");
428
+ case 3:
429
+ resolve(false);
430
+ case 4:
431
+ case "end":
432
+ return _context4.stop();
433
+ }
434
+ }, _callee4);
435
+ }));
436
+ return function check() {
437
+ return _ref4.apply(this, arguments);
438
+ };
439
+ }();
440
+ check();
441
+ });
442
+ },
47
443
  updateExternalEvent: function updateExternalEvent(newExternalEvent) {
48
- var _setExternalEventFn;
49
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(newExternalEvent);
444
+ return this.sendExternalEvents(newExternalEvent);
445
+ },
446
+ clearQueue: function clearQueue() {
447
+ queue.length = 0;
448
+ pendingMarkers.clear();
50
449
  },
51
450
  unmount: function unmount() {
52
- ReactDOM.unmountComponentAtNode(container);
451
+ var _this$clearQueue;
452
+ destroyed = true;
453
+ (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
454
+ gltfTracker.uninstall();
455
+ root.unmount();
456
+ container[ROOT_KEY] = null;
53
457
  }
54
458
  };
55
459
  }
package/lib/index.js CHANGED
@@ -6,40 +6,378 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports["default"] = void 0;
8
8
  exports.renderKitchenSimulator = renderKitchenSimulator;
9
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10
11
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
11
12
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
12
13
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
13
14
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
14
15
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
16
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
15
17
  var _react = _interopRequireDefault(require("react"));
16
- var _reactDom = _interopRequireDefault(require("react-dom"));
17
18
  var _LiteRenderer = _interopRequireDefault(require("./LiteRenderer"));
19
+ var _client = require("react-dom/client");
18
20
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); }
19
21
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
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
+ 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
+ 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__';
27
+ function nextFrame() {
28
+ return new Promise(function (resolve) {
29
+ return requestAnimationFrame(resolve);
30
+ });
31
+ }
32
+ function sleep(ms) {
33
+ return new Promise(function (resolve) {
34
+ return setTimeout(resolve, ms);
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Track GLTF/GLB network activity via XHR + fetch.
40
+ * Returns { getInFlight, waitForIdle, uninstall }.
41
+ */
42
+ function installGltfTracker() {
43
+ var _XHR$prototype, _XHR$prototype2;
44
+ if (typeof window === 'undefined') {
45
+ return {
46
+ getInFlight: function getInFlight() {
47
+ return 0;
48
+ },
49
+ waitForIdle: function () {
50
+ var _waitForIdle = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee() {
51
+ return _regenerator["default"].wrap(function (_context) {
52
+ while (1) switch (_context.prev = _context.next) {
53
+ case 0:
54
+ return _context.abrupt("return", true);
55
+ case 1:
56
+ case "end":
57
+ return _context.stop();
58
+ }
59
+ }, _callee);
60
+ }));
61
+ function waitForIdle() {
62
+ return _waitForIdle.apply(this, arguments);
63
+ }
64
+ return waitForIdle;
65
+ }(),
66
+ uninstall: function uninstall() {}
67
+ };
68
+ }
69
+ var inFlight = 0;
70
+ var listeners = new Set();
71
+ var notify = function notify() {
72
+ var _iterator = _createForOfIteratorHelper(listeners),
73
+ _step;
74
+ try {
75
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
76
+ var fn = _step.value;
77
+ fn(inFlight);
78
+ }
79
+ } catch (err) {
80
+ _iterator.e(err);
81
+ } finally {
82
+ _iterator.f();
83
+ }
84
+ };
85
+ var isGltfUrl = function isGltfUrl(url) {
86
+ try {
87
+ var _url$toString, _url$toString2;
88
+ var s = typeof url === 'string' ? url : (_url$toString = url === null || url === void 0 || (_url$toString2 = url.toString) === null || _url$toString2 === void 0 ? void 0 : _url$toString2.call(url)) !== null && _url$toString !== void 0 ? _url$toString : '';
89
+ var u = s.toLowerCase();
90
+ return u.includes('.gltf') || u.includes('.glb');
91
+ } catch (_unused) {
92
+ return false;
93
+ }
94
+ };
95
+
96
+ // ---- XHR hook ----
97
+ var XHR = window.XMLHttpRequest;
98
+ var originalOpen = null;
99
+ var originalSend = null;
100
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype = XHR.prototype) !== null && _XHR$prototype !== void 0 && _XHR$prototype.open && XHR !== null && XHR !== void 0 && (_XHR$prototype2 = XHR.prototype) !== null && _XHR$prototype2 !== void 0 && _XHR$prototype2.send) {
101
+ originalOpen = XHR.prototype.open;
102
+ originalSend = XHR.prototype.send;
103
+ XHR.prototype.open = function (method, url) {
104
+ var _originalOpen;
105
+ this.__isGltfRequest = isGltfUrl(url);
106
+ for (var _len = arguments.length, rest = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
107
+ rest[_key - 2] = arguments[_key];
108
+ }
109
+ return (_originalOpen = originalOpen).call.apply(_originalOpen, [this, method, url].concat(rest));
110
+ };
111
+ XHR.prototype.send = function () {
112
+ var _this = this;
113
+ if (this.__isGltfRequest) {
114
+ inFlight += 1;
115
+ notify();
116
+ var _done = function done() {
117
+ inFlight -= 1;
118
+ if (inFlight < 0) inFlight = 0;
119
+ notify();
120
+ _this.removeEventListener('loadend', _done);
121
+ };
122
+ this.addEventListener('loadend', _done);
123
+ }
124
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
125
+ args[_key2] = arguments[_key2];
126
+ }
127
+ return originalSend.apply(this, args);
128
+ };
129
+ }
130
+
131
+ // ---- fetch hook ----
132
+ var originalFetch = window.fetch;
133
+ if (typeof originalFetch === 'function') {
134
+ window.fetch = /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2() {
135
+ var _len3,
136
+ args,
137
+ _key3,
138
+ url,
139
+ track,
140
+ _args2 = arguments;
141
+ return _regenerator["default"].wrap(function (_context2) {
142
+ while (1) switch (_context2.prev = _context2.next) {
143
+ case 0:
144
+ for (_len3 = _args2.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
145
+ args[_key3] = _args2[_key3];
146
+ }
147
+ url = args === null || args === void 0 ? void 0 : args[0];
148
+ track = isGltfUrl(url);
149
+ if (track) {
150
+ _context2.next = 1;
151
+ break;
152
+ }
153
+ return _context2.abrupt("return", originalFetch.apply(this, args));
154
+ case 1:
155
+ inFlight += 1;
156
+ notify();
157
+ _context2.prev = 2;
158
+ _context2.next = 3;
159
+ return originalFetch.apply(this, args);
160
+ case 3:
161
+ return _context2.abrupt("return", _context2.sent);
162
+ case 4:
163
+ _context2.prev = 4;
164
+ inFlight -= 1;
165
+ if (inFlight < 0) inFlight = 0;
166
+ notify();
167
+ return _context2.finish(4);
168
+ case 5:
169
+ case "end":
170
+ return _context2.stop();
171
+ }
172
+ }, _callee2, this, [[2,, 4, 5]]);
173
+ }));
174
+ }
175
+ var subscribe = function subscribe(fn) {
176
+ listeners.add(fn);
177
+ fn(inFlight);
178
+ return function () {
179
+ return listeners["delete"](fn);
180
+ };
181
+ };
182
+
183
+ /**
184
+ * Wait until GLTF network is idle (inFlight===0).
185
+ * Includes a grace window to catch loads that start slightly after the event.
186
+ */
187
+ var waitForIdle = /*#__PURE__*/function () {
188
+ var _ref2 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3() {
189
+ var _ref3,
190
+ _ref3$timeoutMs,
191
+ timeoutMs,
192
+ _ref3$graceMs,
193
+ graceMs,
194
+ start,
195
+ _args3 = arguments;
196
+ return _regenerator["default"].wrap(function (_context3) {
197
+ while (1) switch (_context3.prev = _context3.next) {
198
+ case 0:
199
+ _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;
200
+ start = Date.now();
201
+ if (!(graceMs > 0)) {
202
+ _context3.next = 1;
203
+ break;
204
+ }
205
+ _context3.next = 1;
206
+ return sleep(graceMs);
207
+ case 1:
208
+ return _context3.abrupt("return", new Promise(function (resolve, reject) {
209
+ var unsub = null;
210
+ var onChange = function onChange(count) {
211
+ if (count === 0) {
212
+ if (unsub) unsub(); // ✅ safe
213
+ resolve(true);
214
+ return;
215
+ }
216
+ if (Date.now() - start > timeoutMs) {
217
+ if (unsub) unsub();
218
+ reject(new Error('GLTF did not become idle within timeout'));
219
+ }
220
+ };
221
+ unsub = subscribe(onChange);
222
+ }));
223
+ case 2:
224
+ case "end":
225
+ return _context3.stop();
226
+ }
227
+ }, _callee3);
228
+ }));
229
+ return function waitForIdle() {
230
+ return _ref2.apply(this, arguments);
231
+ };
232
+ }();
233
+ var uninstall = function uninstall() {
234
+ var _XHR$prototype3, _XHR$prototype4;
235
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype3 = XHR.prototype) !== null && _XHR$prototype3 !== void 0 && _XHR$prototype3.open && originalOpen) XHR.prototype.open = originalOpen;
236
+ if (XHR !== null && XHR !== void 0 && (_XHR$prototype4 = XHR.prototype) !== null && _XHR$prototype4 !== void 0 && _XHR$prototype4.send && originalSend) XHR.prototype.send = originalSend;
237
+ if (typeof originalFetch === 'function') window.fetch = originalFetch;
238
+ listeners.clear();
239
+ inFlight = 0;
240
+ };
241
+ return {
242
+ getInFlight: function getInFlight() {
243
+ return inFlight;
244
+ },
245
+ waitForIdle: waitForIdle,
246
+ uninstall: uninstall
247
+ };
248
+ }
20
249
  function renderKitchenSimulator(container) {
250
+ var _props$framesPerEvent, _props$waitForGltfIdl, _props$gltfTimeoutMs, _props$gltfGraceMs;
21
251
  var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
252
+ var root = container[ROOT_KEY];
253
+ if (!root) {
254
+ root = (0, _client.createRoot)(container);
255
+ container[ROOT_KEY] = root;
256
+ }
22
257
  var setExternalEventFn = null;
258
+ var queue = [];
259
+ var pendingMarkers = new Set();
260
+ var draining = false;
261
+ var destroyed = false;
262
+ var FRAMES_PER_EVENT = (_props$framesPerEvent = props.framesPerEvent) !== null && _props$framesPerEvent !== void 0 ? _props$framesPerEvent : 2;
23
263
 
24
- // Internal wrapper that exposes a state setter
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;
268
+ var gltfTracker = installGltfTracker();
269
+ function drain() {
270
+ return _drain.apply(this, arguments);
271
+ }
272
+ function _drain() {
273
+ _drain = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5() {
274
+ var _setExternalEventFn, ev, i, _ev$gltfTimeoutMs, _ev$gltfGraceMs;
275
+ return _regenerator["default"].wrap(function (_context5) {
276
+ while (1) switch (_context5.prev = _context5.next) {
277
+ case 0:
278
+ if (!(draining || destroyed)) {
279
+ _context5.next = 1;
280
+ break;
281
+ }
282
+ return _context5.abrupt("return");
283
+ case 1:
284
+ draining = true;
285
+ _context5.prev = 2;
286
+ case 3:
287
+ if (!(queue.length && !destroyed)) {
288
+ _context5.next = 12;
289
+ break;
290
+ }
291
+ ev = queue.shift(); // skip internal markers
292
+ if (!(ev && ev.__batchMarker)) {
293
+ _context5.next = 4;
294
+ break;
295
+ }
296
+ pendingMarkers["delete"](ev.__batchMarker);
297
+ return _context5.abrupt("continue", 3);
298
+ case 4:
299
+ if (setExternalEventFn) {
300
+ _context5.next = 6;
301
+ break;
302
+ }
303
+ _context5.next = 5;
304
+ return nextFrame();
305
+ case 5:
306
+ return _context5.abrupt("continue", 3);
307
+ case 6:
308
+ (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(ev);
309
+
310
+ // baseline spacing for react + legacy lifecycle
311
+ i = 0;
312
+ case 7:
313
+ if (!(i < FRAMES_PER_EVENT)) {
314
+ _context5.next = 9;
315
+ break;
316
+ }
317
+ _context5.next = 8;
318
+ return nextFrame();
319
+ case 8:
320
+ i += 1;
321
+ _context5.next = 7;
322
+ break;
323
+ case 9:
324
+ if (!WAIT_FOR_GLTF_IDLE_AFTER_EACH_EVENT) {
325
+ _context5.next = 11;
326
+ break;
327
+ }
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;
335
+ return nextFrame();
336
+ case 11:
337
+ _context5.next = 3;
338
+ break;
339
+ case 12:
340
+ _context5.prev = 12;
341
+ draining = false;
342
+ return _context5.finish(12);
343
+ case 13:
344
+ case "end":
345
+ return _context5.stop();
346
+ }
347
+ }, _callee5, null, [[2,, 12, 13]]);
348
+ }));
349
+ return _drain.apply(this, arguments);
350
+ }
25
351
  var Wrapper = /*#__PURE__*/function (_React$Component) {
26
352
  function Wrapper(p) {
27
- var _this;
353
+ var _this2;
28
354
  (0, _classCallCheck2["default"])(this, Wrapper);
29
- _this = _callSuper(this, Wrapper, [p]);
30
- _this.state = {
355
+ _this2 = _callSuper(this, Wrapper, [p]);
356
+ _this2.state = {
31
357
  externalEvent: p.externalEvent || null
32
358
  };
33
- setExternalEventFn = _this.setExternalEvent.bind(_this);
34
- return _this;
359
+ _this2._mounted = false;
360
+ return _this2;
35
361
  }
36
362
  (0, _inherits2["default"])(Wrapper, _React$Component);
37
363
  return (0, _createClass2["default"])(Wrapper, [{
38
- key: "setExternalEvent",
39
- value: function setExternalEvent(newEvent) {
40
- this.setState({
41
- externalEvent: newEvent
42
- });
364
+ key: "componentDidMount",
365
+ value: function componentDidMount() {
366
+ var _this3 = this;
367
+ this._mounted = true;
368
+ setExternalEventFn = function setExternalEventFn(newEvent) {
369
+ // ✅ ensure we never setState during render
370
+ if (!_this3._mounted) return;
371
+ _this3.setState({
372
+ externalEvent: newEvent
373
+ });
374
+ };
375
+ }
376
+ }, {
377
+ key: "componentWillUnmount",
378
+ value: function componentWillUnmount() {
379
+ this._mounted = false;
380
+ setExternalEventFn = null;
43
381
  }
44
382
  }, {
45
383
  key: "render",
@@ -49,15 +387,81 @@ function renderKitchenSimulator(container) {
49
387
  }));
50
388
  }
51
389
  }]);
52
- }(_react["default"].Component); // Mount once
53
- _reactDom["default"].render(/*#__PURE__*/_react["default"].createElement(Wrapper, props), container);
390
+ }(_react["default"].Component);
391
+ root.render(/*#__PURE__*/_react["default"].createElement(Wrapper, props));
54
392
  return {
393
+ sendExternalEvents: function sendExternalEvents(eventOrEvents) {
394
+ var events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
395
+ var _iterator2 = _createForOfIteratorHelper(events),
396
+ _step2;
397
+ try {
398
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
399
+ var e = _step2.value;
400
+ queue.push(e);
401
+ }
402
+
403
+ // marker for this batch
404
+ } catch (err) {
405
+ _iterator2.e(err);
406
+ } finally {
407
+ _iterator2.f();
408
+ }
409
+ var token = Symbol('batch');
410
+ pendingMarkers.add(token);
411
+ queue.push({
412
+ __batchMarker: token
413
+ });
414
+ drain();
415
+ return new Promise(function (resolve) {
416
+ var check = /*#__PURE__*/function () {
417
+ var _ref4 = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee4() {
418
+ return _regenerator["default"].wrap(function (_context4) {
419
+ while (1) switch (_context4.prev = _context4.next) {
420
+ case 0:
421
+ if (destroyed) {
422
+ _context4.next = 3;
423
+ break;
424
+ }
425
+ if (!pendingMarkers.has(token)) {
426
+ _context4.next = 2;
427
+ break;
428
+ }
429
+ _context4.next = 1;
430
+ return nextFrame();
431
+ case 1:
432
+ return _context4.abrupt("continue", 0);
433
+ case 2:
434
+ resolve(true);
435
+ return _context4.abrupt("return");
436
+ case 3:
437
+ resolve(false);
438
+ case 4:
439
+ case "end":
440
+ return _context4.stop();
441
+ }
442
+ }, _callee4);
443
+ }));
444
+ return function check() {
445
+ return _ref4.apply(this, arguments);
446
+ };
447
+ }();
448
+ check();
449
+ });
450
+ },
55
451
  updateExternalEvent: function updateExternalEvent(newExternalEvent) {
56
- var _setExternalEventFn;
57
- (_setExternalEventFn = setExternalEventFn) === null || _setExternalEventFn === void 0 || _setExternalEventFn(newExternalEvent);
452
+ return this.sendExternalEvents(newExternalEvent);
453
+ },
454
+ clearQueue: function clearQueue() {
455
+ queue.length = 0;
456
+ pendingMarkers.clear();
58
457
  },
59
458
  unmount: function unmount() {
60
- _reactDom["default"].unmountComponentAtNode(container);
459
+ var _this$clearQueue;
460
+ destroyed = true;
461
+ (_this$clearQueue = this.clearQueue) === null || _this$clearQueue === void 0 || _this$clearQueue.call(this);
462
+ gltfTracker.uninstall();
463
+ root.unmount();
464
+ container[ROOT_KEY] = null;
61
465
  }
62
466
  };
63
467
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitchen-simulator",
3
- "version": "3.2.0",
3
+ "version": "3.4.0",
4
4
  "description": "It is a kitchen simulator (self-contained micro-frontend).",
5
5
  "license": "MIT",
6
6
  "main": "lib/index.js",