sliftutils 1.1.3 → 1.2.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 CHANGED
@@ -26,6 +26,8 @@ We use MobX for state management. Components should use a variable called synced
26
26
 
27
27
  The code automatically updates on save, so do not ever run commands to rerun the site.
28
28
 
29
+ If you need to add a dependency, don't just change the packs to JSON file. Properly use yarn add so you get the latest version, unless the user specifies a version or tells you otherwise.
30
+
29
31
  Use tool calls to read files and directories where possible, instead of running "ls", "dir", etc.
30
32
 
31
33
  Coding Styles
package/index.d.ts CHANGED
@@ -296,6 +296,7 @@ declare module "sliftutils/misc/zip" {
296
296
  import { MaybePromise } from "socket-function/src/types";
297
297
  export declare class Zip {
298
298
  static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
299
+ static gzipSync(buffer: Buffer, level?: number): Buffer;
299
300
  static gunzip(buffer: Buffer): MaybePromise<Buffer>;
300
301
  static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
301
302
  static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
@@ -1121,6 +1122,9 @@ declare module "sliftutils/storage/PrivateFileSystemStorage" {
1121
1122
 
1122
1123
  declare module "sliftutils/storage/StorageObservable" {
1123
1124
  import { IStorage, IStorageSync } from "./IStorage";
1125
+ export declare const storagePendingAccesses: {
1126
+ value: number;
1127
+ };
1124
1128
  export declare class StorageSync<T> implements IStorageSync<T> {
1125
1129
  storage: IStorage<T>;
1126
1130
  private config?;
@@ -1160,6 +1164,13 @@ declare module "sliftutils/storage/StorageObservable" {
1160
1164
  reloadKey(key: string): void;
1161
1165
  reset(): Promise<void>;
1162
1166
  }
1167
+ export declare function waitUntilNextLoad(): Promise<void>;
1168
+
1169
+ }
1170
+
1171
+ declare module "sliftutils/storage/StorageObservableAsync" {
1172
+ /** Reruns the code until all StorageSyncs accessed have loaded their values. Not efficient,although will usually be O(values accessed), just due to how loading works (it won't be quadratic). */
1173
+ export declare function rerunCodeUntilAllLoaded<T>(code: () => T): Promise<T>;
1163
1174
 
1164
1175
  }
1165
1176
 
package/misc/zip.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  import { MaybePromise } from "socket-function/src/types";
4
4
  export declare class Zip {
5
5
  static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
6
+ static gzipSync(buffer: Buffer, level?: number): Buffer;
6
7
  static gunzip(buffer: Buffer): MaybePromise<Buffer>;
7
8
  static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
8
9
  static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
package/misc/zip.ts CHANGED
@@ -24,6 +24,13 @@ export class Zip {
24
24
  return await doStream(new CompressionStream("gzip"), buffer);
25
25
  }
26
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
+ }
27
34
  public static gunzip(buffer: Buffer): MaybePromise<Buffer> {
28
35
  // Switch to the synchronous version if the buffer is small. This is a lot faster in Node.js and clientside.
29
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...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -1,5 +1,6 @@
1
1
  import * as preact from "preact";
2
2
  import { observable, Reaction } from "mobx";
3
+ import { measureBlock } from "socket-function/src/profiling/measure";
3
4
 
4
5
  let globalConstructOrder = 1;
5
6
  export function observer<
@@ -27,7 +28,9 @@ export function observer<
27
28
  render(...args: any[]) {
28
29
  let output: preact.ComponentChild;
29
30
  this.reaction.track(() => {
30
- output = (super.render as any)(...args);
31
+ measureBlock(() => {
32
+ output = (super.render as any)(...args);
33
+ }, `${name}|render`);
31
34
  });
32
35
  return output;
33
36
  }
@@ -1,4 +1,7 @@
1
1
  import { IStorage, IStorageSync } from "./IStorage";
2
+ export declare const storagePendingAccesses: {
3
+ value: number;
4
+ };
2
5
  export declare class StorageSync<T> implements IStorageSync<T> {
3
6
  storage: IStorage<T>;
4
7
  private config?;
@@ -38,3 +41,4 @@ export declare class StorageSync<T> implements IStorageSync<T> {
38
41
  reloadKey(key: string): void;
39
42
  reset(): Promise<void>;
40
43
  }
44
+ export declare function waitUntilNextLoad(): Promise<void>;
@@ -1,8 +1,11 @@
1
1
  import { observable } from "mobx";
2
2
  import { deepFreezeObject, freezeObject, isDefined } from "../misc/types";
3
3
  import { IStorage, IStorageSync } from "./IStorage";
4
+ import { PromiseObj } from "socket-function/src/misc";
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
6
+ export const storagePendingAccesses = { value: 0 };
7
+
8
+ // 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.
6
9
  export class StorageSync<T> implements IStorageSync<T> {
7
10
  cached = observable.map<string, T | undefined>(undefined, { deep: false });
8
11
  infoCached = observable.map<string, { size: number; lastModified: number } | undefined>(undefined, { deep: false });
@@ -85,6 +88,7 @@ export class StorageSync<T> implements IStorageSync<T> {
85
88
  public async getPromise(key: string): Promise<T | undefined> {
86
89
  let value = this.cached.get(key);
87
90
  if (value === undefined) {
91
+ storagePendingAccesses.value++;
88
92
  value = await this.storage.get(key);
89
93
  if (value !== undefined && this.cached.get(key) === undefined) {
90
94
  if (this.config?.freeze === "shallow") {
@@ -94,15 +98,19 @@ export class StorageSync<T> implements IStorageSync<T> {
94
98
  }
95
99
  this.cached.set(key, value);
96
100
  }
101
+ triggerLoad();
97
102
  }
98
103
  return value;
99
104
  }
100
105
  private pendingGetKeys: Promise<string[]> | undefined;
101
106
  public async getKeysPromise(): Promise<string[]> {
107
+ if (this.loadedKeys) {
108
+ return Array.from(this.keys);
109
+ }
110
+ storagePendingAccesses.value++;
102
111
  if (this.pendingGetKeys) {
103
112
  return this.pendingGetKeys;
104
113
  }
105
- if (this.loadedKeys) return Array.from(this.keys);
106
114
  this.loadedKeys = true;
107
115
  this.pendingGetKeys = this.storage.getKeys();
108
116
  void this.pendingGetKeys.finally(() => {
@@ -113,6 +121,7 @@ export class StorageSync<T> implements IStorageSync<T> {
113
121
  this.keys = new Set(keys);
114
122
  this.synced.keySeqNum++;
115
123
  }
124
+ triggerLoad();
116
125
  return Array.from(this.keys);
117
126
  }
118
127
 
@@ -140,4 +149,20 @@ export class StorageSync<T> implements IStorageSync<T> {
140
149
  this.synced.keySeqNum++;
141
150
  await this.storage.reset();
142
151
  }
152
+ }
153
+
154
+ let waitUntilNextLoadWatchers: PromiseObj<void>[] = [];
155
+ export function waitUntilNextLoad(): Promise<void> {
156
+ let promise = new PromiseObj<void>();
157
+ waitUntilNextLoadWatchers.push(promise);
158
+ return promise.promise;
159
+ }
160
+ function triggerLoad() {
161
+ let watchers = waitUntilNextLoadWatchers;
162
+ waitUntilNextLoadWatchers = [];
163
+ void Promise.resolve().finally(() => {
164
+ for (let watcher of watchers) {
165
+ watcher.resolve();
166
+ }
167
+ });
143
168
  }
@@ -0,0 +1,2 @@
1
+ /** Reruns the code until all StorageSyncs accessed have loaded their values. Not efficient,although will usually be O(values accessed), just due to how loading works (it won't be quadratic). */
2
+ export declare function rerunCodeUntilAllLoaded<T>(code: () => T): Promise<T>;
@@ -0,0 +1,20 @@
1
+ import { storagePendingAccesses, waitUntilNextLoad } from "./StorageObservable";
2
+
3
+ /** Reruns the code until all StorageSyncs accessed have loaded their values. Not efficient,although will usually be O(values accessed), just due to how loading works (it won't be quadratic). */
4
+ export async function rerunCodeUntilAllLoaded<T>(code: () => T): Promise<T> {
5
+ while (true) {
6
+ let beforeAccesses = storagePendingAccesses.value;
7
+ try {
8
+ let result = await code();
9
+ if (storagePendingAccesses.value === beforeAccesses) {
10
+ return result;
11
+ }
12
+ } catch (error) {
13
+ if (storagePendingAccesses.value === beforeAccesses) {
14
+ throw error;
15
+ }
16
+ }
17
+ console.log(`Rerunning synchronous check as loading starting while evaluating code. Function name`, code);
18
+ await waitUntilNextLoad();
19
+ }
20
+ }