@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,967 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/modules/crdt/crdt-field.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/modules/crdt</a> crdt-field.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">49.76% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>105/211</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">58.33% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>14/24</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">52.63% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>10/19</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">49.76% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>105/211</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 low'></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></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
361
+ <span class="cline-any cline-neutral">&nbsp;</span>
362
+ <span class="cline-any cline-neutral">&nbsp;</span>
363
+ <span class="cline-any cline-neutral">&nbsp;</span>
364
+ <span class="cline-any cline-neutral">&nbsp;</span>
365
+ <span class="cline-any cline-neutral">&nbsp;</span>
366
+ <span class="cline-any cline-neutral">&nbsp;</span>
367
+ <span class="cline-any cline-yes">1x</span>
368
+ <span class="cline-any cline-yes">1x</span>
369
+ <span class="cline-any cline-yes">1x</span>
370
+ <span class="cline-any cline-yes">1x</span>
371
+ <span class="cline-any cline-neutral">&nbsp;</span>
372
+ <span class="cline-any cline-yes">1x</span>
373
+ <span class="cline-any cline-no">&nbsp;</span>
374
+ <span class="cline-any cline-no">&nbsp;</span>
375
+ <span class="cline-any cline-no">&nbsp;</span>
376
+ <span class="cline-any cline-no">&nbsp;</span>
377
+ <span class="cline-any cline-no">&nbsp;</span>
378
+ <span class="cline-any cline-no">&nbsp;</span>
379
+ <span class="cline-any cline-neutral">&nbsp;</span>
380
+ <span class="cline-any cline-neutral">&nbsp;</span>
381
+ <span class="cline-any cline-neutral">&nbsp;</span>
382
+ <span class="cline-any cline-yes">1x</span>
383
+ <span class="cline-any cline-yes">9x</span>
384
+ <span class="cline-any cline-yes">9x</span>
385
+ <span class="cline-any cline-yes">9x</span>
386
+ <span class="cline-any cline-yes">9x</span>
387
+ <span class="cline-any cline-yes">9x</span>
388
+ <span class="cline-any cline-yes">9x</span>
389
+ <span class="cline-any cline-yes">9x</span>
390
+ <span class="cline-any cline-yes">9x</span>
391
+ <span class="cline-any cline-yes">9x</span>
392
+ <span class="cline-any cline-yes">9x</span>
393
+ <span class="cline-any cline-yes">9x</span>
394
+ <span class="cline-any cline-yes">9x</span>
395
+ <span class="cline-any cline-yes">9x</span>
396
+ <span class="cline-any cline-neutral">&nbsp;</span>
397
+ <span class="cline-any cline-neutral">&nbsp;</span>
398
+ <span class="cline-any cline-neutral">&nbsp;</span>
399
+ <span class="cline-any cline-yes">9x</span>
400
+ <span class="cline-any cline-neutral">&nbsp;</span>
401
+ <span class="cline-any cline-yes">9x</span>
402
+ <span class="cline-any cline-yes">9x</span>
403
+ <span class="cline-any cline-neutral">&nbsp;</span>
404
+ <span class="cline-any cline-neutral">&nbsp;</span>
405
+ <span class="cline-any cline-neutral">&nbsp;</span>
406
+ <span class="cline-any cline-yes">9x</span>
407
+ <span class="cline-any cline-no">&nbsp;</span>
408
+ <span class="cline-any cline-no">&nbsp;</span>
409
+ <span class="cline-any cline-no">&nbsp;</span>
410
+ <span class="cline-any cline-no">&nbsp;</span>
411
+ <span class="cline-any cline-no">&nbsp;</span>
412
+ <span class="cline-any cline-no">&nbsp;</span>
413
+ <span class="cline-any cline-no">&nbsp;</span>
414
+ <span class="cline-any cline-no">&nbsp;</span>
415
+ <span class="cline-any cline-no">&nbsp;</span>
416
+ <span class="cline-any cline-no">&nbsp;</span>
417
+ <span class="cline-any cline-no">&nbsp;</span>
418
+ <span class="cline-any cline-neutral">&nbsp;</span>
419
+ <span class="cline-any cline-yes">9x</span>
420
+ <span class="cline-any cline-neutral">&nbsp;</span>
421
+ <span class="cline-any cline-neutral">&nbsp;</span>
422
+ <span class="cline-any cline-neutral">&nbsp;</span>
423
+ <span class="cline-any cline-neutral">&nbsp;</span>
424
+ <span class="cline-any cline-neutral">&nbsp;</span>
425
+ <span class="cline-any cline-neutral">&nbsp;</span>
426
+ <span class="cline-any cline-yes">9x</span>
427
+ <span class="cline-any cline-yes">9x</span>
428
+ <span class="cline-any cline-yes">9x</span>
429
+ <span class="cline-any cline-yes">9x</span>
430
+ <span class="cline-any cline-yes">9x</span>
431
+ <span class="cline-any cline-yes">9x</span>
432
+ <span class="cline-any cline-yes">9x</span>
433
+ <span class="cline-any cline-yes">9x</span>
434
+ <span class="cline-any cline-no">&nbsp;</span>
435
+ <span class="cline-any cline-no">&nbsp;</span>
436
+ <span class="cline-any cline-no">&nbsp;</span>
437
+ <span class="cline-any cline-no">&nbsp;</span>
438
+ <span class="cline-any cline-yes">9x</span>
439
+ <span class="cline-any cline-yes">9x</span>
440
+ <span class="cline-any cline-yes">9x</span>
441
+ <span class="cline-any cline-yes">9x</span>
442
+ <span class="cline-any cline-neutral">&nbsp;</span>
443
+ <span class="cline-any cline-neutral">&nbsp;</span>
444
+ <span class="cline-any cline-neutral">&nbsp;</span>
445
+ <span class="cline-any cline-neutral">&nbsp;</span>
446
+ <span class="cline-any cline-yes">2x</span>
447
+ <span class="cline-any cline-yes">2x</span>
448
+ <span class="cline-any cline-yes">2x</span>
449
+ <span class="cline-any cline-yes">2x</span>
450
+ <span class="cline-any cline-no">&nbsp;</span>
451
+ <span class="cline-any cline-no">&nbsp;</span>
452
+ <span class="cline-any cline-no">&nbsp;</span>
453
+ <span class="cline-any cline-no">&nbsp;</span>
454
+ <span class="cline-any cline-no">&nbsp;</span>
455
+ <span class="cline-any cline-no">&nbsp;</span>
456
+ <span class="cline-any cline-no">&nbsp;</span>
457
+ <span class="cline-any cline-no">&nbsp;</span>
458
+ <span class="cline-any cline-no">&nbsp;</span>
459
+ <span class="cline-any cline-yes">2x</span>
460
+ <span class="cline-any cline-yes">9x</span>
461
+ <span class="cline-any cline-neutral">&nbsp;</span>
462
+ <span class="cline-any cline-yes">9x</span>
463
+ <span class="cline-any cline-neutral">&nbsp;</span>
464
+ <span class="cline-any cline-neutral">&nbsp;</span>
465
+ <span class="cline-any cline-yes">9x</span>
466
+ <span class="cline-any cline-no">&nbsp;</span>
467
+ <span class="cline-any cline-no">&nbsp;</span>
468
+ <span class="cline-any cline-neutral">&nbsp;</span>
469
+ <span class="cline-any cline-yes">9x</span>
470
+ <span class="cline-any cline-yes">9x</span>
471
+ <span class="cline-any cline-yes">9x</span>
472
+ <span class="cline-any cline-yes">9x</span>
473
+ <span class="cline-any cline-yes">9x</span>
474
+ <span class="cline-any cline-yes">9x</span>
475
+ <span class="cline-any cline-yes">9x</span>
476
+ <span class="cline-any cline-yes">9x</span>
477
+ <span class="cline-any cline-yes">9x</span>
478
+ <span class="cline-any cline-yes">9x</span>
479
+ <span class="cline-any cline-yes">9x</span>
480
+ <span class="cline-any cline-yes">9x</span>
481
+ <span class="cline-any cline-neutral">&nbsp;</span>
482
+ <span class="cline-any cline-neutral">&nbsp;</span>
483
+ <span class="cline-any cline-neutral">&nbsp;</span>
484
+ <span class="cline-any cline-neutral">&nbsp;</span>
485
+ <span class="cline-any cline-neutral">&nbsp;</span>
486
+ <span class="cline-any cline-yes">9x</span>
487
+ <span class="cline-any cline-no">&nbsp;</span>
488
+ <span class="cline-any cline-no">&nbsp;</span>
489
+ <span class="cline-any cline-yes">9x</span>
490
+ <span class="cline-any cline-yes">9x</span>
491
+ <span class="cline-any cline-neutral">&nbsp;</span>
492
+ <span class="cline-any cline-neutral">&nbsp;</span>
493
+ <span class="cline-any cline-neutral">&nbsp;</span>
494
+ <span class="cline-any cline-neutral">&nbsp;</span>
495
+ <span class="cline-any cline-neutral">&nbsp;</span>
496
+ <span class="cline-any cline-neutral">&nbsp;</span>
497
+ <span class="cline-any cline-neutral">&nbsp;</span>
498
+ <span class="cline-any cline-yes">9x</span>
499
+ <span class="cline-any cline-yes">1x</span>
500
+ <span class="cline-any cline-yes">1x</span>
501
+ <span class="cline-any cline-yes">1x</span>
502
+ <span class="cline-any cline-yes">1x</span>
503
+ <span class="cline-any cline-yes">1x</span>
504
+ <span class="cline-any cline-neutral">&nbsp;</span>
505
+ <span class="cline-any cline-yes">9x</span>
506
+ <span class="cline-any cline-neutral">&nbsp;</span>
507
+ <span class="cline-any cline-neutral">&nbsp;</span>
508
+ <span class="cline-any cline-neutral">&nbsp;</span>
509
+ <span class="cline-any cline-neutral">&nbsp;</span>
510
+ <span class="cline-any cline-yes">2x</span>
511
+ <span class="cline-any cline-yes">2x</span>
512
+ <span class="cline-any cline-yes">2x</span>
513
+ <span class="cline-any cline-neutral">&nbsp;</span>
514
+ <span class="cline-any cline-neutral">&nbsp;</span>
515
+ <span class="cline-any cline-neutral">&nbsp;</span>
516
+ <span class="cline-any cline-yes">2x</span>
517
+ <span class="cline-any cline-yes">2x</span>
518
+ <span class="cline-any cline-no">&nbsp;</span>
519
+ <span class="cline-any cline-no">&nbsp;</span>
520
+ <span class="cline-any cline-no">&nbsp;</span>
521
+ <span class="cline-any cline-no">&nbsp;</span>
522
+ <span class="cline-any cline-no">&nbsp;</span>
523
+ <span class="cline-any cline-yes">2x</span>
524
+ <span class="cline-any cline-neutral">&nbsp;</span>
525
+ <span class="cline-any cline-yes">9x</span>
526
+ <span class="cline-any cline-yes">3x</span>
527
+ <span class="cline-any cline-yes">3x</span>
528
+ <span class="cline-any cline-neutral">&nbsp;</span>
529
+ <span class="cline-any cline-neutral">&nbsp;</span>
530
+ <span class="cline-any cline-neutral">&nbsp;</span>
531
+ <span class="cline-any cline-neutral">&nbsp;</span>
532
+ <span class="cline-any cline-neutral">&nbsp;</span>
533
+ <span class="cline-any cline-neutral">&nbsp;</span>
534
+ <span class="cline-any cline-neutral">&nbsp;</span>
535
+ <span class="cline-any cline-yes">9x</span>
536
+ <span class="cline-any cline-no">&nbsp;</span>
537
+ <span class="cline-any cline-no">&nbsp;</span>
538
+ <span class="cline-any cline-no">&nbsp;</span>
539
+ <span class="cline-any cline-no">&nbsp;</span>
540
+ <span class="cline-any cline-no">&nbsp;</span>
541
+ <span class="cline-any cline-no">&nbsp;</span>
542
+ <span class="cline-any cline-no">&nbsp;</span>
543
+ <span class="cline-any cline-no">&nbsp;</span>
544
+ <span class="cline-any cline-no">&nbsp;</span>
545
+ <span class="cline-any cline-no">&nbsp;</span>
546
+ <span class="cline-any cline-no">&nbsp;</span>
547
+ <span class="cline-any cline-no">&nbsp;</span>
548
+ <span class="cline-any cline-no">&nbsp;</span>
549
+ <span class="cline-any cline-no">&nbsp;</span>
550
+ <span class="cline-any cline-no">&nbsp;</span>
551
+ <span class="cline-any cline-no">&nbsp;</span>
552
+ <span class="cline-any cline-no">&nbsp;</span>
553
+ <span class="cline-any cline-no">&nbsp;</span>
554
+ <span class="cline-any cline-no">&nbsp;</span>
555
+ <span class="cline-any cline-no">&nbsp;</span>
556
+ <span class="cline-any cline-neutral">&nbsp;</span>
557
+ <span class="cline-any cline-neutral">&nbsp;</span>
558
+ <span class="cline-any cline-yes">9x</span>
559
+ <span class="cline-any cline-no">&nbsp;</span>
560
+ <span class="cline-any cline-no">&nbsp;</span>
561
+ <span class="cline-any cline-no">&nbsp;</span>
562
+ <span class="cline-any cline-no">&nbsp;</span>
563
+ <span class="cline-any cline-no">&nbsp;</span>
564
+ <span class="cline-any cline-no">&nbsp;</span>
565
+ <span class="cline-any cline-neutral">&nbsp;</span>
566
+ <span class="cline-any cline-no">&nbsp;</span>
567
+ <span class="cline-any cline-no">&nbsp;</span>
568
+ <span class="cline-any cline-no">&nbsp;</span>
569
+ <span class="cline-any cline-no">&nbsp;</span>
570
+ <span class="cline-any cline-no">&nbsp;</span>
571
+ <span class="cline-any cline-no">&nbsp;</span>
572
+ <span class="cline-any cline-no">&nbsp;</span>
573
+ <span class="cline-any cline-no">&nbsp;</span>
574
+ <span class="cline-any cline-no">&nbsp;</span>
575
+ <span class="cline-any cline-neutral">&nbsp;</span>
576
+ <span class="cline-any cline-yes">9x</span>
577
+ <span class="cline-any cline-no">&nbsp;</span>
578
+ <span class="cline-any cline-no">&nbsp;</span>
579
+ <span class="cline-any cline-no">&nbsp;</span>
580
+ <span class="cline-any cline-neutral">&nbsp;</span>
581
+ <span class="cline-any cline-neutral">&nbsp;</span>
582
+ <span class="cline-any cline-neutral">&nbsp;</span>
583
+ <span class="cline-any cline-neutral">&nbsp;</span>
584
+ <span class="cline-any cline-neutral">&nbsp;</span>
585
+ <span class="cline-any cline-yes">9x</span>
586
+ <span class="cline-any cline-yes">3x</span>
587
+ <span class="cline-any cline-yes">3x</span>
588
+ <span class="cline-any cline-neutral">&nbsp;</span>
589
+ <span class="cline-any cline-neutral">&nbsp;</span>
590
+ <span class="cline-any cline-neutral">&nbsp;</span>
591
+ <span class="cline-any cline-neutral">&nbsp;</span>
592
+ <span class="cline-any cline-neutral">&nbsp;</span>
593
+ <span class="cline-any cline-yes">9x</span>
594
+ <span class="cline-any cline-yes">2x</span>
595
+ <span class="cline-any cline-yes">2x</span>
596
+ <span class="cline-any cline-yes">2x</span>
597
+ <span class="cline-any cline-yes">2x</span>
598
+ <span class="cline-any cline-yes">2x</span>
599
+ <span class="cline-any cline-yes">2x</span>
600
+ <span class="cline-any cline-yes">2x</span>
601
+ <span class="cline-any cline-no">&nbsp;</span>
602
+ <span class="cline-any cline-no">&nbsp;</span>
603
+ <span class="cline-any cline-no">&nbsp;</span>
604
+ <span class="cline-any cline-no">&nbsp;</span>
605
+ <span class="cline-any cline-no">&nbsp;</span>
606
+ <span class="cline-any cline-yes">2x</span>
607
+ <span class="cline-any cline-neutral">&nbsp;</span>
608
+ <span class="cline-any cline-yes">9x</span>
609
+ <span class="cline-any cline-yes">1x</span>
610
+ <span class="cline-any cline-yes">1x</span>
611
+ <span class="cline-any cline-yes">1x</span>
612
+ <span class="cline-any cline-neutral">&nbsp;</span>
613
+ <span class="cline-any cline-neutral">&nbsp;</span>
614
+ <span class="cline-any cline-neutral">&nbsp;</span>
615
+ <span class="cline-any cline-yes">1x</span>
616
+ <span class="cline-any cline-yes">1x</span>
617
+ <span class="cline-any cline-yes">1x</span>
618
+ <span class="cline-any cline-yes">1x</span>
619
+ <span class="cline-any cline-yes">1x</span>
620
+ <span class="cline-any cline-yes">1x</span>
621
+ <span class="cline-any cline-no">&nbsp;</span>
622
+ <span class="cline-any cline-no">&nbsp;</span>
623
+ <span class="cline-any cline-no">&nbsp;</span>
624
+ <span class="cline-any cline-no">&nbsp;</span>
625
+ <span class="cline-any cline-neutral">&nbsp;</span>
626
+ <span class="cline-any cline-neutral">&nbsp;</span>
627
+ <span class="cline-any cline-neutral">&nbsp;</span>
628
+ <span class="cline-any cline-no">&nbsp;</span>
629
+ <span class="cline-any cline-no">&nbsp;</span>
630
+ <span class="cline-any cline-no">&nbsp;</span>
631
+ <span class="cline-any cline-no">&nbsp;</span>
632
+ <span class="cline-any cline-no">&nbsp;</span>
633
+ <span class="cline-any cline-yes">1x</span>
634
+ <span class="cline-any cline-yes">9x</span>
635
+ <span class="cline-any cline-neutral">&nbsp;</span>
636
+ <span class="cline-any cline-yes">1x</span>
637
+ <span class="cline-any cline-no">&nbsp;</span>
638
+ <span class="cline-any cline-no">&nbsp;</span>
639
+ <span class="cline-any cline-no">&nbsp;</span>
640
+ <span class="cline-any cline-no">&nbsp;</span>
641
+ <span class="cline-any cline-no">&nbsp;</span>
642
+ <span class="cline-any cline-no">&nbsp;</span>
643
+ <span class="cline-any cline-no">&nbsp;</span>
644
+ <span class="cline-any cline-no">&nbsp;</span>
645
+ <span class="cline-any cline-neutral">&nbsp;</span>
646
+ <span class="cline-any cline-yes">1x</span>
647
+ <span class="cline-any cline-no">&nbsp;</span>
648
+ <span class="cline-any cline-no">&nbsp;</span>
649
+ <span class="cline-any cline-no">&nbsp;</span>
650
+ <span class="cline-any cline-no">&nbsp;</span>
651
+ <span class="cline-any cline-no">&nbsp;</span>
652
+ <span class="cline-any cline-no">&nbsp;</span>
653
+ <span class="cline-any cline-no">&nbsp;</span>
654
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import type { LoroDoc } from 'loro-crdt';
655
+ import type { LocalStore, RemoteDatabaseService } from '../../services/database/index';
656
+ import type { Logger } from '../../services/logger/index';
657
+ import { parseRecordIdString } from '../../utils/index';
658
+ &nbsp;
659
+ // ==================== CURSOR UTILITIES ====================
660
+ &nbsp;
661
+ export const CURSOR_COLORS = [
662
+ '#3b82f6', '#ef4444', '#22c55e', '#f59e0b',
663
+ '#8b5cf6', '#ec4899', '#14b8a6', '#f97316',
664
+ ];
665
+ &nbsp;
666
+ export <span class="fstat-no" title="function not covered" >function cursorColorFromName(name: string): string {</span>
667
+ <span class="cstat-no" title="statement not covered" > let hash = 0;</span>
668
+ <span class="cstat-no" title="statement not covered" > for (let i = 0; i &lt; name.length; i++) {</span>
669
+ <span class="cstat-no" title="statement not covered" > hash = ((hash &lt;&lt; 5) - hash + name.charCodeAt(i)) | 0;</span>
670
+ <span class="cstat-no" title="statement not covered" > }</span>
671
+ <span class="cstat-no" title="statement not covered" > return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];</span>
672
+ <span class="cstat-no" title="statement not covered" >}</span>
673
+ &nbsp;
674
+ // ==================== CRDT FIELD ====================
675
+ &nbsp;
676
+ export class CrdtField {
677
+ private doc: LoroDoc;
678
+ private pushTimer: ReturnType&lt;typeof setTimeout&gt; | null = null;
679
+ private local: LocalStore | null = null;
680
+ private remote: RemoteDatabaseService | null = null;
681
+ private recordId: string | null = null;
682
+ private sessionId: string = '';
683
+ private unsubscribe: (() =&gt; void) | null = null;
684
+ private lastPushTime = 0;
685
+ private lastCursorPushTime = 0;
686
+ private loadedFromCrdt = false;
687
+ private pushRetryCount = 0;
688
+ private logger: Logger | null;
689
+ private cursorsEnabled: boolean;
690
+ /** Remote-push debounce. Local writes happen immediately on every Loro
691
+ * update; the remote UPSERT is coalesced over this window. Configured
692
+ * via `Sp00kyConfig.crdtDebounceMs`, default 500. */
693
+ private remoteDebounceMs: number = 500;
694
+ &nbsp;
695
+ private _onCursorUpdate: ((data: Uint8Array) =&gt; void) | null = null;
696
+ private pendingCursorUpdate: Uint8Array | null = null;
697
+ &nbsp;
698
+ /** Callback set by the editor to receive remote cursor updates.
699
+ * Any cursor data that arrived before this callback was set will be replayed. */
700
+ <span class="fstat-no" title="function not covered" > set onCursorUpdate(cb: ((data: Uint8Array) =&gt; void) | null) {</span>
701
+ <span class="cstat-no" title="statement not covered" > this._onCursorUpdate = cb;</span>
702
+ <span class="cstat-no" title="statement not covered" > if (cb &amp;&amp; this.pendingCursorUpdate) {</span>
703
+ <span class="cstat-no" title="statement not covered" > try { cb(this.pendingCursorUpdate); } catch (e) {</span>
704
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
705
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::onCursorUpdate' },</span>
706
+ <span class="cstat-no" title="statement not covered" > 'Failed to replay pending cursor update'</span>
707
+ <span class="cstat-no" title="statement not covered" > );</span>
708
+ <span class="cstat-no" title="statement not covered" > }</span>
709
+ <span class="cstat-no" title="statement not covered" > this.pendingCursorUpdate = null;</span>
710
+ <span class="cstat-no" title="statement not covered" > }</span>
711
+ <span class="cstat-no" title="statement not covered" > }</span>
712
+ &nbsp;
713
+ <span class="fstat-no" title="function not covered" > get onCursorUpdate() { return this._onCursorUpdate; }</span>
714
+ &nbsp;
715
+ /**
716
+ * @param LoroDocClass the `LoroDoc` constructor, injected by the caller after
717
+ * awaiting {@link loadLoro} — keeps `loro-crdt` out of this module's static
718
+ * import graph so it only ships to apps that use CRDT fields.
719
+ */
720
+ constructor(
721
+ private fieldName: string,
722
+ cursorsEnabled: boolean,
723
+ LoroDocClass: typeof LoroDoc,
724
+ initialState?: Uint8Array,
725
+ logger?: Logger | null,
726
+ ) {
727
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(fieldName)) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
728
+ <span class="cstat-no" title="statement not covered" > throw new Error(</span>
729
+ <span class="cstat-no" title="statement not covered" > `CrdtField: refusing unsafe field identifier '${fieldName}' — must match [a-zA-Z_][a-zA-Z0-9_]*`</span>
730
+ <span class="cstat-no" title="statement not covered" > );</span>
731
+ <span class="cstat-no" title="statement not covered" > }</span>
732
+ this.logger = <span class="branch-0 cbranch-no" title="branch not covered" >logger ?? null;</span>
733
+ this.cursorsEnabled = cursorsEnabled;
734
+ this.doc = new LoroDocClass();
735
+ if (initialState &amp;&amp; initialState.length &gt; 0) {
736
+ // Tolerance: catch bad-snapshot data (corrupt blob, stale legacy
737
+ // value left over from a pre-`bytes` migration) so the editor still
738
+ // mounts. Without this guard the rejection bubbles through
739
+ // `useCrdtField` → permanent fallback `&lt;p&gt;`, with no cursor.
740
+ try {
741
+ this.doc.import(initialState);
742
+ this.loadedFromCrdt = true;
743
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
744
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
745
+ <span class="cstat-no" title="statement not covered" > {</span>
746
+ <span class="cstat-no" title="statement not covered" > error: e,</span>
747
+ <span class="cstat-no" title="statement not covered" > fieldName,</span>
748
+ <span class="cstat-no" title="statement not covered" > Category: 'sp00ky-client::CrdtField::constructor',</span>
749
+ <span class="cstat-no" title="statement not covered" > },</span>
750
+ <span class="cstat-no" title="statement not covered" > 'Initial CRDT state is not a valid LoroDoc snapshot — starting empty and will seed from fallback text'</span>
751
+ <span class="cstat-no" title="statement not covered" > );</span>
752
+ <span class="cstat-no" title="statement not covered" > }</span>
753
+ }
754
+ }
755
+ &nbsp;
756
+ getDoc(): LoroDoc { return this.doc; }
757
+ &nbsp;
758
+ /** Whether the LoroDoc was loaded from saved CRDT state */
759
+ <span class="fstat-no" title="function not covered" > hasContent(): boolean {</span>
760
+ <span class="cstat-no" title="statement not covered" > return this.loadedFromCrdt;</span>
761
+ <span class="cstat-no" title="statement not covered" > }</span>
762
+ &nbsp;
763
+ startSync(
764
+ local: LocalStore,
765
+ remote: RemoteDatabaseService,
766
+ recordId: string,
767
+ sessionId: string,
768
+ debounceMs: number,
769
+ ): void {
770
+ this.local = local;
771
+ this.remote = remote;
772
+ this.recordId = recordId;
773
+ this.sessionId = sessionId;
774
+ this.remoteDebounceMs = debounceMs;
775
+ // Every local Loro update writes the snapshot to the local cache
776
+ // *immediately* (so reload/offline see the latest text), then
777
+ // schedules a debounced push to remote. The local UPSERT is cheap —
778
+ // it's an in-memory SurrealKV write — but errors are swallowed so a
779
+ // bad write never blocks user input.
780
+ this.unsubscribe = this.doc.subscribeLocalUpdates(() =&gt; {
781
+ <span class="cstat-no" title="statement not covered" > void this.persistLocal();</span>
782
+ <span class="cstat-no" title="statement not covered" > this.scheduleRemotePush();</span>
783
+ });
784
+ }
785
+ &nbsp;
786
+ /**
787
+ * Stop syncing this field. Flushes one final remote push by default so the
788
+ * last keystrokes aren't lost. Pass `{ flush: false }` on a bucket switch —
789
+ * the remote session already belongs to the NEXT user, and pushing this
790
+ * (previous user's) snapshot under it would clobber the record remotely.
791
+ */
792
+ stopSync(options: { flush?: boolean } = {}): void {
793
+ const flush = options.flush ?? true;
794
+ if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null; }
795
+ if (this.pushTimer) <span class="branch-0 cbranch-no" title="branch not covered" >{ clearTimeout(this.pushTimer); this.pushTimer = null; }</span>
796
+ if (flush &amp;&amp; this.remote &amp;&amp; this.recordId) { void this.pushToRemote(); }
797
+ }
798
+ &nbsp;
799
+ importRemote(state: Uint8Array): void {
800
+ // Echo suppression: skip imports within `remoteDebounceMs + 200` of
801
+ // our own push. The +200 guards against round-trip jitter where our
802
+ // own write echoes back from the LIVE feed before the debounce
803
+ // window closes.
804
+ if (Date.now() - this.lastPushTime &lt; this.remoteDebounceMs + 200) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
805
+ try {
806
+ this.doc.import(state);
807
+ // Persist the merged snapshot locally so the next reload/offline
808
+ // open sees the freshest converged state without waiting for the
809
+ // LIVE feed.
810
+ void this.persistLocal();
811
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
812
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
813
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::importRemote' },</span>
814
+ <span class="cstat-no" title="statement not covered" > 'Failed to import remote CRDT state'</span>
815
+ <span class="cstat-no" title="statement not covered" > );</span>
816
+ <span class="cstat-no" title="statement not covered" > }</span>
817
+ }
818
+ &nbsp;
819
+ exportSnapshot(): Uint8Array {
820
+ return this.doc.export({ mode: 'snapshot' });
821
+ }
822
+ &nbsp;
823
+ /** Push this session's cursor blob into the parent row at
824
+ * `&lt;field&gt;.cursors[$sid]`. No-op when cursors aren't enabled on this
825
+ * field — the editor still calls this method optimistically, but
826
+ * without `@cursor` on the schema there's nowhere to store the blob.
827
+ * The UPDATE itself fires the parent table's LIVE feed, so other
828
+ * browsers receive the cursor change without a separate `_00_rv` bump. */
829
+ <span class="fstat-no" title="function not covered" > async pushCursorState(encoded: Uint8Array): Promise&lt;void&gt; {</span>
830
+ <span class="cstat-no" title="statement not covered" > if (!this.remote || !this.recordId) return;</span>
831
+ <span class="cstat-no" title="statement not covered" > if (!this.cursorsEnabled) return;</span>
832
+ <span class="cstat-no" title="statement not covered" > this.lastCursorPushTime = Date.now();</span>
833
+ <span class="cstat-no" title="statement not covered" > try {</span>
834
+ <span class="cstat-no" title="statement not covered" > const state = encodeBase64(encoded);</span>
835
+ <span class="cstat-no" title="statement not covered" > await this.remote.query(</span>
836
+ <span class="cstat-no" title="statement not covered" > `UPDATE $id SET ${this.fieldName}.cursors[$sid] = $state RETURN NONE;`,</span>
837
+ <span class="cstat-no" title="statement not covered" > {</span>
838
+ <span class="cstat-no" title="statement not covered" > id: parseRecordIdString(this.recordId),</span>
839
+ <span class="cstat-no" title="statement not covered" > sid: this.sessionId,</span>
840
+ <span class="cstat-no" title="statement not covered" > state,</span>
841
+ <span class="cstat-no" title="statement not covered" > }</span>
842
+ <span class="cstat-no" title="statement not covered" > );</span>
843
+ <span class="cstat-no" title="statement not covered" > } catch (e) {</span>
844
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
845
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::pushCursorState' },</span>
846
+ <span class="cstat-no" title="statement not covered" > 'Failed to push cursor state'</span>
847
+ <span class="cstat-no" title="statement not covered" > );</span>
848
+ <span class="cstat-no" title="statement not covered" > }</span>
849
+ <span class="cstat-no" title="statement not covered" > }</span>
850
+ &nbsp;
851
+ /** Import remote cursor state (called by CrdtManager from LIVE SELECT) */
852
+ <span class="fstat-no" title="function not covered" > importRemoteCursor(base64State: string): void {</span>
853
+ <span class="cstat-no" title="statement not covered" > if (Date.now() - this.lastCursorPushTime &lt; 300) return; // echo suppression</span>
854
+ <span class="cstat-no" title="statement not covered" > try {</span>
855
+ <span class="cstat-no" title="statement not covered" > const data = decodeBase64(base64State);</span>
856
+ <span class="cstat-no" title="statement not covered" > if (this._onCursorUpdate) {</span>
857
+ <span class="cstat-no" title="statement not covered" > this._onCursorUpdate(data);</span>
858
+ <span class="cstat-no" title="statement not covered" > } else {</span>
859
+ // Only keep the latest cursor state — older positions are useless
860
+ <span class="cstat-no" title="statement not covered" > this.pendingCursorUpdate = data;</span>
861
+ <span class="cstat-no" title="statement not covered" > }</span>
862
+ <span class="cstat-no" title="statement not covered" > } catch (e) {</span>
863
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
864
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::importRemoteCursor' },</span>
865
+ <span class="cstat-no" title="statement not covered" > 'Failed to apply remote cursor data'</span>
866
+ <span class="cstat-no" title="statement not covered" > );</span>
867
+ <span class="cstat-no" title="statement not covered" > }</span>
868
+ <span class="cstat-no" title="statement not covered" > }</span>
869
+ &nbsp;
870
+ private <span class="fstat-no" title="function not covered" >scheduleRemotePush(): void {</span>
871
+ <span class="cstat-no" title="statement not covered" > if (this.pushTimer) clearTimeout(this.pushTimer);</span>
872
+ <span class="cstat-no" title="statement not covered" > this.pushTimer = setTimeout(() =&gt; void this.pushToRemote(), this.remoteDebounceMs);</span>
873
+ <span class="cstat-no" title="statement not covered" > }</span>
874
+ &nbsp;
875
+ /** SET path inside a parent row for the current snapshot. `@crdt`-only
876
+ * fields hold the snapshot directly (`&lt;field&gt;`); `@crdt @cursor`
877
+ * fields hold a `{ state, cursors }` object so the snapshot lives at
878
+ * `&lt;field&gt;.state` next to per-session cursor blobs. */
879
+ private statePath(): string {
880
+ return this.cursorsEnabled ? `${this.fieldName}.state` : this.fieldName;
881
+ }
882
+ &nbsp;
883
+ /** Mirror the LoroDoc snapshot into the parent row locally. Runs on
884
+ * every local update and every remote import so reloads (online or
885
+ * offline) see the freshest content immediately. Failures are
886
+ * swallowed — a stale local write must never block user input. */
887
+ private async persistLocal(): Promise&lt;void&gt; {
888
+ if (!this.local || !this.recordId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
889
+ try {
890
+ await this.local.query(
891
+ `UPDATE $id SET ${this.statePath()} = $state RETURN NONE;`,
892
+ { id: parseRecordIdString(this.recordId), state: this.exportSnapshot() }
893
+ );
894
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
895
+ <span class="cstat-no" title="statement not covered" > this.logger?.debug(</span>
896
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::persistLocal' },</span>
897
+ <span class="cstat-no" title="statement not covered" > 'Local CRDT persist failed (best-effort)'</span>
898
+ <span class="cstat-no" title="statement not covered" > );</span>
899
+ <span class="cstat-no" title="statement not covered" > }</span>
900
+ }
901
+ &nbsp;
902
+ private async pushToRemote(): Promise&lt;void&gt; {
903
+ if (!this.remote || !this.recordId) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
904
+ this.lastPushTime = Date.now();
905
+ try {
906
+ // The UPDATE on the parent fires the parent table's LIVE feed,
907
+ // so cross-browser receivers see this change directly in the LIVE
908
+ // payload — no separate `_00_rv` bump or sidecar UPSERT.
909
+ await this.remote.query(
910
+ `UPDATE $id SET ${this.statePath()} = $state RETURN NONE;`,
911
+ { id: parseRecordIdString(this.recordId), state: this.exportSnapshot() }
912
+ );
913
+ this.pushRetryCount = 0;
914
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
915
+ <span class="cstat-no" title="statement not covered" > this.logger?.warn(</span>
916
+ <span class="cstat-no" title="statement not covered" > { error: e, Category: 'sp00ky-client::CrdtField::pushToRemote' },</span>
917
+ <span class="cstat-no" title="statement not covered" > 'Failed to push CRDT state to remote'</span>
918
+ <span class="cstat-no" title="statement not covered" > );</span>
919
+ // Bounded retry. Offline first-loads will exhaust this and stop
920
+ // hammering; the next user keystroke (or the next time a remote
921
+ // event lands once we're back online) will kick off another push.
922
+ <span class="cstat-no" title="statement not covered" > if (this.pushRetryCount &lt; 2) {</span>
923
+ <span class="cstat-no" title="statement not covered" > this.pushRetryCount++;</span>
924
+ <span class="cstat-no" title="statement not covered" > this.scheduleRemotePush();</span>
925
+ <span class="cstat-no" title="statement not covered" > }</span>
926
+ <span class="cstat-no" title="statement not covered" > }</span>
927
+ }
928
+ }
929
+ &nbsp;
930
+ export <span class="fstat-no" title="function not covered" >function decodeBase64(b64: string): Uint8Array {</span>
931
+ <span class="cstat-no" title="statement not covered" > if (typeof atob === 'function') {</span>
932
+ <span class="cstat-no" title="statement not covered" > const binary = atob(b64);</span>
933
+ <span class="cstat-no" title="statement not covered" > const bytes = new Uint8Array(binary.length);</span>
934
+ <span class="cstat-no" title="statement not covered" > for (let i = 0; i &lt; binary.length; i++) bytes[i] = binary.charCodeAt(i);</span>
935
+ <span class="cstat-no" title="statement not covered" > return bytes;</span>
936
+ <span class="cstat-no" title="statement not covered" > }</span>
937
+ <span class="cstat-no" title="statement not covered" > return new Uint8Array(Buffer.from(b64, 'base64'));</span>
938
+ <span class="cstat-no" title="statement not covered" >}</span>
939
+ &nbsp;
940
+ export <span class="fstat-no" title="function not covered" >function encodeBase64(bytes: Uint8Array): string {</span>
941
+ <span class="cstat-no" title="statement not covered" > if (typeof btoa === 'function') {</span>
942
+ <span class="cstat-no" title="statement not covered" > let binary = '';</span>
943
+ <span class="cstat-no" title="statement not covered" > for (let i = 0; i &lt; bytes.length; i++) binary += String.fromCharCode(bytes[i]);</span>
944
+ <span class="cstat-no" title="statement not covered" > return btoa(binary);</span>
945
+ <span class="cstat-no" title="statement not covered" > }</span>
946
+ <span class="cstat-no" title="statement not covered" > return Buffer.from(bytes).toString('base64');</span>
947
+ <span class="cstat-no" title="statement not covered" >}</span>
948
+ &nbsp;</pre></td></tr></table></pre>
949
+
950
+ <div class='push'></div><!-- for sticky footer -->
951
+ </div><!-- /wrapper -->
952
+ <div class='footer quiet pad2 space-top1 center small'>
953
+ Code coverage generated by
954
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
955
+ at 2026-09-09T09:49:51.780Z
956
+ </div>
957
+ <script src="../../../prettify.js"></script>
958
+ <script>
959
+ window.onload = function () {
960
+ prettyPrint();
961
+ };
962
+ </script>
963
+ <script src="../../../sorter.js"></script>
964
+ <script src="../../../block-navigation.js"></script>
965
+ </body>
966
+ </html>
967
+