@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,1474 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for src/modules/crdt/index.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> index.ts</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">77.3% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>218/282</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">67.94% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>53/78</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">90% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>18/20</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">77.3% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>218/282</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 medium'></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></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</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-neutral">&nbsp;</span>
536
+ <span class="cline-any cline-neutral">&nbsp;</span>
537
+ <span class="cline-any cline-neutral">&nbsp;</span>
538
+ <span class="cline-any cline-neutral">&nbsp;</span>
539
+ <span class="cline-any cline-neutral">&nbsp;</span>
540
+ <span class="cline-any cline-neutral">&nbsp;</span>
541
+ <span class="cline-any cline-neutral">&nbsp;</span>
542
+ <span class="cline-any cline-neutral">&nbsp;</span>
543
+ <span class="cline-any cline-neutral">&nbsp;</span>
544
+ <span class="cline-any cline-neutral">&nbsp;</span>
545
+ <span class="cline-any cline-neutral">&nbsp;</span>
546
+ <span class="cline-any cline-neutral">&nbsp;</span>
547
+ <span class="cline-any cline-neutral">&nbsp;</span>
548
+ <span class="cline-any cline-neutral">&nbsp;</span>
549
+ <span class="cline-any cline-neutral">&nbsp;</span>
550
+ <span class="cline-any cline-neutral">&nbsp;</span>
551
+ <span class="cline-any cline-neutral">&nbsp;</span>
552
+ <span class="cline-any cline-neutral">&nbsp;</span>
553
+ <span class="cline-any cline-neutral">&nbsp;</span>
554
+ <span class="cline-any cline-neutral">&nbsp;</span>
555
+ <span class="cline-any cline-neutral">&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-neutral">&nbsp;</span>
559
+ <span class="cline-any cline-neutral">&nbsp;</span>
560
+ <span class="cline-any cline-yes">1x</span>
561
+ <span class="cline-any cline-yes">9x</span>
562
+ <span class="cline-any cline-neutral">&nbsp;</span>
563
+ <span class="cline-any cline-yes">9x</span>
564
+ <span class="cline-any cline-neutral">&nbsp;</span>
565
+ <span class="cline-any cline-neutral">&nbsp;</span>
566
+ <span class="cline-any cline-neutral">&nbsp;</span>
567
+ <span class="cline-any cline-yes">9x</span>
568
+ <span class="cline-any cline-neutral">&nbsp;</span>
569
+ <span class="cline-any cline-neutral">&nbsp;</span>
570
+ <span class="cline-any cline-neutral">&nbsp;</span>
571
+ <span class="cline-any cline-yes">9x</span>
572
+ <span class="cline-any cline-neutral">&nbsp;</span>
573
+ <span class="cline-any cline-neutral">&nbsp;</span>
574
+ <span class="cline-any cline-neutral">&nbsp;</span>
575
+ <span class="cline-any cline-yes">9x</span>
576
+ <span class="cline-any cline-yes">9x</span>
577
+ <span class="cline-any cline-yes">9x</span>
578
+ <span class="cline-any cline-neutral">&nbsp;</span>
579
+ <span class="cline-any cline-yes">9x</span>
580
+ <span class="cline-any cline-neutral">&nbsp;</span>
581
+ <span class="cline-any cline-yes">9x</span>
582
+ <span class="cline-any cline-yes">9x</span>
583
+ <span class="cline-any cline-yes">9x</span>
584
+ <span class="cline-any cline-yes">9x</span>
585
+ <span class="cline-any cline-yes">9x</span>
586
+ <span class="cline-any cline-yes">9x</span>
587
+ <span class="cline-any cline-yes">9x</span>
588
+ <span class="cline-any cline-yes">9x</span>
589
+ <span class="cline-any cline-yes">9x</span>
590
+ <span class="cline-any cline-yes">9x</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-neutral">&nbsp;</span>
594
+ <span class="cline-any cline-neutral">&nbsp;</span>
595
+ <span class="cline-any cline-neutral">&nbsp;</span>
596
+ <span class="cline-any cline-neutral">&nbsp;</span>
597
+ <span class="cline-any cline-neutral">&nbsp;</span>
598
+ <span class="cline-any cline-neutral">&nbsp;</span>
599
+ <span class="cline-any cline-neutral">&nbsp;</span>
600
+ <span class="cline-any cline-neutral">&nbsp;</span>
601
+ <span class="cline-any cline-neutral">&nbsp;</span>
602
+ <span class="cline-any cline-neutral">&nbsp;</span>
603
+ <span class="cline-any cline-neutral">&nbsp;</span>
604
+ <span class="cline-any cline-neutral">&nbsp;</span>
605
+ <span class="cline-any cline-yes">9x</span>
606
+ <span class="cline-any cline-yes">9x</span>
607
+ <span class="cline-any cline-yes">4x</span>
608
+ <span class="cline-any cline-yes">4x</span>
609
+ <span class="cline-any cline-neutral">&nbsp;</span>
610
+ <span class="cline-any cline-neutral">&nbsp;</span>
611
+ <span class="cline-any cline-yes">4x</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-yes">4x</span>
615
+ <span class="cline-any cline-yes">4x</span>
616
+ <span class="cline-any cline-yes">9x</span>
617
+ <span class="cline-any cline-yes">9x</span>
618
+ <span class="cline-any cline-yes">9x</span>
619
+ <span class="cline-any cline-yes">9x</span>
620
+ <span class="cline-any cline-yes">4x</span>
621
+ <span class="cline-any cline-yes">4x</span>
622
+ <span class="cline-any cline-yes">4x</span>
623
+ <span class="cline-any cline-neutral">&nbsp;</span>
624
+ <span class="cline-any cline-neutral">&nbsp;</span>
625
+ <span class="cline-any cline-yes">3x</span>
626
+ <span class="cline-any cline-yes">3x</span>
627
+ <span class="cline-any cline-yes">3x</span>
628
+ <span class="cline-any cline-yes">9x</span>
629
+ <span class="cline-any cline-yes">9x</span>
630
+ <span class="cline-any cline-yes">9x</span>
631
+ <span class="cline-any cline-neutral">&nbsp;</span>
632
+ <span class="cline-any cline-neutral">&nbsp;</span>
633
+ <span class="cline-any cline-neutral">&nbsp;</span>
634
+ <span class="cline-any cline-yes">9x</span>
635
+ <span class="cline-any cline-yes">1x</span>
636
+ <span class="cline-any cline-yes">3x</span>
637
+ <span class="cline-any cline-yes">3x</span>
638
+ <span class="cline-any cline-yes">3x</span>
639
+ <span class="cline-any cline-neutral">&nbsp;</span>
640
+ <span class="cline-any cline-no">&nbsp;</span>
641
+ <span class="cline-any cline-yes">3x</span>
642
+ <span class="cline-any cline-yes">1x</span>
643
+ <span class="cline-any cline-yes">1x</span>
644
+ <span class="cline-any cline-yes">1x</span>
645
+ <span class="cline-any cline-neutral">&nbsp;</span>
646
+ <span class="cline-any cline-yes">9x</span>
647
+ <span class="cline-any cline-yes">4x</span>
648
+ <span class="cline-any cline-yes">4x</span>
649
+ <span class="cline-any cline-yes">4x</span>
650
+ <span class="cline-any cline-neutral">&nbsp;</span>
651
+ <span class="cline-any cline-neutral">&nbsp;</span>
652
+ <span class="cline-any cline-neutral">&nbsp;</span>
653
+ <span class="cline-any cline-neutral">&nbsp;</span>
654
+ <span class="cline-any cline-neutral">&nbsp;</span>
655
+ <span class="cline-any cline-yes">9x</span>
656
+ <span class="cline-any cline-yes">9x</span>
657
+ <span class="cline-any cline-yes">9x</span>
658
+ <span class="cline-any cline-neutral">&nbsp;</span>
659
+ <span class="cline-any cline-neutral">&nbsp;</span>
660
+ <span class="cline-any cline-neutral">&nbsp;</span>
661
+ <span class="cline-any cline-neutral">&nbsp;</span>
662
+ <span class="cline-any cline-neutral">&nbsp;</span>
663
+ <span class="cline-any cline-neutral">&nbsp;</span>
664
+ <span class="cline-any cline-neutral">&nbsp;</span>
665
+ <span class="cline-any cline-neutral">&nbsp;</span>
666
+ <span class="cline-any cline-neutral">&nbsp;</span>
667
+ <span class="cline-any cline-neutral">&nbsp;</span>
668
+ <span class="cline-any cline-yes">9x</span>
669
+ <span class="cline-any cline-yes">9x</span>
670
+ <span class="cline-any cline-yes">9x</span>
671
+ <span class="cline-any cline-yes">9x</span>
672
+ <span class="cline-any cline-yes">9x</span>
673
+ <span class="cline-any cline-yes">9x</span>
674
+ <span class="cline-any cline-yes">9x</span>
675
+ <span class="cline-any cline-yes">9x</span>
676
+ <span class="cline-any cline-yes">9x</span>
677
+ <span class="cline-any cline-yes">9x</span>
678
+ <span class="cline-any cline-neutral">&nbsp;</span>
679
+ <span class="cline-any cline-yes">9x</span>
680
+ <span class="cline-any cline-no">&nbsp;</span>
681
+ <span class="cline-any cline-no">&nbsp;</span>
682
+ <span class="cline-any cline-neutral">&nbsp;</span>
683
+ <span class="cline-any cline-neutral">&nbsp;</span>
684
+ <span class="cline-any cline-neutral">&nbsp;</span>
685
+ <span class="cline-any cline-neutral">&nbsp;</span>
686
+ <span class="cline-any cline-neutral">&nbsp;</span>
687
+ <span class="cline-any cline-neutral">&nbsp;</span>
688
+ <span class="cline-any cline-neutral">&nbsp;</span>
689
+ <span class="cline-any cline-yes">9x</span>
690
+ <span class="cline-any cline-yes">9x</span>
691
+ <span class="cline-any cline-yes">9x</span>
692
+ <span class="cline-any cline-yes">9x</span>
693
+ <span class="cline-any cline-yes">9x</span>
694
+ <span class="cline-any cline-yes">9x</span>
695
+ <span class="cline-any cline-yes">9x</span>
696
+ <span class="cline-any cline-yes">9x</span>
697
+ <span class="cline-any cline-yes">9x</span>
698
+ <span class="cline-any cline-no">&nbsp;</span>
699
+ <span class="cline-any cline-no">&nbsp;</span>
700
+ <span class="cline-any cline-no">&nbsp;</span>
701
+ <span class="cline-any cline-no">&nbsp;</span>
702
+ <span class="cline-any cline-no">&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-yes">9x</span>
707
+ <span class="cline-any cline-yes">9x</span>
708
+ <span class="cline-any cline-yes">9x</span>
709
+ <span class="cline-any cline-yes">9x</span>
710
+ <span class="cline-any cline-neutral">&nbsp;</span>
711
+ <span class="cline-any cline-yes">9x</span>
712
+ <span class="cline-any cline-yes">9x</span>
713
+ <span class="cline-any cline-yes">9x</span>
714
+ <span class="cline-any cline-yes">9x</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-yes">9x</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-yes">9x</span>
726
+ <span class="cline-any cline-yes">7x</span>
727
+ <span class="cline-any cline-yes">7x</span>
728
+ <span class="cline-any cline-neutral">&nbsp;</span>
729
+ <span class="cline-any cline-yes">9x</span>
730
+ <span class="cline-any cline-yes">9x</span>
731
+ <span class="cline-any cline-neutral">&nbsp;</span>
732
+ <span class="cline-any cline-yes">9x</span>
733
+ <span class="cline-any cline-yes">1x</span>
734
+ <span class="cline-any cline-yes">1x</span>
735
+ <span class="cline-any cline-yes">1x</span>
736
+ <span class="cline-any cline-yes">1x</span>
737
+ <span class="cline-any cline-yes">1x</span>
738
+ <span class="cline-any cline-yes">1x</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-yes">1x</span>
742
+ <span class="cline-any cline-yes">1x</span>
743
+ <span class="cline-any cline-neutral">&nbsp;</span>
744
+ <span class="cline-any cline-yes">1x</span>
745
+ <span class="cline-any cline-yes">1x</span>
746
+ <span class="cline-any cline-neutral">&nbsp;</span>
747
+ <span class="cline-any cline-yes">1x</span>
748
+ <span class="cline-any cline-yes">1x</span>
749
+ <span class="cline-any cline-yes">1x</span>
750
+ <span class="cline-any cline-yes">1x</span>
751
+ <span class="cline-any cline-yes">1x</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-yes">9x</span>
759
+ <span class="cline-any cline-no">&nbsp;</span>
760
+ <span class="cline-any cline-no">&nbsp;</span>
761
+ <span class="cline-any cline-no">&nbsp;</span>
762
+ <span class="cline-any cline-no">&nbsp;</span>
763
+ <span class="cline-any cline-no">&nbsp;</span>
764
+ <span class="cline-any cline-no">&nbsp;</span>
765
+ <span class="cline-any cline-no">&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-no">&nbsp;</span>
769
+ <span class="cline-any cline-no">&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-yes">9x</span>
774
+ <span class="cline-any cline-yes">12x</span>
775
+ <span class="cline-any cline-neutral">&nbsp;</span>
776
+ <span class="cline-any cline-neutral">&nbsp;</span>
777
+ <span class="cline-any cline-neutral">&nbsp;</span>
778
+ <span class="cline-any cline-neutral">&nbsp;</span>
779
+ <span class="cline-any cline-neutral">&nbsp;</span>
780
+ <span class="cline-any cline-neutral">&nbsp;</span>
781
+ <span class="cline-any cline-yes">12x</span>
782
+ <span class="cline-any cline-neutral">&nbsp;</span>
783
+ <span class="cline-any cline-yes">12x</span>
784
+ <span class="cline-any cline-yes">12x</span>
785
+ <span class="cline-any cline-yes">3x</span>
786
+ <span class="cline-any cline-neutral">&nbsp;</span>
787
+ <span class="cline-any cline-neutral">&nbsp;</span>
788
+ <span class="cline-any cline-yes">3x</span>
789
+ <span class="cline-any cline-yes">3x</span>
790
+ <span class="cline-any cline-yes">3x</span>
791
+ <span class="cline-any cline-neutral">&nbsp;</span>
792
+ <span class="cline-any cline-neutral">&nbsp;</span>
793
+ <span class="cline-any cline-yes">3x</span>
794
+ <span class="cline-any cline-yes">3x</span>
795
+ <span class="cline-any cline-yes">3x</span>
796
+ <span class="cline-any cline-neutral">&nbsp;</span>
797
+ <span class="cline-any cline-yes">12x</span>
798
+ <span class="cline-any cline-yes">12x</span>
799
+ <span class="cline-any cline-yes">12x</span>
800
+ <span class="cline-any cline-yes">12x</span>
801
+ <span class="cline-any cline-yes">12x</span>
802
+ <span class="cline-any cline-neutral">&nbsp;</span>
803
+ <span class="cline-any cline-yes">12x</span>
804
+ <span class="cline-any cline-yes">1x</span>
805
+ <span class="cline-any cline-yes">1x</span>
806
+ <span class="cline-any cline-yes">1x</span>
807
+ <span class="cline-any cline-yes">1x</span>
808
+ <span class="cline-any cline-yes">1x</span>
809
+ <span class="cline-any cline-yes">1x</span>
810
+ <span class="cline-any cline-neutral">&nbsp;</span>
811
+ <span class="cline-any cline-yes">11x</span>
812
+ <span class="cline-any cline-yes">11x</span>
813
+ <span class="cline-any cline-no">&nbsp;</span>
814
+ <span class="cline-any cline-no">&nbsp;</span>
815
+ <span class="cline-any cline-no">&nbsp;</span>
816
+ <span class="cline-any cline-yes">11x</span>
817
+ <span class="cline-any cline-neutral">&nbsp;</span>
818
+ <span class="cline-any cline-yes">11x</span>
819
+ <span class="cline-any cline-neutral">&nbsp;</span>
820
+ <span class="cline-any cline-yes">11x</span>
821
+ <span class="cline-any cline-yes">11x</span>
822
+ <span class="cline-any cline-yes">11x</span>
823
+ <span class="cline-any cline-yes">11x</span>
824
+ <span class="cline-any cline-yes">11x</span>
825
+ <span class="cline-any cline-yes">12x</span>
826
+ <span class="cline-any cline-no">&nbsp;</span>
827
+ <span class="cline-any cline-no">&nbsp;</span>
828
+ <span class="cline-any cline-no">&nbsp;</span>
829
+ <span class="cline-any cline-no">&nbsp;</span>
830
+ <span class="cline-any cline-no">&nbsp;</span>
831
+ <span class="cline-any cline-yes">12x</span>
832
+ <span class="cline-any cline-neutral">&nbsp;</span>
833
+ <span class="cline-any cline-yes">12x</span>
834
+ <span class="cline-any cline-yes">12x</span>
835
+ <span class="cline-any cline-yes">12x</span>
836
+ <span class="cline-any cline-yes">12x</span>
837
+ <span class="cline-any cline-yes">12x</span>
838
+ <span class="cline-any cline-yes">12x</span>
839
+ <span class="cline-any cline-yes">12x</span>
840
+ <span class="cline-any cline-neutral">&nbsp;</span>
841
+ <span class="cline-any cline-neutral">&nbsp;</span>
842
+ <span class="cline-any cline-neutral">&nbsp;</span>
843
+ <span class="cline-any cline-neutral">&nbsp;</span>
844
+ <span class="cline-any cline-neutral">&nbsp;</span>
845
+ <span class="cline-any cline-yes">9x</span>
846
+ <span class="cline-any cline-no">&nbsp;</span>
847
+ <span class="cline-any cline-no">&nbsp;</span>
848
+ <span class="cline-any cline-neutral">&nbsp;</span>
849
+ <span class="cline-any cline-neutral">&nbsp;</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-yes">9x</span>
854
+ <span class="cline-any cline-yes">2x</span>
855
+ <span class="cline-any cline-yes">2x</span>
856
+ <span class="cline-any cline-neutral">&nbsp;</span>
857
+ <span class="cline-any cline-yes">2x</span>
858
+ <span class="cline-any cline-yes">2x</span>
859
+ <span class="cline-any cline-yes">2x</span>
860
+ <span class="cline-any cline-yes">2x</span>
861
+ <span class="cline-any cline-yes">2x</span>
862
+ <span class="cline-any cline-yes">2x</span>
863
+ <span class="cline-any cline-yes">2x</span>
864
+ <span class="cline-any cline-yes">2x</span>
865
+ <span class="cline-any cline-neutral">&nbsp;</span>
866
+ <span class="cline-any cline-yes">2x</span>
867
+ <span class="cline-any cline-yes">1x</span>
868
+ <span class="cline-any cline-yes">1x</span>
869
+ <span class="cline-any cline-yes">1x</span>
870
+ <span class="cline-any cline-no">&nbsp;</span>
871
+ <span class="cline-any cline-no">&nbsp;</span>
872
+ <span class="cline-any cline-no">&nbsp;</span>
873
+ <span class="cline-any cline-no">&nbsp;</span>
874
+ <span class="cline-any cline-no">&nbsp;</span>
875
+ <span class="cline-any cline-yes">1x</span>
876
+ <span class="cline-any cline-yes">1x</span>
877
+ <span class="cline-any cline-yes">2x</span>
878
+ <span class="cline-any cline-yes">2x</span>
879
+ <span class="cline-any cline-neutral">&nbsp;</span>
880
+ <span class="cline-any cline-neutral">&nbsp;</span>
881
+ <span class="cline-any cline-neutral">&nbsp;</span>
882
+ <span class="cline-any cline-neutral">&nbsp;</span>
883
+ <span class="cline-any cline-neutral">&nbsp;</span>
884
+ <span class="cline-any cline-yes">9x</span>
885
+ <span class="cline-any cline-yes">7x</span>
886
+ <span class="cline-any cline-yes">7x</span>
887
+ <span class="cline-any cline-yes">7x</span>
888
+ <span class="cline-any cline-yes">7x</span>
889
+ <span class="cline-any cline-yes">7x</span>
890
+ <span class="cline-any cline-yes">7x</span>
891
+ <span class="cline-any cline-yes">7x</span>
892
+ <span class="cline-any cline-yes">2x</span>
893
+ <span class="cline-any cline-yes">2x</span>
894
+ <span class="cline-any cline-no">&nbsp;</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-no">&nbsp;</span>
898
+ <span class="cline-any cline-no">&nbsp;</span>
899
+ <span class="cline-any cline-yes">7x</span>
900
+ <span class="cline-any cline-neutral">&nbsp;</span>
901
+ <span class="cline-any cline-neutral">&nbsp;</span>
902
+ <span class="cline-any cline-neutral">&nbsp;</span>
903
+ <span class="cline-any cline-yes">9x</span>
904
+ <span class="cline-any cline-yes">11x</span>
905
+ <span class="cline-any cline-yes">11x</span>
906
+ <span class="cline-any cline-yes">11x</span>
907
+ <span class="cline-any cline-neutral">&nbsp;</span>
908
+ <span class="cline-any cline-neutral">&nbsp;</span>
909
+ <span class="cline-any cline-neutral">&nbsp;</span>
910
+ <span class="cline-any cline-neutral">&nbsp;</span>
911
+ <span class="cline-any cline-yes">9x</span>
912
+ <span class="cline-any cline-yes">11x</span>
913
+ <span class="cline-any cline-yes">10x</span>
914
+ <span class="cline-any cline-neutral">&nbsp;</span>
915
+ <span class="cline-any cline-neutral">&nbsp;</span>
916
+ <span class="cline-any cline-neutral">&nbsp;</span>
917
+ <span class="cline-any cline-neutral">&nbsp;</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">10x</span>
922
+ <span class="cline-any cline-yes">10x</span>
923
+ <span class="cline-any cline-no">&nbsp;</span>
924
+ <span class="cline-any cline-no">&nbsp;</span>
925
+ <span class="cline-any cline-no">&nbsp;</span>
926
+ <span class="cline-any cline-yes">10x</span>
927
+ <span class="cline-any cline-yes">1x</span>
928
+ <span class="cline-any cline-yes">1x</span>
929
+ <span class="cline-any cline-yes">6x</span>
930
+ <span class="cline-any cline-yes">10x</span>
931
+ <span class="cline-any cline-neutral">&nbsp;</span>
932
+ <span class="cline-any cline-yes">11x</span>
933
+ <span class="cline-any cline-yes">3x</span>
934
+ <span class="cline-any cline-yes">2x</span>
935
+ <span class="cline-any cline-yes">2x</span>
936
+ <span class="cline-any cline-yes">1x</span>
937
+ <span class="cline-any cline-yes">1x</span>
938
+ <span class="cline-any cline-yes">8x</span>
939
+ <span class="cline-any cline-yes">11x</span>
940
+ <span class="cline-any cline-neutral">&nbsp;</span>
941
+ <span class="cline-any cline-yes">9x</span>
942
+ <span class="cline-any cline-yes">1x</span>
943
+ <span class="cline-any cline-yes">1x</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-no">&nbsp;</span>
948
+ <span class="cline-any cline-no">&nbsp;</span>
949
+ <span class="cline-any cline-no">&nbsp;</span>
950
+ <span class="cline-any cline-no">&nbsp;</span>
951
+ <span class="cline-any cline-no">&nbsp;</span>
952
+ <span class="cline-any cline-no">&nbsp;</span>
953
+ <span class="cline-any cline-no">&nbsp;</span>
954
+ <span class="cline-any cline-no">&nbsp;</span>
955
+ <span class="cline-any cline-yes">1x</span>
956
+ <span class="cline-any cline-neutral">&nbsp;</span>
957
+ <span class="cline-any cline-yes">9x</span>
958
+ <span class="cline-any cline-yes">10x</span>
959
+ <span class="cline-any cline-yes">10x</span>
960
+ <span class="cline-any cline-neutral">&nbsp;</span>
961
+ <span class="cline-any cline-neutral">&nbsp;</span>
962
+ <span class="cline-any cline-neutral">&nbsp;</span>
963
+ <span class="cline-any cline-neutral">&nbsp;</span>
964
+ <span class="cline-any cline-neutral">&nbsp;</span>
965
+ <span class="cline-any cline-neutral">&nbsp;</span>
966
+ <span class="cline-any cline-yes">9x</span>
967
+ <span class="cline-any cline-yes">9x</span>
968
+ <span class="cline-any cline-no">&nbsp;</span>
969
+ <span class="cline-any cline-no">&nbsp;</span>
970
+ <span class="cline-any cline-no">&nbsp;</span>
971
+ <span class="cline-any cline-no">&nbsp;</span>
972
+ <span class="cline-any cline-yes">9x</span>
973
+ <span class="cline-any cline-yes">9x</span>
974
+ <span class="cline-any cline-no">&nbsp;</span>
975
+ <span class="cline-any cline-no">&nbsp;</span>
976
+ <span class="cline-any cline-no">&nbsp;</span>
977
+ <span class="cline-any cline-no">&nbsp;</span>
978
+ <span class="cline-any cline-yes">9x</span>
979
+ <span class="cline-any cline-yes">9x</span>
980
+ <span class="cline-any cline-no">&nbsp;</span>
981
+ <span class="cline-any cline-no">&nbsp;</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-yes">9x</span>
985
+ <span class="cline-any cline-no">&nbsp;</span>
986
+ <span class="cline-any cline-no">&nbsp;</span>
987
+ <span class="cline-any cline-neutral">&nbsp;</span>
988
+ <span class="cline-any cline-no">&nbsp;</span>
989
+ <span class="cline-any cline-no">&nbsp;</span>
990
+ <span class="cline-any cline-yes">9x</span>
991
+ <span class="cline-any cline-yes">9x</span>
992
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import type { SchemaStructure } from '@spooky-sync/query-builder';
993
+ import type { LocalStore, RemoteDatabaseService } from '../../services/database/index';
994
+ import type { Logger } from '../../services/logger/index';
995
+ import type { Uuid } from 'surrealdb';
996
+ import { CrdtField } from './crdt-field';
997
+ import { loadLoro } from './loro-loader';
998
+ import { parseRecordIdString } from '../../utils/index';
999
+ &nbsp;
1000
+ export { CrdtField, cursorColorFromName, CURSOR_COLORS } from './crdt-field';
1001
+ &nbsp;
1002
+ /**
1003
+ * CrdtManager manages active CrdtField instances and their sync channels.
1004
+ *
1005
+ * Collaborative state lives in two dedicated tables (defined in
1006
+ * `apps/cli/src/meta_tables_remote.surql`):
1007
+ * - `_00_crdt` { record_id, field, state } — one row per (record, field)
1008
+ * - `_00_cursor` { record_id, session_id, field, state } — one row per
1009
+ * (record, session, field)
1010
+ *
1011
+ * Splitting them off the parent row is what makes offline edits mergeable:
1012
+ * each (record, field) gets its own row, so concurrent offline writes don't
1013
+ * collide on the parent's last-write-wins semantics.
1014
+ *
1015
+ * Cross-browser delivery still rides the parent table's existing LIVE feed
1016
+ * to avoid SurrealDB v3 LIVE bugs around dereference-based permission rules
1017
+ * (issues 3602, 4026). On every meta UPSERT the writer also bumps the
1018
+ * parent's `_00_rv` (a no-op assignment); that fires the parent's LIVE
1019
+ * feed, and the receiver pulls the matching `_00_crdt` / `_00_cursor` rows
1020
+ * via subquery. Permission inheritance happens server-side via
1021
+ * `record_id.id != NONE` (SELECT) and `fn::can_update_record` (UPDATE).
1022
+ */
1023
+ export class CrdtManager {
1024
+ private fields = new Map&lt;string, CrdtField&gt;();
1025
+ // One LIVE subscription per parent table (e.g. "thread" → uuid).
1026
+ private liveByTable = new Map&lt;string, Uuid&gt;();
1027
+ // Coalesces concurrent first-time subscribes for the same table. Stamped with
1028
+ // the connection generation it started under, so a restart after a drop can
1029
+ // tell a reusable in-flight attempt from a doomed one.
1030
+ private pendingLive = new Map&lt;string, { promise: Promise&lt;void&gt;; generation: number }&gt;();
1031
+ // Tables whose LIVE died with a dropped socket, awaiting the next `connected`
1032
+ // to be re-subscribed. Held separately because `liveByTable` is cleared on the
1033
+ // drop (those uuids belong to a session that no longer exists).
1034
+ private staleTables = new Set&lt;string&gt;();
1035
+ // Bumped on every socket drop. A LIVE registration that started under an
1036
+ // older generation is discarded rather than recorded, so a drop landing
1037
+ // mid-registration can't leave a uuid from a dead session in `liveByTable`.
1038
+ private connectionGeneration = 0;
1039
+ private connectionUnsubscribes: Array&lt;() =&gt; void&gt; = [];
1040
+ private logger: Logger;
1041
+ // SurrealDB session id, used as the per-session key inside `_00_cursor`.
1042
+ private sessionId: string = '';
1043
+ &nbsp;
1044
+ constructor(
1045
+ private schema: SchemaStructure,
1046
+ private local: LocalStore,
1047
+ private remote: RemoteDatabaseService,
1048
+ logger: Logger,
1049
+ private debounceMs: number = 500,
1050
+ ) {
1051
+ this.logger = logger.child({ service: 'CrdtManager' });
1052
+ this.subscribeToReconnect();
1053
+ }
1054
+ &nbsp;
1055
+ /**
1056
+ * Re-establish table LIVEs after a socket drop.
1057
+ *
1058
+ * A LIVE subscription lives and dies with its WebSocket session, and
1059
+ * `ensureTableSubscription` is memoized on `liveByTable` — so without this,
1060
+ * the first reconnect leaves CRDT realtime permanently dead: the map still
1061
+ * holds a uuid for a subscription the server has forgotten, so every later
1062
+ * `open()` short-circuits and no LIVE is ever re-issued.
1063
+ *
1064
+ * Both drop events matter: the SDK publishes `reconnecting` (not
1065
+ * `disconnected`) when it intends to recover on its own, and `disconnected`
1066
+ * only once it has given up.
1067
+ */
1068
+ private subscribeToReconnect(): void {
1069
+ const onDrop = () =&gt; {
1070
+ this.connectionGeneration++;
1071
+ for (const table of this.liveByTable.keys()) this.staleTables.add(table);
1072
+ // In-flight registrations count too: they'd otherwise complete against
1073
+ // the dead session and be silently dropped by the generation guard.
1074
+ for (const table of this.pendingLive.keys()) this.staleTables.add(table);
1075
+ // No KILL: the server-side subscriptions are already gone, and these
1076
+ // uuids would resolve against the new session.
1077
+ this.liveByTable.clear();
1078
+ };
1079
+ this.connectionUnsubscribes.push(
1080
+ this.remote.subscribeConnection('reconnecting', onDrop),
1081
+ this.remote.subscribeConnection('disconnected', onDrop),
1082
+ this.remote.subscribeConnection('connected', () =&gt; {
1083
+ const tables = Array.from(this.staleTables);
1084
+ this.staleTables.clear();
1085
+ for (const table of tables) {
1086
+ // Only resurrect tables that still have an open field — a drop can
1087
+ // outlive the editor that needed the feed.
1088
+ if (!this.hasOpenFieldFor(table)) <span class="branch-0 cbranch-no" title="branch not covered" >continue;</span>
1089
+ void this.ensureTableSubscription(table);
1090
+ }
1091
+ })
1092
+ );
1093
+ }
1094
+ &nbsp;
1095
+ /** Stop observing transport events. Separate from {@link closeAll}, which also
1096
+ * runs on a bucket switch where the manager keeps being used. */
1097
+ dispose(): void {
1098
+ for (const off of this.connectionUnsubscribes) {
1099
+ try {
1100
+ off();
1101
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch {</span>
1102
+ /* ignore */
1103
+ <span class="cstat-no" title="statement not covered" > }</span>
1104
+ }
1105
+ this.connectionUnsubscribes = [];
1106
+ this.staleTables.clear();
1107
+ }
1108
+ &nbsp;
1109
+ private hasOpenFieldFor(table: string): boolean {
1110
+ const prefix = `${table}:`;
1111
+ return Array.from(this.fields.keys()).some((k) =&gt; k.startsWith(prefix));
1112
+ }
1113
+ &nbsp;
1114
+ /** Set the session id that scopes this client's cursor entries. Must be
1115
+ * called before `open()` for cursors to be pushed under a stable key.
1116
+ * Passed in from `sp00ky.ts` at boot (it already fetches `session::id()`
1117
+ * for the data-module salt). */
1118
+ setSessionId(sessionId: string): void {
1119
+ this.sessionId = sessionId;
1120
+ }
1121
+ &nbsp;
1122
+ /**
1123
+ * Open a CRDT field for collaborative editing.
1124
+ *
1125
+ * @param table - Table name
1126
+ * @param recordId - Full record ID (e.g., "thread:abc")
1127
+ * @param field - Field name (e.g., "title", "content")
1128
+ * @param fallbackText - Current plain text from the record, used to seed the
1129
+ * LoroDoc if no CRDT state exists yet (migration path)
1130
+ */
1131
+ async open(
1132
+ table: string,
1133
+ recordId: string,
1134
+ field: string,
1135
+ fallbackText?: string,
1136
+ ): Promise&lt;CrdtField&gt; {
1137
+ this.assertCrdtField(table, field);
1138
+ const cursorsEnabled = this.fieldHasCursor(table, field);
1139
+ const key = this.makeKey(table, recordId, field);
1140
+ let crdtField = this.fields.get(key);
1141
+ &nbsp;
1142
+ if (crdtField) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1143
+ <span class="cstat-no" title="statement not covered" > return crdtField;</span>
1144
+ <span class="cstat-no" title="statement not covered" > }</span>
1145
+ &nbsp;
1146
+ // Read the snapshot directly off the parent row. `@crdt`-only fields
1147
+ // hold the base64 snapshot inline; `@crdt @cursor` fields hold a
1148
+ // `{ state, cursors }` object so we drill into `.state`. The query
1149
+ // always selects the local row — sync-down already populated it
1150
+ // (the snapshot is a column on the parent, not a sidecar table) so
1151
+ // there is no separate fetch on the happy path.
1152
+ let initialCrdtState: Uint8Array | undefined;
1153
+ try {
1154
+ const [result] = await this.local.query&lt;[unknown]&gt;(
1155
+ `SELECT VALUE ${field} FROM ONLY $id`,
1156
+ { id: parseRecordIdString(recordId) },
1157
+ );
1158
+ const snapshot = this.extractSnapshot(result, cursorsEnabled);
1159
+ if (snapshot) initialCrdtState = snapshot;
1160
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
1161
+ <span class="cstat-no" title="statement not covered" > this.logger.info(</span>
1162
+ <span class="cstat-no" title="statement not covered" > { error: String(e), recordId, field, Category: 'sp00ky-client::CrdtManager::open' },</span>
1163
+ <span class="cstat-no" title="statement not covered" > 'No existing CRDT state found in local cache (continuing with empty doc)'</span>
1164
+ <span class="cstat-no" title="statement not covered" > );</span>
1165
+ <span class="cstat-no" title="statement not covered" > }</span>
1166
+ &nbsp;
1167
+ // Load loro lazily (usually already resolved — the client preloads it at
1168
+ // startup when `config.crdt` is on).
1169
+ const { LoroDoc } = await loadLoro();
1170
+ crdtField = new CrdtField(field, cursorsEnabled, LoroDoc, initialCrdtState, this.logger);
1171
+ crdtField.startSync(this.local, this.remote, recordId, this.sessionId, this.debounceMs);
1172
+ this.fields.set(key, crdtField);
1173
+ &nbsp;
1174
+ this.logger.info(
1175
+ { key, hasInitialState: !!initialCrdtState, hasFallback: !!fallbackText, Category: 'sp00ky-client::CrdtManager::open' },
1176
+ 'CrdtField opened'
1177
+ );
1178
+ &nbsp;
1179
+ // Fire-and-forget: the LIVE subscription receives *future* updates;
1180
+ // the initial snapshot is already in hand. `ensureTableSubscription`
1181
+ // coalesces concurrent calls via `pendingLive`, so this is safe.
1182
+ void this.ensureTableSubscription(table);
1183
+ &nbsp;
1184
+ // Local was empty — a fresh device, a memory-backed local DB after
1185
+ // reload, or a record that hasn't been sync'd locally yet. Pull the
1186
+ // parent row from remote and dispatch its CRDT field; otherwise the
1187
+ // editor sits empty until the parent's LIVE feed happens to fire.
1188
+ if (!initialCrdtState) {
1189
+ void this.fetchAndDispatchRow(table, recordId);
1190
+ }
1191
+ &nbsp;
1192
+ return crdtField;
1193
+ }
1194
+ &nbsp;
1195
+ close(table: string, recordId: string, field: string): void {
1196
+ const key = this.makeKey(table, recordId, field);
1197
+ const crdtField = this.fields.get(key);
1198
+ if (crdtField) {
1199
+ crdtField.stopSync();
1200
+ this.fields.delete(key);
1201
+ }
1202
+ &nbsp;
1203
+ // If no fields on this table remain open, tear down the table-wide LIVE.
1204
+ if (!this.hasOpenFieldFor(table)) {
1205
+ this.killTableSubscription(table);
1206
+ // Also drop any pending post-reconnect restart for it.
1207
+ this.staleTables.delete(table);
1208
+ }
1209
+ &nbsp;
1210
+ this.logger.debug(
1211
+ { key, Category: 'sp00ky-client::CrdtManager::close' },
1212
+ 'CrdtField closed'
1213
+ );
1214
+ }
1215
+ &nbsp;
1216
+ /**
1217
+ * Close every open field + table LIVE. Fields flush a final remote push by
1218
+ * default; pass `{ flush: false }` on a bucket switch, where that flush
1219
+ * would push the previous user's snapshot under the next user's session.
1220
+ */
1221
+ <span class="fstat-no" title="function not covered" > closeAll(options: { flush?: boolean } = {}): void {</span>
1222
+ <span class="cstat-no" title="statement not covered" > for (const [_, field] of this.fields) {</span>
1223
+ <span class="cstat-no" title="statement not covered" > field.stopSync(options);</span>
1224
+ <span class="cstat-no" title="statement not covered" > }</span>
1225
+ <span class="cstat-no" title="statement not covered" > this.fields.clear();</span>
1226
+ <span class="cstat-no" title="statement not covered" > for (const table of Array.from(this.liveByTable.keys())) {</span>
1227
+ <span class="cstat-no" title="statement not covered" > this.killTableSubscription(table);</span>
1228
+ <span class="cstat-no" title="statement not covered" > }</span>
1229
+ // Nothing is open any more, so nothing should be resurrected on the next
1230
+ // reconnect; `open()` will re-subscribe on demand.
1231
+ <span class="cstat-no" title="statement not covered" > this.staleTables.clear();</span>
1232
+ <span class="cstat-no" title="statement not covered" > }</span>
1233
+ &nbsp;
1234
+ /** Ensure a single `LIVE SELECT * FROM &lt;table&gt;` is running, shared across
1235
+ * every open CrdtField on `table`. */
1236
+ private async ensureTableSubscription(table: string): Promise&lt;void&gt; {
1237
+ if (this.liveByTable.has(table)) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1238
+ &nbsp;
1239
+ // Registering a LIVE takes two round-trips, so a socket drop can land
1240
+ // mid-flight. The generation stamp is what keeps that honest: a registration
1241
+ // that started before the drop must never record its uuid, because this
1242
+ // method short-circuits on `liveByTable` and that stale entry (pointing at a
1243
+ // session the server has forgotten) would block every future restart.
1244
+ const generation = this.connectionGeneration;
1245
+ &nbsp;
1246
+ const pending = this.pendingLive.get(table);
1247
+ if (pending) {
1248
+ if (pending.generation === generation) <span class="branch-0 cbranch-no" title="branch not covered" >return pending.promise;</span>
1249
+ // A registration from before the drop is still settling. It will discard
1250
+ // itself; wait it out, then register fresh on the new socket.
1251
+ await pending.promise.catch(() =&gt; {});
1252
+ if (this.liveByTable.has(table)) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1253
+ if (generation !== this.connectionGeneration) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1254
+ // Another caller may have won the race to re-register during that await;
1255
+ // join it rather than opening a second LIVE on the same table.
1256
+ const successor = this.pendingLive.get(table);
1257
+ if (<span class="branch-0 cbranch-no" title="branch not covered" >successor &amp;&amp; successor.generation === generation) <span class="branch-0 cbranch-no" title="branch not covered" >r</span>eturn successor.promise;</span>
1258
+ }
1259
+ &nbsp;
1260
+ const start = (async () =&gt; {
1261
+ try {
1262
+ const [uuid] = await this.remote.query&lt;[Uuid]&gt;(
1263
+ `LIVE SELECT * FROM ${table}`,
1264
+ );
1265
+ &nbsp;
1266
+ if (generation !== this.connectionGeneration) {
1267
+ this.logger.debug(
1268
+ { table, Category: 'sp00ky-client::CrdtManager::ensureTableSubscription' },
1269
+ 'Socket dropped while registering LIVE; discarding it'
1270
+ );
1271
+ return;
1272
+ }
1273
+ &nbsp;
1274
+ const subscription = await this.remote.getClient().liveOf(uuid);
1275
+ subscription.subscribe((message) =&gt; {
1276
+ <span class="cstat-no" title="statement not covered" > if (message.action === 'KILLED') return;</span>
1277
+ <span class="cstat-no" title="statement not covered" > if (message.action !== 'CREATE' &amp;&amp; message.action !== 'UPDATE') return;</span>
1278
+ <span class="cstat-no" title="statement not covered" > this.dispatchRow(table, message.value as Record&lt;string, unknown&gt;);</span>
1279
+ });
1280
+ &nbsp;
1281
+ if (generation !== this.connectionGeneration) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1282
+ &nbsp;
1283
+ this.liveByTable.set(table, uuid);
1284
+ this.logger.info(
1285
+ { table, Category: 'sp00ky-client::CrdtManager::ensureTableSubscription' },
1286
+ 'LIVE SELECT started'
1287
+ );
1288
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
1289
+ <span class="cstat-no" title="statement not covered" > this.logger.warn(</span>
1290
+ <span class="cstat-no" title="statement not covered" > { error: e, table, Category: 'sp00ky-client::CrdtManager::ensureTableSubscription' },</span>
1291
+ <span class="cstat-no" title="statement not covered" > 'Failed to start LIVE SELECT'</span>
1292
+ <span class="cstat-no" title="statement not covered" > );</span>
1293
+ <span class="cstat-no" title="statement not covered" > }</span>
1294
+ })();
1295
+ &nbsp;
1296
+ this.pendingLive.set(table, { promise: start, generation });
1297
+ try {
1298
+ await start;
1299
+ } finally {
1300
+ this.pendingLive.delete(table);
1301
+ }
1302
+ }
1303
+ &nbsp;
1304
+ /** Apply a parent-row payload from a non-LIVE source (e.g. the
1305
+ * list_ref-driven sync engine, when the cross-user LIVE on the
1306
+ * parent table is filtered out by the SurrealDB cross-session
1307
+ * permission gap). Same semantics as the internal `dispatchRow`. */
1308
+ <span class="fstat-no" title="function not covered" > applyRow(table: string, row: Record&lt;string, unknown&gt;): void {</span>
1309
+ <span class="cstat-no" title="statement not covered" > this.dispatchRow(table, row);</span>
1310
+ <span class="cstat-no" title="statement not covered" > }</span>
1311
+ &nbsp;
1312
+ /** Dispatch a parent-row LIVE event to every open CrdtField on that
1313
+ * record. Each open field reads its slice of the row directly — the
1314
+ * CRDT snapshot is a column on the parent now, so there is no
1315
+ * follow-up subquery. */
1316
+ private dispatchRow(table: string, row: Record&lt;string, unknown&gt;): void {
1317
+ const id = row.id != null ? String(row.id<span class="branch-0 cbranch-no" title="branch not covered" >) : '';</span>
1318
+ if (!id) <span class="branch-0 cbranch-no" title="branch not covered" >return;</span>
1319
+ &nbsp;
1320
+ const rowKeyPrefix = `${table}:${id}:`;
1321
+ for (const [key, crdtField] of this.fields) {
1322
+ if (!key.startsWith(rowKeyPrefix)) <span class="branch-0 cbranch-no" title="branch not covered" >continue;</span>
1323
+ const fieldName = key.slice(rowKeyPrefix.length);
1324
+ const cursorsEnabled = this.fieldHasCursor(table, fieldName);
1325
+ const slice = row[fieldName];
1326
+ const snapshot = this.extractSnapshot(slice, cursorsEnabled);
1327
+ if (snapshot) crdtField.importRemote(snapshot);
1328
+ &nbsp;
1329
+ if (cursorsEnabled &amp;&amp; slice &amp;&amp; typeof slice === 'object') {
1330
+ const cursors = (slice as { cursors?: unknown }).cursors;
1331
+ if (cursors &amp;&amp; typeof cursors === 'object') {
1332
+ for (const [sid, blob] of Object.entries(cursors as Record&lt;string, unknown&gt;)) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1333
+ <span class="cstat-no" title="statement not covered" > if (sid === this.sessionId) continue;</span>
1334
+ <span class="cstat-no" title="statement not covered" > if (typeof blob === 'string' &amp;&amp; blob.length &gt; 0) {</span>
1335
+ <span class="cstat-no" title="statement not covered" > crdtField.importRemoteCursor(blob);</span>
1336
+ <span class="cstat-no" title="statement not covered" > }</span>
1337
+ <span class="cstat-no" title="statement not covered" > }</span>
1338
+ }
1339
+ }
1340
+ }
1341
+ }
1342
+ &nbsp;
1343
+ /** One-shot remote fetch for a row whose CRDT field hasn't synced
1344
+ * locally yet (fresh device, memory-backed local DB after reload, …).
1345
+ * Used by `open()` when the local read came up empty. Subsequent
1346
+ * cross-browser updates ride `dispatchRow` via the parent LIVE feed. */
1347
+ private async fetchAndDispatchRow(table: string, id: string): Promise&lt;void&gt; {
1348
+ try {
1349
+ const recordId = parseRecordIdString(id);
1350
+ const [row] = await this.remote.query&lt;[Record&lt;string, unknown&gt; | null]&gt;(
1351
+ `SELECT * FROM ONLY $id`,
1352
+ { id: recordId },
1353
+ );
1354
+ if (!row || typeof row !== 'object') return;
1355
+ this.dispatchRow(table, row as Record&lt;string, unknown&gt;);
1356
+ <span class="branch-0 cbranch-no" title="branch not covered" > } catch (e) {</span>
1357
+ <span class="cstat-no" title="statement not covered" > this.logger.warn(</span>
1358
+ <span class="cstat-no" title="statement not covered" > { error: e, table, id, Category: 'sp00ky-client::CrdtManager::fetchAndDispatchRow' },</span>
1359
+ <span class="cstat-no" title="statement not covered" > 'Failed to fetch parent row for CRDT hydration'</span>
1360
+ <span class="cstat-no" title="statement not covered" > );</span>
1361
+ <span class="cstat-no" title="statement not covered" > }</span>
1362
+ }
1363
+ &nbsp;
1364
+ /** Schema lookup: does `&lt;table&gt;.&lt;field&gt;` carry a `@cursor` annotation?
1365
+ * Determines the on-disk shape (plain snapshot vs. `{ state, cursors }`). */
1366
+ private fieldHasCursor(table: string, field: string): boolean {
1367
+ const tableSchema = this.schema.tables.find((t) =&gt; t.name === table);
1368
+ return !!tableSchema?.columns[field]?.cursor;
1369
+ }
1370
+ &nbsp;
1371
+ /** Pull the LoroDoc snapshot bytes out of a row slice. For `@crdt`-only
1372
+ * the slice IS the snapshot (Uint8Array); for `@crdt @cursor` it's
1373
+ * `{ state, cursors }` where `state` carries the snapshot bytes. */
1374
+ private extractSnapshot(value: unknown, cursorsEnabled: boolean): Uint8Array | undefined {
1375
+ const asBytes = (v: unknown): Uint8Array | undefined =&gt; {
1376
+ if (v instanceof Uint8Array) return v.length &gt; 0 ? <span class="branch-0 cbranch-no" title="branch not covered" >v : undefined;</span>
1377
+ // SurrealDB ferries bytes through several shapes depending on
1378
+ // transport and on whether the field is a top-level `bytes` column
1379
+ // or bytes nested inside a FLEXIBLE object. Round-tripping bytes
1380
+ // through `option&lt;object&gt; FLEXIBLE` (the `@crdt @cursor` shape) in
1381
+ // particular comes back as a plain `number[]` from the local WASM
1382
+ // DB and as `Uint8Array` from the remote WS engine. Normalize all
1383
+ // recognized variants here so the receiving CrdtField doesn't care.
1384
+ if (v instanceof ArrayBuffer) <span class="branch-0 cbranch-no" title="branch not covered" >return new Uint8Array(v);</span>
1385
+ if (ArrayBuffer.isView(v)) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1386
+ <span class="cstat-no" title="statement not covered" > const view = v as ArrayBufferView;</span>
1387
+ <span class="cstat-no" title="statement not covered" > return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);</span>
1388
+ <span class="cstat-no" title="statement not covered" > }</span>
1389
+ if (Array.isArray(v) &amp;&amp; v.length &gt; 0 &amp;&amp; v.every((n) =&gt; typeof n === 'number')) {
1390
+ return Uint8Array.from(v as number[]);
1391
+ }
1392
+ return undefined;
1393
+ };
1394
+ &nbsp;
1395
+ if (cursorsEnabled) {
1396
+ if (value &amp;&amp; typeof value === 'object' &amp;&amp; !(value instanceof Uint8Array)) {
1397
+ return asBytes((value as { state?: unknown }).state);
1398
+ }
1399
+ return undefined;
1400
+ }
1401
+ return asBytes(value);
1402
+ }
1403
+ &nbsp;
1404
+ private killTableSubscription(table: string): void {
1405
+ const uuid = this.liveByTable.get(table);
1406
+ if (uuid) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1407
+ // We're tearing down: KILL on an already-dead/closed LIVE throws, and the
1408
+ // subscription may have ended on its own, so the failure is expected and
1409
+ // safe to drop.
1410
+ <span class="cstat-no" title="statement not covered" > this.remote.query('KILL $uuid', { uuid }).catch((err) =&gt; {</span>
1411
+ <span class="cstat-no" title="statement not covered" > this.logger.debug(</span>
1412
+ <span class="cstat-no" title="statement not covered" > { err, table, Category: 'sp00ky-client::CrdtManager::killTableSubscription' },</span>
1413
+ <span class="cstat-no" title="statement not covered" > 'KILL of table LIVE failed (already closed?)'</span>
1414
+ <span class="cstat-no" title="statement not covered" > );</span>
1415
+ <span class="cstat-no" title="statement not covered" > });</span>
1416
+ <span class="cstat-no" title="statement not covered" > this.liveByTable.delete(table);</span>
1417
+ <span class="cstat-no" title="statement not covered" > }</span>
1418
+ }
1419
+ &nbsp;
1420
+ private makeKey(table: string, recordId: string, field: string): string {
1421
+ return `${table}:${recordId}:${field}`;
1422
+ }
1423
+ &nbsp;
1424
+ /**
1425
+ * Throws if `&lt;table&gt;.&lt;field&gt;` is not annotated `@crdt` in the schema. Catches
1426
+ * typos, removed annotations, and stale schema codegen at the call site instead
1427
+ * of silently producing a non-CRDT writer.
1428
+ */
1429
+ private assertCrdtField(table: string, field: string): void {
1430
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(field)) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1431
+ <span class="cstat-no" title="statement not covered" > throw new Error(</span>
1432
+ <span class="cstat-no" title="statement not covered" > `openCrdtField: refusing unsafe field identifier '${field}' — must match [a-zA-Z_][a-zA-Z0-9_]*`</span>
1433
+ <span class="cstat-no" title="statement not covered" > );</span>
1434
+ <span class="cstat-no" title="statement not covered" > }</span>
1435
+ const tableSchema = this.schema.tables.find((t) =&gt; t.name === table);
1436
+ if (!tableSchema) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1437
+ <span class="cstat-no" title="statement not covered" > throw new Error(</span>
1438
+ <span class="cstat-no" title="statement not covered" > `openCrdtField: unknown table '${table}'. Available: ${this.schema.tables.map((t) =&gt; t.name).join(', ')}`</span>
1439
+ <span class="cstat-no" title="statement not covered" > );</span>
1440
+ <span class="cstat-no" title="statement not covered" > }</span>
1441
+ const column = tableSchema.columns[field];
1442
+ if (!column) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1443
+ <span class="cstat-no" title="statement not covered" > throw new Error(</span>
1444
+ <span class="cstat-no" title="statement not covered" > `openCrdtField: '${table}.${field}' is not in the schema. Available fields: ${Object.keys(tableSchema.columns).join(', ')}`</span>
1445
+ <span class="cstat-no" title="statement not covered" > );</span>
1446
+ <span class="cstat-no" title="statement not covered" > }</span>
1447
+ if (!column.crdt) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1448
+ <span class="cstat-no" title="statement not covered" > throw new Error(</span>
1449
+ <span class="cstat-no" title="statement not covered" > `openCrdtField: '${table}.${field}' is not annotated '@crdt' in the schema. ` +</span>
1450
+ `Add '-- @crdt text' above the field's DEFINE FIELD and regenerate the client schema.`
1451
+ <span class="cstat-no" title="statement not covered" > );</span>
1452
+ <span class="cstat-no" title="statement not covered" > }</span>
1453
+ }
1454
+ }
1455
+ &nbsp;</pre></td></tr></table></pre>
1456
+
1457
+ <div class='push'></div><!-- for sticky footer -->
1458
+ </div><!-- /wrapper -->
1459
+ <div class='footer quiet pad2 space-top1 center small'>
1460
+ Code coverage generated by
1461
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1462
+ at 2026-09-09T09:49:51.780Z
1463
+ </div>
1464
+ <script src="../../../prettify.js"></script>
1465
+ <script>
1466
+ window.onload = function () {
1467
+ prettyPrint();
1468
+ };
1469
+ </script>
1470
+ <script src="../../../sorter.js"></script>
1471
+ <script src="../../../block-navigation.js"></script>
1472
+ </body>
1473
+ </html>
1474
+