querysub 0.697.0 → 0.699.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 +2 -2
- package/src/0-path-value-core/PathRouter.ts +0 -1
- package/src/0-path-value-core/ValidStateComputer.ts +0 -1
- package/src/1-path-client/pathValueClientWatcher.ts +0 -2
- package/src/2-proxy/PathValueProxyWatcher.ts +8 -12
- package/src/4-deploy/git.ts +4 -0
- package/src/deployManager/components/deployButtons.tsx +9 -10
- package/src/errors.ts +18 -0
- package/src/library-components/EdgeNodeSelector.tsx +2 -3
- package/src/library-components/InputLabel.tsx +1 -2
- package/src/library-components/SimpleNotification.tsx +5 -1
- package/src/library-components/URLParam.ts +1 -1
- package/src/library-components/uncaughtToast.tsx +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.699.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",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"pako": "^2.1.0",
|
|
81
81
|
"peggy": "^5.0.6",
|
|
82
82
|
"sliftutils": "^1.7.149",
|
|
83
|
-
"socket-function": "^1.
|
|
83
|
+
"socket-function": "^1.3.1",
|
|
84
84
|
"terser": "^5.31.0",
|
|
85
85
|
"typenode": "^6.6.1",
|
|
86
86
|
"typesafecss": "^0.32.0",
|
|
@@ -492,7 +492,6 @@ export class PathRouter {
|
|
|
492
492
|
public static isLocalPath(path: string): boolean {
|
|
493
493
|
return path.startsWith(LOCAL_DOMAIN_PATH);
|
|
494
494
|
}
|
|
495
|
-
@measureFnc
|
|
496
495
|
public static isSelfAuthority(path: string): boolean {
|
|
497
496
|
if (this.isLocalPath(path)) return true;
|
|
498
497
|
let ourSpec = authorityLookup.getOurSpec();
|
|
@@ -557,7 +557,6 @@ export class ClientWatcher {
|
|
|
557
557
|
// - You can just fragment your writes into different components to isolate any slow loading parts, so partial
|
|
558
558
|
// loading of data really isn't needed.
|
|
559
559
|
// NOTE: Takes ownership of paths and parentPaths, so... don't mutate them after calling this!
|
|
560
|
-
@measureFnc
|
|
561
560
|
public setWatches(watchSpec: WatchSpec) {
|
|
562
561
|
// NOTE: Yes, setWatches shows up as being slow in the profiler. I added measureBlock to every single section, and none of them showed up as being slow.
|
|
563
562
|
// - My best guess is that after we clobber values in various lookups, but BEFORE we return, the destructor for those old resources runs. So their destruct time gets put in our profiler?
|
|
@@ -663,7 +662,6 @@ export class ClientWatcher {
|
|
|
663
662
|
// NOTE: This synchronously predicts the values, so you don't need to wait. When the promise resolves
|
|
664
663
|
// the values will be committed and they won't belost (although it is still possible for them to become
|
|
665
664
|
// rejected).
|
|
666
|
-
@measureFnc
|
|
667
665
|
public setValues(
|
|
668
666
|
config: {
|
|
669
667
|
values: Map<string, Value>;
|
|
@@ -581,8 +581,7 @@ export class PathValueProxyWatcher {
|
|
|
581
581
|
}
|
|
582
582
|
};
|
|
583
583
|
|
|
584
|
-
|
|
585
|
-
public getCallbackPathValue = measureWrap((pathStr: string, syncParentKeys?: "parentKeys"): PathValue | undefined => {
|
|
584
|
+
public getCallbackPathValue = ((pathStr: string, syncParentKeys?: "parentKeys"): PathValue | undefined => {
|
|
586
585
|
const watcher = this.runningWatcher;
|
|
587
586
|
if (!watcher) {
|
|
588
587
|
debugger;
|
|
@@ -671,9 +670,8 @@ export class PathValueProxyWatcher {
|
|
|
671
670
|
}
|
|
672
671
|
|
|
673
672
|
return pathValue;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
public getCallback = measureWrap((pathStr: string, syncParentKeys?: "parentKeys", readTransparent?: "readTransparent"): { value: unknown } | undefined => {
|
|
673
|
+
});
|
|
674
|
+
public getCallback = ((pathStr: string, syncParentKeys?: "parentKeys", readTransparent?: "readTransparent"): { value: unknown } | undefined => {
|
|
677
675
|
if (this.runningWatcher && this.runningWatcher.evaluationGetCallbacks.length > 0) {
|
|
678
676
|
for (let callback of this.runningWatcher.evaluationGetCallbacks) callback(pathStr);
|
|
679
677
|
}
|
|
@@ -752,7 +750,7 @@ export class PathValueProxyWatcher {
|
|
|
752
750
|
}
|
|
753
751
|
return { value: readValue };
|
|
754
752
|
}
|
|
755
|
-
}
|
|
753
|
+
});
|
|
756
754
|
|
|
757
755
|
// We exclude undefined, BUT, we include "null", etc.
|
|
758
756
|
// - This differs from javascript, but... due to how deletions work, we can't/don't differentiate between
|
|
@@ -762,8 +760,7 @@ export class PathValueProxyWatcher {
|
|
|
762
760
|
return value?.value !== undefined;
|
|
763
761
|
};
|
|
764
762
|
|
|
765
|
-
|
|
766
|
-
public setCallback = measureWrap((pathStr: string, value: unknown, inRecursion = false, allowSpecial = false): void => {
|
|
763
|
+
public setCallback = ((pathStr: string, value: unknown, inRecursion = false, allowSpecial = false): void => {
|
|
767
764
|
if (this.runningWatcher && this.runningWatcher.evaluationSetCallbacks.length > 0) {
|
|
768
765
|
for (let callback of this.runningWatcher.evaluationSetCallbacks) callback(pathStr);
|
|
769
766
|
}
|
|
@@ -939,10 +936,9 @@ export class PathValueProxyWatcher {
|
|
|
939
936
|
}
|
|
940
937
|
watcher.pendingWrites.set(pathStr, value);
|
|
941
938
|
}
|
|
942
|
-
}
|
|
939
|
+
});
|
|
943
940
|
/** Syncs keys AND values (as we won't return a key for a value that is undefined). */
|
|
944
|
-
|
|
945
|
-
public getKeys = measureWrap((pathStr: string): string[] => {
|
|
941
|
+
public getKeys = ((pathStr: string): string[] => {
|
|
946
942
|
if (this.runningWatcher && this.runningWatcher.evaluationGetKeysCallbacks.length > 0) {
|
|
947
943
|
for (let callback of this.runningWatcher.evaluationGetKeysCallbacks) callback(pathStr);
|
|
948
944
|
}
|
|
@@ -1026,7 +1022,7 @@ export class PathValueProxyWatcher {
|
|
|
1026
1022
|
keysArray.sort();
|
|
1027
1023
|
|
|
1028
1024
|
return keysArray;
|
|
1029
|
-
}
|
|
1025
|
+
});
|
|
1030
1026
|
|
|
1031
1027
|
private getSymbol = (pathStr: string, symbol: symbol): { value: unknown } | undefined => {
|
|
1032
1028
|
if (symbol === Symbol.toPrimitive) return {
|
package/src/4-deploy/git.ts
CHANGED
|
@@ -144,6 +144,10 @@ export async function getGitDiff(gitDir = "."): Promise<string> {
|
|
|
144
144
|
return diff;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
export async function gitPull(gitDir = ".") {
|
|
148
|
+
await runGitCommand(`git pull`, { cwd: gitDir });
|
|
149
|
+
}
|
|
150
|
+
|
|
147
151
|
export async function getLatestRefOnUpstreamBranch(gitDir = ".") {
|
|
148
152
|
await runGitCommand(`git fetch`, { cwd: gitDir });
|
|
149
153
|
return (await runGitCommand(`git rev-parse @{upstream}`, { cwd: gitDir })).trim();
|
|
@@ -108,7 +108,6 @@ export function confirmNoOverlapDeploy(actionLabel: string, onConfirm: () => voi
|
|
|
108
108
|
|
|
109
109
|
const CONFIRM_LIST_MAX_HEIGHT = 220;
|
|
110
110
|
|
|
111
|
-
/** Deploying every outdated service restarts all of them at once, which is most of the cluster — worth naming what is about to restart before it happens. */
|
|
112
111
|
class DeployAllConfirmModal extends qreact.Component<{ titles: string[]; latestRef: string; onConfirm: () => void }> {
|
|
113
112
|
render() {
|
|
114
113
|
let count = this.props.titles.length;
|
|
@@ -149,16 +148,14 @@ export class UpdateButtons extends qreact.Component<{
|
|
|
149
148
|
state = t.state({
|
|
150
149
|
isDeploying: t.type(false),
|
|
151
150
|
});
|
|
152
|
-
|
|
153
|
-
private deployAll(outdatedServices: ServiceConfig[], latestRef: string) {
|
|
151
|
+
private deployAll(outdatedServices: ServiceConfig[], latestRef: string, noOverlap: boolean) {
|
|
154
152
|
this.state.isDeploying = true;
|
|
155
153
|
// Props are synchronized state, so everything the async work needs is cloned into plain locals HERE, in the synced part
|
|
156
154
|
let toDeploy = outdatedServices.map(service => {
|
|
157
155
|
let updated = deepCloneJSON(service);
|
|
158
156
|
updated.parameters.gitRef = latestRef;
|
|
159
157
|
updated.parameters.releaseTime = Date.now();
|
|
160
|
-
|
|
161
|
-
updated.parameters.overlapTime = DEFAULT_OVERLAP_TIME;
|
|
158
|
+
updated.parameters.overlapTime = noOverlap ? 0 : DEFAULT_OVERLAP_TIME;
|
|
162
159
|
return updated;
|
|
163
160
|
});
|
|
164
161
|
let controller = MachineServiceController(SocketFunction.browserNodeId());
|
|
@@ -232,7 +229,7 @@ export class UpdateButtons extends qreact.Component<{
|
|
|
232
229
|
confirmDeployAll({
|
|
233
230
|
titles: outdatedServices.map(service => service.info.title),
|
|
234
231
|
latestRef: gitInfo.latestRef,
|
|
235
|
-
onConfirm: () => this.deployAll(outdatedServices, gitInfo.latestRef),
|
|
232
|
+
onConfirm: () => this.deployAll(outdatedServices, gitInfo.latestRef, false),
|
|
236
233
|
});
|
|
237
234
|
}}
|
|
238
235
|
>
|
|
@@ -249,15 +246,17 @@ export class UpdateButtons extends qreact.Component<{
|
|
|
249
246
|
</div>)}
|
|
250
247
|
</button>
|
|
251
248
|
<button
|
|
252
|
-
className={buttonStyle.hsl(
|
|
249
|
+
className={buttonStyle.hsl(0, 85, 70)}
|
|
253
250
|
disabled={this.state.isDeploying}
|
|
254
|
-
title="Deploys
|
|
251
|
+
title="Deploys now AND shuts the old instances down as soon as the new ones are up (overlap of 0)"
|
|
255
252
|
onClick={() => {
|
|
256
|
-
|
|
253
|
+
confirmNoOverlapDeploy(`Deploy All (${outdatedServices.length}), No Overlap`, () => {
|
|
254
|
+
this.deployAll(outdatedServices, gitInfo.latestRef, true);
|
|
255
|
+
});
|
|
257
256
|
}}
|
|
258
257
|
>
|
|
259
258
|
<div>
|
|
260
|
-
{bigEmoji("⚡")} <span>{this.state.isDeploying && "⏳ Deploying..." || `Deploy All
|
|
259
|
+
{bigEmoji("⚡")} <span>{this.state.isDeploying && "⏳ Deploying..." || `Deploy All (${outdatedServices.length}), No Overlap`}</span>
|
|
261
260
|
</div>
|
|
262
261
|
</button>
|
|
263
262
|
</>}
|
package/src/errors.ts
CHANGED
|
@@ -121,6 +121,24 @@ export function errorify(error: any, messageOverride?: string) {
|
|
|
121
121
|
return errorObj;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
const STACK_FRAME_PREFIX = /^(\s*)at\s+/;
|
|
125
|
+
const STACK_FRAME_POSITION = /:(\d+):(\d+)\)?\s*$/;
|
|
126
|
+
|
|
127
|
+
export function filterErrorStack(error: unknown): string {
|
|
128
|
+
let stack = errorify(error).stack || "";
|
|
129
|
+
return stack.split("\n").map(filterStackFrame).join("\n");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function filterStackFrame(line: string): string {
|
|
133
|
+
let prefix = STACK_FRAME_PREFIX.exec(line);
|
|
134
|
+
if (!prefix) return line;
|
|
135
|
+
let name = line.slice(prefix[0].length).split(" (")[0].trim();
|
|
136
|
+
if (name.includes("://")) name = "";
|
|
137
|
+
let position = STACK_FRAME_POSITION.exec(line);
|
|
138
|
+
if (!position) return `${prefix[1]}at ${name}`;
|
|
139
|
+
return `${prefix[1]}at ${name && name + " " || ""}(${position[1]}:${position[2]})`;
|
|
140
|
+
}
|
|
141
|
+
|
|
124
142
|
export function assertValue<T>(value: T | undefined | null, message?: string): T {
|
|
125
143
|
if (value === undefined || value === null) {
|
|
126
144
|
throw new Error(`Value is ${value === undefined ? "undefined" : "null"}, ${message}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { css } from "typesafecss";
|
|
2
2
|
import { qreact } from "../4-dom/qreact";
|
|
3
3
|
import { Querysub } from "../4-querysub/Querysub";
|
|
4
|
-
import { sort, timeInSecond } from "socket-function/src/misc";
|
|
4
|
+
import { sort, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
5
5
|
import { formatTime } from "socket-function/src/formatting/format";
|
|
6
6
|
import { isCurrentUserSuperUser } from "../user-implementation/userData";
|
|
7
7
|
import type { EdgeNodeConfig, EdgeNodeStat } from "../4-deploy/edgeNodes";
|
|
@@ -12,8 +12,7 @@ const EDGE_NODE_URL_PARAM = "edgenode";
|
|
|
12
12
|
export class EdgeNodeSelector extends qreact.Component<{}> {
|
|
13
13
|
render() {
|
|
14
14
|
if (!(isCurrentUserSuperUser() || location.hostname.startsWith("127-0-0-1"))) return undefined;
|
|
15
|
-
|
|
16
|
-
Querysub.nowDelayed(timeInSecond);
|
|
15
|
+
Querysub.nowDelayed(timeInMinute);
|
|
17
16
|
let stats = globalThis.EDGE_NODE_STATS;
|
|
18
17
|
const booted = (globalThis as any).BOOTED_EDGE_NODE as EdgeNodeConfig | undefined;
|
|
19
18
|
if (!booted) return undefined;
|
|
@@ -50,7 +50,6 @@ export class InputLabel extends qreact.Component<InputLabelProps> {
|
|
|
50
50
|
render() {
|
|
51
51
|
let { ...props } = { ...this.props };
|
|
52
52
|
let label = props.label || this.props.children;
|
|
53
|
-
(this.props as any).title = this.props.tooltip;
|
|
54
53
|
if (props.fontSize !== undefined) {
|
|
55
54
|
props.style = { ...props.style as any, fontSize: props.fontSize };
|
|
56
55
|
}
|
|
@@ -152,7 +151,7 @@ export class InputLabel extends qreact.Component<InputLabelProps> {
|
|
|
152
151
|
</span>;
|
|
153
152
|
}
|
|
154
153
|
return (
|
|
155
|
-
<label onClick={onClick} class={
|
|
154
|
+
<label onClick={onClick} title={props.tooltip} class={
|
|
156
155
|
css.hbox(props.checkbox ? CHECKBOX_LABEL_GAP : LABEL_GAP).button.relative
|
|
157
156
|
+ " trigger-hover "
|
|
158
157
|
+ props.outerClass
|
|
@@ -29,6 +29,7 @@ export class SimpleNotification extends qreact.Component<{
|
|
|
29
29
|
seqNum = nextSeqNum++;
|
|
30
30
|
render() {
|
|
31
31
|
let { type, title, children, usesClickEvents } = this.props;
|
|
32
|
+
|
|
32
33
|
let color = (
|
|
33
34
|
type === "info" && { h: 188, s: 52 }
|
|
34
35
|
|| type === "warn" && { h: 50, s: 100 }
|
|
@@ -51,7 +52,10 @@ export class SimpleNotification extends qreact.Component<{
|
|
|
51
52
|
+ " " + animClassName
|
|
52
53
|
}
|
|
53
54
|
onClick={() => {
|
|
54
|
-
|
|
55
|
+
let copyText = this.props.tooltip || title;
|
|
56
|
+
Querysub.onCommitFinished(() => {
|
|
57
|
+
void navigator.clipboard.writeText(copyText);
|
|
58
|
+
});
|
|
55
59
|
if (!usesClickEvents) {
|
|
56
60
|
closeCurrentNotification();
|
|
57
61
|
}
|
|
@@ -235,7 +235,7 @@ function loadParamsFromURL() {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
function syncStateToStorage() {
|
|
238
|
-
Querysub.createWriteWatcher(function
|
|
238
|
+
Querysub.createWriteWatcher(function syncStateToStorage() {
|
|
239
239
|
for (let [key, value] of Object.entries(data().params)) {
|
|
240
240
|
if (localStorageKeys.has(key)) {
|
|
241
241
|
localStorage.setItem(key, JSON.stringify(value));
|
|
@@ -4,6 +4,7 @@ import { SimpleNotification } from "./SimpleNotification";
|
|
|
4
4
|
import { qreact } from "../4-dom/qreact";
|
|
5
5
|
import { Querysub } from "../4-querysub/Querysub";
|
|
6
6
|
import { css } from "../4-dom/css";
|
|
7
|
+
import { filterErrorStack } from "../errors";
|
|
7
8
|
|
|
8
9
|
function onUncaught(...args: unknown[]) {
|
|
9
10
|
let error = args[4] as Error;
|
|
@@ -66,7 +67,7 @@ function onMessage(config: {
|
|
|
66
67
|
notification: <SimpleNotification
|
|
67
68
|
type={config.type}
|
|
68
69
|
title={title}
|
|
69
|
-
tooltip={config.stack}
|
|
70
|
+
tooltip={filterErrorStack(config.stack)}
|
|
70
71
|
>
|
|
71
72
|
{/* NOTE: I've stopping showing this info. It's annoying, as they can just look in the console
|
|
72
73
|
(as most of these come from console.error anyways). */}
|