sliftutils 0.86.0 → 0.87.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/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,8 @@ 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?: ((newValue: T) => void) | undefined;
661
665
  } | undefined);
662
666
  transactionStorage: TransactionStorage | undefined;
663
667
  initStorage(): Promise<IStorage<T>>;
@@ -969,6 +973,7 @@ declare module "sliftutils/storage/StorageObservable" {
969
973
  import { IStorage, IStorageSync } from "./IStorage";
970
974
  export declare class StorageSync<T> implements IStorageSync<T> {
971
975
  storage: IStorage<T>;
976
+ private config?;
972
977
  cached: import("mobx").ObservableMap<string, T | undefined>;
973
978
  infoCached: import("mobx").ObservableMap<string, {
974
979
  size: number;
@@ -978,7 +983,10 @@ declare module "sliftutils/storage/StorageObservable" {
978
983
  synced: {
979
984
  keySeqNum: number;
980
985
  };
981
- constructor(storage: IStorage<T>);
986
+ constructor(storage: IStorage<T>, config?: {
987
+ freeze?: "deep" | "shallow" | undefined;
988
+ beforeWrite?: ((newValue: T) => void) | undefined;
989
+ } | undefined);
982
990
  get(key: string): T | undefined;
983
991
  set(key: string, value: T): void;
984
992
  remove(key: string): void;
package/misc/types.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export declare function isDefined<T>(value: T | undefined | null): value is T;
2
+ export declare function freezeObject(value: unknown): unknown;
3
+ export declare function deepFreezeObject(value: unknown): void;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -10,6 +10,8 @@ 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?: ((newValue: T) => void) | undefined;
13
15
  } | undefined);
14
16
  transactionStorage: TransactionStorage | undefined;
15
17
  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?: (newValue: T) => void;
21
24
  }
22
25
  ) {
23
26
  }
@@ -41,7 +44,8 @@ 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
+ this.config
45
49
  );
46
50
 
47
51
  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,10 @@ 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?: ((newValue: T) => void) | undefined;
17
+ } | undefined);
14
18
  get(key: string): T | undefined;
15
19
  set(key: string, value: T): void;
16
20
  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?: (newValue: 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(value);
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
- let prev = this.cache.get(entry.key);
405
- if (!prev) {
406
- anyChanged = true;
407
- } else {
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
  }
@@ -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)}>