@pollen-robotics/reachy-mini-sdk 1.10.0-dev.0.main.b50d97e → 1.10.0-dev.0.main.c8367cd

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 (128) hide show
  1. package/APP_CREATION_GUIDE.md +98 -0
  2. package/dist/lib/central-listener.d.ts +71 -0
  3. package/dist/lib/central-listener.d.ts.map +1 -0
  4. package/dist/lib/central-listener.js +277 -0
  5. package/dist/lib/central-listener.js.map +1 -0
  6. package/dist/lib/central-listener.test.d.ts +2 -0
  7. package/dist/lib/central-listener.test.d.ts.map +1 -0
  8. package/dist/lib/central-listener.test.js +378 -0
  9. package/dist/lib/central-listener.test.js.map +1 -0
  10. package/dist/lib/dc-watchdog.test.d.ts +2 -0
  11. package/dist/lib/dc-watchdog.test.d.ts.map +1 -0
  12. package/dist/lib/dc-watchdog.test.js +154 -0
  13. package/dist/lib/dc-watchdog.test.js.map +1 -0
  14. package/dist/lib/ensure-awake.test.d.ts +2 -0
  15. package/dist/lib/ensure-awake.test.d.ts.map +1 -0
  16. package/dist/lib/ensure-awake.test.js +117 -0
  17. package/dist/lib/ensure-awake.test.js.map +1 -0
  18. package/dist/lib/ice-grace.test.d.ts +2 -0
  19. package/dist/lib/ice-grace.test.d.ts.map +1 -0
  20. package/dist/lib/ice-grace.test.js +211 -0
  21. package/dist/lib/ice-grace.test.js.map +1 -0
  22. package/dist/lib/imu.test.d.ts +2 -0
  23. package/dist/lib/imu.test.d.ts.map +1 -0
  24. package/dist/lib/imu.test.js +78 -0
  25. package/dist/lib/imu.test.js.map +1 -0
  26. package/dist/lib/logger.d.ts +38 -0
  27. package/dist/lib/logger.d.ts.map +1 -0
  28. package/dist/lib/logger.js +81 -0
  29. package/dist/lib/logger.js.map +1 -0
  30. package/dist/lib/pending-replies.d.ts +127 -0
  31. package/dist/lib/pending-replies.d.ts.map +1 -0
  32. package/dist/lib/pending-replies.js +255 -0
  33. package/dist/lib/pending-replies.js.map +1 -0
  34. package/dist/lib/pending-replies.test.d.ts +2 -0
  35. package/dist/lib/pending-replies.test.d.ts.map +1 -0
  36. package/dist/lib/pending-replies.test.js +217 -0
  37. package/dist/lib/pending-replies.test.js.map +1 -0
  38. package/dist/lib/reachy-mini.d.ts +202 -88
  39. package/dist/lib/reachy-mini.d.ts.map +1 -1
  40. package/dist/lib/reachy-mini.js +651 -566
  41. package/dist/lib/reachy-mini.js.map +1 -1
  42. package/dist/lib/redial.test.d.ts +2 -0
  43. package/dist/lib/redial.test.d.ts.map +1 -0
  44. package/dist/lib/redial.test.js +206 -0
  45. package/dist/lib/redial.test.js.map +1 -0
  46. package/dist/lib/request.test.d.ts +2 -0
  47. package/dist/lib/request.test.d.ts.map +1 -0
  48. package/dist/lib/request.test.js +90 -0
  49. package/dist/lib/request.test.js.map +1 -0
  50. package/dist/lib/session-supervisor.d.ts +201 -0
  51. package/dist/lib/session-supervisor.d.ts.map +1 -0
  52. package/dist/lib/session-supervisor.js +520 -0
  53. package/dist/lib/session-supervisor.js.map +1 -0
  54. package/dist/lib/token-store.d.ts +50 -0
  55. package/dist/lib/token-store.d.ts.map +1 -0
  56. package/dist/lib/token-store.js +120 -0
  57. package/dist/lib/token-store.js.map +1 -0
  58. package/dist/lib/token-store.test.d.ts +2 -0
  59. package/dist/lib/token-store.test.d.ts.map +1 -0
  60. package/dist/lib/token-store.test.js +82 -0
  61. package/dist/lib/token-store.test.js.map +1 -0
  62. package/dist/lib/types.d.ts +280 -6
  63. package/dist/lib/types.d.ts.map +1 -1
  64. package/dist/lib/url-helpers.d.ts.map +1 -1
  65. package/dist/lib/url-helpers.js +3 -1
  66. package/dist/lib/url-helpers.js.map +1 -1
  67. package/dist/lib/version.d.ts +2 -0
  68. package/dist/lib/version.d.ts.map +1 -0
  69. package/dist/lib/version.js +5 -0
  70. package/dist/lib/version.js.map +1 -0
  71. package/dist/reachy-mini-sdk.d.ts +15 -1
  72. package/dist/reachy-mini-sdk.d.ts.map +1 -1
  73. package/dist/reachy-mini-sdk.js +12 -0
  74. package/dist/reachy-mini-sdk.js.map +1 -1
  75. package/host/dist/ReachyHost.d.ts.map +1 -1
  76. package/host/dist/chunks/centralRest-uXNMidlL.js +2315 -0
  77. package/host/dist/chunks/index-Beu4GNzi.js +821 -0
  78. package/host/dist/chunks/{mountHost-DjE5zE9R.js → mountHost-BTTF4Uu9.js} +24933 -27488
  79. package/host/dist/components/ConnectingView.d.ts.map +1 -1
  80. package/host/dist/components/DaemonUpdateGate.d.ts +65 -0
  81. package/host/dist/components/DaemonUpdateGate.d.ts.map +1 -0
  82. package/host/dist/components/ErrorView.d.ts.map +1 -1
  83. package/host/dist/components/HostErrorBoundary.d.ts +30 -0
  84. package/host/dist/components/HostErrorBoundary.d.ts.map +1 -0
  85. package/host/dist/components/LeavingView.d.ts.map +1 -1
  86. package/host/dist/components/PickerView.d.ts +16 -14
  87. package/host/dist/components/PickerView.d.ts.map +1 -1
  88. package/host/dist/components/PostOAuthSplash.d.ts +14 -9
  89. package/host/dist/components/PostOAuthSplash.d.ts.map +1 -1
  90. package/host/dist/components/ReachyHostShell.d.ts.map +1 -1
  91. package/host/dist/components/SignInView.d.ts.map +1 -1
  92. package/host/dist/components/TopBar.d.ts +5 -6
  93. package/host/dist/components/TopBar.d.ts.map +1 -1
  94. package/host/dist/components/WelcomeBackOverlay.d.ts.map +1 -1
  95. package/host/dist/embed/index.d.ts +68 -5
  96. package/host/dist/embed/index.d.ts.map +1 -1
  97. package/host/dist/entry/auto.js +2 -2
  98. package/host/dist/entry/embed.d.ts +5 -4
  99. package/host/dist/entry/embed.d.ts.map +1 -1
  100. package/host/dist/entry/embed.js +5 -4
  101. package/host/dist/hooks/useHostBridge.d.ts +35 -1
  102. package/host/dist/hooks/useHostBridge.d.ts.map +1 -1
  103. package/host/dist/hooks/useLatestDaemonVersion.d.ts +7 -0
  104. package/host/dist/hooks/useLatestDaemonVersion.d.ts.map +1 -0
  105. package/host/dist/hooks/useOAuth.d.ts +18 -0
  106. package/host/dist/hooks/useOAuth.d.ts.map +1 -1
  107. package/host/dist/hooks/useRobots.d.ts +0 -2
  108. package/host/dist/hooks/useRobots.d.ts.map +1 -1
  109. package/host/dist/index.d.ts +2 -2
  110. package/host/dist/index.d.ts.map +1 -1
  111. package/host/dist/index.js +11 -10
  112. package/host/dist/lib/daemonRelease.d.ts +84 -0
  113. package/host/dist/lib/daemonRelease.d.ts.map +1 -0
  114. package/host/dist/lib/protocol.d.ts +141 -7
  115. package/host/dist/lib/protocol.d.ts.map +1 -1
  116. package/host/dist/lib/rebootWatch.d.ts +51 -0
  117. package/host/dist/lib/rebootWatch.d.ts.map +1 -0
  118. package/host/dist/lib/sdk-types.d.ts +1 -1
  119. package/host/dist/lib/sdk-types.d.ts.map +1 -1
  120. package/host/dist/lib/sdkStaleness.d.ts +78 -0
  121. package/host/dist/lib/sdkStaleness.d.ts.map +1 -0
  122. package/host/dist/lib/settings.d.ts +16 -5
  123. package/host/dist/lib/settings.d.ts.map +1 -1
  124. package/package.json +11 -4
  125. package/host/dist/chunks/index-lMs2sCXG.js +0 -439
  126. package/host/dist/chunks/reachy-mini-CdsdQLIU.js +0 -1523
  127. package/host/dist/lib/centralListener.d.ts +0 -73
  128. package/host/dist/lib/centralListener.d.ts.map +0 -1
