sliftutils 0.86.0 → 0.88.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/builders/setup.ts +16 -2
- package/index.d.ts +17 -1
- package/misc/types.d.ts +2 -0
- package/misc/types.ts +21 -0
- package/package.json +1 -1
- package/storage/DiskCollection.d.ts +6 -0
- package/storage/DiskCollection.ts +8 -1
- package/storage/StorageObservable.d.ts +9 -1
- package/storage/StorageObservable.ts +15 -3
- package/storage/TransactionStorage.ts +12 -18
- package/web/ExamplePage.tsx +0 -18
package/builders/setup.ts
CHANGED
|
@@ -78,7 +78,7 @@ async function main() {
|
|
|
78
78
|
let packageJsonPath = path.join(targetDir, "package.json");
|
|
79
79
|
if (fs.existsSync(packageJsonPath)) {
|
|
80
80
|
console.log("\nUpdating package.json scripts...");
|
|
81
|
-
updatePackageJson(packageJsonPath);
|
|
81
|
+
await updatePackageJson(packageJsonPath);
|
|
82
82
|
} else {
|
|
83
83
|
console.warn("\nNo package.json found in target directory");
|
|
84
84
|
}
|
|
@@ -136,7 +136,7 @@ function replaceImports(content: string, importMappings: { [key: string]: string
|
|
|
136
136
|
return processedLines.join("\n");
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function updatePackageJson(packageJsonPath: string) {
|
|
139
|
+
async function updatePackageJson(packageJsonPath: string) {
|
|
140
140
|
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
141
141
|
|
|
142
142
|
// Read our current package.json to get the type script
|
|
@@ -191,8 +191,22 @@ function updatePackageJson(packageJsonPath: string) {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
let needsYarnInstall = false;
|
|
195
|
+
|
|
196
|
+
// ALSO, add socket-function, if it doesn't have it already. This fixes intelliSense, for socket-function, which has a lot of useful utilities related to caching.
|
|
197
|
+
if (!packageJson.dependencies?.["socket-function"]) {
|
|
198
|
+
packageJson.dependencies = packageJson.dependencies || {};
|
|
199
|
+
packageJson.dependencies["socket-function"] = "*";
|
|
200
|
+
needsYarnInstall = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
194
203
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 4) + "\n", "utf8");
|
|
195
204
|
console.log(" package.json updated");
|
|
205
|
+
|
|
206
|
+
if (needsYarnInstall) {
|
|
207
|
+
console.log(" Needs yarn install");
|
|
208
|
+
await execSync("yarn install", { cwd: path.dirname(packageJsonPath), stdio: "inherit" });
|
|
209
|
+
}
|
|
196
210
|
}
|
|
197
211
|
|
|
198
212
|
main().catch(error => {
|
package/index.d.ts
CHANGED
|
@@ -127,6 +127,8 @@ declare module "sliftutils/misc/random" {
|
|
|
127
127
|
|
|
128
128
|
declare module "sliftutils/misc/types" {
|
|
129
129
|
export declare function isDefined<T>(value: T | undefined | null): value is T;
|
|
130
|
+
export declare function freezeObject(value: unknown): unknown;
|
|
131
|
+
export declare function deepFreezeObject(value: unknown): void;
|
|
130
132
|
|
|
131
133
|
}
|
|
132
134
|
|
|
@@ -658,6 +660,12 @@ declare module "sliftutils/storage/DiskCollection" {
|
|
|
658
660
|
writeDelay?: number | undefined;
|
|
659
661
|
cbor?: boolean | undefined;
|
|
660
662
|
noPrompt?: boolean | undefined;
|
|
663
|
+
freeze?: "deep" | "shallow" | undefined;
|
|
664
|
+
beforeWrite?: ((update: {
|
|
665
|
+
newValue: T;
|
|
666
|
+
key: string;
|
|
667
|
+
collection: DiskCollection<T>;
|
|
668
|
+
}) => void) | undefined;
|
|
661
669
|
} | undefined);
|
|
662
670
|
transactionStorage: TransactionStorage | undefined;
|
|
663
671
|
initStorage(): Promise<IStorage<T>>;
|
|
@@ -969,6 +977,7 @@ declare module "sliftutils/storage/StorageObservable" {
|
|
|
969
977
|
import { IStorage, IStorageSync } from "./IStorage";
|
|
970
978
|
export declare class StorageSync<T> implements IStorageSync<T> {
|
|
971
979
|
storage: IStorage<T>;
|
|
980
|
+
private config?;
|
|
972
981
|
cached: import("mobx").ObservableMap<string, T | undefined>;
|
|
973
982
|
infoCached: import("mobx").ObservableMap<string, {
|
|
974
983
|
size: number;
|
|
@@ -978,7 +987,14 @@ declare module "sliftutils/storage/StorageObservable" {
|
|
|
978
987
|
synced: {
|
|
979
988
|
keySeqNum: number;
|
|
980
989
|
};
|
|
981
|
-
constructor(storage: IStorage<T
|
|
990
|
+
constructor(storage: IStorage<T>, config?: {
|
|
991
|
+
freeze?: "deep" | "shallow" | undefined;
|
|
992
|
+
beforeWrite?: ((update: {
|
|
993
|
+
newValue: T;
|
|
994
|
+
key: string;
|
|
995
|
+
collection: StorageSync<T>;
|
|
996
|
+
}) => void) | undefined;
|
|
997
|
+
} | undefined);
|
|
982
998
|
get(key: string): T | undefined;
|
|
983
999
|
set(key: string, value: T): void;
|
|
984
1000
|
remove(key: string): void;
|
package/misc/types.d.ts
CHANGED
package/misc/types.ts
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
1
|
export function isDefined<T>(value: T | undefined | null): value is T {
|
|
2
2
|
return value !== undefined && value !== null;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export function freezeObject(value: unknown) {
|
|
7
|
+
if (!value) return value;
|
|
8
|
+
Object.freeze(value);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function deepFreezeObject(value: unknown) {
|
|
12
|
+
if (!value) return;
|
|
13
|
+
if (typeof value !== "object") return;
|
|
14
|
+
if (Object.isFrozen(value)) return;
|
|
15
|
+
|
|
16
|
+
Object.freeze(value);
|
|
17
|
+
|
|
18
|
+
Object.getOwnPropertyNames(value).forEach(prop => {
|
|
19
|
+
const propValue = (value as any)[prop];
|
|
20
|
+
if (propValue && typeof propValue === "object") {
|
|
21
|
+
deepFreezeObject(propValue);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
3
24
|
}
|
package/package.json
CHANGED
|
@@ -10,6 +10,12 @@ export declare class DiskCollection<T> implements IStorageSync<T> {
|
|
|
10
10
|
writeDelay?: number | undefined;
|
|
11
11
|
cbor?: boolean | undefined;
|
|
12
12
|
noPrompt?: boolean | undefined;
|
|
13
|
+
freeze?: "deep" | "shallow" | undefined;
|
|
14
|
+
beforeWrite?: ((update: {
|
|
15
|
+
newValue: T;
|
|
16
|
+
key: string;
|
|
17
|
+
collection: DiskCollection<T>;
|
|
18
|
+
}) => void) | undefined;
|
|
13
19
|
} | undefined);
|
|
14
20
|
transactionStorage: TransactionStorage | undefined;
|
|
15
21
|
initStorage(): Promise<IStorage<T>>;
|
|
@@ -18,6 +18,9 @@ export class DiskCollection<T> implements IStorageSync<T> {
|
|
|
18
18
|
writeDelay?: number;
|
|
19
19
|
cbor?: boolean;
|
|
20
20
|
noPrompt?: boolean;
|
|
21
|
+
freeze?: "shallow" | "deep";
|
|
22
|
+
// May mutate newValue in order to change what will be written
|
|
23
|
+
beforeWrite?: (update: { newValue: T; key: string; collection: DiskCollection<T> }) => void;
|
|
21
24
|
}
|
|
22
25
|
) {
|
|
23
26
|
}
|
|
@@ -41,7 +44,11 @@ export class DiskCollection<T> implements IStorageSync<T> {
|
|
|
41
44
|
private synced = new StorageSync(
|
|
42
45
|
new PendingStorage(`Collection (${this.collectionName})`,
|
|
43
46
|
new DelayedStorage<T>(this.baseStorage)
|
|
44
|
-
)
|
|
47
|
+
),
|
|
48
|
+
{
|
|
49
|
+
freeze: this.config?.freeze,
|
|
50
|
+
beforeWrite: this.config?.beforeWrite && ((update) => this.config!.beforeWrite!({ ...update, collection: this })),
|
|
51
|
+
}
|
|
45
52
|
);
|
|
46
53
|
|
|
47
54
|
public get(key: string): T | undefined {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IStorage, IStorageSync } from "./IStorage";
|
|
2
2
|
export declare class StorageSync<T> implements IStorageSync<T> {
|
|
3
3
|
storage: IStorage<T>;
|
|
4
|
+
private config?;
|
|
4
5
|
cached: import("mobx").ObservableMap<string, T | undefined>;
|
|
5
6
|
infoCached: import("mobx").ObservableMap<string, {
|
|
6
7
|
size: number;
|
|
@@ -10,7 +11,14 @@ export declare class StorageSync<T> implements IStorageSync<T> {
|
|
|
10
11
|
synced: {
|
|
11
12
|
keySeqNum: number;
|
|
12
13
|
};
|
|
13
|
-
constructor(storage: IStorage<T
|
|
14
|
+
constructor(storage: IStorage<T>, config?: {
|
|
15
|
+
freeze?: "deep" | "shallow" | undefined;
|
|
16
|
+
beforeWrite?: ((update: {
|
|
17
|
+
newValue: T;
|
|
18
|
+
key: string;
|
|
19
|
+
collection: StorageSync<T>;
|
|
20
|
+
}) => void) | undefined;
|
|
21
|
+
} | undefined);
|
|
14
22
|
get(key: string): T | undefined;
|
|
15
23
|
set(key: string, value: T): void;
|
|
16
24
|
remove(key: string): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { observable } from "mobx";
|
|
2
|
-
import { isDefined } from "../misc/types";
|
|
2
|
+
import { deepFreezeObject, freezeObject, isDefined } from "../misc/types";
|
|
3
3
|
import { IStorage, IStorageSync } from "./IStorage";
|
|
4
4
|
|
|
5
5
|
// NOTE: At around 500K values (depending on their size to some degree), this will take about 2 minutes to load. But once it does it will be fast. So... keep that in mind. I recommend not exceeding 100K
|
|
@@ -11,7 +11,11 @@ export class StorageSync<T> implements IStorageSync<T> {
|
|
|
11
11
|
keySeqNum: 0,
|
|
12
12
|
}, undefined, { deep: false });
|
|
13
13
|
|
|
14
|
-
constructor(public storage: IStorage<T
|
|
14
|
+
constructor(public storage: IStorage<T>, private config?: {
|
|
15
|
+
freeze?: "shallow" | "deep";
|
|
16
|
+
// May mutate newValue in order to change what will be written
|
|
17
|
+
beforeWrite?: (update: { newValue: T; key: string; collection: StorageSync<T> }) => void;
|
|
18
|
+
}) {
|
|
15
19
|
storage.watchResync?.(async () => {
|
|
16
20
|
// NOTE: If there's multiple tabs open, this'll trigger a lot, so we can't just clear all the values, as that'll cause a render where nothing's loaded.
|
|
17
21
|
this.loadedKeys = false;
|
|
@@ -33,6 +37,9 @@ export class StorageSync<T> implements IStorageSync<T> {
|
|
|
33
37
|
return this.cached.get(key);
|
|
34
38
|
}
|
|
35
39
|
public set(key: string, value: T): void {
|
|
40
|
+
if (this.config?.beforeWrite) {
|
|
41
|
+
this.config.beforeWrite({ newValue: value, key, collection: this });
|
|
42
|
+
}
|
|
36
43
|
if (!this.keys.has(key)) {
|
|
37
44
|
this.keys.add(key);
|
|
38
45
|
this.synced.keySeqNum++;
|
|
@@ -79,7 +86,12 @@ export class StorageSync<T> implements IStorageSync<T> {
|
|
|
79
86
|
let value = this.cached.get(key);
|
|
80
87
|
if (value === undefined) {
|
|
81
88
|
value = await this.storage.get(key);
|
|
82
|
-
if (this.cached.get(key) === undefined) {
|
|
89
|
+
if (value !== undefined && this.cached.get(key) === undefined) {
|
|
90
|
+
if (this.config?.freeze === "shallow") {
|
|
91
|
+
freezeObject(value);
|
|
92
|
+
} else if (this.config?.freeze === "deep") {
|
|
93
|
+
deepFreezeObject(value);
|
|
94
|
+
}
|
|
83
95
|
this.cached.set(key, value);
|
|
84
96
|
}
|
|
85
97
|
}
|
|
@@ -401,26 +401,20 @@ export class TransactionStorage implements IStorage<Buffer> {
|
|
|
401
401
|
this.cache.delete(entry.key);
|
|
402
402
|
} else {
|
|
403
403
|
if (!anyChanged && !initialLoad) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (!prev.value) {
|
|
409
|
-
if (entry.value) {
|
|
410
|
-
anyChanged = true;
|
|
411
|
-
}
|
|
412
|
-
} else {
|
|
413
|
-
if (!entry.value) {
|
|
414
|
-
anyChanged = true;
|
|
415
|
-
} else {
|
|
416
|
-
// Both values, so... it might not have changed
|
|
417
|
-
if (!prev.value.equals(entry.value)) {
|
|
418
|
-
anyChanged = true;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
404
|
+
const hasChanged = () => {
|
|
405
|
+
let prev = this.cache.get(entry.key);
|
|
406
|
+
if (!prev) {
|
|
407
|
+
return true;
|
|
421
408
|
}
|
|
422
|
-
|
|
409
|
+
if (!!prev.value !== !!entry.value) return true;
|
|
410
|
+
if (prev.value && entry.value && !prev.value.equals(entry.value)) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
};
|
|
415
|
+
anyChanged = hasChanged() || anyChanged;
|
|
423
416
|
}
|
|
417
|
+
|
|
424
418
|
this.cache.set(entry.key, entry);
|
|
425
419
|
}
|
|
426
420
|
}
|
package/web/ExamplePage.tsx
CHANGED
|
@@ -13,24 +13,6 @@ export class ExamplePage extends preact.Component {
|
|
|
13
13
|
count: 0,
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
onKeyDown = (e: KeyboardEvent) => {
|
|
17
|
-
// Skip if the current target is an ipnut
|
|
18
|
-
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
let hotkeySelector = `[data-hotkey="${e.code}"]`;
|
|
22
|
-
let elements = document.querySelectorAll(hotkeySelector);
|
|
23
|
-
for (let element of elements) {
|
|
24
|
-
(element as HTMLElement).click();
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
componentDidMount(): void {
|
|
28
|
-
document.addEventListener("keydown", this.onKeyDown);
|
|
29
|
-
}
|
|
30
|
-
componentWillUnmount(): void {
|
|
31
|
-
document.removeEventListener("keydown", this.onKeyDown);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
16
|
render() {
|
|
35
17
|
return (
|
|
36
18
|
<div className={css.pad2(20)}>
|