@spooky-sync/core 0.0.1-canary.239 → 0.0.1-canary.240

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 (139) hide show
  1. package/coverage/lcov-report/base.css +224 -0
  2. package/coverage/lcov-report/block-navigation.js +87 -0
  3. package/coverage/lcov-report/favicon.png +0 -0
  4. package/coverage/lcov-report/index.html +476 -0
  5. package/coverage/lcov-report/prettify.css +1 -0
  6. package/coverage/lcov-report/prettify.js +2 -0
  7. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  8. package/coverage/lcov-report/sorter.js +210 -0
  9. package/coverage/lcov-report/src/boot/auth-flip.saga.ts.html +271 -0
  10. package/coverage/lcov-report/src/boot/boot.saga.ts.html +469 -0
  11. package/coverage/lcov-report/src/boot/bucket-switch.saga.ts.html +430 -0
  12. package/coverage/lcov-report/src/boot/index.html +161 -0
  13. package/coverage/lcov-report/src/boot/preload.saga.ts.html +178 -0
  14. package/coverage/lcov-report/src/client/bucket-handle.ts.html +766 -0
  15. package/coverage/lcov-report/src/client/index.html +161 -0
  16. package/coverage/lcov-report/src/client/router.ts.html +367 -0
  17. package/coverage/lcov-report/src/client/runtime.ts.html +940 -0
  18. package/coverage/lcov-report/src/client/sp00ky-client.ts.html +1531 -0
  19. package/coverage/lcov-report/src/events/index.html +116 -0
  20. package/coverage/lcov-report/src/events/index.ts.html +877 -0
  21. package/coverage/lcov-report/src/index.html +146 -0
  22. package/coverage/lcov-report/src/index.ts.html +202 -0
  23. package/coverage/lcov-report/src/kernel/constants.ts.html +247 -0
  24. package/coverage/lcov-report/src/kernel/effects.ts.html +562 -0
  25. package/coverage/lcov-report/src/kernel/events.ts.html +250 -0
  26. package/coverage/lcov-report/src/kernel/index.html +176 -0
  27. package/coverage/lcov-report/src/kernel/interpreter.ts.html +514 -0
  28. package/coverage/lcov-report/src/kernel/saga.ts.html +304 -0
  29. package/coverage/lcov-report/src/modules/app-release/index.html +116 -0
  30. package/coverage/lcov-report/src/modules/app-release/index.ts.html +712 -0
  31. package/coverage/lcov-report/src/modules/auth/events/index.html +116 -0
  32. package/coverage/lcov-report/src/modules/auth/events/index.ts.html +142 -0
  33. package/coverage/lcov-report/src/modules/auth/index.html +116 -0
  34. package/coverage/lcov-report/src/modules/auth/index.ts.html +1225 -0
  35. package/coverage/lcov-report/src/modules/crdt/crdt-field.ts.html +967 -0
  36. package/coverage/lcov-report/src/modules/crdt/index.html +146 -0
  37. package/coverage/lcov-report/src/modules/crdt/index.ts.html +1474 -0
  38. package/coverage/lcov-report/src/modules/crdt/loro-loader.ts.html +160 -0
  39. package/coverage/lcov-report/src/modules/devtools/flags.ts.html +1132 -0
  40. package/coverage/lcov-report/src/modules/devtools/index.html +161 -0
  41. package/coverage/lcov-report/src/modules/devtools/index.ts.html +2413 -0
  42. package/coverage/lcov-report/src/modules/devtools/storage-info.ts.html +589 -0
  43. package/coverage/lcov-report/src/modules/devtools/versions.ts.html +415 -0
  44. package/coverage/lcov-report/src/modules/feature-flag/index.html +116 -0
  45. package/coverage/lcov-report/src/modules/feature-flag/index.ts.html +1033 -0
  46. package/coverage/lcov-report/src/modules/index.html +116 -0
  47. package/coverage/lcov-report/src/modules/ref-tables.ts.html +349 -0
  48. package/coverage/lcov-report/src/mutation/index.html +191 -0
  49. package/coverage/lcov-report/src/mutation/jobs.ts.html +208 -0
  50. package/coverage/lcov-report/src/mutation/mutation-id.ts.html +190 -0
  51. package/coverage/lcov-report/src/mutation/push.saga.ts.html +730 -0
  52. package/coverage/lcov-report/src/mutation/rows.ts.html +1216 -0
  53. package/coverage/lcov-report/src/mutation/tray.saga.ts.html +229 -0
  54. package/coverage/lcov-report/src/mutation/write.saga.ts.html +583 -0
  55. package/coverage/lcov-report/src/otel/index.html +116 -0
  56. package/coverage/lcov-report/src/otel/index.ts.html +466 -0
  57. package/coverage/lcov-report/src/query/env.ts.html +253 -0
  58. package/coverage/lcov-report/src/query/fetch.saga.ts.html +412 -0
  59. package/coverage/lcov-report/src/query/hash.ts.html +139 -0
  60. package/coverage/lcov-report/src/query/index.html +281 -0
  61. package/coverage/lcov-report/src/query/lifecycle.saga.ts.html +388 -0
  62. package/coverage/lcov-report/src/query/materialize.saga.ts.html +310 -0
  63. package/coverage/lcov-report/src/query/materialize.ts.html +217 -0
  64. package/coverage/lcov-report/src/query/membership.saga.ts.html +709 -0
  65. package/coverage/lcov-report/src/query/membership.ts.html +559 -0
  66. package/coverage/lcov-report/src/query/register.saga.ts.html +655 -0
  67. package/coverage/lcov-report/src/query/render-set.ts.html +265 -0
  68. package/coverage/lcov-report/src/query/sql.ts.html +322 -0
  69. package/coverage/lcov-report/src/query/window-query.ts.html +670 -0
  70. package/coverage/lcov-report/src/services/blobs/blob-cache.ts.html +1894 -0
  71. package/coverage/lcov-report/src/services/blobs/blob-manifest.ts.html +766 -0
  72. package/coverage/lcov-report/src/services/blobs/blob-store.ts.html +1162 -0
  73. package/coverage/lcov-report/src/services/blobs/index.html +161 -0
  74. package/coverage/lcov-report/src/services/blobs/index.ts.html +295 -0
  75. package/coverage/lcov-report/src/services/database/cache-engine.ts.html +664 -0
  76. package/coverage/lcov-report/src/services/database/connection-supervisor.ts.html +1330 -0
  77. package/coverage/lcov-report/src/services/database/database.ts.html +658 -0
  78. package/coverage/lcov-report/src/services/database/engine-factory.ts.html +184 -0
  79. package/coverage/lcov-report/src/services/database/errors.ts.html +187 -0
  80. package/coverage/lcov-report/src/services/database/events/index.html +116 -0
  81. package/coverage/lcov-report/src/services/database/events/index.ts.html +184 -0
  82. package/coverage/lcov-report/src/services/database/index.html +386 -0
  83. package/coverage/lcov-report/src/services/database/index.ts.html +121 -0
  84. package/coverage/lcov-report/src/services/database/local-migrator.ts.html +703 -0
  85. package/coverage/lcov-report/src/services/database/local.ts.html +1633 -0
  86. package/coverage/lcov-report/src/services/database/plan-render.ts.html +409 -0
  87. package/coverage/lcov-report/src/services/database/relation-resolver.ts.html +682 -0
  88. package/coverage/lcov-report/src/services/database/remote.ts.html +1036 -0
  89. package/coverage/lcov-report/src/services/database/sqlite-cache-engine.ts.html +4132 -0
  90. package/coverage/lcov-report/src/services/database/sqlite-lock-verify.ts.html +220 -0
  91. package/coverage/lcov-report/src/services/database/sqlite-open.ts.html +577 -0
  92. package/coverage/lcov-report/src/services/database/sqlite-plan-sql.ts.html +499 -0
  93. package/coverage/lcov-report/src/services/database/sqlite-select.ts.html +478 -0
  94. package/coverage/lcov-report/src/services/database/sqlite-transport.ts.html +757 -0
  95. package/coverage/lcov-report/src/services/database/surql-translate.ts.html +1333 -0
  96. package/coverage/lcov-report/src/services/database/surreal-cache-engine.ts.html +568 -0
  97. package/coverage/lcov-report/src/services/logger/index.html +116 -0
  98. package/coverage/lcov-report/src/services/logger/index.ts.html +154 -0
  99. package/coverage/lcov-report/src/services/persistence/index.html +146 -0
  100. package/coverage/lcov-report/src/services/persistence/localstorage.ts.html +163 -0
  101. package/coverage/lcov-report/src/services/persistence/resilient.ts.html +208 -0
  102. package/coverage/lcov-report/src/services/persistence/surrealdb.ts.html +271 -0
  103. package/coverage/lcov-report/src/services/stream-processor/index.html +146 -0
  104. package/coverage/lcov-report/src/services/stream-processor/index.ts.html +3313 -0
  105. package/coverage/lcov-report/src/services/stream-processor/permissions.ts.html +244 -0
  106. package/coverage/lcov-report/src/services/stream-processor/wasm-types.ts.html +346 -0
  107. package/coverage/lcov-report/src/services/tabs/broker-client.ts.html +934 -0
  108. package/coverage/lcov-report/src/services/tabs/coordinator.ts.html +1984 -0
  109. package/coverage/lcov-report/src/services/tabs/index.html +191 -0
  110. package/coverage/lcov-report/src/services/tabs/leader-locks.ts.html +310 -0
  111. package/coverage/lcov-report/src/services/tabs/protocol.ts.html +871 -0
  112. package/coverage/lcov-report/src/services/tabs/support.ts.html +193 -0
  113. package/coverage/lcov-report/src/services/tabs/tabs-broker-worker.ts.html +2005 -0
  114. package/coverage/lcov-report/src/sp00ky.ts.html +100 -0
  115. package/coverage/lcov-report/src/state/client-state.ts.html +637 -0
  116. package/coverage/lcov-report/src/state/index.html +161 -0
  117. package/coverage/lcov-report/src/state/lifecycle.ts.html +343 -0
  118. package/coverage/lcov-report/src/state/reducers.ts.html +1471 -0
  119. package/coverage/lcov-report/src/state/selectors.ts.html +637 -0
  120. package/coverage/lcov-report/src/sync/connection.saga.ts.html +343 -0
  121. package/coverage/lcov-report/src/sync/index.html +176 -0
  122. package/coverage/lcov-report/src/sync/live.saga.ts.html +232 -0
  123. package/coverage/lcov-report/src/sync/policy.ts.html +1606 -0
  124. package/coverage/lcov-report/src/sync/poll.saga.ts.html +193 -0
  125. package/coverage/lcov-report/src/sync/tabs.saga.ts.html +361 -0
  126. package/coverage/lcov-report/src/types.ts.html +2419 -0
  127. package/coverage/lcov-report/src/utils/blurhash.ts.html +355 -0
  128. package/coverage/lcov-report/src/utils/error-classification.ts.html +244 -0
  129. package/coverage/lcov-report/src/utils/index.html +191 -0
  130. package/coverage/lcov-report/src/utils/index.ts.html +820 -0
  131. package/coverage/lcov-report/src/utils/parser.ts.html +313 -0
  132. package/coverage/lcov-report/src/utils/semver.ts.html +175 -0
  133. package/coverage/lcov-report/src/utils/surql.ts.html +667 -0
  134. package/coverage/lcov.info +19079 -0
  135. package/dist/index.js +3 -3
  136. package/dist/sqlite-open.js +20 -1
  137. package/package.json +3 -3
  138. package/src/services/database/relation-resolver.test.ts +55 -1
  139. package/src/services/database/relation-resolver.ts +0 -0
