sliftutils 0.13.0 → 0.15.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/.cursorrules +4 -0
- package/index.d.ts +4 -2
- package/misc/getSecret.d.ts +1 -0
- package/misc/getSecret.ts +90 -19
- package/package.json +1 -1
- package/render-utils/InputLabel.d.ts +3 -2
- package/render-utils/InputLabel.tsx +15 -24
package/.cursorrules
CHANGED
|
@@ -79,6 +79,10 @@ Coding Styles
|
|
|
79
79
|
|
|
80
80
|
Never use environment variables. All configuration should be on the disk, or, if specific to a current run, passed as command line parameters.
|
|
81
81
|
|
|
82
|
+
Never use inline styles, always use the CSS helper.
|
|
83
|
+
|
|
84
|
+
Don't use as any.
|
|
85
|
+
|
|
82
86
|
|
|
83
87
|
General Styling
|
|
84
88
|
Never use em or rem. Only use px or vw/vh/%.
|
package/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ declare module "sliftutils/misc/fs" {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
declare module "sliftutils/misc/getSecret" {
|
|
22
|
+
export declare function resetSecret(key: string): void;
|
|
22
23
|
export declare const getSecret: {
|
|
23
24
|
(key: string): Promise<string>;
|
|
24
25
|
clear(key: string): void;
|
|
@@ -135,7 +136,6 @@ declare module "sliftutils/render-utils/Input" {
|
|
|
135
136
|
declare module "sliftutils/render-utils/InputLabel" {
|
|
136
137
|
import preact from "preact";
|
|
137
138
|
import { InputProps } from "./Input";
|
|
138
|
-
import { URLParamStr } from "./URLParam";
|
|
139
139
|
export type InputLabelProps = Omit<InputProps, "label" | "title"> & {
|
|
140
140
|
label?: preact.ComponentChild;
|
|
141
141
|
number?: boolean;
|
|
@@ -164,7 +164,9 @@ declare module "sliftutils/render-utils/InputLabel" {
|
|
|
164
164
|
render(): preact.JSX.Element;
|
|
165
165
|
}
|
|
166
166
|
export declare class InputLabelURL extends preact.Component<InputLabelProps & {
|
|
167
|
-
persisted:
|
|
167
|
+
persisted: {
|
|
168
|
+
value: unknown;
|
|
169
|
+
};
|
|
168
170
|
}> {
|
|
169
171
|
render(): preact.JSX.Element;
|
|
170
172
|
}
|
package/misc/getSecret.d.ts
CHANGED
package/misc/getSecret.ts
CHANGED
|
@@ -1,28 +1,99 @@
|
|
|
1
1
|
|
|
2
2
|
import { cache } from "socket-function/src/caching";
|
|
3
|
-
import
|
|
4
|
-
import fs from "fs";
|
|
3
|
+
import { isNode } from "typesafecss";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
const secretStoragePrefix = "secret_";
|
|
6
|
+
|
|
7
|
+
function getStorageKey(key: string) {
|
|
8
|
+
return secretStoragePrefix + key.replace(/[\/\\\.]/g, "_");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resetSecret(key: string) {
|
|
12
|
+
if (isNode()) {
|
|
13
|
+
throw new Error("resetSecret is only supported in the browser");
|
|
11
14
|
}
|
|
15
|
+
localStorage.removeItem(getStorageKey(key));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getSecret = cache(async function getSecret(key: string): Promise<string> {
|
|
19
|
+
if (isNode()) {
|
|
20
|
+
const os = await import("os");
|
|
21
|
+
const fs = await import("fs");
|
|
22
|
+
const jsonIndex = key.indexOf(".json");
|
|
23
|
+
if (jsonIndex === -1) {
|
|
24
|
+
const filePath = os.homedir() + "/" + key;
|
|
25
|
+
return fs.readFileSync(filePath, "utf-8").trim();
|
|
26
|
+
}
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
28
|
+
const pathPart = key.slice(0, jsonIndex + ".json".length);
|
|
29
|
+
const filePath = os.homedir() + "/" + pathPart;
|
|
30
|
+
const contents = fs.readFileSync(filePath, "utf-8");
|
|
31
|
+
const json = JSON.parse(contents);
|
|
17
32
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
const keyPart = key.slice(jsonIndex + ".json.".length);
|
|
34
|
+
if (!keyPart) {
|
|
35
|
+
return JSON.stringify(json);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const value = json[keyPart];
|
|
39
|
+
if (value === undefined) {
|
|
40
|
+
throw new Error(`Expected key "${keyPart}" in ${filePath}, was undefined`);
|
|
41
|
+
}
|
|
42
|
+
return String(value);
|
|
21
43
|
}
|
|
22
44
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
45
|
+
// Browser implementation
|
|
46
|
+
const storageKey = getStorageKey(key);
|
|
47
|
+
const cached = localStorage.getItem(storageKey);
|
|
48
|
+
if (cached) {
|
|
49
|
+
return cached;
|
|
26
50
|
}
|
|
27
|
-
|
|
28
|
-
|
|
51
|
+
|
|
52
|
+
// Show modal to prompt user for secret
|
|
53
|
+
const { showFullscreenModal, FullscreenModal } = await import("../render-utils/FullscreenModal");
|
|
54
|
+
const { showModal } = await import("../render-utils/modal");
|
|
55
|
+
const { observable } = await import("mobx");
|
|
56
|
+
const preact = await import("preact");
|
|
57
|
+
|
|
58
|
+
return new Promise<string>((resolve) => {
|
|
59
|
+
const state = observable({
|
|
60
|
+
value: "",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const { close } = showModal({
|
|
64
|
+
contents: preact.createElement(FullscreenModal, {
|
|
65
|
+
onCancel: () => {
|
|
66
|
+
// Don't allow cancel without a value
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
preact.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 10 } },
|
|
70
|
+
preact.createElement("div", { style: { fontWeight: "bold" } }, `Enter secret for: ${key}`),
|
|
71
|
+
preact.createElement("input", {
|
|
72
|
+
type: "password",
|
|
73
|
+
style: { padding: 10, fontSize: 16 },
|
|
74
|
+
onInput: (e: Event) => {
|
|
75
|
+
state.value = (e.target as HTMLInputElement).value;
|
|
76
|
+
},
|
|
77
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
78
|
+
if (e.code === "Enter" && state.value) {
|
|
79
|
+
localStorage.setItem(storageKey, state.value);
|
|
80
|
+
close();
|
|
81
|
+
resolve(state.value);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
preact.createElement("button", {
|
|
86
|
+
style: { padding: 10, fontSize: 16, cursor: "pointer" },
|
|
87
|
+
onClick: () => {
|
|
88
|
+
if (state.value) {
|
|
89
|
+
localStorage.setItem(storageKey, state.value);
|
|
90
|
+
close();
|
|
91
|
+
resolve(state.value);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
}, "Save"),
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import preact from "preact";
|
|
2
2
|
import { InputProps } from "./Input";
|
|
3
|
-
import { URLParamStr } from "./URLParam";
|
|
4
3
|
export type InputLabelProps = Omit<InputProps, "label" | "title"> & {
|
|
5
4
|
label?: preact.ComponentChild;
|
|
6
5
|
number?: boolean;
|
|
@@ -29,7 +28,9 @@ export declare class InputLabel extends preact.Component<InputLabelProps> {
|
|
|
29
28
|
render(): preact.JSX.Element;
|
|
30
29
|
}
|
|
31
30
|
export declare class InputLabelURL extends preact.Component<InputLabelProps & {
|
|
32
|
-
persisted:
|
|
31
|
+
persisted: {
|
|
32
|
+
value: unknown;
|
|
33
|
+
};
|
|
33
34
|
}> {
|
|
34
35
|
render(): preact.JSX.Element;
|
|
35
36
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import preact from "preact";
|
|
2
2
|
import { Input, InputProps } from "./Input";
|
|
3
3
|
import { css } from "typesafecss";
|
|
4
|
-
import { URLParamStr } from "./URLParam";
|
|
5
4
|
import { lazy } from "socket-function/src/caching";
|
|
6
5
|
import { observer } from "./observer";
|
|
7
6
|
import { observable } from "mobx";
|
|
@@ -83,29 +82,21 @@ export class InputLabel extends preact.Component<InputLabelProps> {
|
|
|
83
82
|
|
|
84
83
|
if ((!props.type || props.type === "number") && props.useDateUI) {
|
|
85
84
|
let value = String(props.value);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// NOTE: When using forceInputValueUpdatesWhenFocused we need hot, otherwise the user's updates
|
|
99
|
-
// won't be visible.
|
|
100
|
-
props.hot = true;
|
|
101
|
-
if (isJSNumber(value)) {
|
|
102
|
-
value = formatDateTimeForInput(+value);
|
|
103
|
-
} else {
|
|
104
|
-
value = "";
|
|
105
|
-
}
|
|
106
|
-
props.value = value;
|
|
107
|
-
addValueMapping(value => (+new Date(value).getTime() || "") + "");
|
|
85
|
+
props.type = "datetime-local";
|
|
86
|
+
props.edit = false;
|
|
87
|
+
props.textarea = false;
|
|
88
|
+
props.number = false;
|
|
89
|
+
props.forceInputValueUpdatesWhenFocused = true;
|
|
90
|
+
// NOTE: When using forceInputValueUpdatesWhenFocused we need hot, otherwise the user's updates
|
|
91
|
+
// won't be visible.
|
|
92
|
+
props.hot = true;
|
|
93
|
+
if (isJSNumber(value)) {
|
|
94
|
+
value = formatDateTimeForInput(+value);
|
|
95
|
+
} else {
|
|
96
|
+
value = "";
|
|
108
97
|
}
|
|
98
|
+
props.value = value;
|
|
99
|
+
addValueMapping(value => (+new Date(value).getTime() || "") + "");
|
|
109
100
|
}
|
|
110
101
|
if (props.fontSize !== undefined) {
|
|
111
102
|
props.style = { ...props.style as any, fontSize: props.fontSize };
|
|
@@ -260,7 +251,7 @@ const pencilSVG = lazy(() => {
|
|
|
260
251
|
|
|
261
252
|
@observer
|
|
262
253
|
export class InputLabelURL extends preact.Component<InputLabelProps & {
|
|
263
|
-
persisted:
|
|
254
|
+
persisted: { value: unknown };
|
|
264
255
|
}> {
|
|
265
256
|
render() {
|
|
266
257
|
this.props.persisted.value;
|