@@ -0,0 +1,117 @@
1
+ /**
2
+ * ensureAwake() bring-up contract.
3
+ *
4
+ * The properties that must hold:
5
+ * - asleep (`motor_mode: 'disabled'`): plays `wake_up` and resolves only
6
+ * once the daemon reports the trajectory completed, so a caller can
7
+ * start commanding poses on resolution without fighting the emote;
8
+ * - a wake the daemon never confirms resolves anyway after the internal
9
+ * trajectory budget (degraded boot beats a boot trapped on the splash);
10
+ * - `gravity_compensation` inherited from a previous session: flip back
11
+ * to `enabled` (position control) WITHOUT replaying the wake emote,
12
+ * and nudge a state refresh so `isAwake()` readers converge;
13
+ * - already `enabled`: strict no-op, nothing on the wire;
14
+ * - unknown motor mode (fresh session): ask for a state snapshot first
15
+ * and fall through to the right branch once it lands.
16
+ *
17
+ * Same approach as the other SDK suites: private surface + a fake data
18
+ * channel, fake timers for the budget paths.
19
+ */
20
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
21
+ import { ReachyMini } from './reachy-mini.js';
22
+ function makeInstance() {
23
+ const r = new ReachyMini();
24
+ const internals = r;
25
+ const sent = [];
26
+ internals._dc = {
27
+ readyState: 'open',
28
+ send: (msg) => { sent.push(JSON.parse(msg)); },
29
+ };
30
+ return { r, internals, sent };
31
+ }
32
+ /** Feed a state frame carrying the given motor mode into the mirror. */
33
+ function pushMotorMode(internals, mode) {
34
+ internals._handleRobotMessage({ state: { motor_mode: mode } });
35
+ }
36
+ beforeEach(() => {
37
+ vi.useFakeTimers();
38
+ });
39
+ afterEach(() => {
40
+ vi.useRealTimers();
41
+ });
42
+ describe('ensureAwake - asleep robot', () => {
43
+ it('plays wake_up and resolves only on trajectory completion', async () => {
44
+ const { r, internals, sent } = makeInstance();
45
+ pushMotorMode(internals, 'disabled');
46
+ const settled = vi.fn();
47
+ const promise = r.ensureAwake().then(settled);
48
+ await vi.advanceTimersByTimeAsync(0);
49
+ // wakeUp() = enable motors + the wake_up command itself.
50
+ expect(sent).toEqual([
51
+ { type: 'set_motor_mode', mode: 'enabled' },
52
+ { type: 'wake_up' },
53
+ ]);
54
+ // Command acked but trajectory not completed yet: must still be pending.
55
+ expect(settled).not.toHaveBeenCalled();
56
+ internals._handleRobotMessage({ command: 'wake_up', completed: true });
57
+ await expect(promise).resolves.toBeUndefined();
58
+ expect(settled).toHaveBeenCalled();
59
+ });
60
+ it('resolves anyway when the daemon never confirms (internal budget)', async () => {
61
+ const { r, internals } = makeInstance();
62
+ pushMotorMode(internals, 'disabled');
63
+ const promise = r.ensureAwake();
64
+ // Past the 5 s trajectory budget with no completion in sight.
65
+ await vi.advanceTimersByTimeAsync(6000);
66
+ await expect(promise).resolves.toBe(true);
67
+ });
68
+ });
69
+ describe('ensureAwake - gravity compensation inherited from a fast handoff', () => {
70
+ it('flips back to enabled without replaying the wake emote', async () => {
71
+ const { r, internals, sent } = makeInstance();
72
+ pushMotorMode(internals, 'gravity_compensation');
73
+ await expect(r.ensureAwake()).resolves.toBe(true);
74
+ const types = sent.map((m) => m.type ?? m.command);
75
+ expect(types).toContain('set_motor_mode');
76
+ expect(types).not.toContain('wake_up');
77
+ expect(sent.find((m) => m.type === 'set_motor_mode')).toEqual({
78
+ type: 'set_motor_mode',
79
+ mode: 'enabled',
80
+ });
81
+ // Cache refresh so isAwake() readers converge on the new mode.
82
+ expect(types).toContain('get_state');
83
+ });
84
+ });
85
+ describe('ensureAwake - already under position control', () => {
86
+ it('is a strict no-op', async () => {
87
+ const { r, internals, sent } = makeInstance();
88
+ pushMotorMode(internals, 'enabled');
89
+ await expect(r.ensureAwake()).resolves.toBe(true);
90
+ expect(sent).toEqual([]);
91
+ });
92
+ });
93
+ describe('ensureAwake - unknown motor mode (fresh session)', () => {
94
+ it('requests a snapshot, then wakes when the state says disabled', async () => {
95
+ const { r, internals, sent } = makeInstance();
96
+ const settled = vi.fn();
97
+ const promise = r.ensureAwake().then(settled);
98
+ await vi.advanceTimersByTimeAsync(0);
99
+ // First move: ask for the state it doesn't have yet.
100
+ expect(sent[0]).toEqual({ type: 'get_state' });
101
+ pushMotorMode(internals, 'disabled');
102
+ await vi.advanceTimersByTimeAsync(0);
103
+ expect(sent.map((m) => m.type)).toContain('wake_up');
104
+ expect(settled).not.toHaveBeenCalled();
105
+ internals._handleRobotMessage({ command: 'wake_up', completed: true });
106
+ await promise;
107
+ expect(settled).toHaveBeenCalled();
108
+ });
109
+ it('falls through on the snapshot timeout and still resolves', async () => {
110
+ const { r } = makeInstance();
111
+ const promise = r.ensureAwake(1000);
112
+ // Snapshot never answered (1 s) + wake budget never confirmed (5 s).
113
+ await vi.advanceTimersByTimeAsync(6100);
114
+ await expect(promise).resolves.toBe(true);
115
+ });
116
+ });
117
+ //# sourceMappingURL=ensure-awake.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-awake.test.js","sourceRoot":"","sources":["../../lib/ensure-awake.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQ9C,SAAS,YAAY;IAKjB,MAAM,CAAC,GAAG,IAAI,UAAU,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,CAAoC,CAAC;IACvD,MAAM,IAAI,GAAmC,EAAE,CAAC;IAChD,SAAS,CAAC,GAAG,GAAG;QACZ,UAAU,EAAE,MAAM;QAClB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC,CAAC,CAAC,CAAC;KACpF,CAAC;IACF,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC;AAED,wEAAwE;AACxE,SAAS,aAAa,CAClB,SAA+B,EAC/B,IAAqD;IAErD,SAAS,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,UAAU,CAAC,GAAG,EAAE;IACZ,EAAE,CAAC,aAAa,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACX,EAAE,CAAC,aAAa,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;QAC9C,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAExB,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;QAErC,yDAAyD;QACzD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACjB,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE;YAC3C,EAAE,IAAI,EAAE,SAAS,EAAE;SACtB,CAAC,CAAC;QACH,yEAAyE;QACzE,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAEvC,SAAS,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAAC;QACxC,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAErC,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,8DAA8D;QAC9D,MAAM,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAExC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kEAAkE,EAAE,GAAG,EAAE;IAC9E,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;QAC9C,aAAa,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;QAEjD,MAAM,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1D,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,SAAS;SAClB,CAAC,CAAC;QACH,+DAA+D;QAC/D,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC1D,EAAE,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;QAC9C,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAEpC,MAAM,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAExB,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;QACrC,qDAAqD;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QAE/C,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QACrC,MAAM,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAEvC,SAAS,CAAC,mBAAmB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,OAAO,CAAC;QACd,MAAM,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,CAAC,EAAE,GAAG,YAAY,EAAE,CAAC;QAE7B,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,qEAAqE;QACrE,MAAM,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAExC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ice-grace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ice-grace.test.d.ts","sourceRoot":"","sources":["../../lib/ice-grace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,211 @@
1
+ /**
2
+ * ICE-blip grace tests, run against an isolated SessionSupervisor with
3
+ * fully faked deps (no ReachyMini instance involved).
4
+ *
5
+ * The properties that must hold:
6
+ * - `disconnected` is debounced ~3 s and escalates only if ICE is
7
+ * still bad when the grace expires; healing (event or re-check)
8
+ * stands the timer down;
9
+ * - `failed` gets a shorter ~1 s debounce;
10
+ * - repeated identical transitions coalesce onto the original timer
11
+ * (no clock reset); a changed reason replaces the timer;
12
+ * - mid-setup failures reject the pending startSession() promise and
13
+ * never start a redial;
14
+ * - while the tab is hidden the grace defers to the next foreground
15
+ * frame; past the visibility ceiling the session is expired
16
+ * immediately (straight to redial when enabled);
17
+ * - escalation funnels into the redial when auto-reconnect is on and
18
+ * falls back to the fatal `error` when it's off;
19
+ * - network listeners forward browser events and uninstall cleanly.
20
+ */
21
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
+ import { SessionSupervisor } from './session-supervisor.js';
23
+ function makeSupervisor(overrides = {}, options = { autoReconnect: true }) {
24
+ const deps = {
25
+ iceState: vi.fn().mockReturnValue('disconnected'),
26
+ hasPc: vi.fn().mockReturnValue(true),
27
+ isStreaming: vi.fn().mockReturnValue(true),
28
+ isMidSetup: vi.fn().mockReturnValue(false),
29
+ isSignalingDown: vi.fn().mockReturnValue(false),
30
+ selectedRobotId: vi.fn().mockReturnValue('robot-1'),
31
+ rejectPendingSession: vi.fn().mockReturnValue(false),
32
+ reconnectSignaling: vi.fn().mockResolvedValue(undefined),
33
+ dial: vi.fn().mockResolvedValue(undefined),
34
+ teardownForRedial: vi.fn(),
35
+ nudgeState: vi.fn(),
36
+ emit: vi.fn(),
37
+ ...overrides,
38
+ };
39
+ return { sup: new SessionSupervisor(deps, options), deps };
40
+ }
41
+ /** Details of every `emit(name, …)` call, in order. */
42
+ function emitted(deps, name) {
43
+ return deps.emit.mock.calls.filter((c) => c[0] === name).map((c) => c[1]);
44
+ }
45
+ /** The `error` emits, unwrapped to their Error payloads. */
46
+ function emittedErrors(deps) {
47
+ return emitted(deps, 'error').map((d) => d.error);
48
+ }
49
+ /** jsdom's document.hidden is read-only; swap in a controllable getter. */
50
+ let hidden = false;
51
+ beforeEach(() => {
52
+ vi.useFakeTimers();
53
+ hidden = false;
54
+ Object.defineProperty(document, 'hidden', {
55
+ configurable: true,
56
+ get: () => hidden,
57
+ });
58
+ });
59
+ afterEach(() => {
60
+ vi.useRealTimers();
61
+ });
62
+ describe('disconnected grace (foreground)', () => {
63
+ it('escalates only after the grace expires with ICE still down', async () => {
64
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
65
+ sup.onIceDisconnected();
66
+ await vi.advanceTimersByTimeAsync(2999);
67
+ expect(emittedErrors(deps)).toHaveLength(0);
68
+ await vi.advanceTimersByTimeAsync(1);
69
+ expect(emittedErrors(deps)).toHaveLength(1);
70
+ expect(String(emittedErrors(deps)[0])).toMatch(/ICE stuck in 'disconnected'/);
71
+ });
72
+ it('funnels into the redial instead of the error when auto-reconnect is on', async () => {
73
+ const { sup, deps } = makeSupervisor();
74
+ sup.onIceDisconnected();
75
+ await vi.runAllTimersAsync();
76
+ expect(emittedErrors(deps)).toHaveLength(0);
77
+ expect(deps.teardownForRedial).toHaveBeenCalled();
78
+ expect(deps.dial).toHaveBeenCalledWith('robot-1');
79
+ expect(emitted(deps, 'sessionReconnected')).toEqual([{ attempt: 1 }]);
80
+ });
81
+ it('stands down when ICE heals before the grace expires', async () => {
82
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
83
+ sup.onIceDisconnected();
84
+ await vi.advanceTimersByTimeAsync(1000);
85
+ sup.onIceHealed();
86
+ await vi.runAllTimersAsync();
87
+ expect(emittedErrors(deps)).toHaveLength(0);
88
+ });
89
+ it('re-checks the live state at expiry (healed without an event)', async () => {
90
+ const { sup, deps } = makeSupervisor({ iceState: vi.fn().mockReturnValue('connected') }, { autoReconnect: false });
91
+ sup.onIceDisconnected();
92
+ await vi.runAllTimersAsync();
93
+ expect(emittedErrors(deps)).toHaveLength(0);
94
+ });
95
+ it('coalesces repeated identical transitions onto the original clock', async () => {
96
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
97
+ sup.onIceDisconnected();
98
+ await vi.advanceTimersByTimeAsync(2000);
99
+ // A flurry of identical events must not reset the 3 s clock…
100
+ sup.onIceDisconnected();
101
+ await vi.advanceTimersByTimeAsync(1000);
102
+ // …so the grace fires 3 s after the FIRST transition.
103
+ expect(emittedErrors(deps)).toHaveLength(1);
104
+ });
105
+ });
106
+ describe('failed grace', () => {
107
+ it('escalates after ~1 s when ICE stays failed', async () => {
108
+ const { sup, deps } = makeSupervisor({ iceState: vi.fn().mockReturnValue('failed') }, { autoReconnect: false });
109
+ sup.onIceFailed();
110
+ await vi.advanceTimersByTimeAsync(1000);
111
+ expect(emittedErrors(deps)).toHaveLength(1);
112
+ expect(String(emittedErrors(deps)[0])).toMatch(/ICE connection failed/);
113
+ });
114
+ it('a disconnected → failed transition replaces the pending grace', async () => {
115
+ const { sup, deps } = makeSupervisor({ iceState: vi.fn().mockReturnValue('failed') }, { autoReconnect: false });
116
+ sup.onIceDisconnected();
117
+ sup.onIceFailed();
118
+ // The failed grace (1 s) wins over the disconnected one (3 s).
119
+ await vi.advanceTimersByTimeAsync(1000);
120
+ expect(emittedErrors(deps)).toHaveLength(1);
121
+ expect(String(emittedErrors(deps)[0])).toMatch(/ICE connection failed/);
122
+ });
123
+ it('rejects a pending startSession() and leaves the retry to its caller', async () => {
124
+ const rejectPendingSession = vi.fn().mockReturnValue(true);
125
+ const { sup, deps } = makeSupervisor({
126
+ iceState: vi.fn().mockReturnValue('failed'),
127
+ rejectPendingSession,
128
+ });
129
+ sup.onIceFailed();
130
+ await vi.advanceTimersByTimeAsync(1000);
131
+ expect(rejectPendingSession).toHaveBeenCalledTimes(1);
132
+ // Not an auto-reconnect case: the promise owner decides.
133
+ expect(deps.dial).not.toHaveBeenCalled();
134
+ // Outside a redial the classic fatal error still surfaces.
135
+ expect(emittedErrors(deps)).toHaveLength(1);
136
+ });
137
+ });
138
+ describe('visibility-deferred grace (hidden tab)', () => {
139
+ it('defers the grace to the foreground, then escalates if still down', async () => {
140
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
141
+ hidden = true;
142
+ sup.onIceDisconnected();
143
+ // Hidden: timers may be throttled, nothing may fire meanwhile.
144
+ await vi.advanceTimersByTimeAsync(10_000);
145
+ expect(emittedErrors(deps)).toHaveLength(0);
146
+ // Back to the foreground within the ceiling: a normal 3 s grace runs.
147
+ hidden = false;
148
+ document.dispatchEvent(new Event('visibilitychange'));
149
+ expect(emittedErrors(deps)).toHaveLength(0);
150
+ await vi.advanceTimersByTimeAsync(3000);
151
+ expect(emittedErrors(deps)).toHaveLength(1);
152
+ expect(String(emittedErrors(deps)[0])).toMatch(/ICE stuck in 'disconnected'/);
153
+ });
154
+ it('does nothing when ICE healed while backgrounded', async () => {
155
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
156
+ hidden = true;
157
+ sup.onIceDisconnected();
158
+ deps.iceState.mockReturnValue('connected');
159
+ hidden = false;
160
+ document.dispatchEvent(new Event('visibilitychange'));
161
+ await vi.runAllTimersAsync();
162
+ expect(emittedErrors(deps)).toHaveLength(0);
163
+ });
164
+ it('past the ceiling, expires the session immediately (straight to redial)', async () => {
165
+ const { sup, deps } = makeSupervisor();
166
+ hidden = true;
167
+ sup.onIceDisconnected();
168
+ // Fake timers also fake Date.now: cross the 60 s ceiling.
169
+ await vi.advanceTimersByTimeAsync(61_000);
170
+ hidden = false;
171
+ document.dispatchEvent(new Event('visibilitychange'));
172
+ await vi.runAllTimersAsync();
173
+ // No extra foreground grace — the redial starts right away.
174
+ expect(emitted(deps, 'sessionReconnecting')).toEqual([
175
+ expect.objectContaining({ cause: 'Session expired while tab was backgrounded' }),
176
+ ]);
177
+ expect(deps.dial).toHaveBeenCalledWith('robot-1');
178
+ });
179
+ it('clearIceGrace disarms the pending visibility handler', async () => {
180
+ const { sup, deps } = makeSupervisor({}, { autoReconnect: false });
181
+ hidden = true;
182
+ sup.onIceDisconnected();
183
+ sup.clearIceGrace();
184
+ hidden = false;
185
+ document.dispatchEvent(new Event('visibilitychange'));
186
+ await vi.runAllTimersAsync();
187
+ expect(emittedErrors(deps)).toHaveLength(0);
188
+ });
189
+ });
190
+ describe('network listeners', () => {
191
+ it('forwards online/offline while installed, stops after uninstall', () => {
192
+ const { sup, deps } = makeSupervisor();
193
+ sup.installNetworkListeners();
194
+ window.dispatchEvent(new Event('online'));
195
+ window.dispatchEvent(new Event('offline'));
196
+ expect(emitted(deps, 'networkOnline')).toHaveLength(1);
197
+ expect(emitted(deps, 'networkOffline')).toHaveLength(1);
198
+ sup.uninstallNetworkListeners();
199
+ window.dispatchEvent(new Event('online'));
200
+ expect(emitted(deps, 'networkOnline')).toHaveLength(1);
201
+ });
202
+ it('is idempotent: double install does not double the events', () => {
203
+ const { sup, deps } = makeSupervisor();
204
+ sup.installNetworkListeners();
205
+ sup.installNetworkListeners();
206
+ window.dispatchEvent(new Event('online'));
207
+ expect(emitted(deps, 'networkOnline')).toHaveLength(1);
208
+ sup.uninstallNetworkListeners();
209
+ });
210
+ });
211
+ //# sourceMappingURL=ice-grace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ice-grace.test.js","sourceRoot":"","sources":["../../lib/ice-grace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=imu.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imu.test.d.ts","sourceRoot":"","sources":["../../lib/imu.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * getImu() wire contract.
3
+ *
4
+ * The properties that must hold:
5
+ * - `getImu()` sends `{type: "get_imu"}` and resolves the daemon's
6
+ * `imu` payload as-is (accelerometer / gyroscope / quaternion /
7
+ * temperature);
8
+ * - an IMU-less robot (Lite, simulation) answers `imu: null` and the
9
+ * call resolves `null` promptly - distinguishable from a timeout;
10
+ * - an old daemon that doesn't know `get_imu` never answers: the
11
+ * shared slot timeout fail-opens to `null` instead of hanging;
12
+ * - a closed data channel rejects immediately (no waiter leaked).
13
+ *
14
+ * Same approach as the other SDK suites: private surface + a fake data
15
+ * channel, fake timers for the fail-open path.
16
+ */
17
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
18
+ import { ReachyMini } from './reachy-mini.js';
19
+ function makeInstance() {
20
+ const r = new ReachyMini();
21
+ const internals = r;
22
+ const sent = [];
23
+ internals._dc = {
24
+ readyState: 'open',
25
+ send: (msg) => { sent.push(JSON.parse(msg)); },
26
+ };
27
+ return { r, internals, sent };
28
+ }
29
+ const READING = {
30
+ accelerometer: [0.01, -0.02, 9.81],
31
+ gyroscope: [0.001, 0.002, -0.003],
32
+ quaternion: [1, 0, 0, 0],
33
+ temperature: 31.5,
34
+ };
35
+ beforeEach(() => {
36
+ vi.useFakeTimers();
37
+ });
38
+ afterEach(() => {
39
+ vi.useRealTimers();
40
+ });
41
+ describe('getImu - wire contract', () => {
42
+ it('sends get_imu and resolves the daemon reading untouched', async () => {
43
+ const { r, internals, sent } = makeInstance();
44
+ const promise = r.getImu();
45
+ expect(sent).toEqual([{ type: 'get_imu' }]);
46
+ internals._handleRobotMessage({ command: 'get_imu', imu: READING });
47
+ await expect(promise).resolves.toEqual(READING);
48
+ });
49
+ it('resolves null promptly on an IMU-less robot (imu: null reply)', async () => {
50
+ const { r, internals } = makeInstance();
51
+ const promise = r.getImu();
52
+ internals._handleRobotMessage({ command: 'get_imu', imu: null });
53
+ // No timer advance: this must be the reply, not the timeout.
54
+ await expect(promise).resolves.toBeNull();
55
+ });
56
+ it('an unrelated message does not settle the pending call', async () => {
57
+ const { r, internals } = makeInstance();
58
+ const settled = vi.fn();
59
+ void r.getImu().then(settled);
60
+ internals._handleRobotMessage({ command: 'get_tracked_face', face_target: { detected: false } });
61
+ await vi.advanceTimersByTimeAsync(0);
62
+ expect(settled).not.toHaveBeenCalled();
63
+ });
64
+ });
65
+ describe('getImu - degraded paths', () => {
66
+ it('fail-opens to null when the daemon never answers (old daemon)', async () => {
67
+ const { r } = makeInstance();
68
+ const promise = r.getImu();
69
+ await vi.advanceTimersByTimeAsync(4000);
70
+ await expect(promise).resolves.toBeNull();
71
+ });
72
+ it('rejects immediately when the data channel is not open', async () => {
73
+ const { r, internals } = makeInstance();
74
+ internals._dc = null;
75
+ await expect(r.getImu()).rejects.toThrow(/Data channel not open/);
76
+ });
77
+ });
78
+ //# sourceMappingURL=imu.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imu.test.js","sourceRoot":"","sources":["../../lib/imu.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Leveled, namespaced console logging shared by the SDK core, the
3
+ * embed runtime, and the host shell.
4
+ *
5
+ * Every log line is prefixed `[reachy:<ns>]` so a devtools filter on
6
+ * `reachy:` shows the whole story (or `reachy:session` just the
7
+ * reconnect layer), and gated by a single global level:
8
+ *
9
+ * - `debug` — per-message traffic (commands, SSE events, media cache
10
+ * ops). Off by default; also hidden behind devtools' "Verbose"
11
+ * filter since it maps to `console.debug`.
12
+ * - `info` — lifecycle transitions (boot phases, wake/sleep steps,
13
+ * reconnects). The default: a handful of lines per session, not
14
+ * per second.
15
+ * - `warn` / `error` — always worth seeing.
16
+ * - `silent` — nothing at all.
17
+ *
18
+ * The level persists in `localStorage["reachy-log"]` so it survives
19
+ * reloads mid-debugging. Flip it from the console without touching
20
+ * code:
21
+ *
22
+ * localStorage.setItem('reachy-log', 'debug'); location.reload();
23
+ *
24
+ * or at runtime via the exported `setLogLevel('debug')`.
25
+ */
26
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
27
+ /** Set the global log level (persisted to localStorage when available). */
28
+ export declare function setLogLevel(level: LogLevel): void;
29
+ export declare function getLogLevel(): LogLevel;
30
+ export interface Logger {
31
+ debug: (...args: unknown[]) => void;
32
+ info: (...args: unknown[]) => void;
33
+ warn: (...args: unknown[]) => void;
34
+ error: (...args: unknown[]) => void;
35
+ }
36
+ /** Create a logger whose lines are prefixed `[reachy:<ns>]`. */
37
+ export declare function createLogger(ns: string): Logger;
38
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../lib/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAwBtE,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAOjD;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAED,MAAM,WAAW,MAAM;IACnB,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACpC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACvC;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAgB/C"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Leveled, namespaced console logging shared by the SDK core, the
3
+ * embed runtime, and the host shell.
4
+ *
5
+ * Every log line is prefixed `[reachy:<ns>]` so a devtools filter on
6
+ * `reachy:` shows the whole story (or `reachy:session` just the
7
+ * reconnect layer), and gated by a single global level:
8
+ *
9
+ * - `debug` — per-message traffic (commands, SSE events, media cache
10
+ * ops). Off by default; also hidden behind devtools' "Verbose"
11
+ * filter since it maps to `console.debug`.
12
+ * - `info` — lifecycle transitions (boot phases, wake/sleep steps,
13
+ * reconnects). The default: a handful of lines per session, not
14
+ * per second.
15
+ * - `warn` / `error` — always worth seeing.
16
+ * - `silent` — nothing at all.
17
+ *
18
+ * The level persists in `localStorage["reachy-log"]` so it survives
19
+ * reloads mid-debugging. Flip it from the console without touching
20
+ * code:
21
+ *
22
+ * localStorage.setItem('reachy-log', 'debug'); location.reload();
23
+ *
24
+ * or at runtime via the exported `setLogLevel('debug')`.
25
+ */
26
+ const ORDER = {
27
+ debug: 0,
28
+ info: 1,
29
+ warn: 2,
30
+ error: 3,
31
+ silent: 4,
32
+ };
33
+ const STORAGE_KEY = 'reachy-log';
34
+ function initialLevel() {
35
+ try {
36
+ const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
37
+ if (stored && stored in ORDER)
38
+ return stored;
39
+ }
40
+ catch {
41
+ /* SSR or storage-denied context */
42
+ }
43
+ return 'info';
44
+ }
45
+ let currentLevel = initialLevel();
46
+ /** Set the global log level (persisted to localStorage when available). */
47
+ export function setLogLevel(level) {
48
+ currentLevel = level;
49
+ try {
50
+ globalThis.localStorage?.setItem(STORAGE_KEY, level);
51
+ }
52
+ catch {
53
+ /* ignore */
54
+ }
55
+ }
56
+ export function getLogLevel() {
57
+ return currentLevel;
58
+ }
59
+ /** Create a logger whose lines are prefixed `[reachy:<ns>]`. */
60
+ export function createLogger(ns) {
61
+ const prefix = `[reachy:${ns}]`;
62
+ return {
63
+ debug: (...args) => {
64
+ if (ORDER[currentLevel] <= ORDER.debug)
65
+ console.debug(prefix, ...args);
66
+ },
67
+ info: (...args) => {
68
+ if (ORDER[currentLevel] <= ORDER.info)
69
+ console.info(prefix, ...args);
70
+ },
71
+ warn: (...args) => {
72
+ if (ORDER[currentLevel] <= ORDER.warn)
73
+ console.warn(prefix, ...args);
74
+ },
75
+ error: (...args) => {
76
+ if (ORDER[currentLevel] <= ORDER.error)
77
+ console.error(prefix, ...args);
78
+ },
79
+ };
80
+ }
81
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../lib/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,MAAM,KAAK,GAA6B;IACpC,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;CACZ,CAAC;AAEF,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC,SAAS,YAAY;IACjB,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7D,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK;YAAE,OAAO,MAAkB,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACL,mCAAmC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,IAAI,YAAY,GAAa,YAAY,EAAE,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,KAAe;IACvC,YAAY,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC;QACD,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACL,YAAY;IAChB,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW;IACvB,OAAO,YAAY,CAAC;AACxB,CAAC;AASD,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,EAAU;IACnC,MAAM,MAAM,GAAG,WAAW,EAAE,GAAG,CAAC;IAChC,OAAO;QACH,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;YACf,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,KAAK;gBAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;YACd,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,IAAI;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;YACd,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,IAAI;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;YACf,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,KAAK;gBAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3E,CAAC;KACJ,CAAC;AACN,CAAC"}