@@ -0,0 +1,1984 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/services/tabs/coordinator.ts</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../../../prettify.css" />
9
+ <link rel="stylesheet" href="../../../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../../../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../../../index.html">All files</a> / <a href="index.html">src/services/tabs</a> coordinator.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">82.65% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>367/444</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">75.89% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>85/112</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">78.18% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>43/55</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">82.65% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>367/444</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a>
91
+ <a name='L26'></a><a href='#L26'>26</a>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a>
99
+ <a name='L34'></a><a href='#L34'>34</a>
100
+ <a name='L35'></a><a href='#L35'>35</a>
101
+ <a name='L36'></a><a href='#L36'>36</a>
102
+ <a name='L37'></a><a href='#L37'>37</a>
103
+ <a name='L38'></a><a href='#L38'>38</a>
104
+ <a name='L39'></a><a href='#L39'>39</a>
105
+ <a name='L40'></a><a href='#L40'>40</a>
106
+ <a name='L41'></a><a href='#L41'>41</a>
107
+ <a name='L42'></a><a href='#L42'>42</a>
108
+ <a name='L43'></a><a href='#L43'>43</a>
109
+ <a name='L44'></a><a href='#L44'>44</a>
110
+ <a name='L45'></a><a href='#L45'>45</a>
111
+ <a name='L46'></a><a href='#L46'>46</a>
112
+ <a name='L47'></a><a href='#L47'>47</a>
113
+ <a name='L48'></a><a href='#L48'>48</a>
114
+ <a name='L49'></a><a href='#L49'>49</a>
115
+ <a name='L50'></a><a href='#L50'>50</a>
116
+ <a name='L51'></a><a href='#L51'>51</a>
117
+ <a name='L52'></a><a href='#L52'>52</a>
118
+ <a name='L53'></a><a href='#L53'>53</a>
119
+ <a name='L54'></a><a href='#L54'>54</a>
120
+ <a name='L55'></a><a href='#L55'>55</a>
121
+ <a name='L56'></a><a href='#L56'>56</a>
122
+ <a name='L57'></a><a href='#L57'>57</a>
123
+ <a name='L58'></a><a href='#L58'>58</a>
124
+ <a name='L59'></a><a href='#L59'>59</a>
125
+ <a name='L60'></a><a href='#L60'>60</a>
126
+ <a name='L61'></a><a href='#L61'>61</a>
127
+ <a name='L62'></a><a href='#L62'>62</a>
128
+ <a name='L63'></a><a href='#L63'>63</a>
129
+ <a name='L64'></a><a href='#L64'>64</a>
130
+ <a name='L65'></a><a href='#L65'>65</a>
131
+ <a name='L66'></a><a href='#L66'>66</a>
132
+ <a name='L67'></a><a href='#L67'>67</a>
133
+ <a name='L68'></a><a href='#L68'>68</a>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a>
141
+ <a name='L76'></a><a href='#L76'>76</a>
142
+ <a name='L77'></a><a href='#L77'>77</a>
143
+ <a name='L78'></a><a href='#L78'>78</a>
144
+ <a name='L79'></a><a href='#L79'>79</a>
145
+ <a name='L80'></a><a href='#L80'>80</a>
146
+ <a name='L81'></a><a href='#L81'>81</a>
147
+ <a name='L82'></a><a href='#L82'>82</a>
148
+ <a name='L83'></a><a href='#L83'>83</a>
149
+ <a name='L84'></a><a href='#L84'>84</a>
150
+ <a name='L85'></a><a href='#L85'>85</a>
151
+ <a name='L86'></a><a href='#L86'>86</a>
152
+ <a name='L87'></a><a href='#L87'>87</a>
153
+ <a name='L88'></a><a href='#L88'>88</a>
154
+ <a name='L89'></a><a href='#L89'>89</a>
155
+ <a name='L90'></a><a href='#L90'>90</a>
156
+ <a name='L91'></a><a href='#L91'>91</a>
157
+ <a name='L92'></a><a href='#L92'>92</a>
158
+ <a name='L93'></a><a href='#L93'>93</a>
159
+ <a name='L94'></a><a href='#L94'>94</a>
160
+ <a name='L95'></a><a href='#L95'>95</a>
161
+ <a name='L96'></a><a href='#L96'>96</a>
162
+ <a name='L97'></a><a href='#L97'>97</a>
163
+ <a name='L98'></a><a href='#L98'>98</a>
164
+ <a name='L99'></a><a href='#L99'>99</a>
165
+ <a name='L100'></a><a href='#L100'>100</a>
166
+ <a name='L101'></a><a href='#L101'>101</a>
167
+ <a name='L102'></a><a href='#L102'>102</a>
168
+ <a name='L103'></a><a href='#L103'>103</a>
169
+ <a name='L104'></a><a href='#L104'>104</a>
170
+ <a name='L105'></a><a href='#L105'>105</a>
171
+ <a name='L106'></a><a href='#L106'>106</a>
172
+ <a name='L107'></a><a href='#L107'>107</a>
173
+ <a name='L108'></a><a href='#L108'>108</a>
174
+ <a name='L109'></a><a href='#L109'>109</a>
175
+ <a name='L110'></a><a href='#L110'>110</a>
176
+ <a name='L111'></a><a href='#L111'>111</a>
177
+ <a name='L112'></a><a href='#L112'>112</a>
178
+ <a name='L113'></a><a href='#L113'>113</a>
179
+ <a name='L114'></a><a href='#L114'>114</a>
180
+ <a name='L115'></a><a href='#L115'>115</a>
181
+ <a name='L116'></a><a href='#L116'>116</a>
182
+ <a name='L117'></a><a href='#L117'>117</a>
183
+ <a name='L118'></a><a href='#L118'>118</a>
184
+ <a name='L119'></a><a href='#L119'>119</a>
185
+ <a name='L120'></a><a href='#L120'>120</a>
186
+ <a name='L121'></a><a href='#L121'>121</a>
187
+ <a name='L122'></a><a href='#L122'>122</a>
188
+ <a name='L123'></a><a href='#L123'>123</a>
189
+ <a name='L124'></a><a href='#L124'>124</a>
190
+ <a name='L125'></a><a href='#L125'>125</a>
191
+ <a name='L126'></a><a href='#L126'>126</a>
192
+ <a name='L127'></a><a href='#L127'>127</a>
193
+ <a name='L128'></a><a href='#L128'>128</a>
194
+ <a name='L129'></a><a href='#L129'>129</a>
195
+ <a name='L130'></a><a href='#L130'>130</a>
196
+ <a name='L131'></a><a href='#L131'>131</a>
197
+ <a name='L132'></a><a href='#L132'>132</a>
198
+ <a name='L133'></a><a href='#L133'>133</a>
199
+ <a name='L134'></a><a href='#L134'>134</a>
200
+ <a name='L135'></a><a href='#L135'>135</a>
201
+ <a name='L136'></a><a href='#L136'>136</a>
202
+ <a name='L137'></a><a href='#L137'>137</a>
203
+ <a name='L138'></a><a href='#L138'>138</a>
204
+ <a name='L139'></a><a href='#L139'>139</a>
205
+ <a name='L140'></a><a href='#L140'>140</a>
206
+ <a name='L141'></a><a href='#L141'>141</a>
207
+ <a name='L142'></a><a href='#L142'>142</a>
208
+ <a name='L143'></a><a href='#L143'>143</a>
209
+ <a name='L144'></a><a href='#L144'>144</a>
210
+ <a name='L145'></a><a href='#L145'>145</a>
211
+ <a name='L146'></a><a href='#L146'>146</a>
212
+ <a name='L147'></a><a href='#L147'>147</a>
213
+ <a name='L148'></a><a href='#L148'>148</a>
214
+ <a name='L149'></a><a href='#L149'>149</a>
215
+ <a name='L150'></a><a href='#L150'>150</a>
216
+ <a name='L151'></a><a href='#L151'>151</a>
217
+ <a name='L152'></a><a href='#L152'>152</a>
218
+ <a name='L153'></a><a href='#L153'>153</a>
219
+ <a name='L154'></a><a href='#L154'>154</a>
220
+ <a name='L155'></a><a href='#L155'>155</a>
221
+ <a name='L156'></a><a href='#L156'>156</a>
222
+ <a name='L157'></a><a href='#L157'>157</a>
223
+ <a name='L158'></a><a href='#L158'>158</a>
224
+ <a name='L159'></a><a href='#L159'>159</a>
225
+ <a name='L160'></a><a href='#L160'>160</a>
226
+ <a name='L161'></a><a href='#L161'>161</a>
227
+ <a name='L162'></a><a href='#L162'>162</a>
228
+ <a name='L163'></a><a href='#L163'>163</a>
229
+ <a name='L164'></a><a href='#L164'>164</a>
230
+ <a name='L165'></a><a href='#L165'>165</a>
231
+ <a name='L166'></a><a href='#L166'>166</a>
232
+ <a name='L167'></a><a href='#L167'>167</a>
233
+ <a name='L168'></a><a href='#L168'>168</a>
234
+ <a name='L169'></a><a href='#L169'>169</a>
235
+ <a name='L170'></a><a href='#L170'>170</a>
236
+ <a name='L171'></a><a href='#L171'>171</a>
237
+ <a name='L172'></a><a href='#L172'>172</a>
238
+ <a name='L173'></a><a href='#L173'>173</a>
239
+ <a name='L174'></a><a href='#L174'>174</a>
240
+ <a name='L175'></a><a href='#L175'>175</a>
241
+ <a name='L176'></a><a href='#L176'>176</a>
242
+ <a name='L177'></a><a href='#L177'>177</a>
243
+ <a name='L178'></a><a href='#L178'>178</a>
244
+ <a name='L179'></a><a href='#L179'>179</a>
245
+ <a name='L180'></a><a href='#L180'>180</a>
246
+ <a name='L181'></a><a href='#L181'>181</a>
247
+ <a name='L182'></a><a href='#L182'>182</a>
248
+ <a name='L183'></a><a href='#L183'>183</a>
249
+ <a name='L184'></a><a href='#L184'>184</a>
250
+ <a name='L185'></a><a href='#L185'>185</a>
251
+ <a name='L186'></a><a href='#L186'>186</a>
252
+ <a name='L187'></a><a href='#L187'>187</a>
253
+ <a name='L188'></a><a href='#L188'>188</a>
254
+ <a name='L189'></a><a href='#L189'>189</a>
255
+ <a name='L190'></a><a href='#L190'>190</a>
256
+ <a name='L191'></a><a href='#L191'>191</a>
257
+ <a name='L192'></a><a href='#L192'>192</a>
258
+ <a name='L193'></a><a href='#L193'>193</a>
259
+ <a name='L194'></a><a href='#L194'>194</a>
260
+ <a name='L195'></a><a href='#L195'>195</a>
261
+ <a name='L196'></a><a href='#L196'>196</a>
262
+ <a name='L197'></a><a href='#L197'>197</a>
263
+ <a name='L198'></a><a href='#L198'>198</a>
264
+ <a name='L199'></a><a href='#L199'>199</a>
265
+ <a name='L200'></a><a href='#L200'>200</a>
266
+ <a name='L201'></a><a href='#L201'>201</a>
267
+ <a name='L202'></a><a href='#L202'>202</a>
268
+ <a name='L203'></a><a href='#L203'>203</a>
269
+ <a name='L204'></a><a href='#L204'>204</a>
270
+ <a name='L205'></a><a href='#L205'>205</a>
271
+ <a name='L206'></a><a href='#L206'>206</a>
272
+ <a name='L207'></a><a href='#L207'>207</a>
273
+ <a name='L208'></a><a href='#L208'>208</a>
274
+ <a name='L209'></a><a href='#L209'>209</a>
275
+ <a name='L210'></a><a href='#L210'>210</a>
276
+ <a name='L211'></a><a href='#L211'>211</a>
277
+ <a name='L212'></a><a href='#L212'>212</a>
278
+ <a name='L213'></a><a href='#L213'>213</a>
279
+ <a name='L214'></a><a href='#L214'>214</a>
280
+ <a name='L215'></a><a href='#L215'>215</a>
281
+ <a name='L216'></a><a href='#L216'>216</a>
282
+ <a name='L217'></a><a href='#L217'>217</a>
283
+ <a name='L218'></a><a href='#L218'>218</a>
284
+ <a name='L219'></a><a href='#L219'>219</a>
285
+ <a name='L220'></a><a href='#L220'>220</a>
286
+ <a name='L221'></a><a href='#L221'>221</a>
287
+ <a name='L222'></a><a href='#L222'>222</a>
288
+ <a name='L223'></a><a href='#L223'>223</a>
289
+ <a name='L224'></a><a href='#L224'>224</a>
290
+ <a name='L225'></a><a href='#L225'>225</a>
291
+ <a name='L226'></a><a href='#L226'>226</a>
292
+ <a name='L227'></a><a href='#L227'>227</a>
293
+ <a name='L228'></a><a href='#L228'>228</a>
294
+ <a name='L229'></a><a href='#L229'>229</a>
295
+ <a name='L230'></a><a href='#L230'>230</a>
296
+ <a name='L231'></a><a href='#L231'>231</a>
297
+ <a name='L232'></a><a href='#L232'>232</a>
298
+ <a name='L233'></a><a href='#L233'>233</a>
299
+ <a name='L234'></a><a href='#L234'>234</a>
300
+ <a name='L235'></a><a href='#L235'>235</a>
301
+ <a name='L236'></a><a href='#L236'>236</a>
302
+ <a name='L237'></a><a href='#L237'>237</a>
303
+ <a name='L238'></a><a href='#L238'>238</a>
304
+ <a name='L239'></a><a href='#L239'>239</a>
305
+ <a name='L240'></a><a href='#L240'>240</a>
306
+ <a name='L241'></a><a href='#L241'>241</a>
307
+ <a name='L242'></a><a href='#L242'>242</a>
308
+ <a name='L243'></a><a href='#L243'>243</a>
309
+ <a name='L244'></a><a href='#L244'>244</a>
310
+ <a name='L245'></a><a href='#L245'>245</a>
311
+ <a name='L246'></a><a href='#L246'>246</a>
312
+ <a name='L247'></a><a href='#L247'>247</a>
313
+ <a name='L248'></a><a href='#L248'>248</a>
314
+ <a name='L249'></a><a href='#L249'>249</a>
315
+ <a name='L250'></a><a href='#L250'>250</a>
316
+ <a name='L251'></a><a href='#L251'>251</a>
317
+ <a name='L252'></a><a href='#L252'>252</a>
318
+ <a name='L253'></a><a href='#L253'>253</a>
319
+ <a name='L254'></a><a href='#L254'>254</a>
320
+ <a name='L255'></a><a href='#L255'>255</a>
321
+ <a name='L256'></a><a href='#L256'>256</a>
322
+ <a name='L257'></a><a href='#L257'>257</a>
323
+ <a name='L258'></a><a href='#L258'>258</a>
324
+ <a name='L259'></a><a href='#L259'>259</a>
325
+ <a name='L260'></a><a href='#L260'>260</a>
326
+ <a name='L261'></a><a href='#L261'>261</a>
327
+ <a name='L262'></a><a href='#L262'>262</a>
328
+ <a name='L263'></a><a href='#L263'>263</a>
329
+ <a name='L264'></a><a href='#L264'>264</a>
330
+ <a name='L265'></a><a href='#L265'>265</a>
331
+ <a name='L266'></a><a href='#L266'>266</a>
332
+ <a name='L267'></a><a href='#L267'>267</a>
333
+ <a name='L268'></a><a href='#L268'>268</a>
334
+ <a name='L269'></a><a href='#L269'>269</a>
335
+ <a name='L270'></a><a href='#L270'>270</a>
336
+ <a name='L271'></a><a href='#L271'>271</a>
337
+ <a name='L272'></a><a href='#L272'>272</a>
338
+ <a name='L273'></a><a href='#L273'>273</a>
339
+ <a name='L274'></a><a href='#L274'>274</a>
340
+ <a name='L275'></a><a href='#L275'>275</a>
341
+ <a name='L276'></a><a href='#L276'>276</a>
342
+ <a name='L277'></a><a href='#L277'>277</a>
343
+ <a name='L278'></a><a href='#L278'>278</a>
344
+ <a name='L279'></a><a href='#L279'>279</a>
345
+ <a name='L280'></a><a href='#L280'>280</a>
346
+ <a name='L281'></a><a href='#L281'>281</a>
347
+ <a name='L282'></a><a href='#L282'>282</a>
348
+ <a name='L283'></a><a href='#L283'>283</a>
349
+ <a name='L284'></a><a href='#L284'>284</a>
350
+ <a name='L285'></a><a href='#L285'>285</a>
351
+ <a name='L286'></a><a href='#L286'>286</a>
352
+ <a name='L287'></a><a href='#L287'>287</a>
353
+ <a name='L288'></a><a href='#L288'>288</a>
354
+ <a name='L289'></a><a href='#L289'>289</a>
355
+ <a name='L290'></a><a href='#L290'>290</a>
356
+ <a name='L291'></a><a href='#L291'>291</a>
357
+ <a name='L292'></a><a href='#L292'>292</a>
358
+ <a name='L293'></a><a href='#L293'>293</a>
359
+ <a name='L294'></a><a href='#L294'>294</a>
360
+ <a name='L295'></a><a href='#L295'>295</a>
361
+ <a name='L296'></a><a href='#L296'>296</a>
362
+ <a name='L297'></a><a href='#L297'>297</a>
363
+ <a name='L298'></a><a href='#L298'>298</a>
364
+ <a name='L299'></a><a href='#L299'>299</a>
365
+ <a name='L300'></a><a href='#L300'>300</a>
366
+ <a name='L301'></a><a href='#L301'>301</a>
367
+ <a name='L302'></a><a href='#L302'>302</a>
368
+ <a name='L303'></a><a href='#L303'>303</a>
369
+ <a name='L304'></a><a href='#L304'>304</a>
370
+ <a name='L305'></a><a href='#L305'>305</a>
371
+ <a name='L306'></a><a href='#L306'>306</a>
372
+ <a name='L307'></a><a href='#L307'>307</a>
373
+ <a name='L308'></a><a href='#L308'>308</a>
374
+ <a name='L309'></a><a href='#L309'>309</a>
375
+ <a name='L310'></a><a href='#L310'>310</a>
376
+ <a name='L311'></a><a href='#L311'>311</a>
377
+ <a name='L312'></a><a href='#L312'>312</a>
378
+ <a name='L313'></a><a href='#L313'>313</a>
379
+ <a name='L314'></a><a href='#L314'>314</a>
380
+ <a name='L315'></a><a href='#L315'>315</a>
381
+ <a name='L316'></a><a href='#L316'>316</a>
382
+ <a name='L317'></a><a href='#L317'>317</a>
383
+ <a name='L318'></a><a href='#L318'>318</a>
384
+ <a name='L319'></a><a href='#L319'>319</a>
385
+ <a name='L320'></a><a href='#L320'>320</a>
386
+ <a name='L321'></a><a href='#L321'>321</a>
387
+ <a name='L322'></a><a href='#L322'>322</a>
388
+ <a name='L323'></a><a href='#L323'>323</a>
389
+ <a name='L324'></a><a href='#L324'>324</a>
390
+ <a name='L325'></a><a href='#L325'>325</a>
391
+ <a name='L326'></a><a href='#L326'>326</a>
392
+ <a name='L327'></a><a href='#L327'>327</a>
393
+ <a name='L328'></a><a href='#L328'>328</a>
394
+ <a name='L329'></a><a href='#L329'>329</a>
395
+ <a name='L330'></a><a href='#L330'>330</a>
396
+ <a name='L331'></a><a href='#L331'>331</a>
397
+ <a name='L332'></a><a href='#L332'>332</a>
398
+ <a name='L333'></a><a href='#L333'>333</a>
399
+ <a name='L334'></a><a href='#L334'>334</a>
400
+ <a name='L335'></a><a href='#L335'>335</a>
401
+ <a name='L336'></a><a href='#L336'>336</a>
402
+ <a name='L337'></a><a href='#L337'>337</a>
403
+ <a name='L338'></a><a href='#L338'>338</a>
404
+ <a name='L339'></a><a href='#L339'>339</a>
405
+ <a name='L340'></a><a href='#L340'>340</a>
406
+ <a name='L341'></a><a href='#L341'>341</a>
407
+ <a name='L342'></a><a href='#L342'>342</a>
408
+ <a name='L343'></a><a href='#L343'>343</a>
409
+ <a name='L344'></a><a href='#L344'>344</a>
410
+ <a name='L345'></a><a href='#L345'>345</a>
411
+ <a name='L346'></a><a href='#L346'>346</a>
412
+ <a name='L347'></a><a href='#L347'>347</a>
413
+ <a name='L348'></a><a href='#L348'>348</a>
414
+ <a name='L349'></a><a href='#L349'>349</a>
415
+ <a name='L350'></a><a href='#L350'>350</a>
416
+ <a name='L351'></a><a href='#L351'>351</a>
417
+ <a name='L352'></a><a href='#L352'>352</a>
418
+ <a name='L353'></a><a href='#L353'>353</a>
419
+ <a name='L354'></a><a href='#L354'>354</a>
420
+ <a name='L355'></a><a href='#L355'>355</a>
421
+ <a name='L356'></a><a href='#L356'>356</a>
422
+ <a name='L357'></a><a href='#L357'>357</a>
423
+ <a name='L358'></a><a href='#L358'>358</a>
424
+ <a name='L359'></a><a href='#L359'>359</a>
425
+ <a name='L360'></a><a href='#L360'>360</a>
426
+ <a name='L361'></a><a href='#L361'>361</a>
427
+ <a name='L362'></a><a href='#L362'>362</a>
428
+ <a name='L363'></a><a href='#L363'>363</a>
429
+ <a name='L364'></a><a href='#L364'>364</a>
430
+ <a name='L365'></a><a href='#L365'>365</a>
431
+ <a name='L366'></a><a href='#L366'>366</a>
432
+ <a name='L367'></a><a href='#L367'>367</a>
433
+ <a name='L368'></a><a href='#L368'>368</a>
434
+ <a name='L369'></a><a href='#L369'>369</a>
435
+ <a name='L370'></a><a href='#L370'>370</a>
436
+ <a name='L371'></a><a href='#L371'>371</a>
437
+ <a name='L372'></a><a href='#L372'>372</a>
438
+ <a name='L373'></a><a href='#L373'>373</a>
439
+ <a name='L374'></a><a href='#L374'>374</a>
440
+ <a name='L375'></a><a href='#L375'>375</a>
441
+ <a name='L376'></a><a href='#L376'>376</a>
442
+ <a name='L377'></a><a href='#L377'>377</a>
443
+ <a name='L378'></a><a href='#L378'>378</a>
444
+ <a name='L379'></a><a href='#L379'>379</a>
445
+ <a name='L380'></a><a href='#L380'>380</a>
446
+ <a name='L381'></a><a href='#L381'>381</a>
447
+ <a name='L382'></a><a href='#L382'>382</a>
448
+ <a name='L383'></a><a href='#L383'>383</a>
449
+ <a name='L384'></a><a href='#L384'>384</a>
450
+ <a name='L385'></a><a href='#L385'>385</a>
451
+ <a name='L386'></a><a href='#L386'>386</a>
452
+ <a name='L387'></a><a href='#L387'>387</a>
453
+ <a name='L388'></a><a href='#L388'>388</a>
454
+ <a name='L389'></a><a href='#L389'>389</a>
455
+ <a name='L390'></a><a href='#L390'>390</a>
456
+ <a name='L391'></a><a href='#L391'>391</a>
457
+ <a name='L392'></a><a href='#L392'>392</a>
458
+ <a name='L393'></a><a href='#L393'>393</a>
459
+ <a name='L394'></a><a href='#L394'>394</a>
460
+ <a name='L395'></a><a href='#L395'>395</a>
461
+ <a name='L396'></a><a href='#L396'>396</a>
462
+ <a name='L397'></a><a href='#L397'>397</a>
463
+ <a name='L398'></a><a href='#L398'>398</a>
464
+ <a name='L399'></a><a href='#L399'>399</a>
465
+ <a name='L400'></a><a href='#L400'>400</a>
466
+ <a name='L401'></a><a href='#L401'>401</a>
467
+ <a name='L402'></a><a href='#L402'>402</a>
468
+ <a name='L403'></a><a href='#L403'>403</a>
469
+ <a name='L404'></a><a href='#L404'>404</a>
470
+ <a name='L405'></a><a href='#L405'>405</a>
471
+ <a name='L406'></a><a href='#L406'>406</a>
472
+ <a name='L407'></a><a href='#L407'>407</a>
473
+ <a name='L408'></a><a href='#L408'>408</a>
474
+ <a name='L409'></a><a href='#L409'>409</a>
475
+ <a name='L410'></a><a href='#L410'>410</a>
476
+ <a name='L411'></a><a href='#L411'>411</a>
477
+ <a name='L412'></a><a href='#L412'>412</a>
478
+ <a name='L413'></a><a href='#L413'>413</a>
479
+ <a name='L414'></a><a href='#L414'>414</a>
480
+ <a name='L415'></a><a href='#L415'>415</a>
481
+ <a name='L416'></a><a href='#L416'>416</a>
482
+ <a name='L417'></a><a href='#L417'>417</a>
483
+ <a name='L418'></a><a href='#L418'>418</a>
484
+ <a name='L419'></a><a href='#L419'>419</a>
485
+ <a name='L420'></a><a href='#L420'>420</a>
486
+ <a name='L421'></a><a href='#L421'>421</a>
487
+ <a name='L422'></a><a href='#L422'>422</a>
488
+ <a name='L423'></a><a href='#L423'>423</a>
489
+ <a name='L424'></a><a href='#L424'>424</a>
490
+ <a name='L425'></a><a href='#L425'>425</a>
491
+ <a name='L426'></a><a href='#L426'>426</a>
492
+ <a name='L427'></a><a href='#L427'>427</a>
493
+ <a name='L428'></a><a href='#L428'>428</a>
494
+ <a name='L429'></a><a href='#L429'>429</a>
495
+ <a name='L430'></a><a href='#L430'>430</a>
496
+ <a name='L431'></a><a href='#L431'>431</a>
497
+ <a name='L432'></a><a href='#L432'>432</a>
498
+ <a name='L433'></a><a href='#L433'>433</a>
499
+ <a name='L434'></a><a href='#L434'>434</a>
500
+ <a name='L435'></a><a href='#L435'>435</a>
501
+ <a name='L436'></a><a href='#L436'>436</a>
502
+ <a name='L437'></a><a href='#L437'>437</a>
503
+ <a name='L438'></a><a href='#L438'>438</a>
504
+ <a name='L439'></a><a href='#L439'>439</a>
505
+ <a name='L440'></a><a href='#L440'>440</a>
506
+ <a name='L441'></a><a href='#L441'>441</a>
507
+ <a name='L442'></a><a href='#L442'>442</a>
508
+ <a name='L443'></a><a href='#L443'>443</a>
509
+ <a name='L444'></a><a href='#L444'>444</a>
510
+ <a name='L445'></a><a href='#L445'>445</a>
511
+ <a name='L446'></a><a href='#L446'>446</a>
512
+ <a name='L447'></a><a href='#L447'>447</a>
513
+ <a name='L448'></a><a href='#L448'>448</a>
514
+ <a name='L449'></a><a href='#L449'>449</a>
515
+ <a name='L450'></a><a href='#L450'>450</a>
516
+ <a name='L451'></a><a href='#L451'>451</a>
517
+ <a name='L452'></a><a href='#L452'>452</a>
518
+ <a name='L453'></a><a href='#L453'>453</a>
519
+ <a name='L454'></a><a href='#L454'>454</a>
520
+ <a name='L455'></a><a href='#L455'>455</a>
521
+ <a name='L456'></a><a href='#L456'>456</a>
522
+ <a name='L457'></a><a href='#L457'>457</a>
523
+ <a name='L458'></a><a href='#L458'>458</a>
524
+ <a name='L459'></a><a href='#L459'>459</a>
525
+ <a name='L460'></a><a href='#L460'>460</a>
526
+ <a name='L461'></a><a href='#L461'>461</a>
527
+ <a name='L462'></a><a href='#L462'>462</a>
528
+ <a name='L463'></a><a href='#L463'>463</a>
529
+ <a name='L464'></a><a href='#L464'>464</a>
530
+ <a name='L465'></a><a href='#L465'>465</a>
531
+ <a name='L466'></a><a href='#L466'>466</a>
532
+ <a name='L467'></a><a href='#L467'>467</a>
533
+ <a name='L468'></a><a href='#L468'>468</a>
534
+ <a name='L469'></a><a href='#L469'>469</a>
535
+ <a name='L470'></a><a href='#L470'>470</a>
536
+ <a name='L471'></a><a href='#L471'>471</a>
537
+ <a name='L472'></a><a href='#L472'>472</a>
538
+ <a name='L473'></a><a href='#L473'>473</a>
539
+ <a name='L474'></a><a href='#L474'>474</a>
540
+ <a name='L475'></a><a href='#L475'>475</a>
541
+ <a name='L476'></a><a href='#L476'>476</a>
542
+ <a name='L477'></a><a href='#L477'>477</a>
543
+ <a name='L478'></a><a href='#L478'>478</a>
544
+ <a name='L479'></a><a href='#L479'>479</a>
545
+ <a name='L480'></a><a href='#L480'>480</a>
546
+ <a name='L481'></a><a href='#L481'>481</a>
547
+ <a name='L482'></a><a href='#L482'>482</a>
548
+ <a name='L483'></a><a href='#L483'>483</a>
549
+ <a name='L484'></a><a href='#L484'>484</a>
550
+ <a name='L485'></a><a href='#L485'>485</a>
551
+ <a name='L486'></a><a href='#L486'>486</a>
552
+ <a name='L487'></a><a href='#L487'>487</a>
553
+ <a name='L488'></a><a href='#L488'>488</a>
554
+ <a name='L489'></a><a href='#L489'>489</a>
555
+ <a name='L490'></a><a href='#L490'>490</a>
556
+ <a name='L491'></a><a href='#L491'>491</a>
557
+ <a name='L492'></a><a href='#L492'>492</a>
558
+ <a name='L493'></a><a href='#L493'>493</a>
559
+ <a name='L494'></a><a href='#L494'>494</a>
560
+ <a name='L495'></a><a href='#L495'>495</a>
561
+ <a name='L496'></a><a href='#L496'>496</a>
562
+ <a name='L497'></a><a href='#L497'>497</a>
563
+ <a name='L498'></a><a href='#L498'>498</a>
564
+ <a name='L499'></a><a href='#L499'>499</a>
565
+ <a name='L500'></a><a href='#L500'>500</a>
566
+ <a name='L501'></a><a href='#L501'>501</a>
567
+ <a name='L502'></a><a href='#L502'>502</a>
568
+ <a name='L503'></a><a href='#L503'>503</a>
569
+ <a name='L504'></a><a href='#L504'>504</a>
570
+ <a name='L505'></a><a href='#L505'>505</a>
571
+ <a name='L506'></a><a href='#L506'>506</a>
572
+ <a name='L507'></a><a href='#L507'>507</a>
573
+ <a name='L508'></a><a href='#L508'>508</a>
574
+ <a name='L509'></a><a href='#L509'>509</a>
575
+ <a name='L510'></a><a href='#L510'>510</a>
576
+ <a name='L511'></a><a href='#L511'>511</a>
577
+ <a name='L512'></a><a href='#L512'>512</a>
578
+ <a name='L513'></a><a href='#L513'>513</a>
579
+ <a name='L514'></a><a href='#L514'>514</a>
580
+ <a name='L515'></a><a href='#L515'>515</a>
581
+ <a name='L516'></a><a href='#L516'>516</a>
582
+ <a name='L517'></a><a href='#L517'>517</a>
583
+ <a name='L518'></a><a href='#L518'>518</a>
584
+ <a name='L519'></a><a href='#L519'>519</a>
585
+ <a name='L520'></a><a href='#L520'>520</a>
586
+ <a name='L521'></a><a href='#L521'>521</a>
587
+ <a name='L522'></a><a href='#L522'>522</a>
588
+ <a name='L523'></a><a href='#L523'>523</a>
589
+ <a name='L524'></a><a href='#L524'>524</a>
590
+ <a name='L525'></a><a href='#L525'>525</a>
591
+ <a name='L526'></a><a href='#L526'>526</a>
592
+ <a name='L527'></a><a href='#L527'>527</a>
593
+ <a name='L528'></a><a href='#L528'>528</a>
594
+ <a name='L529'></a><a href='#L529'>529</a>
595
+ <a name='L530'></a><a href='#L530'>530</a>
596
+ <a name='L531'></a><a href='#L531'>531</a>
597
+ <a name='L532'></a><a href='#L532'>532</a>
598
+ <a name='L533'></a><a href='#L533'>533</a>
599
+ <a name='L534'></a><a href='#L534'>534</a>
600
+ <a name='L535'></a><a href='#L535'>535</a>
601
+ <a name='L536'></a><a href='#L536'>536</a>
602
+ <a name='L537'></a><a href='#L537'>537</a>
603
+ <a name='L538'></a><a href='#L538'>538</a>
604
+ <a name='L539'></a><a href='#L539'>539</a>
605
+ <a name='L540'></a><a href='#L540'>540</a>
606
+ <a name='L541'></a><a href='#L541'>541</a>
607
+ <a name='L542'></a><a href='#L542'>542</a>
608
+ <a name='L543'></a><a href='#L543'>543</a>
609
+ <a name='L544'></a><a href='#L544'>544</a>
610
+ <a name='L545'></a><a href='#L545'>545</a>
611
+ <a name='L546'></a><a href='#L546'>546</a>
612
+ <a name='L547'></a><a href='#L547'>547</a>
613
+ <a name='L548'></a><a href='#L548'>548</a>
614
+ <a name='L549'></a><a href='#L549'>549</a>
615
+ <a name='L550'></a><a href='#L550'>550</a>
616
+ <a name='L551'></a><a href='#L551'>551</a>
617
+ <a name='L552'></a><a href='#L552'>552</a>
618
+ <a name='L553'></a><a href='#L553'>553</a>
619
+ <a name='L554'></a><a href='#L554'>554</a>
620
+ <a name='L555'></a><a href='#L555'>555</a>
621
+ <a name='L556'></a><a href='#L556'>556</a>
622
+ <a name='L557'></a><a href='#L557'>557</a>
623
+ <a name='L558'></a><a href='#L558'>558</a>
624
+ <a name='L559'></a><a href='#L559'>559</a>
625
+ <a name='L560'></a><a href='#L560'>560</a>
626
+ <a name='L561'></a><a href='#L561'>561</a>
627
+ <a name='L562'></a><a href='#L562'>562</a>
628
+ <a name='L563'></a><a href='#L563'>563</a>
629
+ <a name='L564'></a><a href='#L564'>564</a>
630
+ <a name='L565'></a><a href='#L565'>565</a>
631
+ <a name='L566'></a><a href='#L566'>566</a>
632
+ <a name='L567'></a><a href='#L567'>567</a>
633
+ <a name='L568'></a><a href='#L568'>568</a>
634
+ <a name='L569'></a><a href='#L569'>569</a>
635
+ <a name='L570'></a><a href='#L570'>570</a>
636
+ <a name='L571'></a><a href='#L571'>571</a>
637
+ <a name='L572'></a><a href='#L572'>572</a>
638
+ <a name='L573'></a><a href='#L573'>573</a>
639
+ <a name='L574'></a><a href='#L574'>574</a>
640
+ <a name='L575'></a><a href='#L575'>575</a>
641
+ <a name='L576'></a><a href='#L576'>576</a>
642
+ <a name='L577'></a><a href='#L577'>577</a>
643
+ <a name='L578'></a><a href='#L578'>578</a>
644
+ <a name='L579'></a><a href='#L579'>579</a>
645
+ <a name='L580'></a><a href='#L580'>580</a>
646
+ <a name='L581'></a><a href='#L581'>581</a>
647
+ <a name='L582'></a><a href='#L582'>582</a>
648
+ <a name='L583'></a><a href='#L583'>583</a>
649
+ <a name='L584'></a><a href='#L584'>584</a>
650
+ <a name='L585'></a><a href='#L585'>585</a>
651
+ <a name='L586'></a><a href='#L586'>586</a>
652
+ <a name='L587'></a><a href='#L587'>587</a>
653
+ <a name='L588'></a><a href='#L588'>588</a>
654
+ <a name='L589'></a><a href='#L589'>589</a>
655
+ <a name='L590'></a><a href='#L590'>590</a>
656
+ <a name='L591'></a><a href='#L591'>591</a>
657
+ <a name='L592'></a><a href='#L592'>592</a>
658
+ <a name='L593'></a><a href='#L593'>593</a>
659
+ <a name='L594'></a><a href='#L594'>594</a>
660
+ <a name='L595'></a><a href='#L595'>595</a>
661
+ <a name='L596'></a><a href='#L596'>596</a>
662
+ <a name='L597'></a><a href='#L597'>597</a>
663
+ <a name='L598'></a><a href='#L598'>598</a>
664
+ <a name='L599'></a><a href='#L599'>599</a>
665
+ <a name='L600'></a><a href='#L600'>600</a>
666
+ <a name='L601'></a><a href='#L601'>601</a>
667
+ <a name='L602'></a><a href='#L602'>602</a>
668
+ <a name='L603'></a><a href='#L603'>603</a>
669
+ <a name='L604'></a><a href='#L604'>604</a>
670
+ <a name='L605'></a><a href='#L605'>605</a>
671
+ <a name='L606'></a><a href='#L606'>606</a>
672
+ <a name='L607'></a><a href='#L607'>607</a>
673
+ <a name='L608'></a><a href='#L608'>608</a>
674
+ <a name='L609'></a><a href='#L609'>609</a>
675
+ <a name='L610'></a><a href='#L610'>610</a>
676
+ <a name='L611'></a><a href='#L611'>611</a>
677
+ <a name='L612'></a><a href='#L612'>612</a>
678
+ <a name='L613'></a><a href='#L613'>613</a>
679
+ <a name='L614'></a><a href='#L614'>614</a>
680
+ <a name='L615'></a><a href='#L615'>615</a>
681
+ <a name='L616'></a><a href='#L616'>616</a>
682
+ <a name='L617'></a><a href='#L617'>617</a>
683
+ <a name='L618'></a><a href='#L618'>618</a>
684
+ <a name='L619'></a><a href='#L619'>619</a>
685
+ <a name='L620'></a><a href='#L620'>620</a>
686
+ <a name='L621'></a><a href='#L621'>621</a>
687
+ <a name='L622'></a><a href='#L622'>622</a>
688
+ <a name='L623'></a><a href='#L623'>623</a>
689
+ <a name='L624'></a><a href='#L624'>624</a>
690
+ <a name='L625'></a><a href='#L625'>625</a>
691
+ <a name='L626'></a><a href='#L626'>626</a>
692
+ <a name='L627'></a><a href='#L627'>627</a>
693
+ <a name='L628'></a><a href='#L628'>628</a>
694
+ <a name='L629'></a><a href='#L629'>629</a>
695
+ <a name='L630'></a><a href='#L630'>630</a>
696
+ <a name='L631'></a><a href='#L631'>631</a>
697
+ <a name='L632'></a><a href='#L632'>632</a>
698
+ <a name='L633'></a><a href='#L633'>633</a>
699
+ <a name='L634'></a><a href='#L634'>634</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
700
+ <span class="cline-any cline-neutral">&nbsp;</span>
701
+ <span class="cline-any cline-neutral">&nbsp;</span>
702
+ <span class="cline-any cline-neutral">&nbsp;</span>
703
+ <span class="cline-any cline-neutral">&nbsp;</span>
704
+ <span class="cline-any cline-neutral">&nbsp;</span>
705
+ <span class="cline-any cline-neutral">&nbsp;</span>
706
+ <span class="cline-any cline-neutral">&nbsp;</span>
707
+ <span class="cline-any cline-neutral">&nbsp;</span>
708
+ <span class="cline-any cline-neutral">&nbsp;</span>
709
+ <span class="cline-any cline-neutral">&nbsp;</span>
710
+ <span class="cline-any cline-neutral">&nbsp;</span>
711
+ <span class="cline-any cline-neutral">&nbsp;</span>
712
+ <span class="cline-any cline-neutral">&nbsp;</span>
713
+ <span class="cline-any cline-neutral">&nbsp;</span>
714
+ <span class="cline-any cline-neutral">&nbsp;</span>
715
+ <span class="cline-any cline-neutral">&nbsp;</span>
716
+ <span class="cline-any cline-neutral">&nbsp;</span>
717
+ <span class="cline-any cline-neutral">&nbsp;</span>
718
+ <span class="cline-any cline-neutral">&nbsp;</span>
719
+ <span class="cline-any cline-neutral">&nbsp;</span>
720
+ <span class="cline-any cline-neutral">&nbsp;</span>
721
+ <span class="cline-any cline-neutral">&nbsp;</span>
722
+ <span class="cline-any cline-neutral">&nbsp;</span>
723
+ <span class="cline-any cline-neutral">&nbsp;</span>
724
+ <span class="cline-any cline-neutral">&nbsp;</span>
725
+ <span class="cline-any cline-neutral">&nbsp;</span>
726
+ <span class="cline-any cline-neutral">&nbsp;</span>
727
+ <span class="cline-any cline-neutral">&nbsp;</span>
728
+ <span class="cline-any cline-neutral">&nbsp;</span>
729
+ <span class="cline-any cline-neutral">&nbsp;</span>
730
+ <span class="cline-any cline-yes">1x</span>
731
+ <span class="cline-any cline-neutral">&nbsp;</span>
732
+ <span class="cline-any cline-neutral">&nbsp;</span>
733
+ <span class="cline-any cline-neutral">&nbsp;</span>
734
+ <span class="cline-any cline-neutral">&nbsp;</span>
735
+ <span class="cline-any cline-neutral">&nbsp;</span>
736
+ <span class="cline-any cline-neutral">&nbsp;</span>
737
+ <span class="cline-any cline-neutral">&nbsp;</span>
738
+ <span class="cline-any cline-neutral">&nbsp;</span>
739
+ <span class="cline-any cline-neutral">&nbsp;</span>
740
+ <span class="cline-any cline-neutral">&nbsp;</span>
741
+ <span class="cline-any cline-neutral">&nbsp;</span>
742
+ <span class="cline-any cline-neutral">&nbsp;</span>
743
+ <span class="cline-any cline-neutral">&nbsp;</span>
744
+ <span class="cline-any cline-neutral">&nbsp;</span>
745
+ <span class="cline-any cline-neutral">&nbsp;</span>
746
+ <span class="cline-any cline-neutral">&nbsp;</span>
747
+ <span class="cline-any cline-neutral">&nbsp;</span>
748
+ <span class="cline-any cline-neutral">&nbsp;</span>
749
+ <span class="cline-any cline-neutral">&nbsp;</span>
750
+ <span class="cline-any cline-neutral">&nbsp;</span>
751
+ <span class="cline-any cline-neutral">&nbsp;</span>
752
+ <span class="cline-any cline-neutral">&nbsp;</span>
753
+ <span class="cline-any cline-neutral">&nbsp;</span>
754
+ <span class="cline-any cline-neutral">&nbsp;</span>
755
+ <span class="cline-any cline-neutral">&nbsp;</span>
756
+ <span class="cline-any cline-neutral">&nbsp;</span>
757
+ <span class="cline-any cline-neutral">&nbsp;</span>
758
+ <span class="cline-any cline-neutral">&nbsp;</span>
759
+ <span class="cline-any cline-neutral">&nbsp;</span>
760
+ <span class="cline-any cline-neutral">&nbsp;</span>
761
+ <span class="cline-any cline-neutral">&nbsp;</span>
762
+ <span class="cline-any cline-neutral">&nbsp;</span>
763
+ <span class="cline-any cline-neutral">&nbsp;</span>
764
+ <span class="cline-any cline-neutral">&nbsp;</span>
765
+ <span class="cline-any cline-neutral">&nbsp;</span>
766
+ <span class="cline-any cline-neutral">&nbsp;</span>
767
+ <span class="cline-any cline-neutral">&nbsp;</span>
768
+ <span class="cline-any cline-neutral">&nbsp;</span>
769
+ <span class="cline-any cline-neutral">&nbsp;</span>
770
+ <span class="cline-any cline-neutral">&nbsp;</span>
771
+ <span class="cline-any cline-neutral">&nbsp;</span>
772
+ <span class="cline-any cline-neutral">&nbsp;</span>
773
+ <span class="cline-any cline-neutral">&nbsp;</span>
774
+ <span class="cline-any cline-neutral">&nbsp;</span>
775
+ <span class="cline-any cline-yes">1x</span>
776
+ <span class="cline-any cline-yes">13x</span>
777
+ <span class="cline-any cline-yes">13x</span>
778
+ <span class="cline-any cline-yes">13x</span>
779
+ <span class="cline-any cline-neutral">&nbsp;</span>
780
+ <span class="cline-any cline-yes">13x</span>
781
+ <span class="cline-any cline-yes">13x</span>
782
+ <span class="cline-any cline-neutral">&nbsp;</span>
783
+ <span class="cline-any cline-yes">13x</span>
784
+ <span class="cline-any cline-yes">13x</span>
785
+ <span class="cline-any cline-yes">13x</span>
786
+ <span class="cline-any cline-yes">13x</span>
787
+ <span class="cline-any cline-neutral">&nbsp;</span>
788
+ <span class="cline-any cline-yes">13x</span>
789
+ <span class="cline-any cline-yes">11x</span>
790
+ <span class="cline-any cline-yes">11x</span>
791
+ <span class="cline-any cline-yes">11x</span>
792
+ <span class="cline-any cline-yes">11x</span>
793
+ <span class="cline-any cline-yes">11x</span>
794
+ <span class="cline-any cline-yes">11x</span>
795
+ <span class="cline-any cline-yes">11x</span>
796
+ <span class="cline-any cline-yes">11x</span>
797
+ <span class="cline-any cline-neutral">&nbsp;</span>
798
+ <span class="cline-any cline-yes">13x</span>
799
+ <span class="cline-any cline-yes">21x</span>
800
+ <span class="cline-any cline-yes">21x</span>
801
+ <span class="cline-any cline-yes">11x</span>
802
+ <span class="cline-any cline-yes">11x</span>
803
+ <span class="cline-any cline-yes">11x</span>
804
+ <span class="cline-any cline-yes">21x</span>
805
+ <span class="cline-any cline-neutral">&nbsp;</span>
806
+ <span class="cline-any cline-no">&nbsp;</span>
807
+ <span class="cline-any cline-yes">21x</span>
808
+ <span class="cline-any cline-yes">21x</span>
809
+ <span class="cline-any cline-neutral">&nbsp;</span>
810
+ <span class="cline-any cline-yes">13x</span>
811
+ <span class="cline-any cline-yes">13x</span>
812
+ <span class="cline-any cline-yes">13x</span>
813
+ <span class="cline-any cline-neutral">&nbsp;</span>
814
+ <span class="cline-any cline-yes">13x</span>
815
+ <span class="cline-any cline-yes">11x</span>
816
+ <span class="cline-any cline-yes">11x</span>
817
+ <span class="cline-any cline-yes">11x</span>
818
+ <span class="cline-any cline-neutral">&nbsp;</span>
819
+ <span class="cline-any cline-no">&nbsp;</span>
820
+ <span class="cline-any cline-yes">11x</span>
821
+ <span class="cline-any cline-neutral">&nbsp;</span>
822
+ <span class="cline-any cline-yes">13x</span>
823
+ <span class="cline-any cline-yes">3x</span>
824
+ <span class="cline-any cline-yes">4x</span>
825
+ <span class="cline-any cline-yes">3x</span>
826
+ <span class="cline-any cline-yes">3x</span>
827
+ <span class="cline-any cline-yes">4x</span>
828
+ <span class="cline-any cline-neutral">&nbsp;</span>
829
+ <span class="cline-any cline-no">&nbsp;</span>
830
+ <span class="cline-any cline-yes">4x</span>
831
+ <span class="cline-any cline-yes">3x</span>
832
+ <span class="cline-any cline-neutral">&nbsp;</span>
833
+ <span class="cline-any cline-neutral">&nbsp;</span>
834
+ <span class="cline-any cline-yes">13x</span>
835
+ <span class="cline-any cline-yes">2x</span>
836
+ <span class="cline-any cline-yes">2x</span>
837
+ <span class="cline-any cline-yes">2x</span>
838
+ <span class="cline-any cline-yes">2x</span>
839
+ <span class="cline-any cline-yes">2x</span>
840
+ <span class="cline-any cline-yes">2x</span>
841
+ <span class="cline-any cline-neutral">&nbsp;</span>
842
+ <span class="cline-any cline-yes">13x</span>
843
+ <span class="cline-any cline-no">&nbsp;</span>
844
+ <span class="cline-any cline-no">&nbsp;</span>
845
+ <span class="cline-any cline-neutral">&nbsp;</span>
846
+ <span class="cline-any cline-yes">13x</span>
847
+ <span class="cline-any cline-no">&nbsp;</span>
848
+ <span class="cline-any cline-no">&nbsp;</span>
849
+ <span class="cline-any cline-yes">13x</span>
850
+ <span class="cline-any cline-neutral">&nbsp;</span>
851
+ <span class="cline-any cline-neutral">&nbsp;</span>
852
+ <span class="cline-any cline-neutral">&nbsp;</span>
853
+ <span class="cline-any cline-neutral">&nbsp;</span>
854
+ <span class="cline-any cline-neutral">&nbsp;</span>
855
+ <span class="cline-any cline-neutral">&nbsp;</span>
856
+ <span class="cline-any cline-yes">1x</span>
857
+ <span class="cline-any cline-yes">11x</span>
858
+ <span class="cline-any cline-yes">11x</span>
859
+ <span class="cline-any cline-yes">11x</span>
860
+ <span class="cline-any cline-neutral">&nbsp;</span>
861
+ <span class="cline-any cline-yes">11x</span>
862
+ <span class="cline-any cline-neutral">&nbsp;</span>
863
+ <span class="cline-any cline-yes">11x</span>
864
+ <span class="cline-any cline-yes">12x</span>
865
+ <span class="cline-any cline-yes">12x</span>
866
+ <span class="cline-any cline-yes">12x</span>
867
+ <span class="cline-any cline-yes">14x</span>
868
+ <span class="cline-any cline-yes">14x</span>
869
+ <span class="cline-any cline-yes">12x</span>
870
+ <span class="cline-any cline-yes">12x</span>
871
+ <span class="cline-any cline-yes">12x</span>
872
+ <span class="cline-any cline-yes">12x</span>
873
+ <span class="cline-any cline-yes">12x</span>
874
+ <span class="cline-any cline-yes">12x</span>
875
+ <span class="cline-any cline-neutral">&nbsp;</span>
876
+ <span class="cline-any cline-yes">11x</span>
877
+ <span class="cline-any cline-yes">25x</span>
878
+ <span class="cline-any cline-yes">12x</span>
879
+ <span class="cline-any cline-yes">12x</span>
880
+ <span class="cline-any cline-yes">12x</span>
881
+ <span class="cline-any cline-neutral">&nbsp;</span>
882
+ <span class="cline-any cline-no">&nbsp;</span>
883
+ <span class="cline-any cline-yes">12x</span>
884
+ <span class="cline-any cline-yes">25x</span>
885
+ <span class="cline-any cline-yes">25x</span>
886
+ <span class="cline-any cline-neutral">&nbsp;</span>
887
+ <span class="cline-any cline-yes">11x</span>
888
+ <span class="cline-any cline-yes">13x</span>
889
+ <span class="cline-any cline-no">&nbsp;</span>
890
+ <span class="cline-any cline-no">&nbsp;</span>
891
+ <span class="cline-any cline-no">&nbsp;</span>
892
+ <span class="cline-any cline-yes">13x</span>
893
+ <span class="cline-any cline-yes">13x</span>
894
+ <span class="cline-any cline-yes">13x</span>
895
+ <span class="cline-any cline-no">&nbsp;</span>
896
+ <span class="cline-any cline-no">&nbsp;</span>
897
+ <span class="cline-any cline-yes">13x</span>
898
+ <span class="cline-any cline-neutral">&nbsp;</span>
899
+ <span class="cline-any cline-yes">11x</span>
900
+ <span class="cline-any cline-yes">1x</span>
901
+ <span class="cline-any cline-yes">1x</span>
902
+ <span class="cline-any cline-neutral">&nbsp;</span>
903
+ <span class="cline-any cline-neutral">&nbsp;</span>
904
+ <span class="cline-any cline-neutral">&nbsp;</span>
905
+ <span class="cline-any cline-neutral">&nbsp;</span>
906
+ <span class="cline-any cline-yes">11x</span>
907
+ <span class="cline-any cline-yes">2x</span>
908
+ <span class="cline-any cline-yes">1x</span>
909
+ <span class="cline-any cline-yes">1x</span>
910
+ <span class="cline-any cline-yes">2x</span>
911
+ <span class="cline-any cline-neutral">&nbsp;</span>
912
+ <span class="cline-any cline-no">&nbsp;</span>
913
+ <span class="cline-any cline-yes">2x</span>
914
+ <span class="cline-any cline-yes">11x</span>
915
+ <span class="cline-any cline-no">&nbsp;</span>
916
+ <span class="cline-any cline-no">&nbsp;</span>
917
+ <span class="cline-any cline-yes">11x</span>
918
+ <span class="cline-any cline-neutral">&nbsp;</span>
919
+ <span class="cline-any cline-neutral">&nbsp;</span>
920
+ <span class="cline-any cline-neutral">&nbsp;</span>
921
+ <span class="cline-any cline-yes">1x</span>
922
+ <span class="cline-any cline-yes">21x</span>
923
+ <span class="cline-any cline-yes">21x</span>
924
+ <span class="cline-any cline-yes">21x</span>
925
+ <span class="cline-any cline-yes">21x</span>
926
+ <span class="cline-any cline-neutral">&nbsp;</span>
927
+ <span class="cline-any cline-neutral">&nbsp;</span>
928
+ <span class="cline-any cline-neutral">&nbsp;</span>
929
+ <span class="cline-any cline-yes">21x</span>
930
+ <span class="cline-any cline-yes">21x</span>
931
+ <span class="cline-any cline-yes">21x</span>
932
+ <span class="cline-any cline-yes">21x</span>
933
+ <span class="cline-any cline-yes">21x</span>
934
+ <span class="cline-any cline-yes">21x</span>
935
+ <span class="cline-any cline-yes">21x</span>
936
+ <span class="cline-any cline-yes">21x</span>
937
+ <span class="cline-any cline-yes">21x</span>
938
+ <span class="cline-any cline-neutral">&nbsp;</span>
939
+ <span class="cline-any cline-yes">21x</span>
940
+ <span class="cline-any cline-yes">21x</span>
941
+ <span class="cline-any cline-neutral">&nbsp;</span>
942
+ <span class="cline-any cline-yes">21x</span>
943
+ <span class="cline-any cline-yes">21x</span>
944
+ <span class="cline-any cline-neutral">&nbsp;</span>
945
+ <span class="cline-any cline-neutral">&nbsp;</span>
946
+ <span class="cline-any cline-neutral">&nbsp;</span>
947
+ <span class="cline-any cline-neutral">&nbsp;</span>
948
+ <span class="cline-any cline-neutral">&nbsp;</span>
949
+ <span class="cline-any cline-neutral">&nbsp;</span>
950
+ <span class="cline-any cline-neutral">&nbsp;</span>
951
+ <span class="cline-any cline-yes">21x</span>
952
+ <span class="cline-any cline-yes">21x</span>
953
+ <span class="cline-any cline-yes">21x</span>
954
+ <span class="cline-any cline-neutral">&nbsp;</span>
955
+ <span class="cline-any cline-neutral">&nbsp;</span>
956
+ <span class="cline-any cline-neutral">&nbsp;</span>
957
+ <span class="cline-any cline-yes">21x</span>
958
+ <span class="cline-any cline-yes">21x</span>
959
+ <span class="cline-any cline-yes">21x</span>
960
+ <span class="cline-any cline-yes">21x</span>
961
+ <span class="cline-any cline-yes">21x</span>
962
+ <span class="cline-any cline-yes">21x</span>
963
+ <span class="cline-any cline-yes">21x</span>
964
+ <span class="cline-any cline-yes">23x</span>
965
+ <span class="cline-any cline-neutral">&nbsp;</span>
966
+ <span class="cline-any cline-yes">23x</span>
967
+ <span class="cline-any cline-yes">21x</span>
968
+ <span class="cline-any cline-yes">11x</span>
969
+ <span class="cline-any cline-yes">21x</span>
970
+ <span class="cline-any cline-yes">11x</span>
971
+ <span class="cline-any cline-yes">21x</span>
972
+ <span class="cline-any cline-yes">9x</span>
973
+ <span class="cline-any cline-yes">21x</span>
974
+ <span class="cline-any cline-yes">21x</span>
975
+ <span class="cline-any cline-neutral">&nbsp;</span>
976
+ <span class="cline-any cline-neutral">&nbsp;</span>
977
+ <span class="cline-any cline-no">&nbsp;</span>
978
+ <span class="cline-any cline-yes">21x</span>
979
+ <span class="cline-any cline-yes">21x</span>
980
+ <span class="cline-any cline-yes">21x</span>
981
+ <span class="cline-any cline-yes">21x</span>
982
+ <span class="cline-any cline-no">&nbsp;</span>
983
+ <span class="cline-any cline-no">&nbsp;</span>
984
+ <span class="cline-any cline-no">&nbsp;</span>
985
+ <span class="cline-any cline-no">&nbsp;</span>
986
+ <span class="cline-any cline-yes">21x</span>
987
+ <span class="cline-any cline-neutral">&nbsp;</span>
988
+ <span class="cline-any cline-neutral">&nbsp;</span>
989
+ <span class="cline-any cline-yes">21x</span>
990
+ <span class="cline-any cline-yes">48x</span>
991
+ <span class="cline-any cline-no">&nbsp;</span>
992
+ <span class="cline-any cline-no">&nbsp;</span>
993
+ <span class="cline-any cline-no">&nbsp;</span>
994
+ <span class="cline-any cline-no">&nbsp;</span>
995
+ <span class="cline-any cline-yes">48x</span>
996
+ <span class="cline-any cline-yes">48x</span>
997
+ <span class="cline-any cline-neutral">&nbsp;</span>
998
+ <span class="cline-any cline-yes">21x</span>
999
+ <span class="cline-any cline-yes">2x</span>
1000
+ <span class="cline-any cline-yes">2x</span>
1001
+ <span class="cline-any cline-yes">2x</span>
1002
+ <span class="cline-any cline-neutral">&nbsp;</span>
1003
+ <span class="cline-any cline-yes">21x</span>
1004
+ <span class="cline-any cline-yes">24x</span>
1005
+ <span class="cline-any cline-yes">22x</span>
1006
+ <span class="cline-any cline-yes">22x</span>
1007
+ <span class="cline-any cline-yes">22x</span>
1008
+ <span class="cline-any cline-yes">22x</span>
1009
+ <span class="cline-any cline-yes">22x</span>
1010
+ <span class="cline-any cline-yes">22x</span>
1011
+ <span class="cline-any cline-yes">22x</span>
1012
+ <span class="cline-any cline-yes">22x</span>
1013
+ <span class="cline-any cline-yes">22x</span>
1014
+ <span class="cline-any cline-yes">22x</span>
1015
+ <span class="cline-any cline-yes">22x</span>
1016
+ <span class="cline-any cline-yes">22x</span>
1017
+ <span class="cline-any cline-yes">24x</span>
1018
+ <span class="cline-any cline-yes">21x</span>
1019
+ <span class="cline-any cline-yes">21x</span>
1020
+ <span class="cline-any cline-yes">21x</span>
1021
+ <span class="cline-any cline-yes">24x</span>
1022
+ <span class="cline-any cline-neutral">&nbsp;</span>
1023
+ <span class="cline-any cline-neutral">&nbsp;</span>
1024
+ <span class="cline-any cline-neutral">&nbsp;</span>
1025
+ <span class="cline-any cline-yes">21x</span>
1026
+ <span class="cline-any cline-yes">21x</span>
1027
+ <span class="cline-any cline-yes">21x</span>
1028
+ <span class="cline-any cline-yes">21x</span>
1029
+ <span class="cline-any cline-no">&nbsp;</span>
1030
+ <span class="cline-any cline-no">&nbsp;</span>
1031
+ <span class="cline-any cline-no">&nbsp;</span>
1032
+ <span class="cline-any cline-yes">21x</span>
1033
+ <span class="cline-any cline-yes">21x</span>
1034
+ <span class="cline-any cline-yes">20x</span>
1035
+ <span class="cline-any cline-yes">20x</span>
1036
+ <span class="cline-any cline-yes">20x</span>
1037
+ <span class="cline-any cline-yes">20x</span>
1038
+ <span class="cline-any cline-yes">21x</span>
1039
+ <span class="cline-any cline-no">&nbsp;</span>
1040
+ <span class="cline-any cline-no">&nbsp;</span>
1041
+ <span class="cline-any cline-no">&nbsp;</span>
1042
+ <span class="cline-any cline-no">&nbsp;</span>
1043
+ <span class="cline-any cline-yes">21x</span>
1044
+ <span class="cline-any cline-yes">21x</span>
1045
+ <span class="cline-any cline-yes">21x</span>
1046
+ <span class="cline-any cline-yes">21x</span>
1047
+ <span class="cline-any cline-yes">21x</span>
1048
+ <span class="cline-any cline-yes">21x</span>
1049
+ <span class="cline-any cline-no">&nbsp;</span>
1050
+ <span class="cline-any cline-no">&nbsp;</span>
1051
+ <span class="cline-any cline-no">&nbsp;</span>
1052
+ <span class="cline-any cline-no">&nbsp;</span>
1053
+ <span class="cline-any cline-yes">21x</span>
1054
+ <span class="cline-any cline-yes">21x</span>
1055
+ <span class="cline-any cline-yes">21x</span>
1056
+ <span class="cline-any cline-no">&nbsp;</span>
1057
+ <span class="cline-any cline-yes">21x</span>
1058
+ <span class="cline-any cline-yes">21x</span>
1059
+ <span class="cline-any cline-yes">21x</span>
1060
+ <span class="cline-any cline-neutral">&nbsp;</span>
1061
+ <span class="cline-any cline-neutral">&nbsp;</span>
1062
+ <span class="cline-any cline-neutral">&nbsp;</span>
1063
+ <span class="cline-any cline-yes">21x</span>
1064
+ <span class="cline-any cline-yes">1x</span>
1065
+ <span class="cline-any cline-yes">1x</span>
1066
+ <span class="cline-any cline-neutral">&nbsp;</span>
1067
+ <span class="cline-any cline-neutral">&nbsp;</span>
1068
+ <span class="cline-any cline-yes">1x</span>
1069
+ <span class="cline-any cline-no">&nbsp;</span>
1070
+ <span class="cline-any cline-yes">1x</span>
1071
+ <span class="cline-any cline-yes">1x</span>
1072
+ <span class="cline-any cline-no">&nbsp;</span>
1073
+ <span class="cline-any cline-no">&nbsp;</span>
1074
+ <span class="cline-any cline-no">&nbsp;</span>
1075
+ <span class="cline-any cline-yes">1x</span>
1076
+ <span class="cline-any cline-yes">1x</span>
1077
+ <span class="cline-any cline-yes">1x</span>
1078
+ <span class="cline-any cline-yes">1x</span>
1079
+ <span class="cline-any cline-yes">1x</span>
1080
+ <span class="cline-any cline-yes">1x</span>
1081
+ <span class="cline-any cline-yes">1x</span>
1082
+ <span class="cline-any cline-no">&nbsp;</span>
1083
+ <span class="cline-any cline-no">&nbsp;</span>
1084
+ <span class="cline-any cline-no">&nbsp;</span>
1085
+ <span class="cline-any cline-no">&nbsp;</span>
1086
+ <span class="cline-any cline-yes">1x</span>
1087
+ <span class="cline-any cline-yes">1x</span>
1088
+ <span class="cline-any cline-yes">1x</span>
1089
+ <span class="cline-any cline-yes">1x</span>
1090
+ <span class="cline-any cline-yes">1x</span>
1091
+ <span class="cline-any cline-yes">1x</span>
1092
+ <span class="cline-any cline-yes">1x</span>
1093
+ <span class="cline-any cline-yes">1x</span>
1094
+ <span class="cline-any cline-neutral">&nbsp;</span>
1095
+ <span class="cline-any cline-neutral">&nbsp;</span>
1096
+ <span class="cline-any cline-neutral">&nbsp;</span>
1097
+ <span class="cline-any cline-yes">21x</span>
1098
+ <span class="cline-any cline-neutral">&nbsp;</span>
1099
+ <span class="cline-any cline-neutral">&nbsp;</span>
1100
+ <span class="cline-any cline-neutral">&nbsp;</span>
1101
+ <span class="cline-any cline-neutral">&nbsp;</span>
1102
+ <span class="cline-any cline-yes">15x</span>
1103
+ <span class="cline-any cline-yes">15x</span>
1104
+ <span class="cline-any cline-yes">15x</span>
1105
+ <span class="cline-any cline-yes">15x</span>
1106
+ <span class="cline-any cline-yes">15x</span>
1107
+ <span class="cline-any cline-yes">15x</span>
1108
+ <span class="cline-any cline-yes">15x</span>
1109
+ <span class="cline-any cline-yes">15x</span>
1110
+ <span class="cline-any cline-neutral">&nbsp;</span>
1111
+ <span class="cline-any cline-neutral">&nbsp;</span>
1112
+ <span class="cline-any cline-neutral">&nbsp;</span>
1113
+ <span class="cline-any cline-neutral">&nbsp;</span>
1114
+ <span class="cline-any cline-yes">15x</span>
1115
+ <span class="cline-any cline-yes">15x</span>
1116
+ <span class="cline-any cline-yes">15x</span>
1117
+ <span class="cline-any cline-yes">15x</span>
1118
+ <span class="cline-any cline-yes">15x</span>
1119
+ <span class="cline-any cline-yes">15x</span>
1120
+ <span class="cline-any cline-yes">15x</span>
1121
+ <span class="cline-any cline-neutral">&nbsp;</span>
1122
+ <span class="cline-any cline-no">&nbsp;</span>
1123
+ <span class="cline-any cline-no">&nbsp;</span>
1124
+ <span class="cline-any cline-no">&nbsp;</span>
1125
+ <span class="cline-any cline-no">&nbsp;</span>
1126
+ <span class="cline-any cline-no">&nbsp;</span>
1127
+ <span class="cline-any cline-yes">15x</span>
1128
+ <span class="cline-any cline-yes">15x</span>
1129
+ <span class="cline-any cline-yes">15x</span>
1130
+ <span class="cline-any cline-yes">15x</span>
1131
+ <span class="cline-any cline-yes">15x</span>
1132
+ <span class="cline-any cline-yes">15x</span>
1133
+ <span class="cline-any cline-yes">15x</span>
1134
+ <span class="cline-any cline-neutral">&nbsp;</span>
1135
+ <span class="cline-any cline-yes">13x</span>
1136
+ <span class="cline-any cline-neutral">&nbsp;</span>
1137
+ <span class="cline-any cline-neutral">&nbsp;</span>
1138
+ <span class="cline-any cline-neutral">&nbsp;</span>
1139
+ <span class="cline-any cline-neutral">&nbsp;</span>
1140
+ <span class="cline-any cline-neutral">&nbsp;</span>
1141
+ <span class="cline-any cline-neutral">&nbsp;</span>
1142
+ <span class="cline-any cline-neutral">&nbsp;</span>
1143
+ <span class="cline-any cline-neutral">&nbsp;</span>
1144
+ <span class="cline-any cline-yes">13x</span>
1145
+ <span class="cline-any cline-yes">13x</span>
1146
+ <span class="cline-any cline-yes">13x</span>
1147
+ <span class="cline-any cline-yes">13x</span>
1148
+ <span class="cline-any cline-yes">13x</span>
1149
+ <span class="cline-any cline-yes">13x</span>
1150
+ <span class="cline-any cline-yes">13x</span>
1151
+ <span class="cline-any cline-yes">13x</span>
1152
+ <span class="cline-any cline-yes">13x</span>
1153
+ <span class="cline-any cline-neutral">&nbsp;</span>
1154
+ <span class="cline-any cline-neutral">&nbsp;</span>
1155
+ <span class="cline-any cline-yes">13x</span>
1156
+ <span class="cline-any cline-no">&nbsp;</span>
1157
+ <span class="cline-any cline-no">&nbsp;</span>
1158
+ <span class="cline-any cline-no">&nbsp;</span>
1159
+ <span class="cline-any cline-no">&nbsp;</span>
1160
+ <span class="cline-any cline-yes">13x</span>
1161
+ <span class="cline-any cline-yes">15x</span>
1162
+ <span class="cline-any cline-yes">2x</span>
1163
+ <span class="cline-any cline-yes">2x</span>
1164
+ <span class="cline-any cline-yes">2x</span>
1165
+ <span class="cline-any cline-yes">2x</span>
1166
+ <span class="cline-any cline-yes">2x</span>
1167
+ <span class="cline-any cline-yes">2x</span>
1168
+ <span class="cline-any cline-yes">2x</span>
1169
+ <span class="cline-any cline-neutral">&nbsp;</span>
1170
+ <span class="cline-any cline-neutral">&nbsp;</span>
1171
+ <span class="cline-any cline-neutral">&nbsp;</span>
1172
+ <span class="cline-any cline-neutral">&nbsp;</span>
1173
+ <span class="cline-any cline-neutral">&nbsp;</span>
1174
+ <span class="cline-any cline-neutral">&nbsp;</span>
1175
+ <span class="cline-any cline-yes">2x</span>
1176
+ <span class="cline-any cline-yes">2x</span>
1177
+ <span class="cline-any cline-yes">2x</span>
1178
+ <span class="cline-any cline-yes">2x</span>
1179
+ <span class="cline-any cline-yes">2x</span>
1180
+ <span class="cline-any cline-yes">2x</span>
1181
+ <span class="cline-any cline-yes">2x</span>
1182
+ <span class="cline-any cline-yes">2x</span>
1183
+ <span class="cline-any cline-yes">2x</span>
1184
+ <span class="cline-any cline-yes">2x</span>
1185
+ <span class="cline-any cline-yes">2x</span>
1186
+ <span class="cline-any cline-yes">15x</span>
1187
+ <span class="cline-any cline-neutral">&nbsp;</span>
1188
+ <span class="cline-any cline-yes">21x</span>
1189
+ <span class="cline-any cline-yes">1x</span>
1190
+ <span class="cline-any cline-yes">1x</span>
1191
+ <span class="cline-any cline-yes">1x</span>
1192
+ <span class="cline-any cline-neutral">&nbsp;</span>
1193
+ <span class="cline-any cline-neutral">&nbsp;</span>
1194
+ <span class="cline-any cline-yes">1x</span>
1195
+ <span class="cline-any cline-neutral">&nbsp;</span>
1196
+ <span class="cline-any cline-yes">21x</span>
1197
+ <span class="cline-any cline-neutral">&nbsp;</span>
1198
+ <span class="cline-any cline-yes">21x</span>
1199
+ <span class="cline-any cline-yes">14x</span>
1200
+ <span class="cline-any cline-yes">14x</span>
1201
+ <span class="cline-any cline-yes">14x</span>
1202
+ <span class="cline-any cline-yes">14x</span>
1203
+ <span class="cline-any cline-yes">14x</span>
1204
+ <span class="cline-any cline-yes">14x</span>
1205
+ <span class="cline-any cline-neutral">&nbsp;</span>
1206
+ <span class="cline-any cline-yes">21x</span>
1207
+ <span class="cline-any cline-yes">4x</span>
1208
+ <span class="cline-any cline-yes">4x</span>
1209
+ <span class="cline-any cline-yes">4x</span>
1210
+ <span class="cline-any cline-neutral">&nbsp;</span>
1211
+ <span class="cline-any cline-yes">21x</span>
1212
+ <span class="cline-any cline-yes">11x</span>
1213
+ <span class="cline-any cline-yes">11x</span>
1214
+ <span class="cline-any cline-yes">11x</span>
1215
+ <span class="cline-any cline-yes">11x</span>
1216
+ <span class="cline-any cline-yes">11x</span>
1217
+ <span class="cline-any cline-yes">11x</span>
1218
+ <span class="cline-any cline-no">&nbsp;</span>
1219
+ <span class="cline-any cline-no">&nbsp;</span>
1220
+ <span class="cline-any cline-no">&nbsp;</span>
1221
+ <span class="cline-any cline-no">&nbsp;</span>
1222
+ <span class="cline-any cline-yes">11x</span>
1223
+ <span class="cline-any cline-yes">11x</span>
1224
+ <span class="cline-any cline-yes">11x</span>
1225
+ <span class="cline-any cline-yes">11x</span>
1226
+ <span class="cline-any cline-yes">11x</span>
1227
+ <span class="cline-any cline-yes">11x</span>
1228
+ <span class="cline-any cline-yes">11x</span>
1229
+ <span class="cline-any cline-yes">11x</span>
1230
+ <span class="cline-any cline-yes">11x</span>
1231
+ <span class="cline-any cline-yes">11x</span>
1232
+ <span class="cline-any cline-yes">11x</span>
1233
+ <span class="cline-any cline-yes">11x</span>
1234
+ <span class="cline-any cline-yes">11x</span>
1235
+ <span class="cline-any cline-yes">11x</span>
1236
+ <span class="cline-any cline-yes">11x</span>
1237
+ <span class="cline-any cline-yes">11x</span>
1238
+ <span class="cline-any cline-neutral">&nbsp;</span>
1239
+ <span class="cline-any cline-yes">21x</span>
1240
+ <span class="cline-any cline-yes">11x</span>
1241
+ <span class="cline-any cline-yes">11x</span>
1242
+ <span class="cline-any cline-yes">11x</span>
1243
+ <span class="cline-any cline-yes">11x</span>
1244
+ <span class="cline-any cline-yes">11x</span>
1245
+ <span class="cline-any cline-yes">11x</span>
1246
+ <span class="cline-any cline-no">&nbsp;</span>
1247
+ <span class="cline-any cline-no">&nbsp;</span>
1248
+ <span class="cline-any cline-no">&nbsp;</span>
1249
+ <span class="cline-any cline-no">&nbsp;</span>
1250
+ <span class="cline-any cline-yes">11x</span>
1251
+ <span class="cline-any cline-yes">11x</span>
1252
+ <span class="cline-any cline-yes">11x</span>
1253
+ <span class="cline-any cline-neutral">&nbsp;</span>
1254
+ <span class="cline-any cline-neutral">&nbsp;</span>
1255
+ <span class="cline-any cline-yes">11x</span>
1256
+ <span class="cline-any cline-yes">11x</span>
1257
+ <span class="cline-any cline-yes">11x</span>
1258
+ <span class="cline-any cline-yes">11x</span>
1259
+ <span class="cline-any cline-neutral">&nbsp;</span>
1260
+ <span class="cline-any cline-neutral">&nbsp;</span>
1261
+ <span class="cline-any cline-neutral">&nbsp;</span>
1262
+ <span class="cline-any cline-neutral">&nbsp;</span>
1263
+ <span class="cline-any cline-yes">11x</span>
1264
+ <span class="cline-any cline-yes">11x</span>
1265
+ <span class="cline-any cline-yes">11x</span>
1266
+ <span class="cline-any cline-yes">12x</span>
1267
+ <span class="cline-any cline-yes">11x</span>
1268
+ <span class="cline-any cline-yes">11x</span>
1269
+ <span class="cline-any cline-yes">11x</span>
1270
+ <span class="cline-any cline-yes">11x</span>
1271
+ <span class="cline-any cline-yes">11x</span>
1272
+ <span class="cline-any cline-yes">11x</span>
1273
+ <span class="cline-any cline-yes">11x</span>
1274
+ <span class="cline-any cline-yes">11x</span>
1275
+ <span class="cline-any cline-yes">11x</span>
1276
+ <span class="cline-any cline-yes">11x</span>
1277
+ <span class="cline-any cline-yes">11x</span>
1278
+ <span class="cline-any cline-yes">11x</span>
1279
+ <span class="cline-any cline-yes">11x</span>
1280
+ <span class="cline-any cline-yes">11x</span>
1281
+ <span class="cline-any cline-yes">11x</span>
1282
+ <span class="cline-any cline-yes">11x</span>
1283
+ <span class="cline-any cline-yes">11x</span>
1284
+ <span class="cline-any cline-yes">1x</span>
1285
+ <span class="cline-any cline-yes">1x</span>
1286
+ <span class="cline-any cline-yes">1x</span>
1287
+ <span class="cline-any cline-yes">1x</span>
1288
+ <span class="cline-any cline-no">&nbsp;</span>
1289
+ <span class="cline-any cline-yes">12x</span>
1290
+ <span class="cline-any cline-yes">11x</span>
1291
+ <span class="cline-any cline-yes">11x</span>
1292
+ <span class="cline-any cline-yes">11x</span>
1293
+ <span class="cline-any cline-neutral">&nbsp;</span>
1294
+ <span class="cline-any cline-yes">21x</span>
1295
+ <span class="cline-any cline-yes">9x</span>
1296
+ <span class="cline-any cline-yes">2x</span>
1297
+ <span class="cline-any cline-neutral">&nbsp;</span>
1298
+ <span class="cline-any cline-neutral">&nbsp;</span>
1299
+ <span class="cline-any cline-neutral">&nbsp;</span>
1300
+ <span class="cline-any cline-yes">2x</span>
1301
+ <span class="cline-any cline-yes">9x</span>
1302
+ <span class="cline-any cline-neutral">&nbsp;</span>
1303
+ <span class="cline-any cline-yes">21x</span>
1304
+ <span class="cline-any cline-no">&nbsp;</span>
1305
+ <span class="cline-any cline-no">&nbsp;</span>
1306
+ <span class="cline-any cline-no">&nbsp;</span>
1307
+ <span class="cline-any cline-no">&nbsp;</span>
1308
+ <span class="cline-any cline-no">&nbsp;</span>
1309
+ <span class="cline-any cline-no">&nbsp;</span>
1310
+ <span class="cline-any cline-neutral">&nbsp;</span>
1311
+ <span class="cline-any cline-no">&nbsp;</span>
1312
+ <span class="cline-any cline-no">&nbsp;</span>
1313
+ <span class="cline-any cline-neutral">&nbsp;</span>
1314
+ <span class="cline-any cline-yes">21x</span>
1315
+ <span class="cline-any cline-no">&nbsp;</span>
1316
+ <span class="cline-any cline-no">&nbsp;</span>
1317
+ <span class="cline-any cline-yes">21x</span>
1318
+ <span class="cline-any cline-no">&nbsp;</span>
1319
+ <span class="cline-any cline-no">&nbsp;</span>
1320
+ <span class="cline-any cline-yes">21x</span>
1321
+ <span class="cline-any cline-no">&nbsp;</span>
1322
+ <span class="cline-any cline-no">&nbsp;</span>
1323
+ <span class="cline-any cline-neutral">&nbsp;</span>
1324
+ <span class="cline-any cline-yes">21x</span>
1325
+ <span class="cline-any cline-yes">21x</span>
1326
+ <span class="cline-any cline-yes">21x</span>
1327
+ <span class="cline-any cline-yes">9x</span>
1328
+ <span class="cline-any cline-yes">21x</span>
1329
+ <span class="cline-any cline-yes">21x</span>
1330
+ <span class="cline-any cline-yes">21x</span>
1331
+ <span class="cline-any cline-yes">21x</span>
1332
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/**
1333
+ * TabsCoordinator: the per-tab role state machine for shared-tabs mode. Sits
1334
+ * between the broker client (election events, ports) and the rest of the
1335
+ * client (engine + sync), which it drives exclusively through
1336
+ * {@link CoordinatorHooks} so this module depends on neither concrete class.
1337
+ *
1338
+ * Roles:
1339
+ * - leader: owns the sqlite worker (OPFS) and the sync loop; serves follower
1340
+ * ports handed over by the broker.
1341
+ * - follower: LocalStore ops go over dbPort into the leader's worker; sync
1342
+ * concerns are forwarded over syncPort (see {@link SyncForwarder}).
1343
+ * - solo: broker unavailable/rejected; exactly the pre-shared-tabs behavior.
1344
+ *
1345
+ * Bucket switches are namespace moves: `moveToBucket` re-hellos under the new
1346
+ * bucketId and the ordinary election machinery assigns the new role there.
1347
+ */
1348
+ import type { Logger } from '../logger/index';
1349
+ import type { StorageHealth } from '../../types';
1350
+ import { TabBrokerClient } from './broker-client';
1351
+ import { acquireLeaderTabLock, type LeaderLockHandle } from './leader-locks';
1352
+ import {
1353
+ tabLockName,
1354
+ workerLockName,
1355
+ type FollowerToLeaderMessage,
1356
+ type IngestTuple,
1357
+ type LeaderToFollowerMessage,
1358
+ type TabId,
1359
+ type TabRole,
1360
+ } from './protocol';
1361
+ &nbsp;
1362
+ /** How long boot waits for a role (election + attach) before going solo. */
1363
+ const START_TIMEOUT_MS = 15_000;
1364
+ &nbsp;
1365
+ export interface CoordinatorHooks {
1366
+ /** Become the store owner: spawn the worker, open the pool under the given
1367
+ * per-leadership lock. Returns the resulting health for db-ready relays.
1368
+ * `resumeHeld` = keep the existing live worker (broker-restart path). */
1369
+ adoptOwner(
1370
+ bucketId: string,
1371
+ opts: { workerLockName: string; allowMemoryFallback: boolean; forceTakeover: boolean; resumeHeld: boolean }
1372
+ ): Promise&lt;StorageHealth&gt;;
1373
+ /** Attach to a leader's worker through `dbPort`. */
1374
+ adoptAttached(
1375
+ dbPort: MessagePort,
1376
+ snapshot: { bucketId: string; storageHealth: StorageHealth; leadershipId: number }
1377
+ ): Promise&lt;void&gt;;
1378
+ /** We were leader and got demoted (zombie thaw / stale promotion): tear the
1379
+ * owned worker down so its SAH handles free up. */
1380
+ releaseOwnership(): Promise&lt;void&gt;;
1381
+ /** The leader (or its port) is gone; queue/park ops until a new role lands. */
1382
+ onLeaderLost(reason: string): void;
1383
+ /** Leader side: forward a follower's dbPort into the owned worker. */
1384
+ exposeClientPort(clientId: string, port: MessagePort): Promise&lt;void&gt;;
1385
+ removeClientPort(clientId: string): Promise&lt;void&gt;;
1386
+ /** Sync layer role changes (implemented in the sync module). */
1387
+ /** Synchronous leader WIRING. Must not block: the role is published and
1388
+ * `leader-ready` is sent right after it returns. */
1389
+ becomeSyncLeader(hub: LeaderSyncHub): void;
1390
+ /** The blocking half of the leader handover (outbox drain, LIVE restart),
1391
+ * run in the background once the tab is already a working leader. */
1392
+ resumeSyncLeaderDuties(): Promise&lt;void&gt;;
1393
+ becomeSyncFollower(forwarder: SyncForwarder): void;
1394
+ becomeSyncSolo(): void;
1395
+ /** Current storage health, for db-ready sent to late-joining followers. */
1396
+ currentStorageHealth(): StorageHealth;
1397
+ }
1398
+ &nbsp;
1399
+ // ---- leader side: per-follower syncPort hub ---------------------------------
1400
+ &nbsp;
1401
+ export interface FollowerChannel {
1402
+ tabId: TabId;
1403
+ send(msg: LeaderToFollowerMessage): void;
1404
+ }
1405
+ &nbsp;
1406
+ /** Leader-side fan-out surface handed to the sync layer. The sync router
1407
+ * (modules/sync/tab-router.ts) registers itself as the message handler. */
1408
+ export class LeaderSyncHub {
1409
+ private followers = new Map&lt;TabId, MessagePort&gt;();
1410
+ private seq = 0;
1411
+ onFollowerMessage:
1412
+ | ((tabId: TabId, msg: FollowerToLeaderMessage) =&gt; void)
1413
+ | null = null;
1414
+ onFollowerDetached: ((tabId: TabId) =&gt; void) | null = null;
1415
+ &nbsp;
1416
+ constructor(
1417
+ readonly leadershipId: number,
1418
+ private logger: Logger
1419
+ ) {}
1420
+ &nbsp;
1421
+ attach(tabId: TabId, port: MessagePort): void {
1422
+ this.detach(tabId);
1423
+ this.followers.set(tabId, port);
1424
+ port.onmessage = (ev: MessageEvent) =&gt; {
1425
+ this.onFollowerMessage?.(tabId, ev.data as FollowerToLeaderMessage);
1426
+ };
1427
+ port.onmessageerror = <span class="fstat-no" title="function not covered" >() =&gt; this.detach(tabId);</span>
1428
+ port.start?.();
1429
+ }
1430
+ &nbsp;
1431
+ detach(tabId: TabId): void {
1432
+ const port = this.followers.get(tabId);
1433
+ if (!port) return;
1434
+ this.followers.delete(tabId);
1435
+ try {
1436
+ port.close();
1437
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1438
+ /* ignore */
1439
+ <span class="cstat-no" title="statement not covered" > }</span>
1440
+ this.<span class="branch-0 cbranch-no" title="branch not covered" >onFollowerDetached?.(tabId);</span>
1441
+ }
1442
+ &nbsp;
1443
+ detachAll(): void {
1444
+ for (const tabId of [...this.followers.keys()]) this.detach(tabId);
1445
+ }
1446
+ &nbsp;
1447
+ sendTo(tabId: TabId, msg: LeaderToFollowerMessage): void {
1448
+ try {
1449
+ this.followers.get(tabId)?.postMessage(msg);
1450
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1451
+ /* dead port; broker re-mints */
1452
+ <span class="cstat-no" title="statement not covered" > }</span>
1453
+ }
1454
+ &nbsp;
1455
+ broadcast(msg: LeaderToFollowerMessage, exceptTabId?: TabId): void {
1456
+ for (const [tabId, port] of this.followers) {
1457
+ if (tabId === exceptTabId) continue;
1458
+ try {
1459
+ port.postMessage(msg);
1460
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1461
+ /* ignore */
1462
+ <span class="cstat-no" title="statement not covered" > }</span>
1463
+ }
1464
+ }
1465
+ &nbsp;
1466
+ /** Stamped ingest relay; seq lets followers detect gaps. */
1467
+ relayIngest(tuples: IngestTuple[], exceptTabId?: TabId): void {
1468
+ if (this.followers.size === 0) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1469
+ this.broadcast(
1470
+ { type: 'ingest-relay', tuples, leadershipId: this.leadershipId, seq: ++this.seq },
1471
+ exceptTabId
1472
+ );
1473
+ }
1474
+ &nbsp;
1475
+ <span class="fstat-no" title="function not covered" > get followerCount(): number {</span>
1476
+ <span class="cstat-no" title="statement not covered" > return this.followers.size;</span>
1477
+ <span class="cstat-no" title="statement not covered" > }</span>
1478
+ &nbsp;
1479
+ <span class="fstat-no" title="function not covered" > get relayedBatches(): number {</span>
1480
+ <span class="cstat-no" title="statement not covered" > return this.seq;</span>
1481
+ <span class="cstat-no" title="statement not covered" > }</span>
1482
+ }
1483
+ &nbsp;
1484
+ // ---- follower side: syncPort forwarder ---------------------------------------
1485
+ &nbsp;
1486
+ /** Follower half of the syncPort. Queues while detached (leaderless window)
1487
+ * and flushes on rebind; a lost-in-flight mutation notify is additionally
1488
+ * backstopped by the new leader reloading the shared outbox from the store. */
1489
+ export class SyncForwarder {
1490
+ private port: MessagePort | null = null;
1491
+ private queued: FollowerToLeaderMessage[] = [];
1492
+ onLeaderMessage: ((msg: LeaderToFollowerMessage) =&gt; void) | null = null;
1493
+ &nbsp;
1494
+ constructor(private tabId: TabId) {}
1495
+ &nbsp;
1496
+ rebind(port: MessagePort): void {
1497
+ this.unbind();
1498
+ this.port = port;
1499
+ port.onmessage = (ev: MessageEvent) =&gt; {
1500
+ this.onLeaderMessage?.(ev.data as LeaderToFollowerMessage);
1501
+ };
1502
+ port.start?.();
1503
+ this.post({ type: 'sync-hello', tabId: this.tabId });
1504
+ const backlog = this.queued;
1505
+ this.queued = [];
1506
+ for (const msg of backlog) <span class="branch-0 cbranch-no" title="branch not covered" >this.post(msg);</span>
1507
+ }
1508
+ &nbsp;
1509
+ unbind(): void {
1510
+ if (this.port) {
1511
+ try {
1512
+ this.port.close();
1513
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1514
+ /* ignore */
1515
+ <span class="cstat-no" title="statement not covered" > }</span>
1516
+ }
1517
+ this.port = null;
1518
+ }
1519
+ &nbsp;
1520
+ private post(msg: FollowerToLeaderMessage): void {
1521
+ if (!this.port) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1522
+ <span class="cstat-no" title="statement not covered" > this.queued.push(msg);</span>
1523
+ <span class="cstat-no" title="statement not covered" > return;</span>
1524
+ <span class="cstat-no" title="statement not covered" > }</span>
1525
+ try {
1526
+ this.port.postMessage(msg);
1527
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1528
+ <span class="cstat-no" title="statement not covered" > this.queued.push(msg);</span>
1529
+ <span class="cstat-no" title="statement not covered" > }</span>
1530
+ }
1531
+ &nbsp;
1532
+ mutationEnqueued(mutationId: string): void {
1533
+ this.post({ type: 'mutation-enqueued', mutationId });
1534
+ }
1535
+ /** An optimistic write this tab just ingested. Deliberately NOT queued while
1536
+ * detached: a new leader primes its circuit from the shared store, which
1537
+ * already holds the row, and replaying a stale tuple at it later would put
1538
+ * an older `_00_rv` in its version memo. */
1539
+ ingest(tuples: IngestTuple[]): void {
1540
+ if (!this.port) return;
1541
+ try {
1542
+ this.port.postMessage({ type: 'ingest', tuples });
1543
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1544
+ /* dead port; broker re-mints */
1545
+ <span class="cstat-no" title="statement not covered" > }</span>
1546
+ }
1547
+ <span class="fstat-no" title="function not covered" > requestPoll(): void {</span>
1548
+ <span class="cstat-no" title="statement not covered" > this.post({ type: 'request-poll' });</span>
1549
+ <span class="cstat-no" title="statement not covered" > }</span>
1550
+ }
1551
+ &nbsp;
1552
+ // ---- the coordinator ----------------------------------------------------------
1553
+ &nbsp;
1554
+ export class TabsCoordinator {
1555
+ role: TabRole = 'solo';
1556
+ leadershipId = 0;
1557
+ leaderTabId: TabId | null = null;
1558
+ brokerRole: 'pending' | TabRole = 'pending';
1559
+ /** Wall time from `become-leader` to `leader-ready` on the last promotion.
1560
+ * Reported in the DevTools role-change event: a slow store adopt (OPFS pool
1561
+ * contention after the previous leader died) shows up here as seconds. */
1562
+ lastPromotionMs: number | null = null;
1563
+ private fingerprint: string;
1564
+ private bucketId: string;
1565
+ private broker: TabBrokerClient;
1566
+ private hub: LeaderSyncHub | null = null;
1567
+ private forwarder: SyncForwarder | null = null;
1568
+ private roleListeners = new Set&lt;(role: TabRole) =&gt; void&gt;();
1569
+ private startResolve: ((role: TabRole) =&gt; void) | null = null;
1570
+ private startReject: ((e: Error) =&gt; void) | null = null;
1571
+ /** Ports received before start() resolves or between roles. */
1572
+ private promotionChain: Promise&lt;void&gt; = Promise.resolve();
1573
+ private closed = false;
1574
+ &nbsp;
1575
+ constructor(
1576
+ private deps: {
1577
+ tabId: TabId;
1578
+ fingerprint: string;
1579
+ hooks: CoordinatorHooks;
1580
+ logger: Logger;
1581
+ /** Fired on pagehide while this tab leads: last-chance OPFS release. */
1582
+ onLeaderPageHide?: () =&gt; void;
1583
+ }
1584
+ ) {
1585
+ this.fingerprint = deps.fingerprint;
1586
+ this.bucketId = 'anon';
1587
+ // The URL is built HERE (same directory as the worker source) so the
1588
+ // published flat bundle's rewritten './tabs-broker-worker.js' resolves at
1589
+ // the dist top level, exactly like the sqlite worker URL does.
1590
+ this.broker = new TabBrokerClient(
1591
+ new URL('./tabs-broker-worker.ts', import.meta.url),
1592
+ deps.tabId,
1593
+ {
1594
+ onBecomeLeader: (msg) =&gt; this.enqueue(() =&gt; this.promote(msg)),
1595
+ onDemote: (leadershipId) =&gt; this.enqueue(() =&gt; this.demote(leadershipId)),
1596
+ onLeaderReady: (leadershipId, leaderTabId) =&gt; {
1597
+ this.leaderTabId = leaderTabId;
1598
+ void leadershipId;
1599
+ },
1600
+ onAttachFollowerPorts: (followerTabId, leadershipId, dbPort, syncPort) =&gt;
1601
+ this.enqueue(() =&gt; this.serveFollower(followerTabId, leadershipId, dbPort, syncPort)),
1602
+ onUseFollowerPorts: (leaderTabId, leadershipId, dbPort, syncPort) =&gt;
1603
+ this.enqueue(() =&gt; this.attachToLeader(leaderTabId, leadershipId, dbPort, syncPort)),
1604
+ onCloseFollowerPorts: (leadershipId) =&gt;
1605
+ this.enqueue(() =&gt; this.handleLeaderGone(leadershipId)),
1606
+ onUnsupported: <span class="fstat-no" title="function not covered" >() =&gt; this.fallbackToSolo('broker rejected this tab'),</span>
1607
+ onBrokerRestarted: <span class="fstat-no" title="function not covered" >() =&gt; {</span>
1608
+ // Direct MessageChannels survive a broker restart; roles get
1609
+ // re-confirmed by the fresh election (heldLeadership fast path).
1610
+ <span class="cstat-no" title="statement not covered" > },</span>
1611
+ },
1612
+ deps.logger
1613
+ );
1614
+ if (typeof window !== <span class="branch-0 cbranch-no" title="branch not covered" >'undefined' &amp;&amp; deps.onLeaderPageHide) <span class="branch-0 cbranch-no" title="branch not covered" >{</span></span>
1615
+ <span class="cstat-no" title="statement not covered" > window.addEventListener('pagehide', () =&gt; {</span>
1616
+ <span class="cstat-no" title="statement not covered" > if (this.role === 'leader') deps.onLeaderPageHide?.();</span>
1617
+ <span class="cstat-no" title="statement not covered" > });</span>
1618
+ <span class="cstat-no" title="statement not covered" > }</span>
1619
+ }
1620
+ &nbsp;
1621
+ /** Serialize role transitions; each is small but async (worker opens). */
1622
+ private enqueue(fn: () =&gt; Promise&lt;void&gt;): void {
1623
+ this.promotionChain = this.promotionChain.then(fn, fn).catch((e) =&gt; {
1624
+ <span class="cstat-no" title="statement not covered" > this.deps.logger.error(</span>
1625
+ <span class="cstat-no" title="statement not covered" > { err: e, Category: 'sp00ky-client::TabsCoordinator' },</span>
1626
+ <span class="cstat-no" title="statement not covered" > 'Role transition failed'</span>
1627
+ <span class="cstat-no" title="statement not covered" > );</span>
1628
+ });
1629
+ }
1630
+ &nbsp;
1631
+ onRoleChange(cb: (role: TabRole) =&gt; void): () =&gt; void {
1632
+ this.roleListeners.add(cb);
1633
+ return () =&gt; this.roleListeners.delete(cb);
1634
+ }
1635
+ &nbsp;
1636
+ private setRole(role: TabRole): void {
1637
+ if (this.role !== role) {
1638
+ this.role = role;
1639
+ this.deps.logger.info(
1640
+ {
1641
+ role,
1642
+ leadershipId: this.leadershipId,
1643
+ promotionMs: role === 'leader' ? this.lastPromotionMs : undefined,
1644
+ Category: 'sp00ky-client::TabsCoordinator',
1645
+ },
1646
+ 'Tab role changed'
1647
+ );
1648
+ for (const cb of this.roleListeners) cb(role);
1649
+ }
1650
+ if (this.startResolve &amp;&amp; role !== 'solo') {
1651
+ this.startResolve(role);
1652
+ this.startResolve = null;
1653
+ }
1654
+ }
1655
+ &nbsp;
1656
+ /** Connect the broker and resolve once this tab has a usable store role.
1657
+ * Rejects when no role lands in time; the caller then boots solo. */
1658
+ start(bucketId: string): Promise&lt;TabRole&gt; {
1659
+ this.bucketId = bucketId;
1660
+ return new Promise&lt;TabRole&gt;((resolve, reject) =&gt; {
1661
+ const timeout = setTimeout(() =&gt; {
1662
+ <span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
1663
+ <span class="cstat-no" title="statement not covered" > this.startReject = null;</span>
1664
+ <span class="cstat-no" title="statement not covered" > reject(new Error('shared-tabs: no role assigned in time'));</span>
1665
+ }, START_TIMEOUT_MS);
1666
+ this.startResolve = (role) =&gt; {
1667
+ clearTimeout(timeout);
1668
+ this.startReject = null;
1669
+ resolve(role);
1670
+ };
1671
+ this.startReject = <span class="fstat-no" title="function not covered" >(e) =&gt; {</span>
1672
+ <span class="cstat-no" title="statement not covered" > clearTimeout(timeout);</span>
1673
+ <span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
1674
+ <span class="cstat-no" title="statement not covered" > reject(e);</span>
1675
+ <span class="cstat-no" title="statement not covered" > };</span>
1676
+ this.broker
1677
+ .connect({
1678
+ fingerprint: this.fingerprint,
1679
+ bucketId,
1680
+ heldLeadership: () =&gt;
1681
+ this.role === <span class="branch-0 cbranch-no" title="branch not covered" >'leader'</span>
1682
+ <span class="cstat-no" title="statement not covered" > ? {</span>
1683
+ <span class="cstat-no" title="statement not covered" > leadershipId: this.leadershipId,</span>
1684
+ <span class="cstat-no" title="statement not covered" > workerLockName: workerLockName(this.fingerprint, this.bucketId, this.leadershipId),</span>
1685
+ <span class="cstat-no" title="statement not covered" > }</span>
1686
+ : null,
1687
+ })
1688
+ .catch((e) =&gt; {
1689
+ <span class="cstat-no" title="statement not covered" > this.startReject?.(e instanceof Error ? e : new Error(String(e)));</span>
1690
+ });
1691
+ });
1692
+ }
1693
+ &nbsp;
1694
+ /** Bucket switch: leave the old namespace, join the new one. Resolves when
1695
+ * a role lands in the new namespace. */
1696
+ moveToBucket(bucketId: string): Promise&lt;TabRole&gt; {
1697
+ return new Promise&lt;TabRole&gt;((resolve, reject) =&gt; {
1698
+ this.enqueue(async () =&gt; {
1699
+ // Tear down the old role locally; the broker's rehello handling evicts
1700
+ // us from the old namespace and re-elects there.
1701
+ if (this.role === 'leader') await this.teardownLeader(<span class="branch-0 cbranch-no" title="branch not covered" >);</span>
1702
+ <span class="cstat-no" title="statement not covered" > else if (this.role === 'follower') this.teardownFollower('bucket switch');</span>
1703
+ this.bucketId = bucketId;
1704
+ const timeout = setTimeout(() =&gt; {
1705
+ <span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
1706
+ <span class="cstat-no" title="statement not covered" > this.startReject = null;</span>
1707
+ <span class="cstat-no" title="statement not covered" > reject(new Error('shared-tabs: no role assigned after bucket switch'));</span>
1708
+ }, START_TIMEOUT_MS);
1709
+ this.startResolve = (role) =&gt; {
1710
+ clearTimeout(timeout);
1711
+ this.startReject = null;
1712
+ resolve(role);
1713
+ };
1714
+ this.startReject = <span class="fstat-no" title="function not covered" >(e) =&gt; {</span>
1715
+ <span class="cstat-no" title="statement not covered" > clearTimeout(timeout);</span>
1716
+ <span class="cstat-no" title="statement not covered" > this.startResolve = null;</span>
1717
+ <span class="cstat-no" title="statement not covered" > reject(e);</span>
1718
+ <span class="cstat-no" title="statement not covered" > };</span>
1719
+ await this.broker.rehello({
1720
+ fingerprint: this.fingerprint,
1721
+ bucketId,
1722
+ heldLeadership: () =&gt; null,
1723
+ });
1724
+ });
1725
+ });
1726
+ }
1727
+ &nbsp;
1728
+ // ---- transitions -----------------------------------------------------------
1729
+ &nbsp;
1730
+ private async promote(msg: {
1731
+ leadershipId: number;
1732
+ forceTakeover: boolean;
1733
+ allowMemoryFallback: boolean;
1734
+ resumeHeld: boolean;
1735
+ }): Promise&lt;void&gt; {
1736
+ if (this.closed) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1737
+ if (msg.leadershipId &lt;= this.<span class="branch-0 cbranch-no" title="branch not covered" >leadershipId &amp;&amp; !msg.resumeHeld) <span class="branch-0 cbranch-no" title="branch not covered" >r</span>eturn;</span>
1738
+ const previousRole = this.role;
1739
+ const startedAt = Date.now();
1740
+ try {
1741
+ if (previousRole === 'follower') this.teardownFollower('promoted');
1742
+ this.leadershipId = msg.leadershipId;
1743
+ // The tab lock is the broker's CRASH detector: it queues a request on
1744
+ // this name, and being granted means this tab died (locks release on tab
1745
+ // death instantly, unlike the 15s pong timeout). Steal only when the
1746
+ // broker said the previous holder is a frozen zombie.
1747
+ const lock = await acquireLeaderTabLock(tabLockName(this.fingerprint, this.bucketId), {
1748
+ steal: msg.forceTakeover,
1749
+ });
1750
+ if (!lock) <span class="branch-0 cbranch-no" title="branch not covered" >throw new Error('leader tab lock unavailable');</span>
1751
+ this.<span class="branch-0 cbranch-no" title="branch not covered" >tabLock?.release()</span>;
1752
+ this.tabLock = lock;
1753
+ lock.onLost(() =&gt; {
1754
+ // Stolen from under us (we were presumed dead): resign.
1755
+ <span class="cstat-no" title="statement not covered" > this.enqueue(async () =&gt; {</span>
1756
+ <span class="cstat-no" title="statement not covered" > if (this.leadershipId !== msg.leadershipId || this.role !== 'leader') return;</span>
1757
+ <span class="cstat-no" title="statement not covered" > await this.teardownLeader();</span>
1758
+ <span class="cstat-no" title="statement not covered" > this.deps.hooks.onLeaderLost('tab lock stolen');</span>
1759
+ <span class="cstat-no" title="statement not covered" > });</span>
1760
+ });
1761
+ const health = await this.deps.hooks.adoptOwner(this.bucketId, {
1762
+ workerLockName: workerLockName(this.fingerprint, this.bucketId, msg.leadershipId),
1763
+ allowMemoryFallback: msg.allowMemoryFallback,
1764
+ forceTakeover: msg.forceTakeover,
1765
+ resumeHeld: msg.resumeHeld,
1766
+ });
1767
+ void health;
1768
+ this.hub = new LeaderSyncHub(msg.leadershipId, this.deps.logger);
1769
+ // Owning the store IS being the leader: it unparks this tab's ops and is
1770
+ // everything a follower's ports need. Publish the role and tell the
1771
+ // broker BEFORE any sync work. Waiting on the outbox drain and the LIVE
1772
+ // restart here once left a tab that owned the OPFS pool but never sent
1773
+ // `leader-ready`, so the broker minted no follower ports and re-elected
1774
+ // no one (`assignFollowerPorts` and `electIfNeeded` both bail on that
1775
+ // state) and the whole namespace stayed leaderless until a reload. Those
1776
+ // duties are best-effort anyway: the list_ref poll covers a missing LIVE.
1777
+ this.deps.hooks.becomeSyncLeader(this.hub);
1778
+ this.lastPromotionMs = Date.now() - startedAt;
1779
+ this.setRole('leader');
1780
+ this.broker.send({
1781
+ type: 'leader-ready',
1782
+ tabId: this.deps.tabId,
1783
+ bucketId: this.bucketId,
1784
+ leadershipId: msg.leadershipId,
1785
+ });
1786
+ // Off the promotion chain on purpose: a hang here must not block the
1787
+ // next role transition (demote, bucket switch) for the life of the tab.
1788
+ void this.deps.hooks.resumeSyncLeaderDuties().catch((e) =&gt; {
1789
+ <span class="cstat-no" title="statement not covered" > this.deps.logger.error(</span>
1790
+ <span class="cstat-no" title="statement not covered" > { err: e, Category: 'sp00ky-client::TabsCoordinator' },</span>
1791
+ <span class="cstat-no" title="statement not covered" > 'Leader duties failed after promotion'</span>
1792
+ <span class="cstat-no" title="statement not covered" > );</span>
1793
+ });
1794
+ } catch (e) {
1795
+ const reason = e instanceof Error ? e.<span class="branch-0 cbranch-no" title="branch not covered" >message : String(e);</span>
1796
+ this.deps.logger.error(
1797
+ { err: e, Category: 'sp00ky-client::TabsCoordinator' },
1798
+ 'Promotion failed'
1799
+ );
1800
+ this.<span class="branch-0 cbranch-no" title="branch not covered" >hub?.detachAll()</span>;
1801
+ this.hub = null;
1802
+ // Give back everything this attempt claimed. The broker does NOT demote a
1803
+ // tab whose promotion failed (leader-failed clears leadership without a
1804
+ // demote), so nothing else ever frees these. The tab lock name is shared
1805
+ // per namespace, so keeping it after failing to lead makes EVERY later
1806
+ // election in this namespace fail with 'leader tab lock unavailable' —
1807
+ // one OPFS-busy promotion would wedge the whole app into solo mode.
1808
+ if (previousRole === 'leader') <span class="branch-0 cbranch-no" title="branch not covered" >await this.deps.hooks.releaseOwnership();</span>
1809
+ this.tabLock?.release();
1810
+ this.tabLock = null;
1811
+ this.broker.send({
1812
+ type: 'leader-failed',
1813
+ tabId: this.deps.tabId,
1814
+ bucketId: this.bucketId,
1815
+ leadershipId: msg.leadershipId,
1816
+ reason,
1817
+ });
1818
+ }
1819
+ }
1820
+ &nbsp;
1821
+ private async demote(leadershipId: number): Promise&lt;void&gt; {
1822
+ if (this.role !== 'leader' || leadershipId !== this.leadershipId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1823
+ await this.teardownLeader();
1824
+ this.deps.hooks.onLeaderLost('demoted');
1825
+ // Stay roleless; the broker sends use-follower-ports (or become-leader)
1826
+ // for whatever comes next.
1827
+ }
1828
+ &nbsp;
1829
+ private tabLock: LeaderLockHandle | null = null;
1830
+ &nbsp;
1831
+ private async teardownLeader(): Promise&lt;void&gt; {
1832
+ this.hub?.detachAll();
1833
+ this.hub = null;
1834
+ await this.deps.hooks.releaseOwnership();
1835
+ this.tabLock?.release();
1836
+ this.tabLock = null;
1837
+ }
1838
+ &nbsp;
1839
+ private teardownFollower(reason: string): void {
1840
+ this.forwarder?.unbind();
1841
+ this.deps.hooks.onLeaderLost(reason);
1842
+ }
1843
+ &nbsp;
1844
+ private async serveFollower(
1845
+ followerTabId: TabId,
1846
+ leadershipId: number,
1847
+ dbPort: MessagePort,
1848
+ syncPort: MessagePort
1849
+ ): Promise&lt;void&gt; {
1850
+ if (this.role !== 'leader' || leadershipId !== this.leadershipId || !this.hub) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1851
+ <span class="cstat-no" title="statement not covered" > dbPort.close();</span>
1852
+ <span class="cstat-no" title="statement not covered" > syncPort.close();</span>
1853
+ <span class="cstat-no" title="statement not covered" > return;</span>
1854
+ <span class="cstat-no" title="statement not covered" > }</span>
1855
+ await this.deps.hooks.exposeClientPort(followerTabId, dbPort);
1856
+ this.hub.attach(followerTabId, syncPort);
1857
+ this.hub.sendTo(followerTabId, {
1858
+ type: 'db-ready',
1859
+ leadershipId,
1860
+ bucketId: this.bucketId,
1861
+ storageHealth: this.deps.hooks.currentStorageHealth(),
1862
+ });
1863
+ this.broker.send({
1864
+ type: 'follower-port-attached',
1865
+ tabId: this.deps.tabId,
1866
+ bucketId: this.bucketId,
1867
+ leadershipId,
1868
+ followerTabId,
1869
+ });
1870
+ }
1871
+ &nbsp;
1872
+ private async attachToLeader(
1873
+ leaderTabId: TabId,
1874
+ leadershipId: number,
1875
+ dbPort: MessagePort,
1876
+ syncPort: MessagePort
1877
+ ): Promise&lt;void&gt; {
1878
+ if (this.closed || this.role === 'leader') <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1879
+ <span class="cstat-no" title="statement not covered" > dbPort.close();</span>
1880
+ <span class="cstat-no" title="statement not covered" > syncPort.close();</span>
1881
+ <span class="cstat-no" title="statement not covered" > return;</span>
1882
+ <span class="cstat-no" title="statement not covered" > }</span>
1883
+ this.leaderTabId = leaderTabId;
1884
+ this.leadershipId = leadershipId;
1885
+ if (!this.forwarder) this.forwarder = new SyncForwarder(this.deps.tabId);
1886
+ // db-ready arrives on the syncPort and carries the snapshot the engine
1887
+ // needs; bind sync first, adopt the store on receipt.
1888
+ const forwarder = this.forwarder;
1889
+ await new Promise&lt;void&gt;((resolve) =&gt; {
1890
+ let adopted = false;
1891
+ let attached = false;
1892
+ // Relay traffic that lands between `db-ready` and the sync hooks being
1893
+ // installed (the store adopt is async). Held and replayed, not dropped:
1894
+ // on a first attach there is no previous handler, so an ingest-relay or
1895
+ // settled notice in that window used to vanish.
1896
+ const pending: LeaderToFollowerMessage[] = [];
1897
+ const previousHandler = forwarder.onLeaderMessage;
1898
+ forwarder.onLeaderMessage = (msg) =&gt; {
1899
+ if (msg.type === 'db-ready' &amp;&amp; !adopted) {
1900
+ adopted = true;
1901
+ void this.deps.hooks
1902
+ .adoptAttached(dbPort, {
1903
+ bucketId: msg.bucketId,
1904
+ storageHealth: msg.storageHealth,
1905
+ leadershipId: msg.leadershipId,
1906
+ })
1907
+ .then(() =&gt; {
1908
+ this.deps.hooks.becomeSyncFollower(forwarder);
1909
+ attached = true;
1910
+ const backlog = pending.splice(0);
1911
+ for (const m of backlog) forwarder.onLeaderMessage?.(m);
1912
+ this.setRole('follower');
1913
+ resolve();
1914
+ });
1915
+ return;
1916
+ }
1917
+ if (!attached) {
1918
+ pending.push(msg);
1919
+ return;
1920
+ <span class="branch-0 cbranch-no" title="branch not covered" > }</span>
1921
+ <span class="cstat-no" title="statement not covered" > previousHandler?.(msg);</span>
1922
+ };
1923
+ forwarder.rebind(syncPort);
1924
+ });
1925
+ }
1926
+ &nbsp;
1927
+ private async handleLeaderGone(leadershipId: number): Promise&lt;void&gt; {
1928
+ if (this.role === 'leader') return;
1929
+ if (leadershipId &lt; this.leadershipId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1930
+ // Detached limbo: the engine parks ops via onLeaderLost and the role stays
1931
+ // 'follower' (this tab is still in shared mode, just between leaders). The
1932
+ // next use-follower-ports or become-leader resolves it either way.
1933
+ this.teardownFollower('leader gone');
1934
+ }
1935
+ &nbsp;
1936
+ private <span class="fstat-no" title="function not covered" >fallbackToSolo(reason: string): void {</span>
1937
+ <span class="cstat-no" title="statement not covered" > this.deps.logger.warn(</span>
1938
+ <span class="cstat-no" title="statement not covered" > { reason, Category: 'sp00ky-client::TabsCoordinator' },</span>
1939
+ <span class="cstat-no" title="statement not covered" > 'Shared-tabs unavailable; running solo'</span>
1940
+ <span class="cstat-no" title="statement not covered" > );</span>
1941
+ <span class="cstat-no" title="statement not covered" > this.deps.hooks.becomeSyncSolo();</span>
1942
+ <span class="cstat-no" title="statement not covered" > this.setRole('solo');</span>
1943
+ // start() treats solo as a rejection so the caller boots the plain path.
1944
+ <span class="cstat-no" title="statement not covered" > this.startReject?.(new Error(`shared-tabs unavailable: ${reason}`));</span>
1945
+ <span class="cstat-no" title="statement not covered" > }</span>
1946
+ &nbsp;
1947
+ <span class="fstat-no" title="function not covered" > get syncHub(): LeaderSyncHub | null {</span>
1948
+ <span class="cstat-no" title="statement not covered" > return this.hub;</span>
1949
+ <span class="cstat-no" title="statement not covered" > }</span>
1950
+ <span class="fstat-no" title="function not covered" > get syncForwarder(): SyncForwarder | null {</span>
1951
+ <span class="cstat-no" title="statement not covered" > return this.forwarder;</span>
1952
+ <span class="cstat-no" title="statement not covered" > }</span>
1953
+ <span class="fstat-no" title="function not covered" > get tabId(): TabId {</span>
1954
+ <span class="cstat-no" title="statement not covered" > return this.deps.tabId;</span>
1955
+ <span class="cstat-no" title="statement not covered" > }</span>
1956
+ &nbsp;
1957
+ async stop(): Promise&lt;void&gt; {
1958
+ this.closed = true;
1959
+ if (this.role === 'leader') await this.teardownLeader();
1960
+ else this.forwarder?.unbind();
1961
+ this.broker.send({ type: 'shutdown', tabId: this.deps.tabId, bucketId: this.bucketId });
1962
+ this.broker.close();
1963
+ }
1964
+ }
1965
+ &nbsp;</pre></td></tr></table></pre>
1966
+
1967
+ <div class='push'></div><!-- for sticky footer -->
1968
+ </div><!-- /wrapper -->
1969
+ <div class='footer quiet pad2 space-top1 center small'>
1970
+ Code coverage generated by
1971
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1972
+ at 2026-09-09T09:49:51.780Z
1973
+ </div>
1974
+ <script src="../../../prettify.js"></script>
1975
+ <script>
1976
+ window.onload = function () {
1977
+ prettyPrint();
1978
+ };
1979
+ </script>
1980
+ <script src="../../../sorter.js"></script>
1981
+ <script src="../../../block-navigation.js"></script>
1982
+ </body>
1983
+ </html>
1984
+