@ordergroove/smi-serve 1.11.0 → 1.12.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.12.0](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.11.0...@ordergroove/smi-serve@1.12.0) (2026-02-12)
7
+
8
+
9
+ ### Features
10
+
11
+ * automatically refresh customer auth when impersonating ([32f09ac](https://github.com/ordergroove/plush-toys/commit/32f09acc58c0c1415130d5f7355b8b39d87fb095))
12
+
13
+
14
+
15
+
16
+
6
17
  # [1.11.0](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.10.1...@ordergroove/smi-serve@1.11.0) (2026-02-11)
7
18
 
8
19
 
@@ -1,12 +1,118 @@
1
1
  <?xml version="1.0" ?>
2
2
  <!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
3
- <coverage lines-valid="570" lines-covered="218" line-rate="0.3824" branches-valid="218" branches-covered="45" branch-rate="0.2064" timestamp="1770835493470" complexity="0" version="0.1">
3
+ <coverage lines-valid="646" lines-covered="228" line-rate="0.3529" branches-valid="233" branches-covered="45" branch-rate="0.1931" timestamp="1770854860430" complexity="0" version="0.1">
4
4
  <sources>
5
5
  <source>/home/jenkins/agent/workspace/Ordergroove_plush-toys_master/plush-toys/packages/smi-serve</source>
6
6
  </sources>
7
7
  <packages>
8
- <package name="smi-serve" line-rate="0.4067" branch-rate="0.2368">
8
+ <package name="smi-serve" line-rate="0.3725" branch-rate="0.2195">
9
9
  <classes>
10
+ <class name="auth-monitor.js" filename="src/auth-monitor.js" line-rate="0.12" branch-rate="0">
11
+ <methods>
12
+ <method name="(anonymous_0)" hits="0" signature="()V">
13
+ <lines>
14
+ <line number="11" hits="0"/>
15
+ </lines>
16
+ </method>
17
+ <method name="(anonymous_1)" hits="0" signature="()V">
18
+ <lines>
19
+ <line number="27" hits="0"/>
20
+ </lines>
21
+ </method>
22
+ <method name="(anonymous_2)" hits="0" signature="()V">
23
+ <lines>
24
+ <line number="28" hits="0"/>
25
+ </lines>
26
+ </method>
27
+ <method name="(anonymous_3)" hits="0" signature="()V">
28
+ <lines>
29
+ <line number="40" hits="0"/>
30
+ </lines>
31
+ </method>
32
+ <method name="(anonymous_4)" hits="0" signature="()V">
33
+ <lines>
34
+ <line number="87" hits="0"/>
35
+ </lines>
36
+ </method>
37
+ <method name="(anonymous_5)" hits="0" signature="()V">
38
+ <lines>
39
+ <line number="88" hits="0"/>
40
+ </lines>
41
+ </method>
42
+ <method name="(anonymous_6)" hits="0" signature="()V">
43
+ <lines>
44
+ <line number="93" hits="0"/>
45
+ </lines>
46
+ </method>
47
+ <method name="(anonymous_7)" hits="0" signature="()V">
48
+ <lines>
49
+ <line number="103" hits="0"/>
50
+ </lines>
51
+ </method>
52
+ <method name="(anonymous_8)" hits="0" signature="()V">
53
+ <lines>
54
+ <line number="111" hits="0"/>
55
+ </lines>
56
+ </method>
57
+ <method name="parseJwtExpiry" hits="0" signature="()V">
58
+ <lines>
59
+ <line number="124" hits="0"/>
60
+ </lines>
61
+ </method>
62
+ </methods>
63
+ <lines>
64
+ <line number="1" hits="1" branch="false"/>
65
+ <line number="2" hits="1" branch="false"/>
66
+ <line number="3" hits="1" branch="false"/>
67
+ <line number="4" hits="1" branch="false"/>
68
+ <line number="5" hits="1" branch="false"/>
69
+ <line number="12" hits="0" branch="false"/>
70
+ <line number="13" hits="0" branch="false"/>
71
+ <line number="14" hits="0" branch="false"/>
72
+ <line number="15" hits="0" branch="false"/>
73
+ <line number="16" hits="0" branch="false"/>
74
+ <line number="17" hits="0" branch="false"/>
75
+ <line number="18" hits="0" branch="false"/>
76
+ <line number="19" hits="0" branch="false"/>
77
+ <line number="20" hits="0" branch="false"/>
78
+ <line number="21" hits="0" branch="false"/>
79
+ <line number="28" hits="0" branch="false"/>
80
+ <line number="29" hits="0" branch="false"/>
81
+ <line number="31" hits="0" branch="true" condition-coverage="0% (0/4)"/>
82
+ <line number="32" hits="0" branch="false"/>
83
+ <line number="41" hits="0" branch="false"/>
84
+ <line number="44" hits="0" branch="false"/>
85
+ <line number="45" hits="0" branch="false"/>
86
+ <line number="47" hits="0" branch="false"/>
87
+ <line number="49" hits="0" branch="false"/>
88
+ <line number="51" hits="0" branch="false"/>
89
+ <line number="54" hits="0" branch="false"/>
90
+ <line number="57" hits="0" branch="false"/>
91
+ <line number="58" hits="0" branch="false"/>
92
+ <line number="61" hits="0" branch="false"/>
93
+ <line number="69" hits="0" branch="false"/>
94
+ <line number="72" hits="0" branch="false"/>
95
+ <line number="75" hits="0" branch="false"/>
96
+ <line number="76" hits="0" branch="false"/>
97
+ <line number="77" hits="0" branch="false"/>
98
+ <line number="79" hits="0" branch="false"/>
99
+ <line number="80" hits="0" branch="false"/>
100
+ <line number="88" hits="0" branch="false"/>
101
+ <line number="89" hits="0" branch="false"/>
102
+ <line number="93" hits="0" branch="false"/>
103
+ <line number="94" hits="0" branch="false"/>
104
+ <line number="95" hits="0" branch="false"/>
105
+ <line number="104" hits="0" branch="false"/>
106
+ <line number="105" hits="0" branch="false"/>
107
+ <line number="112" hits="0" branch="true" condition-coverage="0% (0/2)"/>
108
+ <line number="113" hits="0" branch="false"/>
109
+ <line number="114" hits="0" branch="false"/>
110
+ <line number="125" hits="0" branch="false"/>
111
+ <line number="126" hits="0" branch="false"/>
112
+ <line number="127" hits="0" branch="false"/>
113
+ <line number="130" hits="1" branch="false"/>
114
+ </lines>
115
+ </class>
10
116
  <class name="auth.js" filename="src/auth.js" line-rate="0.9333" branch-rate="0.8332999999999999">
11
117
  <methods>
12
118
  <method name="getValidLoginAndCurrentMerchant" hits="11" signature="()V">
@@ -33,56 +139,79 @@
33
139
  <line number="30" hits="2" branch="false"/>
34
140
  </lines>
35
141
  </class>
36
- <class name="impersonate.js" filename="src/impersonate.js" line-rate="0.1666" branch-rate="0">
142
+ <class name="hmac-refresh.js" filename="src/hmac-refresh.js" line-rate="0.2857" branch-rate="0">
37
143
  <methods>
38
- <method name="tryToLoadSelectedCustomer" hits="0" signature="()V">
144
+ <method name="refreshHmacAuth" hits="0" signature="()V">
39
145
  <lines>
40
- <line number="9" hits="0"/>
146
+ <line number="12" hits="0"/>
41
147
  </lines>
42
148
  </method>
43
- <method name="impersonate" hits="0" signature="()V">
149
+ </methods>
150
+ <lines>
151
+ <line number="1" hits="1" branch="false"/>
152
+ <line number="14" hits="0" branch="false"/>
153
+ <line number="22" hits="0" branch="true" condition-coverage="0% (0/4)"/>
154
+ <line number="23" hits="0" branch="false"/>
155
+ <line number="26" hits="0" branch="false"/>
156
+ <line number="29" hits="0" branch="false"/>
157
+ <line number="35" hits="1" branch="false"/>
158
+ </lines>
159
+ </class>
160
+ <class name="impersonate.js" filename="src/impersonate.js" line-rate="0.1746" branch-rate="0">
161
+ <methods>
162
+ <method name="searchCustomers" hits="0" signature="()V">
44
163
  <lines>
45
- <line number="21" hits="0"/>
164
+ <line number="19" hits="0"/>
46
165
  </lines>
47
166
  </method>
48
- <method name="(anonymous_2)" hits="0" signature="()V">
167
+ <method name="tryToLoadSelectedCustomer" hits="0" signature="()V">
49
168
  <lines>
50
- <line number="27" hits="0"/>
169
+ <line number="40" hits="0"/>
170
+ </lines>
171
+ </method>
172
+ <method name="impersonate" hits="0" signature="()V">
173
+ <lines>
174
+ <line number="52" hits="0"/>
51
175
  </lines>
52
176
  </method>
53
177
  <method name="(anonymous_3)" hits="0" signature="()V">
54
178
  <lines>
55
- <line number="49" hits="0"/>
179
+ <line number="58" hits="0"/>
56
180
  </lines>
57
181
  </method>
58
182
  <method name="(anonymous_4)" hits="0" signature="()V">
59
183
  <lines>
60
- <line number="59" hits="0"/>
184
+ <line number="66" hits="0"/>
61
185
  </lines>
62
186
  </method>
63
187
  <method name="(anonymous_5)" hits="0" signature="()V">
64
188
  <lines>
65
- <line number="83" hits="0"/>
189
+ <line number="76" hits="0"/>
66
190
  </lines>
67
191
  </method>
68
192
  <method name="(anonymous_6)" hits="0" signature="()V">
69
193
  <lines>
70
- <line number="86" hits="0"/>
194
+ <line number="100" hits="0"/>
195
+ </lines>
196
+ </method>
197
+ <method name="(anonymous_7)" hits="0" signature="()V">
198
+ <lines>
199
+ <line number="103" hits="0"/>
71
200
  </lines>
72
201
  </method>
73
202
  <method name="saveSelectedCustomer" hits="0" signature="()V">
74
203
  <lines>
75
- <line number="95" hits="0"/>
204
+ <line number="112" hits="0"/>
76
205
  </lines>
77
206
  </method>
78
207
  <method name="loadSelectedCustomer" hits="0" signature="()V">
79
208
  <lines>
80
- <line number="104" hits="0"/>
209
+ <line number="121" hits="0"/>
81
210
  </lines>
82
211
  </method>
83
212
  <method name="cliCallSelectCustomer" hits="0" signature="()V">
84
213
  <lines>
85
- <line number="116" hits="0"/>
214
+ <line number="133" hits="0"/>
86
215
  </lines>
87
216
  </method>
88
217
  </methods>
@@ -93,60 +222,63 @@
93
222
  <line number="4" hits="1" branch="false"/>
94
223
  <line number="5" hits="1" branch="false"/>
95
224
  <line number="7" hits="1" branch="false"/>
96
- <line number="10" hits="0" branch="false"/>
97
- <line number="12" hits="0" branch="true" condition-coverage="0% (0/2)"/>
98
- <line number="13" hits="0" branch="false"/>
99
- <line number="16" hits="0" branch="false"/>
100
- <line number="17" hits="0" branch="false"/>
101
- <line number="22" hits="0" branch="false"/>
102
- <line number="26" hits="0" branch="false"/>
103
- <line number="27" hits="0" branch="false"/>
104
- <line number="28" hits="0" branch="false"/>
105
- <line number="29" hits="0" branch="false"/>
106
- <line number="30" hits="0" branch="true" condition-coverage="0% (0/2)"/>
107
- <line number="31" hits="0" branch="false"/>
108
- <line number="44" hits="0" branch="true" condition-coverage="0% (0/2)"/>
109
- <line number="46" hits="0" branch="false"/>
110
- <line number="48" hits="0" branch="true" condition-coverage="0% (0/2)"/>
111
- <line number="49" hits="0" branch="false"/>
112
- <line number="51" hits="0" branch="false"/>
113
- <line number="55" hits="0" branch="true" condition-coverage="0% (0/2)"/>
114
- <line number="56" hits="0" branch="false"/>
115
- <line number="57" hits="0" branch="true" condition-coverage="0% (0/2)"/>
225
+ <line number="20" hits="0" branch="false"/>
226
+ <line number="33" hits="0" branch="true" condition-coverage="0% (0/2)"/>
227
+ <line number="34" hits="0" branch="false"/>
228
+ <line number="37" hits="0" branch="false"/>
229
+ <line number="41" hits="0" branch="false"/>
230
+ <line number="43" hits="0" branch="true" condition-coverage="0% (0/2)"/>
231
+ <line number="44" hits="0" branch="false"/>
232
+ <line number="47" hits="0" branch="false"/>
233
+ <line number="48" hits="0" branch="false"/>
234
+ <line number="53" hits="0" branch="false"/>
235
+ <line number="57" hits="0" branch="false"/>
116
236
  <line number="58" hits="0" branch="false"/>
117
237
  <line number="59" hits="0" branch="false"/>
118
- <line number="60" hits="0" branch="true" condition-coverage="0% (0/2)"/>
119
- <line number="61" hits="0" branch="false"/>
238
+ <line number="60" hits="0" branch="false"/>
239
+ <line number="61" hits="0" branch="true" condition-coverage="0% (0/2)"/>
120
240
  <line number="63" hits="0" branch="false"/>
121
- <line number="69" hits="0" branch="true" condition-coverage="0% (0/5)"/>
122
- <line number="70" hits="0" branch="false"/>
123
- <line number="71" hits="0" branch="true" condition-coverage="0% (0/2)"/>
124
- <line number="72" hits="0" branch="false"/>
125
- <line number="76" hits="0" branch="true" condition-coverage="0% (0/2)"/>
126
- <line number="77" hits="0" branch="false"/>
127
- <line number="83" hits="0" branch="true" condition-coverage="0% (0/2)"/>
241
+ <line number="65" hits="0" branch="true" condition-coverage="0% (0/2)"/>
242
+ <line number="66" hits="0" branch="false"/>
243
+ <line number="68" hits="0" branch="false"/>
244
+ <line number="72" hits="0" branch="true" condition-coverage="0% (0/2)"/>
245
+ <line number="73" hits="0" branch="false"/>
246
+ <line number="74" hits="0" branch="true" condition-coverage="0% (0/2)"/>
247
+ <line number="75" hits="0" branch="false"/>
248
+ <line number="76" hits="0" branch="false"/>
249
+ <line number="77" hits="0" branch="true" condition-coverage="0% (0/2)"/>
250
+ <line number="78" hits="0" branch="false"/>
251
+ <line number="80" hits="0" branch="false"/>
252
+ <line number="86" hits="0" branch="true" condition-coverage="0% (0/5)"/>
128
253
  <line number="87" hits="0" branch="false"/>
254
+ <line number="88" hits="0" branch="true" condition-coverage="0% (0/2)"/>
129
255
  <line number="89" hits="0" branch="false"/>
130
- <line number="92" hits="0" branch="false"/>
131
- <line number="96" hits="0" branch="false"/>
132
- <line number="105" hits="0" branch="false"/>
256
+ <line number="93" hits="0" branch="true" condition-coverage="0% (0/2)"/>
257
+ <line number="94" hits="0" branch="false"/>
258
+ <line number="100" hits="0" branch="true" condition-coverage="0% (0/2)"/>
259
+ <line number="104" hits="0" branch="false"/>
133
260
  <line number="106" hits="0" branch="false"/>
134
- <line number="109" hits="0" branch="true" condition-coverage="0% (0/5)"/>
135
- <line number="110" hits="0" branch="false"/>
261
+ <line number="109" hits="0" branch="false"/>
136
262
  <line number="113" hits="0" branch="false"/>
137
- <line number="118" hits="0" branch="true" condition-coverage="0% (0/2)"/>
138
- <line number="119" hits="0" branch="false"/>
139
- <line number="120" hits="0" branch="true" condition-coverage="0% (0/4)"/>
140
- <line number="121" hits="0" branch="false"/>
141
- <line number="124" hits="0" branch="false"/>
263
+ <line number="122" hits="0" branch="false"/>
264
+ <line number="123" hits="0" branch="false"/>
265
+ <line number="126" hits="0" branch="true" condition-coverage="0% (0/5)"/>
142
266
  <line number="127" hits="0" branch="false"/>
143
- <line number="128" hits="0" branch="false"/>
144
267
  <line number="130" hits="0" branch="false"/>
145
- <line number="139" hits="0" branch="false"/>
146
- <line number="142" hits="1" branch="false"/>
147
- <line number="143" hits="1" branch="false"/>
148
- <line number="144" hits="1" branch="false"/>
149
- <line number="145" hits="1" branch="false"/>
268
+ <line number="135" hits="0" branch="true" condition-coverage="0% (0/2)"/>
269
+ <line number="136" hits="0" branch="false"/>
270
+ <line number="137" hits="0" branch="true" condition-coverage="0% (0/4)"/>
271
+ <line number="138" hits="0" branch="false"/>
272
+ <line number="141" hits="0" branch="false"/>
273
+ <line number="144" hits="0" branch="false"/>
274
+ <line number="145" hits="0" branch="false"/>
275
+ <line number="147" hits="0" branch="false"/>
276
+ <line number="156" hits="0" branch="false"/>
277
+ <line number="159" hits="1" branch="false"/>
278
+ <line number="160" hits="1" branch="false"/>
279
+ <line number="161" hits="1" branch="false"/>
280
+ <line number="162" hits="1" branch="false"/>
281
+ <line number="163" hits="1" branch="false"/>
150
282
  </lines>
151
283
  </class>
152
284
  <class name="init.js" filename="src/init.js" line-rate="0.875" branch-rate="0.375">
@@ -723,91 +855,106 @@
723
855
  <line number="84" hits="2" branch="false"/>
724
856
  </lines>
725
857
  </class>
726
- <class name="serve.js" filename="src/serve.js" line-rate="0.14279999999999998" branch-rate="0">
858
+ <class name="serve.js" filename="src/serve.js" line-rate="0.1315" branch-rate="0">
727
859
  <methods>
728
860
  <method name="getGlobals" hits="0" signature="()V">
729
861
  <lines>
730
- <line number="13" hits="0"/>
862
+ <line number="14" hits="0"/>
731
863
  </lines>
732
864
  </method>
733
865
  <method name="(anonymous_1)" hits="0" signature="()V">
734
866
  <lines>
735
- <line number="51" hits="0"/>
867
+ <line number="55" hits="0"/>
736
868
  </lines>
737
869
  </method>
738
870
  <method name="(anonymous_2)" hits="0" signature="()V">
739
871
  <lines>
740
- <line number="56" hits="0"/>
872
+ <line number="60" hits="0"/>
741
873
  </lines>
742
874
  </method>
743
875
  <method name="(anonymous_3)" hits="0" signature="()V">
744
876
  <lines>
745
- <line number="66" hits="0"/>
877
+ <line number="70" hits="0"/>
746
878
  </lines>
747
879
  </method>
748
880
  <method name="(anonymous_4)" hits="0" signature="()V">
749
881
  <lines>
750
- <line number="80" hits="0"/>
882
+ <line number="84" hits="0"/>
751
883
  </lines>
752
884
  </method>
753
885
  <method name="(anonymous_5)" hits="0" signature="()V">
754
886
  <lines>
755
- <line number="85" hits="0"/>
887
+ <line number="89" hits="0"/>
756
888
  </lines>
757
889
  </method>
758
890
  <method name="(anonymous_6)" hits="0" signature="()V">
759
891
  <lines>
760
- <line number="86" hits="0"/>
892
+ <line number="90" hits="0"/>
761
893
  </lines>
762
894
  </method>
763
895
  <method name="(anonymous_7)" hits="0" signature="()V">
764
896
  <lines>
765
- <line number="93" hits="0"/>
897
+ <line number="97" hits="0"/>
766
898
  </lines>
767
899
  </method>
768
900
  <method name="(anonymous_8)" hits="0" signature="()V">
769
901
  <lines>
770
- <line number="96" hits="0"/>
902
+ <line number="100" hits="0"/>
771
903
  </lines>
772
904
  </method>
773
905
  <method name="(anonymous_9)" hits="0" signature="()V">
774
906
  <lines>
775
- <line number="129" hits="0"/>
907
+ <line number="133" hits="0"/>
776
908
  </lines>
777
909
  </method>
778
910
  <method name="(anonymous_10)" hits="0" signature="()V">
779
911
  <lines>
780
- <line number="135" hits="0"/>
912
+ <line number="139" hits="0"/>
781
913
  </lines>
782
914
  </method>
783
915
  <method name="(anonymous_11)" hits="0" signature="()V">
784
916
  <lines>
785
- <line number="143" hits="0"/>
917
+ <line number="147" hits="0"/>
786
918
  </lines>
787
919
  </method>
788
920
  <method name="(anonymous_12)" hits="0" signature="()V">
789
921
  <lines>
790
- <line number="154" hits="0"/>
922
+ <line number="158" hits="0"/>
791
923
  </lines>
792
924
  </method>
793
925
  <method name="(anonymous_13)" hits="0" signature="()V">
794
926
  <lines>
795
- <line number="182" hits="0"/>
927
+ <line number="185" hits="0"/>
796
928
  </lines>
797
929
  </method>
798
930
  <method name="(anonymous_14)" hits="0" signature="()V">
799
931
  <lines>
800
- <line number="185" hits="0"/>
932
+ <line number="188" hits="0"/>
801
933
  </lines>
802
934
  </method>
803
935
  <method name="(anonymous_15)" hits="0" signature="()V">
804
936
  <lines>
805
- <line number="186" hits="0"/>
937
+ <line number="189" hits="0"/>
806
938
  </lines>
807
939
  </method>
808
940
  <method name="serve" hits="0" signature="()V">
809
941
  <lines>
810
- <line number="200" hits="0"/>
942
+ <line number="203" hits="0"/>
943
+ </lines>
944
+ </method>
945
+ <method name="(anonymous_17)" hits="0" signature="()V">
946
+ <lines>
947
+ <line number="273" hits="0"/>
948
+ </lines>
949
+ </method>
950
+ <method name="(anonymous_18)" hits="0" signature="()V">
951
+ <lines>
952
+ <line number="288" hits="0"/>
953
+ </lines>
954
+ </method>
955
+ <method name="(anonymous_19)" hits="0" signature="()V">
956
+ <lines>
957
+ <line number="296" hits="0"/>
811
958
  </lines>
812
959
  </method>
813
960
  </methods>
@@ -822,94 +969,110 @@
822
969
  <line number="8" hits="1" branch="false"/>
823
970
  <line number="10" hits="1" branch="false"/>
824
971
  <line number="11" hits="1" branch="false"/>
825
- <line number="14" hits="0" branch="false"/>
826
- <line number="19" hits="0" branch="true" condition-coverage="0% (0/2)"/>
827
- <line number="20" hits="0" branch="false"/>
828
- <line number="22" hits="0" branch="true" condition-coverage="0% (0/2)"/>
829
- <line number="24" hits="0" branch="false"/>
830
- <line number="39" hits="0" branch="false"/>
831
- <line number="40" hits="0" branch="true" condition-coverage="0% (0/2)"/>
832
- <line number="42" hits="0" branch="false"/>
833
- <line number="51" hits="1" branch="false"/>
834
- <line number="52" hits="0" branch="true" condition-coverage="0% (0/4)"/>
835
- <line number="53" hits="0" branch="false"/>
836
- <line number="56" hits="0" branch="false"/>
837
- <line number="58" hits="0" branch="false"/>
838
- <line number="59" hits="0" branch="true" condition-coverage="0% (0/2)"/>
972
+ <line number="12" hits="1" branch="false"/>
973
+ <line number="15" hits="0" branch="false"/>
974
+ <line number="20" hits="0" branch="true" condition-coverage="0% (0/2)"/>
975
+ <line number="21" hits="0" branch="false"/>
976
+ <line number="23" hits="0" branch="true" condition-coverage="0% (0/2)"/>
977
+ <line number="25" hits="0" branch="false"/>
978
+ <line number="40" hits="0" branch="false"/>
979
+ <line number="41" hits="0" branch="true" condition-coverage="0% (0/2)"/>
980
+ <line number="43" hits="0" branch="false"/>
981
+ <line number="55" hits="1" branch="false"/>
982
+ <line number="56" hits="0" branch="true" condition-coverage="0% (0/4)"/>
983
+ <line number="57" hits="0" branch="false"/>
839
984
  <line number="60" hits="0" branch="false"/>
840
985
  <line number="62" hits="0" branch="false"/>
986
+ <line number="63" hits="0" branch="true" condition-coverage="0% (0/2)"/>
987
+ <line number="64" hits="0" branch="false"/>
841
988
  <line number="66" hits="0" branch="false"/>
842
- <line number="68" hits="0" branch="false"/>
843
- <line number="69" hits="0" branch="true" condition-coverage="0% (0/2)"/>
844
989
  <line number="70" hits="0" branch="false"/>
845
990
  <line number="72" hits="0" branch="false"/>
846
- <line number="75" hits="0" branch="false"/>
847
- <line number="77" hits="0" branch="false"/>
848
- <line number="80" hits="1" branch="false"/>
991
+ <line number="73" hits="0" branch="true" condition-coverage="0% (0/2)"/>
992
+ <line number="74" hits="0" branch="false"/>
993
+ <line number="76" hits="0" branch="false"/>
994
+ <line number="79" hits="0" branch="false"/>
849
995
  <line number="81" hits="0" branch="false"/>
850
- <line number="83" hits="0" branch="false"/>
851
- <line number="86" hits="0" branch="false"/>
852
- <line number="88" hits="0" branch="false"/>
996
+ <line number="84" hits="1" branch="false"/>
997
+ <line number="85" hits="0" branch="false"/>
998
+ <line number="87" hits="0" branch="false"/>
999
+ <line number="90" hits="0" branch="false"/>
853
1000
  <line number="92" hits="0" branch="false"/>
854
- <line number="94" hits="0" branch="false"/>
855
1001
  <line number="96" hits="0" branch="false"/>
856
- <line number="99" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1002
+ <line number="98" hits="0" branch="false"/>
857
1003
  <line number="100" hits="0" branch="false"/>
858
- <line number="102" hits="0" branch="false"/>
1004
+ <line number="103" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1005
+ <line number="104" hits="0" branch="false"/>
859
1006
  <line number="106" hits="0" branch="false"/>
860
- <line number="107" hits="0" branch="false"/>
861
- <line number="108" hits="0" branch="false"/>
862
- <line number="114" hits="0" branch="false"/>
863
- <line number="128" hits="0" branch="false"/>
864
- <line number="129" hits="0" branch="false"/>
865
- <line number="130" hits="0" branch="false"/>
866
- <line number="135" hits="0" branch="false"/>
867
- <line number="136" hits="0" branch="false"/>
868
- <line number="143" hits="0" branch="false"/>
869
- <line number="144" hits="0" branch="false"/>
870
- <line number="151" hits="0" branch="false"/>
871
- <line number="154" hits="1" branch="false"/>
1007
+ <line number="110" hits="0" branch="false"/>
1008
+ <line number="111" hits="0" branch="false"/>
1009
+ <line number="112" hits="0" branch="false"/>
1010
+ <line number="118" hits="0" branch="false"/>
1011
+ <line number="132" hits="0" branch="false"/>
1012
+ <line number="133" hits="0" branch="false"/>
1013
+ <line number="134" hits="0" branch="false"/>
1014
+ <line number="139" hits="0" branch="false"/>
1015
+ <line number="140" hits="0" branch="false"/>
1016
+ <line number="147" hits="0" branch="false"/>
1017
+ <line number="148" hits="0" branch="false"/>
872
1018
  <line number="155" hits="0" branch="false"/>
873
- <line number="157" hits="0" branch="false"/>
874
- <line number="158" hits="0" branch="false"/>
1019
+ <line number="158" hits="1" branch="false"/>
875
1020
  <line number="159" hits="0" branch="false"/>
876
- <line number="163" hits="0" branch="true" condition-coverage="0% (0/2)"/>
877
- <line number="167" hits="0" branch="false"/>
878
- <line number="168" hits="0" branch="false"/>
1021
+ <line number="161" hits="0" branch="false"/>
1022
+ <line number="162" hits="0" branch="false"/>
1023
+ <line number="166" hits="0" branch="true" condition-coverage="0% (0/2)"/>
879
1024
  <line number="170" hits="0" branch="false"/>
880
- <line number="172" hits="0" branch="false"/>
881
- <line number="183" hits="0" branch="false"/>
882
- <line number="184" hits="0" branch="false"/>
1025
+ <line number="171" hits="0" branch="false"/>
1026
+ <line number="173" hits="0" branch="false"/>
1027
+ <line number="175" hits="0" branch="false"/>
883
1028
  <line number="186" hits="0" branch="false"/>
884
- <line number="188" hits="0" branch="true" condition-coverage="0% (0/4)"/>
1029
+ <line number="187" hits="0" branch="false"/>
885
1030
  <line number="189" hits="0" branch="false"/>
886
- <line number="197" hits="0" branch="false"/>
887
- <line number="201" hits="0" branch="false"/>
888
- <line number="202" hits="0" branch="false"/>
1031
+ <line number="191" hits="0" branch="true" condition-coverage="0% (0/4)"/>
1032
+ <line number="192" hits="0" branch="false"/>
1033
+ <line number="200" hits="0" branch="false"/>
889
1034
  <line number="204" hits="0" branch="false"/>
890
1035
  <line number="205" hits="0" branch="false"/>
891
- <line number="206" hits="0" branch="false"/>
892
- <line number="208" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1036
+ <line number="207" hits="0" branch="false"/>
1037
+ <line number="208" hits="0" branch="false"/>
893
1038
  <line number="209" hits="0" branch="false"/>
1039
+ <line number="211" hits="0" branch="true" condition-coverage="0% (0/2)"/>
894
1040
  <line number="212" hits="0" branch="false"/>
895
- <line number="214" hits="0" branch="false"/>
896
1041
  <line number="215" hits="0" branch="false"/>
897
- <line number="216" hits="0" branch="false"/>
1042
+ <line number="217" hits="0" branch="false"/>
898
1043
  <line number="218" hits="0" branch="false"/>
1044
+ <line number="219" hits="0" branch="false"/>
899
1045
  <line number="221" hits="0" branch="false"/>
900
- <line number="252" hits="0" branch="true" condition-coverage="0% (0/2)"/>
901
- <line number="253" hits="0" branch="false"/>
902
- <line number="256" hits="0" branch="false"/>
903
- <line number="258" hits="0" branch="false"/>
1046
+ <line number="222" hits="0" branch="false"/>
1047
+ <line number="225" hits="0" branch="false"/>
1048
+ <line number="256" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1049
+ <line number="257" hits="0" branch="false"/>
904
1050
  <line number="260" hits="0" branch="false"/>
905
- <line number="265" hits="0" branch="false"/>
906
- <line number="266" hits="0" branch="true" condition-coverage="0% (0/2)"/>
907
- <line number="267" hits="0" branch="false"/>
908
- <line number="268" hits="0" branch="false"/>
909
- <line number="269" hits="0" branch="false"/>
910
- <line number="270" hits="0" branch="false"/>
911
- <line number="272" hits="0" branch="false"/>
912
- <line number="274" hits="1" branch="false"/>
1051
+ <line number="262" hits="0" branch="false"/>
1052
+ <line number="265" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1053
+ <line number="266" hits="0" branch="false"/>
1054
+ <line number="273" hits="0" branch="false"/>
1055
+ <line number="274" hits="0" branch="false"/>
1056
+ <line number="279" hits="0" branch="false"/>
1057
+ <line number="282" hits="0" branch="false"/>
1058
+ <line number="283" hits="0" branch="false"/>
1059
+ <line number="285" hits="0" branch="false"/>
1060
+ <line number="288" hits="0" branch="false"/>
1061
+ <line number="289" hits="0" branch="false"/>
1062
+ <line number="290" hits="0" branch="false"/>
1063
+ <line number="293" hits="0" branch="false"/>
1064
+ <line number="296" hits="0" branch="false"/>
1065
+ <line number="297" hits="0" branch="false"/>
1066
+ <line number="298" hits="0" branch="false"/>
1067
+ <line number="302" hits="0" branch="false"/>
1068
+ <line number="307" hits="0" branch="false"/>
1069
+ <line number="308" hits="0" branch="true" condition-coverage="0% (0/2)"/>
1070
+ <line number="309" hits="0" branch="false"/>
1071
+ <line number="310" hits="0" branch="false"/>
1072
+ <line number="311" hits="0" branch="false"/>
1073
+ <line number="312" hits="0" branch="false"/>
1074
+ <line number="314" hits="0" branch="false"/>
1075
+ <line number="316" hits="1" branch="false"/>
913
1076
  </lines>
914
1077
  </class>
915
1078
  <class name="utils.js" filename="src/utils.js" line-rate="0.4893" branch-rate="0.44439999999999996">
@@ -1,16 +1,18 @@
1
1
  ---------------------|---------|----------|---------|---------|-------------------------------------------------------------------------
2
2
  File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
3
3
  ---------------------|---------|----------|---------|---------|-------------------------------------------------------------------------
4
- All files | 37.71 | 20.64 | 30.27 | 38.24 |
5
- src | 40.07 | 23.68 | 32.35 | 40.67 |
4
+ All files | 34.97 | 19.31 | 26.61 | 35.29 |
5
+ src | 36.9 | 21.95 | 28.2 | 37.25 |
6
+ auth-monitor.js | 12 | 0 | 0 | 12 | 12-127
6
7
  auth.js | 93.33 | 83.33 | 100 | 93.33 | 14
7
- impersonate.js | 15.87 | 0 | 0 | 16.66 | 10-139
8
+ hmac-refresh.js | 28.57 | 0 | 0 | 28.57 | 14-29
9
+ impersonate.js | 16.92 | 0 | 0 | 17.46 | 20-156
8
10
  init.js | 84.09 | 37.5 | 87.5 | 87.5 | 38,53-54,64,83
9
11
  login.js | 13.46 | 16.66 | 6.66 | 14 | 6-87
10
12
  pull-theme.js | 77.77 | 56.25 | 93.75 | 76.84 | 25-28,37-38,69-70,77,94,102-103,117,130,145,156-157,163-166,178,234-239
11
13
  push-theme.js | 0 | 0 | 0 | 0 | 1-130
12
14
  select-merchant.js | 50 | 35.29 | 22.22 | 55.88 | 19,31,38,43-81
13
- serve.js | 14.14 | 0 | 0 | 14.28 | 14-42,52-77,81-151,155-272
15
+ serve.js | 13.04 | 0 | 0 | 13.15 | 15-43,56-81,85-155,159-314
14
16
  utils.js | 47.91 | 44.44 | 36.84 | 48.93 | 13,23-31,40,53-70,81-94,104,141-145,154-158,169-174,194-196
15
17
  src/partials | 0 | 0 | 0 | 0 |
16
18
  devmode.js | 0 | 0 | 100 | 0 | 4-21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordergroove/smi-serve",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Utility to serve a Subscription Manager template locally",
5
5
  "keywords": [],
6
6
  "author": "Eugenio Lattanzio <eugenio.lattanzio@ordergroove.com>",
@@ -34,5 +34,5 @@
34
34
  "devDependencies": {
35
35
  "memfs": "^4.8.2"
36
36
  },
37
- "gitHead": "4630740e3093b00663aada0d2ffecdef3db29575"
37
+ "gitHead": "850de75bb74051ac6b073ba74593455831c22f4d"
38
38
  }
@@ -0,0 +1,130 @@
1
+ const { EventEmitter } = require('events');
2
+ const readline = require('readline');
3
+ const { login } = require('./login');
4
+ const { readRcEnv, writeRcEnv } = require('./utils');
5
+ const { refreshHmacAuth } = require('./hmac-refresh');
6
+
7
+ /**
8
+ * Monitors JWT token expiry and orchestrates refresh flow
9
+ */
10
+ class AuthMonitor extends EventEmitter {
11
+ constructor({ args, token, merchant, merchantUserId }) {
12
+ super();
13
+ this.args = args;
14
+ this.token = token;
15
+ this.merchant = merchant;
16
+ this.merchantUserId = merchantUserId;
17
+ this.expiryTime = parseJwtExpiry(token);
18
+ this.checkIntervalMs = 30 * 1000; // 30 seconds
19
+ this.bufferMinutes = 5; // Notify 5 min before expiry
20
+ this.isRefreshing = false;
21
+ this.interval = null;
22
+ }
23
+
24
+ /**
25
+ * Start monitoring token expiry
26
+ */
27
+ start() {
28
+ this.interval = setInterval(() => {
29
+ const minutesUntilExpiry = this.getMinutesUntilExpiry();
30
+
31
+ if (minutesUntilExpiry <= this.bufferMinutes && !this.isRefreshing) {
32
+ this.promptForRefresh();
33
+ }
34
+ }, this.checkIntervalMs);
35
+ }
36
+
37
+ /**
38
+ * Prompt user and refresh authentication
39
+ */
40
+ async promptForRefresh() {
41
+ this.isRefreshing = true;
42
+
43
+ // Show console notification
44
+ console.log('\nāš ļø Auth token will expire soon!');
45
+ console.log('Press Enter to refresh authentication (a browser window will open)...');
46
+
47
+ try {
48
+ // Wait for user to press Enter
49
+ await this.waitForEnter();
50
+
51
+ console.log('šŸ”„ Refreshing authentication...');
52
+
53
+ // Use existing login flow to get new JWT token
54
+ const newToken = await login(this.args);
55
+
56
+ // Update .ogrc.json with new token
57
+ const existingSettings = await readRcEnv(this.args);
58
+ await writeRcEnv(this.args, { ...existingSettings, token: newToken });
59
+
60
+ // Refresh customer HMAC auth with new token
61
+ const updatedCustomer = await refreshHmacAuth({
62
+ args: this.args,
63
+ token: newToken,
64
+ merchant: this.merchant,
65
+ merchantUserId: this.merchantUserId
66
+ });
67
+
68
+ // Build new HMAC auth string
69
+ const newHmacAuth = [updatedCustomer.merchant_user_id, updatedCustomer.ts, updatedCustomer.hash].join('|');
70
+
71
+ // Emit event with updated auth data
72
+ this.emit('auth-refreshed', { hmacAuth: newHmacAuth });
73
+
74
+ // Update expiry time for next check
75
+ this.token = newToken;
76
+ this.expiryTime = parseJwtExpiry(newToken);
77
+ this.isRefreshing = false;
78
+ } catch (error) {
79
+ this.emit('refresh-failed', error);
80
+ this.isRefreshing = false;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Wait for user to press Enter
86
+ */
87
+ waitForEnter() {
88
+ return new Promise(resolve => {
89
+ const rl = readline.createInterface({
90
+ input: process.stdin,
91
+ output: process.stdout
92
+ });
93
+ rl.question('', () => {
94
+ rl.close();
95
+ resolve();
96
+ });
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Get minutes until token expires
102
+ */
103
+ getMinutesUntilExpiry() {
104
+ const msUntilExpiry = this.expiryTime * 1000 - Date.now();
105
+ return Math.floor(msUntilExpiry / (60 * 1000));
106
+ }
107
+
108
+ /**
109
+ * Stop monitoring
110
+ */
111
+ stop() {
112
+ if (this.interval) {
113
+ clearInterval(this.interval);
114
+ this.interval = null;
115
+ }
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Parse JWT token to extract expiry timestamp
121
+ * @param {string} token - JWT token
122
+ * @returns {number} Unix timestamp (seconds)
123
+ */
124
+ function parseJwtExpiry(token) {
125
+ const [_header, payload, _signature] = token.split('.');
126
+ const decoded = JSON.parse(Buffer.from(payload, 'base64').toString('binary'));
127
+ return decoded.exp; // Unix timestamp in seconds
128
+ }
129
+
130
+ module.exports = { AuthMonitor };
@@ -0,0 +1,35 @@
1
+ const { searchCustomers } = require('./impersonate');
2
+
3
+ /**
4
+ * Re-fetch customer HMAC auth after JWT token refresh
5
+ * @param {object} params - Refresh parameters
6
+ * @param {object} params.args - CLI arguments
7
+ * @param {string} params.token - New JWT token
8
+ * @param {object} params.merchant - Merchant object
9
+ * @param {string} params.merchantUserId - Customer's merchant user ID
10
+ * @returns {Promise<object>} Updated customer object with fresh HMAC
11
+ */
12
+ async function refreshHmacAuth({ args, token, merchant, merchantUserId }) {
13
+ // Search for customer by their ID using new token
14
+ const data = await searchCustomers({
15
+ args,
16
+ token,
17
+ merchant,
18
+ query: merchantUserId,
19
+ pageLimit: 1
20
+ });
21
+
22
+ if (!data.customers || data.customers.length === 0) {
23
+ throw new Error(`Customer ${merchantUserId} not found`);
24
+ }
25
+
26
+ const customer = data.customers[0];
27
+
28
+ // Return customer with fresh HMAC timestamp
29
+ return {
30
+ ...customer,
31
+ ts: data.hmac_timestamp
32
+ };
33
+ }
34
+
35
+ module.exports = { refreshHmacAuth };
@@ -6,6 +6,37 @@ const { writeRcEnv, readRcEnv } = require('./utils');
6
6
 
7
7
  inquirer.registerPrompt('autocomplete', inquirerPrompt);
8
8
 
9
+ /**
10
+ * Search for customers via the customer search API
11
+ * @param {object} params - Search parameters
12
+ * @param {object} params.args - CLI arguments
13
+ * @param {string} params.token - JWT token
14
+ * @param {object} params.merchant - Merchant object
15
+ * @param {string} params.query - Search query
16
+ * @param {number} [params.pageLimit=10] - Maximum number of results
17
+ * @returns {Promise<object>} Response with customers array and hmac_timestamp
18
+ */
19
+ async function searchCustomers({ args, token, merchant, query, pageLimit = 10 }) {
20
+ const fetchRes = await fetch(
21
+ `${getRC3Url(args)}csa/customer_search_req_keyword.json?${new URLSearchParams([
22
+ ['merchant_id', merchant.public_id],
23
+ ['q', query],
24
+ ['page_limit', pageLimit]
25
+ ])}`,
26
+ {
27
+ headers: {
28
+ authorization: `Bearer ${token}`
29
+ }
30
+ }
31
+ );
32
+
33
+ if (fetchRes.status !== 200) {
34
+ return { customers: [], hmac_timestamp: null };
35
+ }
36
+
37
+ return await fetchRes.json();
38
+ }
39
+
9
40
  async function tryToLoadSelectedCustomer(args) {
10
41
  const selectedCustomer = await loadSelectedCustomer(args);
11
42
 
@@ -28,22 +59,8 @@ async function impersonate(args, autoSelectSingleResult = true) {
28
59
  az = [];
29
60
  customersResponse = [];
30
61
  if (!input) return [];
31
- const fetchRes = await fetch(
32
- `${getRC3Url(args)}csa/customer_search_req_keyword.json?${new URLSearchParams([
33
- ['merchant_id', merchant.public_id],
34
- ['q', input],
35
- ['page_limit', 10]
36
- ])}`,
37
- {
38
- headers: {
39
- authorization: `Bearer ${token}`
40
- }
41
- }
42
- );
43
-
44
- if (fetchRes.status !== 200) return [];
45
62
 
46
- customersResponse = await fetchRes.json();
63
+ customersResponse = await searchCustomers({ args, token, merchant, query: input, pageLimit: 10 });
47
64
 
48
65
  az = (customersResponse.customers || []).map(
49
66
  ({ first_name, last_name, email }) => `${first_name} ${last_name} (${email})`
@@ -143,3 +160,4 @@ exports.impersonate = impersonate;
143
160
  exports.saveSelectedCustomer = saveSelectedCustomer;
144
161
  exports.loadSelectedCustomer = loadSelectedCustomer;
145
162
  exports.cliCallSelectCustomer = cliCallSelectCustomer;
163
+ exports.searchCustomers = searchCustomers;
package/src/serve.js CHANGED
@@ -9,6 +9,7 @@ const { getValidLoginAndCurrentMerchant } = require('./auth');
9
9
 
10
10
  const { open, getcwd, glob, readPackageJson, packageJsonKeys } = require('./utils');
11
11
  const { impersonate } = require('./impersonate');
12
+ const { AuthMonitor } = require('./auth-monitor');
12
13
 
13
14
  async function getGlobals(argv) {
14
15
  let hmacAuth = "''",
@@ -40,11 +41,14 @@ async function getGlobals(argv) {
40
41
  const smiCoreVersion = packageJson[packageJsonKeys.OG_SECTION]?.[packageJsonKeys.CORE_VERSION] || 'latest';
41
42
 
42
43
  return {
43
- OG_MERCHANT_ID: merchant ? merchant.public_id : '',
44
- OG_HMAC_AUTH: hmacAuth,
45
- OG_ENV: argv.env,
46
- OG_SMI_CORE_VERSION: smiCoreVersion,
47
- OG_LOCAL_ASSETS: !!argv.localAssets
44
+ globals: {
45
+ OG_MERCHANT_ID: merchant ? merchant.public_id : '',
46
+ OG_HMAC_AUTH: hmacAuth,
47
+ OG_ENV: argv.env,
48
+ OG_SMI_CORE_VERSION: smiCoreVersion,
49
+ OG_LOCAL_ASSETS: !!argv.localAssets
50
+ },
51
+ impersonation: 'impersonate' in argv ? { token: _token, merchant, customer } : null
48
52
  };
49
53
  }
50
54
 
@@ -151,10 +155,9 @@ const smiDevModePlugin = (globals, args) => {
151
155
  return plugin;
152
156
  };
153
157
 
154
- const getPlugins = async argv => {
158
+ const getPlugins = async (argv, globals) => {
155
159
  const { cwd, outdir } = argv;
156
160
 
157
- const globals = await getGlobals(argv);
158
161
  const devModePlugin = smiDevModePlugin(globals, argv);
159
162
  const less = lessLoader({
160
163
  plugins: [new VariableExtractorPlugin()]
@@ -215,7 +218,8 @@ async function serve(argv) {
215
218
  const smiIndexSource = fs.readFileSync(`${__dirname}/partials/index.html`, 'utf8');
216
219
  fs.writeFileSync(smiIndexFile, smiIndexSource, { encoding: 'utf8' });
217
220
 
218
- const plugins = await getPlugins(argv);
221
+ const { globals, impersonation } = await getGlobals(argv);
222
+ const plugins = await getPlugins(argv, globals);
219
223
 
220
224
  /** @type {import('esbuild').BuildOptions} */
221
225
  const buildConf = {
@@ -257,6 +261,44 @@ async function serve(argv) {
257
261
 
258
262
  await ctx.watch();
259
263
 
264
+ // Set up token refresh monitoring when impersonating
265
+ if (impersonation) {
266
+ const authMonitor = new AuthMonitor({
267
+ args: argv,
268
+ token: impersonation.token,
269
+ merchant: impersonation.merchant,
270
+ merchantUserId: impersonation.customer.merchant_user_id
271
+ });
272
+
273
+ authMonitor.on('auth-refreshed', async ({ hmacAuth }) => {
274
+ console.log('āœ… Authentication refreshed! Updating dev server...');
275
+
276
+ // Update globals in-place with new auth. The smiDevModePlugin holds a reference to this object,
277
+ // and its onLoad callback calls JSON.stringify(globals) on each build, so the
278
+ // mutation will be picked up when ctx.rebuild() re-executes the virtual module loader.
279
+ globals.OG_HMAC_AUTH = hmacAuth;
280
+
281
+ // Trigger rebuild to inject new auth into frontend
282
+ console.log('šŸ”Ø Rebuilding with updated auth...');
283
+ await ctx.rebuild();
284
+
285
+ console.log('āœ… Dev server updated successfully!\n');
286
+ });
287
+
288
+ authMonitor.on('refresh-failed', error => {
289
+ console.error('\nāŒ Failed to refresh authentication:', error.message);
290
+ console.error('Please restart the dev server when ready.\n');
291
+ });
292
+
293
+ authMonitor.start();
294
+
295
+ // Stop monitoring on shutdown
296
+ process.on('SIGINT', () => {
297
+ authMonitor.stop();
298
+ process.exit();
299
+ });
300
+ }
301
+
260
302
  const { port: actualPort, host: actualHost } = await ctx.serve({
261
303
  servedir: path.join(cwd, outdir),
262
304
  port