querysub 0.563.0 → 0.565.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.563.0",
3
+ "version": "0.565.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -71,7 +71,7 @@
71
71
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
72
72
  "pako": "^2.1.0",
73
73
  "peggy": "^5.0.6",
74
- "sliftutils": "^1.7.41",
74
+ "sliftutils": "^1.7.43",
75
75
  "socket-function": "^1.2.26",
76
76
  "terser": "^5.31.0",
77
77
  "typenode": "^6.6.1",
@@ -167,6 +167,12 @@ export class ProcessesView extends qreact.Component<{
167
167
  serviceId?: string;
168
168
  }> {
169
169
  render() {
170
+ // Callers hand us one entry per service instance, so a machine running several instances arrives several times. Each machine's processes are listed once, whatever it is running.
171
+ let machines = new Map<string, { machineId: string; applyNodeId: string }>();
172
+ for (let machine of this.props.machines) {
173
+ if (machines.has(machine.machineId)) continue;
174
+ machines.set(machine.machineId, machine);
175
+ }
170
176
  return <div className={css.vbox(14).fillWidth}>
171
177
  <div className={css.hbox(10).alignItems("center")}>
172
178
  <h3 className={css.flexGrow(1)}>Processes</h3>
@@ -176,7 +182,7 @@ export class ProcessesView extends qreact.Component<{
176
182
  Invalidate
177
183
  </Button>
178
184
  </div>
179
- {this.props.machines.map(machine => <MachineProcesses
185
+ {[...machines.values()].map(machine => <MachineProcesses
180
186
  key={machine.machineId}
181
187
  machineId={machine.machineId}
182
188
  applyNodeId={machine.applyNodeId}
@@ -35,10 +35,6 @@ export class ServiceDetailPage extends qreact.Component {
35
35
  expandedErrors: t.lookup({
36
36
  expanded: t.type(false)
37
37
  }),
38
- // Milliseconds; 0 means no scheduled time picked yet (use Deploy Now instead)
39
- switchTime: t.number(0),
40
- // Seconds until the release goes live; 0 means use the default (DEFAULT_OVERLAP_TIME)
41
- releaseDelayOverride: t.number(0),
42
38
  // Seconds the old instances outlive the release; 0 means use the default (DEFAULT_OVERLAP_TIME). The no-overlap deploy buttons are how you deploy with an actual 0.
43
39
  overlapOverride: t.number(0),
44
40
  });
@@ -64,8 +60,6 @@ export class ServiceDetailPage extends qreact.Component {
64
60
  await MachineServiceController(SocketFunction.browserNodeId()).setServiceConfigs.promise([deployConfig]);
65
61
  Querysub.commit(() => {
66
62
  this.state.editorState = stripReleaseState(deployConfig);
67
- this.state.switchTime = 0;
68
- this.state.releaseDelayOverride = 0;
69
63
  this.state.overlapOverride = 0;
70
64
  });
71
65
  } finally {
@@ -93,16 +87,16 @@ export class ServiceDetailPage extends qreact.Component {
93
87
  modal = showModal({
94
88
  content: <div className={css.fixed.pos(0, 0).size("100vw", "100vh").hsla(0, 0, 0, 0.5).display("flex").alignItems("center").justifyContent("center")}>
95
89
  <div className={css.vbox(12).pad2(20).hsl(0, 0, 12).colorhsl(0, 0, 90).bord2(0, 0, 40) + " keepModalsOpen"}>
96
- <div className={css.boldStyle.fontSize(16)}>⚡ Force deploy now?</div>
90
+ <div className={css.boldStyle.fontSize(16)}>🚀 Deploy?</div>
97
91
  <div>{deployConfig.parameters.overlapTime
98
- ? `This config becomes live immediately. The old instances keep running for ${formatTime(deployConfig.parameters.overlapTime)} before shutting down.`
99
- : "This config becomes live immediately. The old instances are shut down right away, with no overlap."}</div>
92
+ ? `This config goes live now. The old instances keep running for ${formatTime(deployConfig.parameters.overlapTime)} before shutting down.`
93
+ : "This config goes live now. The old instances are shut down as soon as the new ones are up, so anything still connected to them is dropped."}</div>
100
94
  <div className={css.hbox(10)}>
101
95
  <Button hue={0} onClick={() => {
102
96
  modal && modal.close();
103
97
  deployConfig.parameters.releaseTime = Date.now();
104
98
  this.deployConfig(deployConfig);
105
- }}>⚡ Force Deploy Now</Button>
99
+ }}>🚀 Deploy</Button>
106
100
  <Button onClick={() => modal && modal.close()}>Cancel</Button>
107
101
  </div>
108
102
  </div>
@@ -642,70 +636,39 @@ export class ServiceDetailPage extends qreact.Component {
642
636
  <span title={`${configCompareKey(config)} !== ${configCompareKey(deployedConfig)}`}>Only the title / notes changed, so this saves immediately</span>
643
637
  </div>;
644
638
  }
645
- let releaseDelaySeconds = this.state.releaseDelayOverride || Math.round(DEFAULT_OVERLAP_TIME / timeInSecond);
646
- let releaseDelay = releaseDelaySeconds * timeInSecond;
647
639
  let overlapSeconds = this.state.overlapOverride || Math.round(DEFAULT_OVERLAP_TIME / timeInSecond);
648
640
  let overlap = overlapSeconds * timeInSecond;
649
- let scheduledTime = this.state.switchTime;
650
- const makeDeployConfig = (releaseTime: number, overlapTime = overlap) => {
641
+ // A deploy always goes live now. The overlap is what keeps the service up: the old instances keep running for it, so connections have somewhere to go while clients move across.
642
+ const makeDeployConfig = (overlapTime = overlap) => {
651
643
  let deployConfig = deepCloneJSON(config);
652
- deployConfig.parameters.releaseTime = releaseTime;
644
+ deployConfig.parameters.releaseTime = Date.now();
653
645
  deployConfig.parameters.overlapTime = overlapTime;
654
646
  return deployConfig;
655
647
  };
656
- // The switch time is inferred from the release delay, unless one is explicitly set
657
- let effectiveSwitchTime = scheduledTime || now + releaseDelay;
658
648
  return <div className={css.vbox(10).pad2(12).fillWidth.bord2(45, 70, 45).hsl(45, 60, 95)}>
659
649
  <div className={css.boldStyle.fontSize(15)}>
660
650
  ✏️ Edited (only in this browser, until deployed)
661
651
  </div>
662
652
  <ParametersDiff base={liveParameters} next={config.parameters} />
663
- <div className={css.hbox(12).wrap}>
653
+ <div className={css.hbox(12).wrap.alignItems("center")}>
664
654
  <button
665
655
  className={css.pad2(12, 8).button.bord2(0, 0, 20).hsl(120, 70, 85).fontWeight("bold")}
666
656
  disabled={this.state.isDeploying}
657
+ title="Deploys now; the old instances keep running for the overlap before shutting down"
667
658
  onClick={() => {
668
- this.deployConfig(makeDeployConfig(scheduledTime || Date.now() + releaseDelay));
659
+ this.confirmForceDeployNow(makeDeployConfig());
669
660
  }}>
670
- {this.state.isDeploying && "⏳ Scheduling..." || scheduledDeploy && "🔁 Replace Scheduled Deploy" || "🚀 Schedule Deploy"}
661
+ {this.state.isDeploying && "⏳ Deploying..." || "🚀 Deploy"}
671
662
  </button>
672
663
  <button
673
- className={css.pad2(12, 8).button.bord2(0, 0, 20).hsl(35, 70, 85)}
664
+ className={css.pad2(12, 8).button.bord2(0, 90, 30).hsl(0, 85, 65).colorhsl(0, 0, 100).fontWeight("bold")}
674
665
  disabled={this.state.isDeploying}
675
- title="Deploys with the configured release delay, but the old instances shut down immediately at release time (overlap of 0)"
666
+ title="Deploys now AND shuts the old instances down as soon as the new ones are up (overlap of 0)"
676
667
  onClick={() => {
677
- this.deployConfig(makeDeployConfig(scheduledTime || Date.now() + releaseDelay, 0));
668
+ this.confirmForceDeployNow(makeDeployConfig(0));
678
669
  }}>
679
670
  Deploy, No Overlap
680
671
  </button>
681
- <div className={css.opacity(scheduledTime && 1 || 0.5)}>
682
- <InputLabel
683
- label="Switch at"
684
- isDatetime
685
- value={effectiveSwitchTime}
686
- onChangeValue={value => {
687
- this.state.switchTime = Number(value) || 0;
688
- }}
689
- />
690
- </div>
691
- <span>(in {formatTime(effectiveSwitchTime - now)})</span>
692
- {!!scheduledTime && <button
693
- className={css.pad2(12, 8).button.bord2(0, 0, 20).hsl(0, 0, 90)}
694
- onClick={() => {
695
- this.state.switchTime = 0;
696
- }}>
697
- ↺ Reset to delay-based time
698
- </button>}
699
- <div className={css.opacity(scheduledTime && 0.5 || 1)}>
700
- <InputLabel
701
- label="Release in (seconds)"
702
- integer
703
- value={releaseDelaySeconds}
704
- onChangeValue={value => {
705
- this.state.releaseDelayOverride = Number(value) || 0;
706
- }}
707
- />
708
- </div>
709
672
  <InputLabel
710
673
  label="Overlap (seconds)"
711
674
  integer
@@ -715,29 +678,6 @@ export class ServiceDetailPage extends qreact.Component {
715
678
  }}
716
679
  />
717
680
  </div>
718
- {!!scheduledTime && scheduledTime <= now && <span className={css.colorhsl(0, 85, 45).fontWeight("bold")}>
719
- ⚠️ The switch time is in the past — deploying switches immediately
720
- </span>}
721
- <div className={css.hbox(8)}>
722
- <button
723
- className={css.pad2(12, 8).button.bord2(45, 80, 35).hsl(45, 85, 82)}
724
- disabled={this.state.isDeploying}
725
- title="Deploys immediately; the old instances keep running for their configured overlap before shutting down"
726
- onClick={() => {
727
- this.confirmForceDeployNow(makeDeployConfig(Date.now()));
728
- }}>
729
- ⚡ Force Deploy Now
730
- </button>
731
- <button
732
- className={css.pad2(12, 8).button.bord2(0, 90, 30).hsl(0, 85, 65).colorhsl(0, 0, 100).fontWeight("bold")}
733
- disabled={this.state.isDeploying}
734
- title="Deploys immediately AND shuts down the old instances immediately (overlap of 0)"
735
- onClick={() => {
736
- this.confirmForceDeployNow(makeDeployConfig(Date.now(), 0));
737
- }}>
738
- ⚡ Force Deploy Now, No Overlap
739
- </button>
740
- </div>
741
681
  </div>;
742
682
  })()}
743
683
 
@@ -77,21 +77,16 @@ export class UpdateButtons extends qreact.Component<{
77
77
  state = t.state({
78
78
  isDeploying: t.type(false),
79
79
  });
80
- // Deploys all outdated services to latestRef. "scheduled" delays the release by the configured overlap (an unrelated value, but a reasonable delay); "now" releases immediately but the old instances still run out their configured overlap; "nowNoOverlap" releases immediately AND shuts the old instances down immediately.
81
- private deployAll(outdatedServices: ServiceConfig[], latestRef: string, mode: "scheduled" | "now" | "nowNoOverlap") {
80
+ // Deploys all outdated services to latestRef, always immediately. The overlap is what keeps the service up - the old instances keep running for it, so connections have somewhere to go while clients move across. noOverlap kills them at once instead.
81
+ private deployAll(outdatedServices: ServiceConfig[], latestRef: string, noOverlap: boolean) {
82
82
  this.state.isDeploying = true;
83
83
  // Props are synchronized state, so everything the async work needs is cloned into plain locals HERE, in the synced part
84
84
  let toDeploy = outdatedServices.map(service => {
85
85
  let updated = deepCloneJSON(service);
86
86
  updated.parameters.gitRef = latestRef;
87
- if (mode === "scheduled") {
88
- let overlap = updated.parameters.overlapTime ?? DEFAULT_OVERLAP_TIME;
89
- updated.parameters.releaseTime = Date.now() + overlap;
90
- } else {
91
- updated.parameters.releaseTime = Date.now();
92
- if (mode === "nowNoOverlap") {
93
- updated.parameters.overlapTime = 0;
94
- }
87
+ updated.parameters.releaseTime = Date.now();
88
+ if (noOverlap) {
89
+ updated.parameters.overlapTime = 0;
95
90
  }
96
91
  return updated;
97
92
  });
@@ -112,14 +107,14 @@ export class UpdateButtons extends qreact.Component<{
112
107
  content: <div className={css.vbox(12).pad2(20)}>
113
108
  <div className={css.boldStyle.fontSize(16)}>⚡ Deploy all {outdatedServices.length} services now?</div>
114
109
  <div>{noOverlap
115
- ? "All services release immediately AND their old instances shut down immediately (overlap of 0)."
116
- : "All services release immediately. Their old instances keep running for their configured overlap before shutting down."}</div>
110
+ ? "The old instances are shut down as soon as the new ones are up (overlap of 0), so anything still connected to them is dropped."
111
+ : "The old instances keep running for their configured overlap before shutting down, so connections have somewhere to go while clients move across."}</div>
117
112
  <div className={css.hbox(10)}>
118
113
  <button
119
114
  className={css.pad2(12, 8).button + (noOverlap && css.bord2(0, 90, 30).hsl(0, 85, 65).colorhsl(0, 0, 100).fontWeight("bold") || css.bord2(45, 80, 35).hsl(45, 85, 82))}
120
115
  onClick={() => {
121
116
  modal && modal.close();
122
- this.deployAll(outdatedServices, latestRef, noOverlap ? "nowNoOverlap" : "now");
117
+ this.deployAll(outdatedServices, latestRef, noOverlap);
123
118
  }}>
124
119
  ⚡ Deploy Now
125
120
  </button>
@@ -188,8 +183,9 @@ export class UpdateButtons extends qreact.Component<{
188
183
  <button
189
184
  className={buttonStyle.hsl(120, 70, 90)}
190
185
  disabled={this.state.isDeploying}
186
+ title="Deploys now; the old instances keep running for their configured overlap before shutting down"
191
187
  onClick={() => {
192
- this.deployAll(outdatedServices, gitInfo.latestRef, "scheduled");
188
+ this.confirmDeployAllNow(outdatedServices, gitInfo.latestRef, false);
193
189
  }}
194
190
  >
195
191
  <div>
@@ -204,28 +200,16 @@ export class UpdateButtons extends qreact.Component<{
204
200
  <RenderGitRefInfo gitRef={ref} />
205
201
  </div>)}
206
202
  </button>
207
- <button
208
- className={buttonStyle.hsl(45, 85, 82)}
209
- disabled={this.state.isDeploying}
210
- title="Deploys immediately; the old instances keep running for their configured overlap before shutting down"
211
- onClick={() => {
212
- this.confirmDeployAllNow(outdatedServices, gitInfo.latestRef, false);
213
- }}
214
- >
215
- <div>
216
- {bigEmoji("⚡")} <span>{this.state.isDeploying && "⏳ Deploying..." || `Deploy All (${outdatedServices.length}) Now (no delay)`}</span>
217
- </div>
218
- </button>
219
203
  <button
220
204
  className={buttonStyle.hsl(0, 85, 70)}
221
205
  disabled={this.state.isDeploying}
222
- title="Deploys immediately AND shuts down the old instances immediately (overlap of 0)"
206
+ title="Deploys now AND shuts the old instances down as soon as the new ones are up (overlap of 0)"
223
207
  onClick={() => {
224
208
  this.confirmDeployAllNow(outdatedServices, gitInfo.latestRef, true);
225
209
  }}
226
210
  >
227
211
  <div>
228
- {bigEmoji("⚡")} <span>{this.state.isDeploying && "⏳ Deploying..." || `Deploy All (${outdatedServices.length}) Now (no delay, no overlap)`}</span>
212
+ {bigEmoji("⚡")} <span>{this.state.isDeploying && "⏳ Deploying..." || `Deploy All (${outdatedServices.length}), No Overlap`}</span>
229
213
  </div>
230
214
  </button>
231
215
  </>}