@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.
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +476 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/boot/auth-flip.saga.ts.html +271 -0
- package/coverage/lcov-report/src/boot/boot.saga.ts.html +469 -0
- package/coverage/lcov-report/src/boot/bucket-switch.saga.ts.html +430 -0
- package/coverage/lcov-report/src/boot/index.html +161 -0
- package/coverage/lcov-report/src/boot/preload.saga.ts.html +178 -0
- package/coverage/lcov-report/src/client/bucket-handle.ts.html +766 -0
- package/coverage/lcov-report/src/client/index.html +161 -0
- package/coverage/lcov-report/src/client/router.ts.html +367 -0
- package/coverage/lcov-report/src/client/runtime.ts.html +940 -0
- package/coverage/lcov-report/src/client/sp00ky-client.ts.html +1531 -0
- package/coverage/lcov-report/src/events/index.html +116 -0
- package/coverage/lcov-report/src/events/index.ts.html +877 -0
- package/coverage/lcov-report/src/index.html +146 -0
- package/coverage/lcov-report/src/index.ts.html +202 -0
- package/coverage/lcov-report/src/kernel/constants.ts.html +247 -0
- package/coverage/lcov-report/src/kernel/effects.ts.html +562 -0
- package/coverage/lcov-report/src/kernel/events.ts.html +250 -0
- package/coverage/lcov-report/src/kernel/index.html +176 -0
- package/coverage/lcov-report/src/kernel/interpreter.ts.html +514 -0
- package/coverage/lcov-report/src/kernel/saga.ts.html +304 -0
- package/coverage/lcov-report/src/modules/app-release/index.html +116 -0
- package/coverage/lcov-report/src/modules/app-release/index.ts.html +712 -0
- package/coverage/lcov-report/src/modules/auth/events/index.html +116 -0
- package/coverage/lcov-report/src/modules/auth/events/index.ts.html +142 -0
- package/coverage/lcov-report/src/modules/auth/index.html +116 -0
- package/coverage/lcov-report/src/modules/auth/index.ts.html +1225 -0
- package/coverage/lcov-report/src/modules/crdt/crdt-field.ts.html +967 -0
- package/coverage/lcov-report/src/modules/crdt/index.html +146 -0
- package/coverage/lcov-report/src/modules/crdt/index.ts.html +1474 -0
- package/coverage/lcov-report/src/modules/crdt/loro-loader.ts.html +160 -0
- package/coverage/lcov-report/src/modules/devtools/flags.ts.html +1132 -0
- package/coverage/lcov-report/src/modules/devtools/index.html +161 -0
- package/coverage/lcov-report/src/modules/devtools/index.ts.html +2413 -0
- package/coverage/lcov-report/src/modules/devtools/storage-info.ts.html +589 -0
- package/coverage/lcov-report/src/modules/devtools/versions.ts.html +415 -0
- package/coverage/lcov-report/src/modules/feature-flag/index.html +116 -0
- package/coverage/lcov-report/src/modules/feature-flag/index.ts.html +1033 -0
- package/coverage/lcov-report/src/modules/index.html +116 -0
- package/coverage/lcov-report/src/modules/ref-tables.ts.html +349 -0
- package/coverage/lcov-report/src/mutation/index.html +191 -0
- package/coverage/lcov-report/src/mutation/jobs.ts.html +208 -0
- package/coverage/lcov-report/src/mutation/mutation-id.ts.html +190 -0
- package/coverage/lcov-report/src/mutation/push.saga.ts.html +730 -0
- package/coverage/lcov-report/src/mutation/rows.ts.html +1216 -0
- package/coverage/lcov-report/src/mutation/tray.saga.ts.html +229 -0
- package/coverage/lcov-report/src/mutation/write.saga.ts.html +583 -0
- package/coverage/lcov-report/src/otel/index.html +116 -0
- package/coverage/lcov-report/src/otel/index.ts.html +466 -0
- package/coverage/lcov-report/src/query/env.ts.html +253 -0
- package/coverage/lcov-report/src/query/fetch.saga.ts.html +412 -0
- package/coverage/lcov-report/src/query/hash.ts.html +139 -0
- package/coverage/lcov-report/src/query/index.html +281 -0
- package/coverage/lcov-report/src/query/lifecycle.saga.ts.html +388 -0
- package/coverage/lcov-report/src/query/materialize.saga.ts.html +310 -0
- package/coverage/lcov-report/src/query/materialize.ts.html +217 -0
- package/coverage/lcov-report/src/query/membership.saga.ts.html +709 -0
- package/coverage/lcov-report/src/query/membership.ts.html +559 -0
- package/coverage/lcov-report/src/query/register.saga.ts.html +655 -0
- package/coverage/lcov-report/src/query/render-set.ts.html +265 -0
- package/coverage/lcov-report/src/query/sql.ts.html +322 -0
- package/coverage/lcov-report/src/query/window-query.ts.html +670 -0
- package/coverage/lcov-report/src/services/blobs/blob-cache.ts.html +1894 -0
- package/coverage/lcov-report/src/services/blobs/blob-manifest.ts.html +766 -0
- package/coverage/lcov-report/src/services/blobs/blob-store.ts.html +1162 -0
- package/coverage/lcov-report/src/services/blobs/index.html +161 -0
- package/coverage/lcov-report/src/services/blobs/index.ts.html +295 -0
- package/coverage/lcov-report/src/services/database/cache-engine.ts.html +664 -0
- package/coverage/lcov-report/src/services/database/connection-supervisor.ts.html +1330 -0
- package/coverage/lcov-report/src/services/database/database.ts.html +658 -0
- package/coverage/lcov-report/src/services/database/engine-factory.ts.html +184 -0
- package/coverage/lcov-report/src/services/database/errors.ts.html +187 -0
- package/coverage/lcov-report/src/services/database/events/index.html +116 -0
- package/coverage/lcov-report/src/services/database/events/index.ts.html +184 -0
- package/coverage/lcov-report/src/services/database/index.html +386 -0
- package/coverage/lcov-report/src/services/database/index.ts.html +121 -0
- package/coverage/lcov-report/src/services/database/local-migrator.ts.html +703 -0
- package/coverage/lcov-report/src/services/database/local.ts.html +1633 -0
- package/coverage/lcov-report/src/services/database/plan-render.ts.html +409 -0
- package/coverage/lcov-report/src/services/database/relation-resolver.ts.html +682 -0
- package/coverage/lcov-report/src/services/database/remote.ts.html +1036 -0
- package/coverage/lcov-report/src/services/database/sqlite-cache-engine.ts.html +4132 -0
- package/coverage/lcov-report/src/services/database/sqlite-lock-verify.ts.html +220 -0
- package/coverage/lcov-report/src/services/database/sqlite-open.ts.html +577 -0
- package/coverage/lcov-report/src/services/database/sqlite-plan-sql.ts.html +499 -0
- package/coverage/lcov-report/src/services/database/sqlite-select.ts.html +478 -0
- package/coverage/lcov-report/src/services/database/sqlite-transport.ts.html +757 -0
- package/coverage/lcov-report/src/services/database/surql-translate.ts.html +1333 -0
- package/coverage/lcov-report/src/services/database/surreal-cache-engine.ts.html +568 -0
- package/coverage/lcov-report/src/services/logger/index.html +116 -0
- package/coverage/lcov-report/src/services/logger/index.ts.html +154 -0
- package/coverage/lcov-report/src/services/persistence/index.html +146 -0
- package/coverage/lcov-report/src/services/persistence/localstorage.ts.html +163 -0
- package/coverage/lcov-report/src/services/persistence/resilient.ts.html +208 -0
- package/coverage/lcov-report/src/services/persistence/surrealdb.ts.html +271 -0
- package/coverage/lcov-report/src/services/stream-processor/index.html +146 -0
- package/coverage/lcov-report/src/services/stream-processor/index.ts.html +3313 -0
- package/coverage/lcov-report/src/services/stream-processor/permissions.ts.html +244 -0
- package/coverage/lcov-report/src/services/stream-processor/wasm-types.ts.html +346 -0
- package/coverage/lcov-report/src/services/tabs/broker-client.ts.html +934 -0
- package/coverage/lcov-report/src/services/tabs/coordinator.ts.html +1984 -0
- package/coverage/lcov-report/src/services/tabs/index.html +191 -0
- package/coverage/lcov-report/src/services/tabs/leader-locks.ts.html +310 -0
- package/coverage/lcov-report/src/services/tabs/protocol.ts.html +871 -0
- package/coverage/lcov-report/src/services/tabs/support.ts.html +193 -0
- package/coverage/lcov-report/src/services/tabs/tabs-broker-worker.ts.html +2005 -0
- package/coverage/lcov-report/src/sp00ky.ts.html +100 -0
- package/coverage/lcov-report/src/state/client-state.ts.html +637 -0
- package/coverage/lcov-report/src/state/index.html +161 -0
- package/coverage/lcov-report/src/state/lifecycle.ts.html +343 -0
- package/coverage/lcov-report/src/state/reducers.ts.html +1471 -0
- package/coverage/lcov-report/src/state/selectors.ts.html +637 -0
- package/coverage/lcov-report/src/sync/connection.saga.ts.html +343 -0
- package/coverage/lcov-report/src/sync/index.html +176 -0
- package/coverage/lcov-report/src/sync/live.saga.ts.html +232 -0
- package/coverage/lcov-report/src/sync/policy.ts.html +1606 -0
- package/coverage/lcov-report/src/sync/poll.saga.ts.html +193 -0
- package/coverage/lcov-report/src/sync/tabs.saga.ts.html +361 -0
- package/coverage/lcov-report/src/types.ts.html +2419 -0
- package/coverage/lcov-report/src/utils/blurhash.ts.html +355 -0
- package/coverage/lcov-report/src/utils/error-classification.ts.html +244 -0
- package/coverage/lcov-report/src/utils/index.html +191 -0
- package/coverage/lcov-report/src/utils/index.ts.html +820 -0
- package/coverage/lcov-report/src/utils/parser.ts.html +313 -0
- package/coverage/lcov-report/src/utils/semver.ts.html +175 -0
- package/coverage/lcov-report/src/utils/surql.ts.html +667 -0
- package/coverage/lcov.info +19079 -0
- package/dist/index.js +3 -3
- package/dist/sqlite-open.js +20 -1
- package/package.json +3 -3
- package/src/services/database/relation-resolver.test.ts +55 -1
- package/src/services/database/relation-resolver.ts +0 -0
|
@@ -0,0 +1,1132 @@
|
|
|
1
|
+
|
|
2
|
+
<!doctype html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
|
|
5
|
+
<head>
|
|
6
|
+
<title>Code coverage report for src/modules/devtools/flags.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/devtools</a> flags.ts</h1>
|
|
23
|
+
<div class='clearfix'>
|
|
24
|
+
|
|
25
|
+
<div class='fl pad1y space-right2'>
|
|
26
|
+
<span class="strong">7.59% </span>
|
|
27
|
+
<span class="quiet">Statements</span>
|
|
28
|
+
<span class='fraction'>12/158</span>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<div class='fl pad1y space-right2'>
|
|
33
|
+
<span class="strong">100% </span>
|
|
34
|
+
<span class="quiet">Branches</span>
|
|
35
|
+
<span class='fraction'>1/1</span>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
<div class='fl pad1y space-right2'>
|
|
40
|
+
<span class="strong">6.66% </span>
|
|
41
|
+
<span class="quiet">Functions</span>
|
|
42
|
+
<span class='fraction'>1/15</span>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<div class='fl pad1y space-right2'>
|
|
47
|
+
<span class="strong">7.59% </span>
|
|
48
|
+
<span class="quiet">Lines</span>
|
|
49
|
+
<span class='fraction'>12/158</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>
|
|
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></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
|
|
416
|
+
<span class="cline-any cline-neutral"> </span>
|
|
417
|
+
<span class="cline-any cline-neutral"> </span>
|
|
418
|
+
<span class="cline-any cline-neutral"> </span>
|
|
419
|
+
<span class="cline-any cline-neutral"> </span>
|
|
420
|
+
<span class="cline-any cline-neutral"> </span>
|
|
421
|
+
<span class="cline-any cline-neutral"> </span>
|
|
422
|
+
<span class="cline-any cline-neutral"> </span>
|
|
423
|
+
<span class="cline-any cline-neutral"> </span>
|
|
424
|
+
<span class="cline-any cline-neutral"> </span>
|
|
425
|
+
<span class="cline-any cline-neutral"> </span>
|
|
426
|
+
<span class="cline-any cline-neutral"> </span>
|
|
427
|
+
<span class="cline-any cline-neutral"> </span>
|
|
428
|
+
<span class="cline-any cline-neutral"> </span>
|
|
429
|
+
<span class="cline-any cline-neutral"> </span>
|
|
430
|
+
<span class="cline-any cline-neutral"> </span>
|
|
431
|
+
<span class="cline-any cline-neutral"> </span>
|
|
432
|
+
<span class="cline-any cline-neutral"> </span>
|
|
433
|
+
<span class="cline-any cline-neutral"> </span>
|
|
434
|
+
<span class="cline-any cline-neutral"> </span>
|
|
435
|
+
<span class="cline-any cline-neutral"> </span>
|
|
436
|
+
<span class="cline-any cline-neutral"> </span>
|
|
437
|
+
<span class="cline-any cline-neutral"> </span>
|
|
438
|
+
<span class="cline-any cline-neutral"> </span>
|
|
439
|
+
<span class="cline-any cline-neutral"> </span>
|
|
440
|
+
<span class="cline-any cline-neutral"> </span>
|
|
441
|
+
<span class="cline-any cline-neutral"> </span>
|
|
442
|
+
<span class="cline-any cline-neutral"> </span>
|
|
443
|
+
<span class="cline-any cline-neutral"> </span>
|
|
444
|
+
<span class="cline-any cline-neutral"> </span>
|
|
445
|
+
<span class="cline-any cline-neutral"> </span>
|
|
446
|
+
<span class="cline-any cline-neutral"> </span>
|
|
447
|
+
<span class="cline-any cline-neutral"> </span>
|
|
448
|
+
<span class="cline-any cline-neutral"> </span>
|
|
449
|
+
<span class="cline-any cline-neutral"> </span>
|
|
450
|
+
<span class="cline-any cline-neutral"> </span>
|
|
451
|
+
<span class="cline-any cline-neutral"> </span>
|
|
452
|
+
<span class="cline-any cline-neutral"> </span>
|
|
453
|
+
<span class="cline-any cline-neutral"> </span>
|
|
454
|
+
<span class="cline-any cline-neutral"> </span>
|
|
455
|
+
<span class="cline-any cline-neutral"> </span>
|
|
456
|
+
<span class="cline-any cline-neutral"> </span>
|
|
457
|
+
<span class="cline-any cline-neutral"> </span>
|
|
458
|
+
<span class="cline-any cline-neutral"> </span>
|
|
459
|
+
<span class="cline-any cline-neutral"> </span>
|
|
460
|
+
<span class="cline-any cline-neutral"> </span>
|
|
461
|
+
<span class="cline-any cline-neutral"> </span>
|
|
462
|
+
<span class="cline-any cline-neutral"> </span>
|
|
463
|
+
<span class="cline-any cline-neutral"> </span>
|
|
464
|
+
<span class="cline-any cline-neutral"> </span>
|
|
465
|
+
<span class="cline-any cline-neutral"> </span>
|
|
466
|
+
<span class="cline-any cline-neutral"> </span>
|
|
467
|
+
<span class="cline-any cline-neutral"> </span>
|
|
468
|
+
<span class="cline-any cline-neutral"> </span>
|
|
469
|
+
<span class="cline-any cline-neutral"> </span>
|
|
470
|
+
<span class="cline-any cline-neutral"> </span>
|
|
471
|
+
<span class="cline-any cline-neutral"> </span>
|
|
472
|
+
<span class="cline-any cline-neutral"> </span>
|
|
473
|
+
<span class="cline-any cline-neutral"> </span>
|
|
474
|
+
<span class="cline-any cline-neutral"> </span>
|
|
475
|
+
<span class="cline-any cline-neutral"> </span>
|
|
476
|
+
<span class="cline-any cline-neutral"> </span>
|
|
477
|
+
<span class="cline-any cline-neutral"> </span>
|
|
478
|
+
<span class="cline-any cline-neutral"> </span>
|
|
479
|
+
<span class="cline-any cline-neutral"> </span>
|
|
480
|
+
<span class="cline-any cline-neutral"> </span>
|
|
481
|
+
<span class="cline-any cline-neutral"> </span>
|
|
482
|
+
<span class="cline-any cline-neutral"> </span>
|
|
483
|
+
<span class="cline-any cline-neutral"> </span>
|
|
484
|
+
<span class="cline-any cline-neutral"> </span>
|
|
485
|
+
<span class="cline-any cline-neutral"> </span>
|
|
486
|
+
<span class="cline-any cline-neutral"> </span>
|
|
487
|
+
<span class="cline-any cline-neutral"> </span>
|
|
488
|
+
<span class="cline-any cline-neutral"> </span>
|
|
489
|
+
<span class="cline-any cline-neutral"> </span>
|
|
490
|
+
<span class="cline-any cline-neutral"> </span>
|
|
491
|
+
<span class="cline-any cline-neutral"> </span>
|
|
492
|
+
<span class="cline-any cline-neutral"> </span>
|
|
493
|
+
<span class="cline-any cline-neutral"> </span>
|
|
494
|
+
<span class="cline-any cline-neutral"> </span>
|
|
495
|
+
<span class="cline-any cline-neutral"> </span>
|
|
496
|
+
<span class="cline-any cline-neutral"> </span>
|
|
497
|
+
<span class="cline-any cline-neutral"> </span>
|
|
498
|
+
<span class="cline-any cline-neutral"> </span>
|
|
499
|
+
<span class="cline-any cline-neutral"> </span>
|
|
500
|
+
<span class="cline-any cline-neutral"> </span>
|
|
501
|
+
<span class="cline-any cline-neutral"> </span>
|
|
502
|
+
<span class="cline-any cline-neutral"> </span>
|
|
503
|
+
<span class="cline-any cline-neutral"> </span>
|
|
504
|
+
<span class="cline-any cline-neutral"> </span>
|
|
505
|
+
<span class="cline-any cline-neutral"> </span>
|
|
506
|
+
<span class="cline-any cline-neutral"> </span>
|
|
507
|
+
<span class="cline-any cline-neutral"> </span>
|
|
508
|
+
<span class="cline-any cline-neutral"> </span>
|
|
509
|
+
<span class="cline-any cline-neutral"> </span>
|
|
510
|
+
<span class="cline-any cline-neutral"> </span>
|
|
511
|
+
<span class="cline-any cline-neutral"> </span>
|
|
512
|
+
<span class="cline-any cline-neutral"> </span>
|
|
513
|
+
<span class="cline-any cline-neutral"> </span>
|
|
514
|
+
<span class="cline-any cline-neutral"> </span>
|
|
515
|
+
<span class="cline-any cline-neutral"> </span>
|
|
516
|
+
<span class="cline-any cline-neutral"> </span>
|
|
517
|
+
<span class="cline-any cline-neutral"> </span>
|
|
518
|
+
<span class="cline-any cline-neutral"> </span>
|
|
519
|
+
<span class="cline-any cline-neutral"> </span>
|
|
520
|
+
<span class="cline-any cline-neutral"> </span>
|
|
521
|
+
<span class="cline-any cline-neutral"> </span>
|
|
522
|
+
<span class="cline-any cline-neutral"> </span>
|
|
523
|
+
<span class="cline-any cline-neutral"> </span>
|
|
524
|
+
<span class="cline-any cline-neutral"> </span>
|
|
525
|
+
<span class="cline-any cline-neutral"> </span>
|
|
526
|
+
<span class="cline-any cline-neutral"> </span>
|
|
527
|
+
<span class="cline-any cline-neutral"> </span>
|
|
528
|
+
<span class="cline-any cline-neutral"> </span>
|
|
529
|
+
<span class="cline-any cline-neutral"> </span>
|
|
530
|
+
<span class="cline-any cline-neutral"> </span>
|
|
531
|
+
<span class="cline-any cline-neutral"> </span>
|
|
532
|
+
<span class="cline-any cline-neutral"> </span>
|
|
533
|
+
<span class="cline-any cline-neutral"> </span>
|
|
534
|
+
<span class="cline-any cline-neutral"> </span>
|
|
535
|
+
<span class="cline-any cline-no"> </span>
|
|
536
|
+
<span class="cline-any cline-no"> </span>
|
|
537
|
+
<span class="cline-any cline-no"> </span>
|
|
538
|
+
<span class="cline-any cline-no"> </span>
|
|
539
|
+
<span class="cline-any cline-no"> </span>
|
|
540
|
+
<span class="cline-any cline-no"> </span>
|
|
541
|
+
<span class="cline-any cline-no"> </span>
|
|
542
|
+
<span class="cline-any cline-no"> </span>
|
|
543
|
+
<span class="cline-any cline-no"> </span>
|
|
544
|
+
<span class="cline-any cline-no"> </span>
|
|
545
|
+
<span class="cline-any cline-neutral"> </span>
|
|
546
|
+
<span class="cline-any cline-no"> </span>
|
|
547
|
+
<span class="cline-any cline-no"> </span>
|
|
548
|
+
<span class="cline-any cline-no"> </span>
|
|
549
|
+
<span class="cline-any cline-neutral"> </span>
|
|
550
|
+
<span class="cline-any cline-neutral"> </span>
|
|
551
|
+
<span class="cline-any cline-no"> </span>
|
|
552
|
+
<span class="cline-any cline-no"> </span>
|
|
553
|
+
<span class="cline-any cline-no"> </span>
|
|
554
|
+
<span class="cline-any cline-neutral"> </span>
|
|
555
|
+
<span class="cline-any cline-no"> </span>
|
|
556
|
+
<span class="cline-any cline-no"> </span>
|
|
557
|
+
<span class="cline-any cline-no"> </span>
|
|
558
|
+
<span class="cline-any cline-neutral"> </span>
|
|
559
|
+
<span class="cline-any cline-no"> </span>
|
|
560
|
+
<span class="cline-any cline-no"> </span>
|
|
561
|
+
<span class="cline-any cline-no"> </span>
|
|
562
|
+
<span class="cline-any cline-neutral"> </span>
|
|
563
|
+
<span class="cline-any cline-yes">1x</span>
|
|
564
|
+
<span class="cline-any cline-yes">1x</span>
|
|
565
|
+
<span class="cline-any cline-neutral"> </span>
|
|
566
|
+
<span class="cline-any cline-neutral"> </span>
|
|
567
|
+
<span class="cline-any cline-neutral"> </span>
|
|
568
|
+
<span class="cline-any cline-neutral"> </span>
|
|
569
|
+
<span class="cline-any cline-neutral"> </span>
|
|
570
|
+
<span class="cline-any cline-neutral"> </span>
|
|
571
|
+
<span class="cline-any cline-neutral"> </span>
|
|
572
|
+
<span class="cline-any cline-neutral"> </span>
|
|
573
|
+
<span class="cline-any cline-yes">1x</span>
|
|
574
|
+
<span class="cline-any cline-no"> </span>
|
|
575
|
+
<span class="cline-any cline-no"> </span>
|
|
576
|
+
<span class="cline-any cline-no"> </span>
|
|
577
|
+
<span class="cline-any cline-no"> </span>
|
|
578
|
+
<span class="cline-any cline-no"> </span>
|
|
579
|
+
<span class="cline-any cline-no"> </span>
|
|
580
|
+
<span class="cline-any cline-no"> </span>
|
|
581
|
+
<span class="cline-any cline-no"> </span>
|
|
582
|
+
<span class="cline-any cline-no"> </span>
|
|
583
|
+
<span class="cline-any cline-neutral"> </span>
|
|
584
|
+
<span class="cline-any cline-neutral"> </span>
|
|
585
|
+
<span class="cline-any cline-no"> </span>
|
|
586
|
+
<span class="cline-any cline-no"> </span>
|
|
587
|
+
<span class="cline-any cline-no"> </span>
|
|
588
|
+
<span class="cline-any cline-no"> </span>
|
|
589
|
+
<span class="cline-any cline-no"> </span>
|
|
590
|
+
<span class="cline-any cline-no"> </span>
|
|
591
|
+
<span class="cline-any cline-no"> </span>
|
|
592
|
+
<span class="cline-any cline-no"> </span>
|
|
593
|
+
<span class="cline-any cline-no"> </span>
|
|
594
|
+
<span class="cline-any cline-no"> </span>
|
|
595
|
+
<span class="cline-any cline-no"> </span>
|
|
596
|
+
<span class="cline-any cline-no"> </span>
|
|
597
|
+
<span class="cline-any cline-no"> </span>
|
|
598
|
+
<span class="cline-any cline-no"> </span>
|
|
599
|
+
<span class="cline-any cline-no"> </span>
|
|
600
|
+
<span class="cline-any cline-neutral"> </span>
|
|
601
|
+
<span class="cline-any cline-no"> </span>
|
|
602
|
+
<span class="cline-any cline-neutral"> </span>
|
|
603
|
+
<span class="cline-any cline-no"> </span>
|
|
604
|
+
<span class="cline-any cline-neutral"> </span>
|
|
605
|
+
<span class="cline-any cline-neutral"> </span>
|
|
606
|
+
<span class="cline-any cline-no"> </span>
|
|
607
|
+
<span class="cline-any cline-no"> </span>
|
|
608
|
+
<span class="cline-any cline-no"> </span>
|
|
609
|
+
<span class="cline-any cline-no"> </span>
|
|
610
|
+
<span class="cline-any cline-no"> </span>
|
|
611
|
+
<span class="cline-any cline-neutral"> </span>
|
|
612
|
+
<span class="cline-any cline-neutral"> </span>
|
|
613
|
+
<span class="cline-any cline-no"> </span>
|
|
614
|
+
<span class="cline-any cline-no"> </span>
|
|
615
|
+
<span class="cline-any cline-no"> </span>
|
|
616
|
+
<span class="cline-any cline-neutral"> </span>
|
|
617
|
+
<span class="cline-any cline-no"> </span>
|
|
618
|
+
<span class="cline-any cline-neutral"> </span>
|
|
619
|
+
<span class="cline-any cline-no"> </span>
|
|
620
|
+
<span class="cline-any cline-no"> </span>
|
|
621
|
+
<span class="cline-any cline-no"> </span>
|
|
622
|
+
<span class="cline-any cline-no"> </span>
|
|
623
|
+
<span class="cline-any cline-neutral"> </span>
|
|
624
|
+
<span class="cline-any cline-no"> </span>
|
|
625
|
+
<span class="cline-any cline-no"> </span>
|
|
626
|
+
<span class="cline-any cline-no"> </span>
|
|
627
|
+
<span class="cline-any cline-no"> </span>
|
|
628
|
+
<span class="cline-any cline-no"> </span>
|
|
629
|
+
<span class="cline-any cline-no"> </span>
|
|
630
|
+
<span class="cline-any cline-no"> </span>
|
|
631
|
+
<span class="cline-any cline-no"> </span>
|
|
632
|
+
<span class="cline-any cline-no"> </span>
|
|
633
|
+
<span class="cline-any cline-no"> </span>
|
|
634
|
+
<span class="cline-any cline-no"> </span>
|
|
635
|
+
<span class="cline-any cline-no"> </span>
|
|
636
|
+
<span class="cline-any cline-no"> </span>
|
|
637
|
+
<span class="cline-any cline-neutral"> </span>
|
|
638
|
+
<span class="cline-any cline-no"> </span>
|
|
639
|
+
<span class="cline-any cline-no"> </span>
|
|
640
|
+
<span class="cline-any cline-neutral"> </span>
|
|
641
|
+
<span class="cline-any cline-neutral"> </span>
|
|
642
|
+
<span class="cline-any cline-neutral"> </span>
|
|
643
|
+
<span class="cline-any cline-neutral"> </span>
|
|
644
|
+
<span class="cline-any cline-neutral"> </span>
|
|
645
|
+
<span class="cline-any cline-neutral"> </span>
|
|
646
|
+
<span class="cline-any cline-neutral"> </span>
|
|
647
|
+
<span class="cline-any cline-neutral"> </span>
|
|
648
|
+
<span class="cline-any cline-yes">1x</span>
|
|
649
|
+
<span class="cline-any cline-no"> </span>
|
|
650
|
+
<span class="cline-any cline-no"> </span>
|
|
651
|
+
<span class="cline-any cline-neutral"> </span>
|
|
652
|
+
<span class="cline-any cline-no"> </span>
|
|
653
|
+
<span class="cline-any cline-no"> </span>
|
|
654
|
+
<span class="cline-any cline-no"> </span>
|
|
655
|
+
<span class="cline-any cline-no"> </span>
|
|
656
|
+
<span class="cline-any cline-neutral"> </span>
|
|
657
|
+
<span class="cline-any cline-neutral"> </span>
|
|
658
|
+
<span class="cline-any cline-neutral"> </span>
|
|
659
|
+
<span class="cline-any cline-neutral"> </span>
|
|
660
|
+
<span class="cline-any cline-neutral"> </span>
|
|
661
|
+
<span class="cline-any cline-neutral"> </span>
|
|
662
|
+
<span class="cline-any cline-yes">1x</span>
|
|
663
|
+
<span class="cline-any cline-no"> </span>
|
|
664
|
+
<span class="cline-any cline-no"> </span>
|
|
665
|
+
<span class="cline-any cline-no"> </span>
|
|
666
|
+
<span class="cline-any cline-no"> </span>
|
|
667
|
+
<span class="cline-any cline-no"> </span>
|
|
668
|
+
<span class="cline-any cline-no"> </span>
|
|
669
|
+
<span class="cline-any cline-no"> </span>
|
|
670
|
+
<span class="cline-any cline-neutral"> </span>
|
|
671
|
+
<span class="cline-any cline-neutral"> </span>
|
|
672
|
+
<span class="cline-any cline-neutral"> </span>
|
|
673
|
+
<span class="cline-any cline-no"> </span>
|
|
674
|
+
<span class="cline-any cline-no"> </span>
|
|
675
|
+
<span class="cline-any cline-no"> </span>
|
|
676
|
+
<span class="cline-any cline-no"> </span>
|
|
677
|
+
<span class="cline-any cline-no"> </span>
|
|
678
|
+
<span class="cline-any cline-no"> </span>
|
|
679
|
+
<span class="cline-any cline-neutral"> </span>
|
|
680
|
+
<span class="cline-any cline-no"> </span>
|
|
681
|
+
<span class="cline-any cline-no"> </span>
|
|
682
|
+
<span class="cline-any cline-no"> </span>
|
|
683
|
+
<span class="cline-any cline-no"> </span>
|
|
684
|
+
<span class="cline-any cline-neutral"> </span>
|
|
685
|
+
<span class="cline-any cline-yes">1x</span>
|
|
686
|
+
<span class="cline-any cline-no"> </span>
|
|
687
|
+
<span class="cline-any cline-no"> </span>
|
|
688
|
+
<span class="cline-any cline-no"> </span>
|
|
689
|
+
<span class="cline-any cline-no"> </span>
|
|
690
|
+
<span class="cline-any cline-no"> </span>
|
|
691
|
+
<span class="cline-any cline-no"> </span>
|
|
692
|
+
<span class="cline-any cline-no"> </span>
|
|
693
|
+
<span class="cline-any cline-no"> </span>
|
|
694
|
+
<span class="cline-any cline-neutral"> </span>
|
|
695
|
+
<span class="cline-any cline-yes">1x</span>
|
|
696
|
+
<span class="cline-any cline-no"> </span>
|
|
697
|
+
<span class="cline-any cline-no"> </span>
|
|
698
|
+
<span class="cline-any cline-no"> </span>
|
|
699
|
+
<span class="cline-any cline-no"> </span>
|
|
700
|
+
<span class="cline-any cline-neutral"> </span>
|
|
701
|
+
<span class="cline-any cline-neutral"> </span>
|
|
702
|
+
<span class="cline-any cline-neutral"> </span>
|
|
703
|
+
<span class="cline-any cline-neutral"> </span>
|
|
704
|
+
<span class="cline-any cline-neutral"> </span>
|
|
705
|
+
<span class="cline-any cline-neutral"> </span>
|
|
706
|
+
<span class="cline-any cline-neutral"> </span>
|
|
707
|
+
<span class="cline-any cline-neutral"> </span>
|
|
708
|
+
<span class="cline-any cline-neutral"> </span>
|
|
709
|
+
<span class="cline-any cline-neutral"> </span>
|
|
710
|
+
<span class="cline-any cline-neutral"> </span>
|
|
711
|
+
<span class="cline-any cline-neutral"> </span>
|
|
712
|
+
<span class="cline-any cline-yes">1x</span>
|
|
713
|
+
<span class="cline-any cline-no"> </span>
|
|
714
|
+
<span class="cline-any cline-no"> </span>
|
|
715
|
+
<span class="cline-any cline-no"> </span>
|
|
716
|
+
<span class="cline-any cline-no"> </span>
|
|
717
|
+
<span class="cline-any cline-no"> </span>
|
|
718
|
+
<span class="cline-any cline-no"> </span>
|
|
719
|
+
<span class="cline-any cline-no"> </span>
|
|
720
|
+
<span class="cline-any cline-no"> </span>
|
|
721
|
+
<span class="cline-any cline-no"> </span>
|
|
722
|
+
<span class="cline-any cline-no"> </span>
|
|
723
|
+
<span class="cline-any cline-no"> </span>
|
|
724
|
+
<span class="cline-any cline-no"> </span>
|
|
725
|
+
<span class="cline-any cline-no"> </span>
|
|
726
|
+
<span class="cline-any cline-neutral"> </span>
|
|
727
|
+
<span class="cline-any cline-no"> </span>
|
|
728
|
+
<span class="cline-any cline-no"> </span>
|
|
729
|
+
<span class="cline-any cline-no"> </span>
|
|
730
|
+
<span class="cline-any cline-neutral"> </span>
|
|
731
|
+
<span class="cline-any cline-no"> </span>
|
|
732
|
+
<span class="cline-any cline-no"> </span>
|
|
733
|
+
<span class="cline-any cline-no"> </span>
|
|
734
|
+
<span class="cline-any cline-no"> </span>
|
|
735
|
+
<span class="cline-any cline-no"> </span>
|
|
736
|
+
<span class="cline-any cline-no"> </span>
|
|
737
|
+
<span class="cline-any cline-yes">1x</span>
|
|
738
|
+
<span class="cline-any cline-neutral"> </span>
|
|
739
|
+
<span class="cline-any cline-yes">1x</span>
|
|
740
|
+
<span class="cline-any cline-neutral"> </span>
|
|
741
|
+
<span class="cline-any cline-yes">1x</span>
|
|
742
|
+
<span class="cline-any cline-neutral"> </span>
|
|
743
|
+
<span class="cline-any cline-no"> </span>
|
|
744
|
+
<span class="cline-any cline-no"> </span>
|
|
745
|
+
<span class="cline-any cline-no"> </span>
|
|
746
|
+
<span class="cline-any cline-neutral"> </span>
|
|
747
|
+
<span class="cline-any cline-neutral"> </span>
|
|
748
|
+
<span class="cline-any cline-neutral"> </span>
|
|
749
|
+
<span class="cline-any cline-neutral"> </span>
|
|
750
|
+
<span class="cline-any cline-neutral"> </span>
|
|
751
|
+
<span class="cline-any cline-neutral"> </span>
|
|
752
|
+
<span class="cline-any cline-neutral"> </span>
|
|
753
|
+
<span class="cline-any cline-neutral"> </span>
|
|
754
|
+
<span class="cline-any cline-no"> </span>
|
|
755
|
+
<span class="cline-any cline-no"> </span>
|
|
756
|
+
<span class="cline-any cline-no"> </span>
|
|
757
|
+
<span class="cline-any cline-no"> </span>
|
|
758
|
+
<span class="cline-any cline-no"> </span>
|
|
759
|
+
<span class="cline-any cline-no"> </span>
|
|
760
|
+
<span class="cline-any cline-no"> </span>
|
|
761
|
+
<span class="cline-any cline-no"> </span>
|
|
762
|
+
<span class="cline-any cline-no"> </span>
|
|
763
|
+
<span class="cline-any cline-no"> </span>
|
|
764
|
+
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
|
765
|
+
* Feature flag administration for the DevTools panel.
|
|
766
|
+
*
|
|
767
|
+
* Two independent capabilities, deliberately kept apart:
|
|
768
|
+
*
|
|
769
|
+
* - **Local overrides** force a variant in THIS browser. No auth, no network,
|
|
770
|
+
* works signed out and offline. Delegated straight to `FeatureFlagModule`.
|
|
771
|
+
* - **Remote changes** flip a flag for EVERY user. These require the caller to
|
|
772
|
+
* be listed in `_00_admin` (`spky admin add <user>`); SurrealDB enforces
|
|
773
|
+
* that, not this file. A non-admin's SELECT returns `[]` rather than an
|
|
774
|
+
* error, and the `fn::feature::*` calls are hard-denied.
|
|
775
|
+
*
|
|
776
|
+
* Everything remote goes through `RemoteDatabaseService` with BOUND params.
|
|
777
|
+
* The panel's generic `runQuery` bridge is not usable here: it interpolates
|
|
778
|
+
* the SurrealQL into an `inspectedWindow.eval` string (so a DB-sourced flag
|
|
779
|
+
* key or record id would be concatenated into code) and it rejects after 10s,
|
|
780
|
+
* which a materialize over a real user table will exceed.
|
|
781
|
+
*
|
|
782
|
+
* Definitions live in `_00_feature_flag`, which is NOT synced to clients, so
|
|
783
|
+
* flags must be read remotely. Per-user assignments live in `_00_user_feature`,
|
|
784
|
+
* which IS live-synced, so those come from the local store for free.
|
|
785
|
+
*/
|
|
786
|
+
|
|
787
|
+
import type { Logger } from '../../services/logger/index';
|
|
788
|
+
import { parseRecordIdString } from '../../utils/index';
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* The only thing this service needs from a database. Structural rather than
|
|
792
|
+
* `AbstractDatabaseService`, because the local side is a `LocalStore` (which
|
|
793
|
+
* wraps a database rather than extending it) and both must satisfy it.
|
|
794
|
+
*/
|
|
795
|
+
export interface FlagQueryable {
|
|
796
|
+
query<T extends unknown[]>(query: string, vars?: Record<string, unknown>): Promise<T>;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** A targeting rule as written by `spky flag` / `fn::feature::allow`. */
|
|
800
|
+
export interface DevToolsFlagRule {
|
|
801
|
+
kind: 'allowlist' | 'rollout' | string;
|
|
802
|
+
variant: string;
|
|
803
|
+
/** Allowlist only. Stored as record-id STRINGS, not records. */
|
|
804
|
+
users?: string[];
|
|
805
|
+
/** Rollout only, 0..100. */
|
|
806
|
+
percent?: number;
|
|
807
|
+
priority: number;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export interface DevToolsFlagRow {
|
|
811
|
+
key: string;
|
|
812
|
+
description?: string;
|
|
813
|
+
variants: string[];
|
|
814
|
+
default_variant: string;
|
|
815
|
+
enabled: boolean;
|
|
816
|
+
payloads?: Record<string, unknown>;
|
|
817
|
+
rules: DevToolsFlagRule[];
|
|
818
|
+
updated_at?: string;
|
|
819
|
+
/** Derived: the variant the signed-in user is allowlisted into, if any. */
|
|
820
|
+
selfAllowlistedVariant?: string;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export interface DevToolsFlagAssignment {
|
|
824
|
+
key: string;
|
|
825
|
+
variant: string;
|
|
826
|
+
payload?: unknown;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
export interface DevToolsFlagOverride {
|
|
830
|
+
variant: string;
|
|
831
|
+
payload?: unknown;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export interface DevToolsFlagsSnapshot {
|
|
835
|
+
at: number;
|
|
836
|
+
/** Null when signed out. */
|
|
837
|
+
userId: string | null;
|
|
838
|
+
/** True when `_00_admin` has a row for the signed-in user. */
|
|
839
|
+
isAdmin: boolean;
|
|
840
|
+
/** Empty for non-admins — SurrealDB filters the rows, it does not error. */
|
|
841
|
+
flags: DevToolsFlagRow[];
|
|
842
|
+
/** From the LOCAL `_00_user_feature`: what this browser actually resolves. */
|
|
843
|
+
assignments: DevToolsFlagAssignment[];
|
|
844
|
+
/** Local-only forced variants, page-origin localStorage. */
|
|
845
|
+
overrides: Record<string, DevToolsFlagOverride>;
|
|
846
|
+
/**
|
|
847
|
+
* Set when the internal schema predates this feature (no `_00_admin` table),
|
|
848
|
+
* or the remote read failed. The panel shows it instead of a bare empty
|
|
849
|
+
* state, so "not migrated" doesn't look like "you're not an admin".
|
|
850
|
+
*/
|
|
851
|
+
error?: string;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
export interface DevToolsFlagResult {
|
|
855
|
+
success: boolean;
|
|
856
|
+
error?: string;
|
|
857
|
+
/** Users re-evaluated by `fn::feature::materialize`. */
|
|
858
|
+
users?: number;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/** The slice of `FeatureFlagModule` this service needs. */
|
|
862
|
+
export interface LocalOverrideStore {
|
|
863
|
+
setLocalOverride(key: string, variant: string | null, payload?: unknown): void;
|
|
864
|
+
clearLocalOverrides(): void;
|
|
865
|
+
getLocalOverrides(): Record<string, DevToolsFlagOverride>;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export interface FlagsAdminDeps {
|
|
869
|
+
remote: FlagQueryable;
|
|
870
|
+
local: FlagQueryable;
|
|
871
|
+
logger: Logger;
|
|
872
|
+
/** Resolves the signed-in user's record id, or null. */
|
|
873
|
+
currentUserId: () => string | null;
|
|
874
|
+
/** Set once the client has built its FeatureFlagModule. */
|
|
875
|
+
overrides: () => LocalOverrideStore | null;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* SurrealDB's SDK returns one entry per statement; each entry is the rows for
|
|
880
|
+
* that statement, but older/local shapes wrap them as `{ status, result }`.
|
|
881
|
+
* `getTableData` in `index.ts` unwraps the same three shapes inline — this is
|
|
882
|
+
* that logic, reusable.
|
|
883
|
+
*/
|
|
884
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function statementRows(result: unknown, index = 0): unknown[] {</span></span>
|
|
885
|
+
<span class="cstat-no" title="statement not covered" > if (!Array.isArray(result)) return [];</span>
|
|
886
|
+
<span class="cstat-no" title="statement not covered" > const entry = result[index];</span>
|
|
887
|
+
<span class="cstat-no" title="statement not covered" > if (Array.isArray(entry)) return entry;</span>
|
|
888
|
+
<span class="cstat-no" title="statement not covered" > if (entry && typeof entry === 'object' && 'result' in entry) {</span>
|
|
889
|
+
<span class="cstat-no" title="statement not covered" > const inner = (entry as { result?: unknown }).result;</span>
|
|
890
|
+
<span class="cstat-no" title="statement not covered" > return Array.isArray(inner) ? inner : inner === undefined || inner === null ? [] : [inner];</span>
|
|
891
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
892
|
+
<span class="cstat-no" title="statement not covered" > return entry === undefined || entry === null ? [] : [entry];</span>
|
|
893
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
894
|
+
|
|
895
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function message(err: unknown): string {</span></span>
|
|
896
|
+
<span class="cstat-no" title="statement not covered" > return err instanceof Error ? err.message : String(err);</span>
|
|
897
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
898
|
+
|
|
899
|
+
/** Row shape guards. `key` is the only field the panel truly can't do without. */
|
|
900
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function hasStringKey(row: unknown): row is { key: string } {</span></span>
|
|
901
|
+
<span class="cstat-no" title="statement not covered" > return !!row && typeof row === 'object' && typeof (row as { key?: unknown }).key === 'string';</span>
|
|
902
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
903
|
+
|
|
904
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function isAssignment(row: unknown): row is DevToolsFlagAssignment {</span></span>
|
|
905
|
+
<span class="cstat-no" title="statement not covered" > return hasStringKey(row);</span>
|
|
906
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
907
|
+
|
|
908
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function isFlagRow(row: unknown): row is DevToolsFlagRow {</span></span>
|
|
909
|
+
<span class="cstat-no" title="statement not covered" > return hasStringKey(row);</span>
|
|
910
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
911
|
+
|
|
912
|
+
export class FlagsAdminService {
|
|
913
|
+
constructor(private deps: FlagsAdminDeps) {}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Everything the Access tab renders, in one round trip per source.
|
|
917
|
+
*
|
|
918
|
+
* Each section fails independently: a remote read that throws downgrades to
|
|
919
|
+
* `isAdmin: false` plus an `error`, while local assignments and overrides
|
|
920
|
+
* still render. Signing out must not blank the whole tab.
|
|
921
|
+
*/
|
|
922
|
+
<span class="fstat-no" title="function not covered" > async getFlags(): Promise<DevToolsFlagsSnapshot> {</span>
|
|
923
|
+
<span class="cstat-no" title="statement not covered" > const userId = this.deps.currentUserId();</span>
|
|
924
|
+
<span class="cstat-no" title="statement not covered" > const snapshot: DevToolsFlagsSnapshot = {</span>
|
|
925
|
+
<span class="cstat-no" title="statement not covered" > at: Date.now(),</span>
|
|
926
|
+
<span class="cstat-no" title="statement not covered" > userId,</span>
|
|
927
|
+
<span class="cstat-no" title="statement not covered" > isAdmin: false,</span>
|
|
928
|
+
<span class="cstat-no" title="statement not covered" > flags: [],</span>
|
|
929
|
+
<span class="cstat-no" title="statement not covered" > assignments: [],</span>
|
|
930
|
+
<span class="cstat-no" title="statement not covered" > overrides: this.deps.overrides()?.getLocalOverrides() ?? {},</span>
|
|
931
|
+
<span class="cstat-no" title="statement not covered" > };</span>
|
|
932
|
+
|
|
933
|
+
// Local assignments: available signed out and offline.
|
|
934
|
+
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
935
|
+
<span class="cstat-no" title="statement not covered" > const rows = statementRows(</span>
|
|
936
|
+
<span class="cstat-no" title="statement not covered" > await this.deps.local.query('SELECT key, variant, payload FROM _00_user_feature')</span>
|
|
937
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
938
|
+
<span class="cstat-no" title="statement not covered" > snapshot.assignments = rows.filter(isAssignment).map((r) => ({</span>
|
|
939
|
+
<span class="cstat-no" title="statement not covered" > key: r.key,</span>
|
|
940
|
+
<span class="cstat-no" title="statement not covered" > variant: r.variant,</span>
|
|
941
|
+
<span class="cstat-no" title="statement not covered" > payload: r.payload,</span>
|
|
942
|
+
<span class="cstat-no" title="statement not covered" > }));</span>
|
|
943
|
+
<span class="cstat-no" title="statement not covered" > } catch (err) {</span>
|
|
944
|
+
<span class="cstat-no" title="statement not covered" > this.deps.logger.debug(</span>
|
|
945
|
+
<span class="cstat-no" title="statement not covered" > { err, Category: 'sp00ky-client::FlagsAdminService::getFlags' },</span>
|
|
946
|
+
<span class="cstat-no" title="statement not covered" > 'Local feature assignments unavailable'</span>
|
|
947
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
948
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
949
|
+
|
|
950
|
+
<span class="cstat-no" title="statement not covered" > if (!userId) return snapshot;</span>
|
|
951
|
+
|
|
952
|
+
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
953
|
+
// Self-scoped by `_00_admin`'s own select rule: an admin sees exactly
|
|
954
|
+
// their own row, a non-admin sees nothing. The roster never leaks.
|
|
955
|
+
<span class="cstat-no" title="statement not covered" > const admin = statementRows(</span>
|
|
956
|
+
<span class="cstat-no" title="statement not covered" > await this.deps.remote.query('SELECT VALUE id FROM _00_admin WHERE user = $auth.id LIMIT 1')</span>
|
|
957
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
958
|
+
<span class="cstat-no" title="statement not covered" > snapshot.isAdmin = admin.length > 0;</span>
|
|
959
|
+
<span class="cstat-no" title="statement not covered" > } catch (err) {</span>
|
|
960
|
+
// A missing `_00_admin` table means the deployment hasn't applied the
|
|
961
|
+
// internal schema yet. Say so — otherwise it reads as "not an admin".
|
|
962
|
+
<span class="cstat-no" title="statement not covered" > snapshot.error = `Could not check admin status: ${message(err)}. If this deployment predates the Access tab, run \`spky migrate\` (or redeploy) to apply the internal schema.`;</span>
|
|
963
|
+
<span class="cstat-no" title="statement not covered" > return snapshot;</span>
|
|
964
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
965
|
+
|
|
966
|
+
<span class="cstat-no" title="statement not covered" > if (!snapshot.isAdmin) return snapshot;</span>
|
|
967
|
+
|
|
968
|
+
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
969
|
+
<span class="cstat-no" title="statement not covered" > const rows = statementRows(</span>
|
|
970
|
+
<span class="cstat-no" title="statement not covered" > await this.deps.remote.query(</span>
|
|
971
|
+
<span class="cstat-no" title="statement not covered" > 'SELECT key, description, variants, default_variant, enabled, payloads, rules, updated_at ' +</span>
|
|
972
|
+
'FROM _00_feature_flag ORDER BY key ASC'
|
|
973
|
+
<span class="cstat-no" title="statement not covered" > )</span>
|
|
974
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
975
|
+
<span class="cstat-no" title="statement not covered" > snapshot.flags = rows</span>
|
|
976
|
+
<span class="cstat-no" title="statement not covered" > .filter(isFlagRow)</span>
|
|
977
|
+
<span class="cstat-no" title="statement not covered" > .map((flag) => ({</span>
|
|
978
|
+
<span class="cstat-no" title="statement not covered" > ...flag,</span>
|
|
979
|
+
<span class="cstat-no" title="statement not covered" > rules: Array.isArray(flag.rules) ? flag.rules : [],</span>
|
|
980
|
+
<span class="cstat-no" title="statement not covered" > variants: Array.isArray(flag.variants) ? flag.variants : [],</span>
|
|
981
|
+
<span class="cstat-no" title="statement not covered" > selfAllowlistedVariant: selfAllowlistedVariant(flag, userId),</span>
|
|
982
|
+
<span class="cstat-no" title="statement not covered" > }));</span>
|
|
983
|
+
<span class="cstat-no" title="statement not covered" > } catch (err) {</span>
|
|
984
|
+
<span class="cstat-no" title="statement not covered" > snapshot.error = `Could not read feature flags: ${message(err)}`;</span>
|
|
985
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
986
|
+
|
|
987
|
+
<span class="cstat-no" title="statement not covered" > return snapshot;</span>
|
|
988
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Flip a flag's global `enabled` bit for EVERY user, then re-materialize.
|
|
992
|
+
*
|
|
993
|
+
* Both statements are one request so they share a transaction: if the
|
|
994
|
+
* materialize fails, the `enabled` change rolls back rather than leaving the
|
|
995
|
+
* definition and the assignments disagreeing.
|
|
996
|
+
*/
|
|
997
|
+
<span class="fstat-no" title="function not covered" > async setFlagEnabled(key: string, enabled: boolean): Promise<DevToolsFlagResult> {</span>
|
|
998
|
+
<span class="cstat-no" title="statement not covered" > return this.mutate(</span>
|
|
999
|
+
<span class="cstat-no" title="statement not covered" > 'UPDATE _00_feature_flag SET enabled = $enabled WHERE key = $key; ' +</span>
|
|
1000
|
+
'RETURN fn::feature::materialize($key);',
|
|
1001
|
+
<span class="cstat-no" title="statement not covered" > { key, enabled },</span>
|
|
1002
|
+
<span class="cstat-no" title="statement not covered" > 1</span>
|
|
1003
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1004
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Add or remove a user from `$key`'s allowlist for `$variant`, then
|
|
1008
|
+
* re-materialize. Defaults to the signed-in user, so the common case
|
|
1009
|
+
* ("turn this on for me, for real") needs no user picker.
|
|
1010
|
+
*/
|
|
1011
|
+
<span class="fstat-no" title="function not covered" > async setFlagUserVariant(</span>
|
|
1012
|
+
<span class="cstat-no" title="statement not covered" > key: string,</span>
|
|
1013
|
+
<span class="cstat-no" title="statement not covered" > variant: string,</span>
|
|
1014
|
+
<span class="cstat-no" title="statement not covered" > remove: boolean,</span>
|
|
1015
|
+
<span class="cstat-no" title="statement not covered" > userId?: string</span>
|
|
1016
|
+
<span class="cstat-no" title="statement not covered" > ): Promise<DevToolsFlagResult> {</span>
|
|
1017
|
+
<span class="cstat-no" title="statement not covered" > const target = userId ?? this.deps.currentUserId();</span>
|
|
1018
|
+
<span class="cstat-no" title="statement not covered" > if (!target) return { success: false, error: 'Not signed in' };</span>
|
|
1019
|
+
|
|
1020
|
+
// `fn::feature::allow/disallow` declare `$user: record`. A string binding
|
|
1021
|
+
// is rejected by the type check, so parse it into a real RecordId.
|
|
1022
|
+
<span class="cstat-no" title="statement not covered" > let user;</span>
|
|
1023
|
+
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
1024
|
+
<span class="cstat-no" title="statement not covered" > user = parseRecordIdString(target);</span>
|
|
1025
|
+
<span class="cstat-no" title="statement not covered" > } catch (err) {</span>
|
|
1026
|
+
<span class="cstat-no" title="statement not covered" > return { success: false, error: `Invalid user id '${target}': ${message(err)}` };</span>
|
|
1027
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1028
|
+
|
|
1029
|
+
<span class="cstat-no" title="statement not covered" > return remove</span>
|
|
1030
|
+
<span class="cstat-no" title="statement not covered" > ? this.mutate('RETURN fn::feature::disallow($key, $user);', { key, user }, 0)</span>
|
|
1031
|
+
<span class="cstat-no" title="statement not covered" > : this.mutate('RETURN fn::feature::allow($key, $variant, $user);', { key, variant, user }, 0);</span>
|
|
1032
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1033
|
+
|
|
1034
|
+
<span class="fstat-no" title="function not covered" > setLocalFlagOverride(</span>
|
|
1035
|
+
<span class="cstat-no" title="statement not covered" > key: string,</span>
|
|
1036
|
+
<span class="cstat-no" title="statement not covered" > variant: string | null,</span>
|
|
1037
|
+
<span class="cstat-no" title="statement not covered" > payload?: unknown</span>
|
|
1038
|
+
<span class="cstat-no" title="statement not covered" > ): { overrides: Record<string, DevToolsFlagOverride> } {</span>
|
|
1039
|
+
<span class="cstat-no" title="statement not covered" > const store = this.deps.overrides();</span>
|
|
1040
|
+
<span class="cstat-no" title="statement not covered" > store?.setLocalOverride(key, variant, payload);</span>
|
|
1041
|
+
<span class="cstat-no" title="statement not covered" > return { overrides: store?.getLocalOverrides() ?? {} };</span>
|
|
1042
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1043
|
+
|
|
1044
|
+
<span class="fstat-no" title="function not covered" > clearLocalFlagOverrides(): { overrides: Record<string, DevToolsFlagOverride> } {</span>
|
|
1045
|
+
<span class="cstat-no" title="statement not covered" > const store = this.deps.overrides();</span>
|
|
1046
|
+
<span class="cstat-no" title="statement not covered" > store?.clearLocalOverrides();</span>
|
|
1047
|
+
<span class="cstat-no" title="statement not covered" > return { overrides: store?.getLocalOverrides() ?? {} };</span>
|
|
1048
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Run a remote mutation, reporting the materialize count from `$index`.
|
|
1052
|
+
*
|
|
1053
|
+
* Retries on a transaction conflict. `fn::feature::allow` / `disallow` are
|
|
1054
|
+
* read-modify-write over `_00_feature_flag.rules`, so two admins acting on
|
|
1055
|
+
* the same flag at once collide. SurrealDB detects this and fails the loser
|
|
1056
|
+
* with "Transaction conflict ... can be retried" rather than losing the
|
|
1057
|
+
* write — verified against 3.1 — so nothing is silently dropped. Retrying
|
|
1058
|
+
* turns that into the outcome the user expected instead of a raw engine
|
|
1059
|
+
* error they can do nothing with.
|
|
1060
|
+
*/
|
|
1061
|
+
<span class="fstat-no" title="function not covered" > private async mutate(</span>
|
|
1062
|
+
<span class="cstat-no" title="statement not covered" > sql: string,</span>
|
|
1063
|
+
<span class="cstat-no" title="statement not covered" > vars: Record<string, unknown>,</span>
|
|
1064
|
+
<span class="cstat-no" title="statement not covered" > index: number</span>
|
|
1065
|
+
<span class="cstat-no" title="statement not covered" > ): Promise<DevToolsFlagResult> {</span>
|
|
1066
|
+
<span class="cstat-no" title="statement not covered" > let lastError: unknown;</span>
|
|
1067
|
+
<span class="cstat-no" title="statement not covered" > for (let attempt = 0; attempt < MUTATE_ATTEMPTS; attempt++) {</span>
|
|
1068
|
+
<span class="cstat-no" title="statement not covered" > try {</span>
|
|
1069
|
+
<span class="cstat-no" title="statement not covered" > const result = await this.deps.remote.query(sql, vars);</span>
|
|
1070
|
+
<span class="cstat-no" title="statement not covered" > const materialized = statementRows(result, index)[0] as { users?: number } | undefined;</span>
|
|
1071
|
+
<span class="cstat-no" title="statement not covered" > return { success: true, users: materialized?.users };</span>
|
|
1072
|
+
<span class="cstat-no" title="statement not covered" > } catch (err) {</span>
|
|
1073
|
+
<span class="cstat-no" title="statement not covered" > lastError = err;</span>
|
|
1074
|
+
<span class="cstat-no" title="statement not covered" > if (!isRetryableConflict(err) || attempt === MUTATE_ATTEMPTS - 1) break;</span>
|
|
1075
|
+
// Staggered so two collided clients don't line up again on the retry.
|
|
1076
|
+
<span class="cstat-no" title="statement not covered" > await sleep(40 * (attempt + 1) + Math.random() * 40);</span>
|
|
1077
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1078
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1079
|
+
|
|
1080
|
+
<span class="cstat-no" title="statement not covered" > this.deps.logger.warn(</span>
|
|
1081
|
+
<span class="cstat-no" title="statement not covered" > { err: lastError, sql, Category: 'sp00ky-client::FlagsAdminService::mutate' },</span>
|
|
1082
|
+
<span class="cstat-no" title="statement not covered" > 'Feature flag mutation failed'</span>
|
|
1083
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1084
|
+
<span class="cstat-no" title="statement not covered" > return { success: false, error: message(lastError) };</span>
|
|
1085
|
+
<span class="cstat-no" title="statement not covered" > }</span>
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const MUTATE_ATTEMPTS = 3;
|
|
1089
|
+
|
|
1090
|
+
const sleep = <span class="fstat-no" title="function not covered" >(ms: number) => new Promise((resolve) => setTimeout(resolve, ms));</span>
|
|
1091
|
+
|
|
1092
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function isRetryableConflict(err: unknown): boolean {</span></span>
|
|
1093
|
+
<span class="cstat-no" title="statement not covered" > return /transaction (write )?conflict/i.test(message(err));</span>
|
|
1094
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Which variant, if any, this user is explicitly allowlisted into.
|
|
1098
|
+
*
|
|
1099
|
+
* `rules[].users` holds record-id strings (`flag.rs` serialises them as JSON),
|
|
1100
|
+
* so compare as strings. Lets the panel show "you're on the list" without
|
|
1101
|
+
* making the user reason about a raw rules blob.
|
|
1102
|
+
*/
|
|
1103
|
+
<span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >function selfAllowlistedVariant(flag: DevToolsFlagRow, userId: string): string | undefined {</span></span>
|
|
1104
|
+
<span class="cstat-no" title="statement not covered" > const rules = Array.isArray(flag.rules) ? flag.rules : [];</span>
|
|
1105
|
+
<span class="cstat-no" title="statement not covered" > const hit = rules.find(</span>
|
|
1106
|
+
<span class="cstat-no" title="statement not covered" > (rule) =></span>
|
|
1107
|
+
<span class="cstat-no" title="statement not covered" > rule?.kind === 'allowlist' &&</span>
|
|
1108
|
+
<span class="cstat-no" title="statement not covered" > Array.isArray(rule.users) &&</span>
|
|
1109
|
+
<span class="cstat-no" title="statement not covered" > rule.users.some((u) => String(u) === userId)</span>
|
|
1110
|
+
<span class="cstat-no" title="statement not covered" > );</span>
|
|
1111
|
+
<span class="cstat-no" title="statement not covered" > return hit?.variant;</span>
|
|
1112
|
+
<span class="cstat-no" title="statement not covered" >}</span>
|
|
1113
|
+
</pre></td></tr></table></pre>
|
|
1114
|
+
|
|
1115
|
+
<div class='push'></div><!-- for sticky footer -->
|
|
1116
|
+
</div><!-- /wrapper -->
|
|
1117
|
+
<div class='footer quiet pad2 space-top1 center small'>
|
|
1118
|
+
Code coverage generated by
|
|
1119
|
+
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
|
|
1120
|
+
at 2026-09-09T09:49:51.780Z
|
|
1121
|
+
</div>
|
|
1122
|
+
<script src="../../../prettify.js"></script>
|
|
1123
|
+
<script>
|
|
1124
|
+
window.onload = function () {
|
|
1125
|
+
prettyPrint();
|
|
1126
|
+
};
|
|
1127
|
+
</script>
|
|
1128
|
+
<script src="../../../sorter.js"></script>
|
|
1129
|
+
<script src="../../../block-navigation.js"></script>
|
|
1130
|
+
</body>
|
|
1131
|
+
</html>
|
|
1132
|
+
|