sliftutils 1.2.1 → 1.2.3
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/.cursor/rules/api-calls.mdc +16 -0
- package/.cursor/rules/coding-styles.mdc +50 -0
- package/.cursor/rules/components.mdc +44 -0
- package/.cursor/rules/disk-collection.mdc +31 -0
- package/.cursor/rules/general-guidelines.mdc +11 -0
- package/.cursor/rules/mobx-state.mdc +33 -0
- package/.cursor/rules/styling-css.mdc +99 -0
- package/builders/setup.ts +2 -2
- package/index.d.ts +104 -15
- package/misc/zip.d.ts +2 -13
- package/misc/zip.ts +2 -104
- package/package.json +2 -2
- package/render-utils/InputLabel.d.ts +1 -2
- package/render-utils/InputLabel.tsx +1 -1
- package/storage/backblaze.d.ts +97 -0
- package/storage/backblaze.ts +915 -0
- package/yarn.lock +4 -4
- package/.cursorrules +0 -251
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: API calls and controller usage
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Calls
|
|
7
|
+
|
|
8
|
+
When in an event callback (which must be async):
|
|
9
|
+
```typescript
|
|
10
|
+
APIController(getExtNodeId()).getModels.promise()
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When in a render function/context:
|
|
14
|
+
```typescript
|
|
15
|
+
APIController(getExtNodeId()).getModels()
|
|
16
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: General coding styles and conventions
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Coding Styles
|
|
7
|
+
|
|
8
|
+
- Times should almost always be in milliseconds, and if not told, you should assume a time is in milliseconds.
|
|
9
|
+
- Don't make functions that will never be reused and are short. This just makes the code too confusing. If the function is under five lines and it's not being reused, you shouldn't create it, unless instructed explicitly to create it.
|
|
10
|
+
- Comments should be used sparingly and only if it's required to explain what's being done. If you're calling a function called `createImageName` and the comment is called "create imageName", you will be fired because that's just stupid. It's a waste of lines.
|
|
11
|
+
- Comments go in the line before, never after the semicolon.
|
|
12
|
+
- Try not to use `null`, and instead always use `undefined`.
|
|
13
|
+
- Almost never check for `undefined` or `null`, just check for falsey.
|
|
14
|
+
- When functions have more than one primitive parameter, such that you could confuse them (even something as simple as start time and end time), you should put them inside of an object and call that object `config`, so the function only has a single parameter.
|
|
15
|
+
- Never use return codes, always prefer to throw on error. Include a lot of context information in the error. If any values might be extremely large, such as parsing a file, limit them (ex, to 500 characters).
|
|
16
|
+
- Use double quotes, not single quotes.
|
|
17
|
+
- Never use the ternary operator. Instead, do this: `x ? y : z` => `x && y || z`.
|
|
18
|
+
- Never use non-null assertion operator, instead check the value, and if necessary (because it is accessed in nested functions), use a `const` variable to preserve the type.
|
|
19
|
+
- Errors should almost always use a template string to include the expected value, and the actual value, as in: `throw new Error(\`Expected X, was \${Y}\`);`
|
|
20
|
+
- Don't use switch statements. Use if/else statements instead.
|
|
21
|
+
- Don't use `!` when accessing a value from a map. Use the get / if undefined initialize and set, and then use style. It's faster, and more type safe.
|
|
22
|
+
- Sort with this function, which takes a single function to map each object to a sortable value:
|
|
23
|
+
```typescript
|
|
24
|
+
import { sort } from "socket-function/src/misc";
|
|
25
|
+
export function sort<T>(arr: T[], sortKey: (obj: T) => unknown);
|
|
26
|
+
```
|
|
27
|
+
- Prefer to return, instead of using else statements. Handle error cases, warn/throw, and then return. Your main case should be below, not in an if statement, just in the main code.
|
|
28
|
+
- Use template strings for error messages, which include the exact value that triggered the error, and the exact expectation it failed.
|
|
29
|
+
- Use functions to prevent code duplication only when something is actually duplicated.
|
|
30
|
+
- Don't recreate collections or URL parameters, import them instead.
|
|
31
|
+
- Do not redefine types. Import the types correctly.
|
|
32
|
+
- Do not use types when they can be inferred.
|
|
33
|
+
- Constants that are arbitrary and that we might to reconfigure should be near the top of the file under the imports, not buried within functions.
|
|
34
|
+
- Never use environment variables. All configuration should be on the disk, or, if specific to a current run, passed as command line parameters.
|
|
35
|
+
- Never use inline styles, always use the CSS helper.
|
|
36
|
+
- Don't use `as any`.
|
|
37
|
+
- When you're making fetch calls and you get a type of `any`, you need to cast it to the actual type, and not leave it as `any`. The same goes for when you're deserializing values.
|
|
38
|
+
- DO NOT redeclare constants and copy their value. JUST IMPORT THEM!
|
|
39
|
+
- DO NOT redeclare types. JUST IMPORT THEM!
|
|
40
|
+
- Do not try catch for no reason. If you can't actually handle the exception, just let it throw.
|
|
41
|
+
- For input events, always use `event.currentTarget`.
|
|
42
|
+
- Use `ref={elem => }` callbacks. NEVER use `.createRef`.
|
|
43
|
+
- NEVER render images with a fixed width+height. This will cause them to be stretched or cut off. This is terrible. Only set the width or height.
|
|
44
|
+
- Callback held should be avoided by using async and `import { PromiseObj } from "socket-function/src/misc";`. Most of the time, if there's an event callback, you should wrap it with a promise obj so that you can wait for it asynchronously.
|
|
45
|
+
- `import { keyBy, keyByArray } from "socket-function/src/misc";` Should be used when you need to create lookups from lists and you know what key you want. This can clobber values, or it can gather in an array.
|
|
46
|
+
```typescript
|
|
47
|
+
let example: Map<number, { x: number }> = keyBy([{ x: 5 }, { x: 6 }]);
|
|
48
|
+
let example: Map<number, { x: number }[]> = keyByArray([{ x: 5 }, { x: 5, version: 2 }, { x: 6 }]);
|
|
49
|
+
```
|
|
50
|
+
- Never use `alert`. If there's an error, you should throw it.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Custom components and input handling
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Components
|
|
7
|
+
|
|
8
|
+
## Anchor
|
|
9
|
+
|
|
10
|
+
`Anchor` (`<a>` tag), for linking using `URLParam`:
|
|
11
|
+
```tsx
|
|
12
|
+
<Anchor
|
|
13
|
+
params={[[todolistURL, listKey]]}
|
|
14
|
+
>
|
|
15
|
+
{list.name}
|
|
16
|
+
</Anchor>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`URLParam`, Parameters which are stored in the URL. The second argument is the default, which can be a number, string, or an object. Set and get with `.value`.
|
|
20
|
+
```typescript
|
|
21
|
+
const todolistURL = new URLParam("todolist", "");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## InputLabel
|
|
25
|
+
|
|
26
|
+
`InputLabel` and `InputLabelURL` should be used for inputs. For example:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
<InputLabelURL
|
|
30
|
+
label="Show Previous Video"
|
|
31
|
+
checkbox
|
|
32
|
+
persisted={showPreviousVideoURL}
|
|
33
|
+
/>
|
|
34
|
+
<InputLabel
|
|
35
|
+
label="Notes"
|
|
36
|
+
fillWidth
|
|
37
|
+
value={node.notes || ""}
|
|
38
|
+
onChangeValue={async (value) => {
|
|
39
|
+
const updatedNode = deepCloneJSON(node);
|
|
40
|
+
updatedNode.notes = value;
|
|
41
|
+
await VideoNode.set(node.id, updatedNode);
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: DiskCollection usage and patterns
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DiskCollection
|
|
7
|
+
|
|
8
|
+
If you read an object from a collection, and then mutate it, and want to set it in the collection, you have to shallow copy it, so the `DiskCollection` picks up the change.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
let x = collection.get("x");
|
|
12
|
+
x.y = Math.random();
|
|
13
|
+
// Not asynchronous, as sets are synchronous
|
|
14
|
+
collection.set("x", { ...x });
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The non-async functions should be called in render functions, the async functions should be called in event handlers, etc. Except for `.set`, which should be called in both cases.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
get(key: string): T | undefined;
|
|
21
|
+
async getPromise(key: string): Promise<T | undefined>;
|
|
22
|
+
set(key: string, value: T): void;
|
|
23
|
+
remove(key: string): void;
|
|
24
|
+
getKeys(): string[];
|
|
25
|
+
getKeysPromise(): Promise<string[]>;
|
|
26
|
+
getEntries(): [string, T][];
|
|
27
|
+
getValues(): T[];
|
|
28
|
+
async getValuesPromise(): Promise<T[]>;
|
|
29
|
+
getInfo(key: string);
|
|
30
|
+
async reset();
|
|
31
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: General project guidelines and tool usage
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# General Guidelines
|
|
7
|
+
|
|
8
|
+
- The code automatically updates on save, so do not ever run commands to rerun the site.
|
|
9
|
+
- Don't run shell commands when you need to create or move small code files. You should use tool calls to do this. You should also use tool calls to make files within folders. You don't need to make the folder, just make the file. The folder will automatically be created.
|
|
10
|
+
- If you need to add a dependency, don't just change the package.json file. Properly use `yarn add` so you get the latest version, unless the user specifies a version or tells you otherwise.
|
|
11
|
+
- Use tool calls to read files and directories where possible, instead of running "ls", "dir", etc.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: MobX state management and component structure
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MobX State Management
|
|
7
|
+
|
|
8
|
+
We use MobX for state management. Components should use a variable called `synced` that is an observable for their state, and never `Component.state`. All components need the `@observer` decorator.
|
|
9
|
+
|
|
10
|
+
For example:
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import preact from "preact";
|
|
14
|
+
import { observable } from "mobx";
|
|
15
|
+
|
|
16
|
+
@observer
|
|
17
|
+
class Example extends preact.Component {
|
|
18
|
+
synced = observable({
|
|
19
|
+
x: 0,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
render() {
|
|
23
|
+
return <div>
|
|
24
|
+
<button onClick={() => this.synced.x++}>
|
|
25
|
+
Click me
|
|
26
|
+
</button>
|
|
27
|
+
<p>
|
|
28
|
+
{this.synced.x}
|
|
29
|
+
</p>
|
|
30
|
+
</div>;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CSS and Styling guidelines
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Styling and CSS
|
|
7
|
+
|
|
8
|
+
- Never use `em` or `rem`. Only use `px` or `vw`/`vh`/`%`.
|
|
9
|
+
- Don't add font colors unless asked to add styling. Don't add any aesthetics beyond `hbox`/`vbox`/`pad2` unless asked to add styling. Don't add `fontSize` unless asked to add styling. If you believe styling is possible, just tell the user, "I can add styling, but won't do it unless you ask me to."
|
|
10
|
+
- Never use `h1`/`h2`/`h3` etc. These classes have extremely large built-in margins and paddings, instead set the font size explicitly.
|
|
11
|
+
- Make sure to not use `fillWidth`, where `flexGrow(1)` would suffice.
|
|
12
|
+
- Add very little styling, such as colours, rounding, etc, unless asked to add more styling.
|
|
13
|
+
|
|
14
|
+
## CSS Helper
|
|
15
|
+
|
|
16
|
+
CSS should be set using `className={css.cssPropertyName(cssPropertyValue).anotherPropertyName...}`. Always use the `css` helper for styling.
|
|
17
|
+
|
|
18
|
+
For example:
|
|
19
|
+
```tsx
|
|
20
|
+
<div className={css.width(100).height(50)}>My width is 100px, my height is 50px</div>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
All css fields can be set in this way, with the function being the field name and the argument being the value. Generally speaking, the CSS helpers are on two lines. Wrapping is fine.
|
|
24
|
+
```tsx
|
|
25
|
+
className={css.size(100, 100).hbox(4)
|
|
26
|
+
.hsl(0, 50, 50).borderRadius(4)
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Conditionals come after, and should use this style:
|
|
31
|
+
```tsx
|
|
32
|
+
className={css
|
|
33
|
+
...
|
|
34
|
+
+ (conditionalExample && css.opacity(0.5))
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
Specifically, it should be a value that you check for, and then the value. Don't use ternary, don't do `|| ""`. If you have multiple values, chain them with `||` and `&&`. Keep the CSS simple, don't add too much, because it's easier to add than to remove.
|
|
38
|
+
|
|
39
|
+
## Aliases and Helpers
|
|
40
|
+
|
|
41
|
+
If you want to make a NON-Button feel like a button, you can use `css.button`, which makes the background color change on hover, and make the cursor a pointer. Only use this if the background color is set, otherwise you need to message it's a button in another way. And never use it for `<Button>`/`<button>` which already does this.
|
|
42
|
+
|
|
43
|
+
Generally use `hbox`/`vbox` to set the spacing between elements, instead of using margins.
|
|
44
|
+
|
|
45
|
+
There are also some special aliases, some of which take parameters, some of which don't (which allows them to be chained like so: `css.vbox0.wrap`):
|
|
46
|
+
```typescript
|
|
47
|
+
let nonCallAliases = {
|
|
48
|
+
relative: (c: CSSHelperTypeBase) => c.position("relative"),
|
|
49
|
+
absolute: (c: CSSHelperTypeBase) => c.position("absolute"),
|
|
50
|
+
fixed: (c: CSSHelperTypeBase) => c.position("fixed"),
|
|
51
|
+
wrap: (c: CSSHelperTypeBase) => c.flexWrap("wrap").display("flex", "soft").alignItems("center", "soft"),
|
|
52
|
+
marginAuto: (c: CSSHelperTypeBase) => c.margin("auto"),
|
|
53
|
+
fillBoth: (c: CSSHelperTypeBase) => c.width("100%").height("100%"),
|
|
54
|
+
fillWidth: (c: CSSHelperTypeBase) => c.width("100%"),
|
|
55
|
+
fillHeight: (c: CSSHelperTypeBase) => c.height("100%"),
|
|
56
|
+
flexShrink0: (c: CSSHelperTypeBase) => c.flexShrink(0),
|
|
57
|
+
ellipsis: (c: CSSHelperTypeBase) => c.overflow("hidden").textOverflow("ellipsis").whiteSpace("nowrap").display("inline-block"),
|
|
58
|
+
overflowAuto: (c: CSSHelperTypeBase) => c.overflow("auto"),
|
|
59
|
+
overflowHidden: (c: CSSHelperTypeBase) => c.overflow("hidden"),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let callAliases = {
|
|
63
|
+
hbox: (c: CSSHelperTypeBase, gap: number, rowGap?: number) => c.display("flex").flexDirection("row").rowGap(rowGap ?? gap).columnGap(gap).alignItems("center", "soft"),
|
|
64
|
+
vbox: (c: CSSHelperTypeBase, gap: number, columnGap?: number) => c.display("flex").flexDirection("column").rowGap(gap).columnGap(columnGap ?? gap).alignItems("start", "soft"),
|
|
65
|
+
pad2: (c: CSSHelperTypeBase, value: number, verticalValue?: number): CSSHelperTypeBase => {
|
|
66
|
+
if (verticalValue !== undefined) return c.padding(`${verticalValue}px ${value}px` as any);
|
|
67
|
+
return c.padding(value);
|
|
68
|
+
},
|
|
69
|
+
hsl: (c: CSSHelperTypeBase, h: number, s: number, l: number): CSSHelperTypeBase => c.background(`hsl(${h}, ${s}%, ${l}%)`),
|
|
70
|
+
hslhover: (c: CSSHelperTypeBase, h: number, s: number, l: number): CSSHelperTypeBase => c.background(`hsl(${h}, ${s}%, ${l}%)`, "hover"),
|
|
71
|
+
hsla: (c: CSSHelperTypeBase, h: number, s: number, l: number, a: number): CSSHelperTypeBase => c.background(`hsla(${h}, ${s}%, ${l}%, ${a})`),
|
|
72
|
+
hslahover: (c: CSSHelperTypeBase, h: number, s: number, l: number, a: number): CSSHelperTypeBase => c.background(`hsla(${h}, ${s}%, ${l}%, ${a})`, "hover"),
|
|
73
|
+
bord: (c: CSSHelperTypeBase, width: number, color: string | { h: number; s: number; l: number; a?: number; }, style = "solid"): CSSHelperTypeBase => {
|
|
74
|
+
let colorStr = typeof color === "string" ? color : `hsla(${color.h}, ${color.s}%, ${color.l}%, ${color.a ?? 1})`;
|
|
75
|
+
return c.border(`${width}px ${style} ${colorStr}`);
|
|
76
|
+
},
|
|
77
|
+
bord2: (c: CSSHelperTypeBase, h: number, s: number, l: number, width = 1, style = "solid"): CSSHelperTypeBase => {
|
|
78
|
+
return c.border(`${width}px ${style} hsla(${h}, ${s}%, ${l}%, 1)`);
|
|
79
|
+
},
|
|
80
|
+
hslcolor: (c: CSSHelperTypeBase, h: number, s: number, l: number): CSSHelperTypeBase => c.color(`hsl(${h}, ${s}%, ${l}%)`),
|
|
81
|
+
colorhsl: (c: CSSHelperTypeBase, h: number, s: number, l: number): CSSHelperTypeBase => c.color(`hsl(${h}, ${s}%, ${l}%)`),
|
|
82
|
+
hslacolor: (c: CSSHelperTypeBase, h: number, s: number, l: number, a: number): CSSHelperTypeBase => c.color(`hsla(${h}, ${s}%, ${l}%, ${a})`),
|
|
83
|
+
colorhsla: (c: CSSHelperTypeBase, h: number, s: number, l: number, a: number): CSSHelperTypeBase => c.color(`hsla(${h}, ${s}%, ${l}%, ${a})`),
|
|
84
|
+
size: (c: CSSHelperTypeBase, width: LengthOrPercentage, height: LengthOrPercentage) => c.width(width).height(height),
|
|
85
|
+
pos: (c: CSSHelperTypeBase, x: LengthOrPercentage, y: LengthOrPercentage) => c.left(x).top(y),
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Animations
|
|
90
|
+
|
|
91
|
+
For animation keyframes, a style tag is required.
|
|
92
|
+
```tsx
|
|
93
|
+
<style>{`
|
|
94
|
+
@keyframes spinner-ring {
|
|
95
|
+
0% { transform: rotate(0deg); }
|
|
96
|
+
100% { transform: rotate(360deg); }
|
|
97
|
+
}
|
|
98
|
+
`}</style>
|
|
99
|
+
```
|
package/builders/setup.ts
CHANGED
|
@@ -11,8 +11,8 @@ async function main() {
|
|
|
11
11
|
console.log(`Target: ${targetDir}`);
|
|
12
12
|
|
|
13
13
|
// Directories and files to copy
|
|
14
|
-
let directoriesToScan = ["electron", "extension", "web", "nodejs", "assets", ".vscode"];
|
|
15
|
-
let rootFiles = [".
|
|
14
|
+
let directoriesToScan = ["electron", "extension", "web", "nodejs", "assets", ".vscode", ".cursor"];
|
|
15
|
+
let rootFiles = [".eslintrc.js", ".gitignore", "tsconfig.json"];
|
|
16
16
|
|
|
17
17
|
// Import path mappings to convert relative imports to package imports
|
|
18
18
|
let importMappings: { [key: string]: string } = {
|
package/index.d.ts
CHANGED
|
@@ -291,19 +291,8 @@ declare module "sliftutils/misc/yamlBase" {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
declare module "sliftutils/misc/zip" {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
import { MaybePromise } from "socket-function/src/types";
|
|
297
|
-
export declare class Zip {
|
|
298
|
-
static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
|
|
299
|
-
static gzipSync(buffer: Buffer, level?: number): Buffer;
|
|
300
|
-
static gunzip(buffer: Buffer): MaybePromise<Buffer>;
|
|
301
|
-
static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
|
|
302
|
-
static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
|
|
303
|
-
static gunzipSync(buffer: Buffer): Buffer;
|
|
304
|
-
private static gunzipUntrackedSync;
|
|
305
|
-
static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
|
|
306
|
-
}
|
|
294
|
+
import { Zip } from "socket-function/src/Zip";
|
|
295
|
+
export { Zip };
|
|
307
296
|
|
|
308
297
|
}
|
|
309
298
|
|
|
@@ -453,7 +442,7 @@ declare module "sliftutils/render-utils/Input" {
|
|
|
453
442
|
|
|
454
443
|
declare module "sliftutils/render-utils/InputLabel" {
|
|
455
444
|
import preact from "preact";
|
|
456
|
-
type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
|
|
445
|
+
export type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
|
|
457
446
|
/** ONLY throttles onChangeValue */
|
|
458
447
|
throttle?: number;
|
|
459
448
|
flavor?: "large" | "small" | "none";
|
|
@@ -513,7 +502,6 @@ declare module "sliftutils/render-utils/InputLabel" {
|
|
|
513
502
|
}> {
|
|
514
503
|
render(): preact.JSX.Element;
|
|
515
504
|
}
|
|
516
|
-
export {};
|
|
517
505
|
|
|
518
506
|
}
|
|
519
507
|
|
|
@@ -1235,6 +1223,107 @@ declare module "sliftutils/storage/TransactionStorage" {
|
|
|
1235
1223
|
|
|
1236
1224
|
}
|
|
1237
1225
|
|
|
1226
|
+
declare module "sliftutils/storage/backblaze" {
|
|
1227
|
+
/// <reference types="node" />
|
|
1228
|
+
/// <reference types="node" />
|
|
1229
|
+
export declare class ArchivesBackblaze {
|
|
1230
|
+
private config;
|
|
1231
|
+
constructor(config: {
|
|
1232
|
+
bucketName: string;
|
|
1233
|
+
public?: boolean;
|
|
1234
|
+
immutable?: boolean;
|
|
1235
|
+
cacheTime?: number;
|
|
1236
|
+
});
|
|
1237
|
+
private bucketName;
|
|
1238
|
+
private bucketId;
|
|
1239
|
+
private logging;
|
|
1240
|
+
enableLogging(): void;
|
|
1241
|
+
private log;
|
|
1242
|
+
getDebugName(): string;
|
|
1243
|
+
private getBucketAPI;
|
|
1244
|
+
private last503Reset;
|
|
1245
|
+
private apiRetryLogic;
|
|
1246
|
+
get(fileName: string, config?: {
|
|
1247
|
+
range?: {
|
|
1248
|
+
start: number;
|
|
1249
|
+
end: number;
|
|
1250
|
+
};
|
|
1251
|
+
retryCount?: number;
|
|
1252
|
+
}): Promise<Buffer | undefined>;
|
|
1253
|
+
set(fileName: string, data: Buffer): Promise<void>;
|
|
1254
|
+
del(fileName: string): Promise<void>;
|
|
1255
|
+
setLargeFile(config: {
|
|
1256
|
+
path: string;
|
|
1257
|
+
getNextData(): Promise<Buffer | undefined>;
|
|
1258
|
+
}): Promise<void>;
|
|
1259
|
+
getInfo(fileName: string): Promise<{
|
|
1260
|
+
writeTime: number;
|
|
1261
|
+
size: number;
|
|
1262
|
+
} | undefined>;
|
|
1263
|
+
find(prefix: string, config?: {
|
|
1264
|
+
shallow?: boolean;
|
|
1265
|
+
type: "files" | "folders";
|
|
1266
|
+
}): Promise<string[]>;
|
|
1267
|
+
findInfo(prefix: string, config?: {
|
|
1268
|
+
shallow?: boolean;
|
|
1269
|
+
type: "files" | "folders";
|
|
1270
|
+
}): Promise<{
|
|
1271
|
+
path: string;
|
|
1272
|
+
createTime: number;
|
|
1273
|
+
size: number;
|
|
1274
|
+
}[]>;
|
|
1275
|
+
assertPathValid(path: string): Promise<void>;
|
|
1276
|
+
getURL(path: string): Promise<string>;
|
|
1277
|
+
getDownloadAuthorization(config: {
|
|
1278
|
+
fileNamePrefix?: string;
|
|
1279
|
+
validDurationInSeconds: number;
|
|
1280
|
+
b2ContentDisposition?: string;
|
|
1281
|
+
b2ContentLanguage?: string;
|
|
1282
|
+
b2Expires?: string;
|
|
1283
|
+
b2CacheControl?: string;
|
|
1284
|
+
b2ContentEncoding?: string;
|
|
1285
|
+
b2ContentType?: string;
|
|
1286
|
+
}): Promise<{
|
|
1287
|
+
bucketId: string;
|
|
1288
|
+
fileNamePrefix: string;
|
|
1289
|
+
authorizationToken: string;
|
|
1290
|
+
}>;
|
|
1291
|
+
}
|
|
1292
|
+
export declare const getArchivesBackblaze: {
|
|
1293
|
+
(key: string): ArchivesBackblaze;
|
|
1294
|
+
clear(key: string): void;
|
|
1295
|
+
clearAll(): void;
|
|
1296
|
+
forceSet(key: string, value: ArchivesBackblaze): void;
|
|
1297
|
+
getAllKeys(): string[];
|
|
1298
|
+
get(key: string): ArchivesBackblaze | undefined;
|
|
1299
|
+
};
|
|
1300
|
+
export declare const getArchivesBackblazePrivateImmutable: {
|
|
1301
|
+
(key: string): ArchivesBackblaze;
|
|
1302
|
+
clear(key: string): void;
|
|
1303
|
+
clearAll(): void;
|
|
1304
|
+
forceSet(key: string, value: ArchivesBackblaze): void;
|
|
1305
|
+
getAllKeys(): string[];
|
|
1306
|
+
get(key: string): ArchivesBackblaze | undefined;
|
|
1307
|
+
};
|
|
1308
|
+
export declare const getArchivesBackblazePublicImmutable: {
|
|
1309
|
+
(key: string): ArchivesBackblaze;
|
|
1310
|
+
clear(key: string): void;
|
|
1311
|
+
clearAll(): void;
|
|
1312
|
+
forceSet(key: string, value: ArchivesBackblaze): void;
|
|
1313
|
+
getAllKeys(): string[];
|
|
1314
|
+
get(key: string): ArchivesBackblaze | undefined;
|
|
1315
|
+
};
|
|
1316
|
+
export declare const getArchivesBackblazePublic: {
|
|
1317
|
+
(key: string): ArchivesBackblaze;
|
|
1318
|
+
clear(key: string): void;
|
|
1319
|
+
clearAll(): void;
|
|
1320
|
+
forceSet(key: string, value: ArchivesBackblaze): void;
|
|
1321
|
+
getAllKeys(): string[];
|
|
1322
|
+
get(key: string): ArchivesBackblaze | undefined;
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1238
1327
|
declare module "sliftutils/storage/fileSystemPointer" {
|
|
1239
1328
|
export type FileSystemPointer = string;
|
|
1240
1329
|
export declare function storeFileSystemPointer(config: {
|
package/misc/zip.d.ts
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { MaybePromise } from "socket-function/src/types";
|
|
4
|
-
export declare class Zip {
|
|
5
|
-
static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
|
|
6
|
-
static gzipSync(buffer: Buffer, level?: number): Buffer;
|
|
7
|
-
static gunzip(buffer: Buffer): MaybePromise<Buffer>;
|
|
8
|
-
static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
|
|
9
|
-
static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
|
|
10
|
-
static gunzipSync(buffer: Buffer): Buffer;
|
|
11
|
-
private static gunzipUntrackedSync;
|
|
12
|
-
static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
|
|
13
|
-
}
|
|
1
|
+
import { Zip } from "socket-function/src/Zip";
|
|
2
|
+
export { Zip };
|
package/misc/zip.ts
CHANGED
|
@@ -5,109 +5,7 @@ import * as pako from "pako";
|
|
|
5
5
|
|
|
6
6
|
import { setFlag } from "socket-function/require/compileFlags";
|
|
7
7
|
import { MaybePromise } from "socket-function/src/types";
|
|
8
|
+
import { Zip } from "socket-function/src/Zip";
|
|
8
9
|
setFlag(require, "pako", "allowclient", true);
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export class Zip {
|
|
13
|
-
@measureFnc
|
|
14
|
-
public static async gzip(buffer: Buffer, level?: number): Promise<Buffer> {
|
|
15
|
-
if (isNode()) {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
zlib.gzip(buffer, { level }, (err: any, result: Buffer) => {
|
|
18
|
-
if (err) reject(err);
|
|
19
|
-
else resolve(result);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
} else {
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
return await doStream(new CompressionStream("gzip"), buffer);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
@measureFnc
|
|
28
|
-
public static gzipSync(buffer: Buffer, level?: number): Buffer {
|
|
29
|
-
if (isNode()) {
|
|
30
|
-
return Buffer.from(zlib.gzipSync(buffer, { level }));
|
|
31
|
-
}
|
|
32
|
-
return Buffer.from(pako.deflate(buffer));
|
|
33
|
-
}
|
|
34
|
-
public static gunzip(buffer: Buffer): MaybePromise<Buffer> {
|
|
35
|
-
// Switch to the synchronous version if the buffer is small. This is a lot faster in Node.js and clientside.
|
|
36
|
-
// - On tests of random small amounts of data, this seems to be up to 7X faster (on node). However, on non-random data, on the actual data we're using, it seems to be almost 50 times faster. So... definitely worth it...
|
|
37
|
-
if (buffer.length < SYNC_THRESHOLD_BYTES) {
|
|
38
|
-
let time = Date.now();
|
|
39
|
-
let result = Zip.gunzipSync(buffer);
|
|
40
|
-
let duration = Date.now() - time;
|
|
41
|
-
if (duration > 50) {
|
|
42
|
-
// Wait, so we don't lock up the main thread. And if we already wait it 50ms, then waiting for one frame is marginal, even client-side.
|
|
43
|
-
return ((async () => {
|
|
44
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
45
|
-
return result;
|
|
46
|
-
}))();
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
49
|
-
}
|
|
50
|
-
return Zip.gunzipAsyncBase(buffer);
|
|
51
|
-
}
|
|
52
|
-
@measureFnc
|
|
53
|
-
public static async gunzipAsyncBase(buffer: Buffer): Promise<Buffer> {
|
|
54
|
-
return Zip.gunzipUntracked(buffer);
|
|
55
|
-
}
|
|
56
|
-
// A base function, so we can avoid instrumentation for batch calls
|
|
57
|
-
public static async gunzipUntracked(buffer: Buffer): Promise<Buffer> {
|
|
58
|
-
if (isNode()) {
|
|
59
|
-
return await new Promise<Buffer>((resolve, reject) => {
|
|
60
|
-
zlib.gunzip(buffer, (err: any, result: Buffer) => {
|
|
61
|
-
if (err) reject(err);
|
|
62
|
-
else resolve(result);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return await doStream(new DecompressionStream("gzip"), buffer);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@measureFnc
|
|
70
|
-
public static gunzipSync(buffer: Buffer): Buffer {
|
|
71
|
-
return this.gunzipUntrackedSync(buffer);
|
|
72
|
-
}
|
|
73
|
-
private static gunzipUntrackedSync(buffer: Buffer): Buffer {
|
|
74
|
-
if (isNode()) {
|
|
75
|
-
return Buffer.from(zlib.gunzipSync(buffer));
|
|
76
|
-
}
|
|
77
|
-
return Buffer.from(pako.inflate(buffer));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@measureFnc
|
|
81
|
-
public static async gunzipBatch(buffers: Buffer[]): Promise<Buffer[]> {
|
|
82
|
-
let time = Date.now();
|
|
83
|
-
buffers = await Promise.all(buffers.map(x => {
|
|
84
|
-
if (x.length < SYNC_THRESHOLD_BYTES) {
|
|
85
|
-
return this.gunzipUntrackedSync(x);
|
|
86
|
-
}
|
|
87
|
-
return this.gunzipUntracked(x);
|
|
88
|
-
}));
|
|
89
|
-
time = Date.now() - time;
|
|
90
|
-
let totalSize = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
|
|
91
|
-
//console.log(`Gunzip ${formatNumber(totalSize)}B at ${formatNumber(totalSize / time * 1000)}B/s`);
|
|
92
|
-
return buffers;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function doStream(stream: GenericTransformStream, buffer: Buffer): Promise<Buffer> {
|
|
97
|
-
let reader = stream.readable.getReader();
|
|
98
|
-
let writer = stream.writable.getWriter();
|
|
99
|
-
let writePromise = writer.write(buffer);
|
|
100
|
-
let closePromise = writer.close();
|
|
101
|
-
|
|
102
|
-
let outputBuffers: Buffer[] = [];
|
|
103
|
-
while (true) {
|
|
104
|
-
let { value, done } = await reader.read();
|
|
105
|
-
if (done) {
|
|
106
|
-
await writePromise;
|
|
107
|
-
await closePromise;
|
|
108
|
-
return Buffer.concat(outputBuffers);
|
|
109
|
-
}
|
|
110
|
-
outputBuffers.push(Buffer.from(value));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
}
|
|
11
|
+
export { Zip };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sliftutils",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"mobx": "^6.13.3",
|
|
51
51
|
"preact-old-types": "^10.28.1",
|
|
52
52
|
"shell-quote": "^1.8.3",
|
|
53
|
-
"socket-function": "^1.
|
|
53
|
+
"socket-function": "^1.1.1",
|
|
54
54
|
"typenode": "*",
|
|
55
55
|
"typesafecss": "*",
|
|
56
56
|
"ws": "^8.18.3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import preact from "preact";
|
|
2
|
-
type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
|
|
2
|
+
export type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
|
|
3
3
|
/** ONLY throttles onChangeValue */
|
|
4
4
|
throttle?: number;
|
|
5
5
|
flavor?: "large" | "small" | "none";
|
|
@@ -59,4 +59,3 @@ export declare class InputLabelURL extends preact.Component<InputLabelProps & {
|
|
|
59
59
|
}> {
|
|
60
60
|
render(): preact.JSX.Element;
|
|
61
61
|
}
|
|
62
|
-
export {};
|
|
@@ -6,7 +6,7 @@ import { observer } from "./observer";
|
|
|
6
6
|
import { observable } from "mobx";
|
|
7
7
|
|
|
8
8
|
// IMPORTANT! InputProps is in both InputLabel.tsx and Input.tsx, so the types export correctly
|
|
9
|
-
type InputProps = (
|
|
9
|
+
export type InputProps = (
|
|
10
10
|
preact.JSX.HTMLAttributes<HTMLInputElement>
|
|
11
11
|
& {
|
|
12
12
|
/** ONLY throttles onChangeValue */
|