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