sliftutils 0.76.0 → 0.78.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
@@ -69,11 +69,6 @@ declare module "sliftutils/misc/matchFilter" {
69
69
 
70
70
  }
71
71
 
72
- declare module "sliftutils/misc/objects" {
73
- export declare function entries<T extends object>(object: T): [keyof T, T[keyof T]][];
74
-
75
- }
76
-
77
72
  declare module "sliftutils/misc/openrouter" {
78
73
  export type MessageHistory = {
79
74
  role: "system" | "user" | "assistant";
@@ -150,9 +145,14 @@ declare module "sliftutils/misc/yamlBase" {
150
145
  declare module "sliftutils/misc/zip" {
151
146
  /// <reference types="node" />
152
147
  /// <reference types="node" />
148
+ import { MaybePromise } from "socket-function/src/types";
153
149
  export declare class Zip {
154
150
  static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
155
- static gunzip(buffer: Buffer): Promise<Buffer>;
151
+ static gunzip(buffer: Buffer): MaybePromise<Buffer>;
152
+ static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
153
+ static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
154
+ static gunzipSync(buffer: Buffer): Buffer;
155
+ private static gunzipUntrackedSync;
156
156
  static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
157
157
  }
158
158
 
package/misc/zip.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
+ import { MaybePromise } from "socket-function/src/types";
3
4
  export declare class Zip {
4
5
  static gzip(buffer: Buffer, level?: number): Promise<Buffer>;
5
- static gunzip(buffer: Buffer): Promise<Buffer>;
6
+ static gunzip(buffer: Buffer): MaybePromise<Buffer>;
7
+ static gunzipAsyncBase(buffer: Buffer): Promise<Buffer>;
8
+ static gunzipUntracked(buffer: Buffer): Promise<Buffer>;
9
+ static gunzipSync(buffer: Buffer): Buffer;
10
+ private static gunzipUntrackedSync;
6
11
  static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
7
12
  }
package/misc/zip.ts CHANGED
@@ -1,18 +1,86 @@
1
+ import { isNode } from "socket-function/src/misc";
2
+ import { measureFnc } from "socket-function/src/profiling/measure";
3
+ import zlib from "zlib";
4
+ import * as pako from "pako";
1
5
 
6
+ import { setFlag } from "socket-function/require/compileFlags";
7
+ import { MaybePromise } from "socket-function/src/types";
8
+ setFlag(require, "pako", "allowclient", true);
9
+
10
+ const SYNC_THRESHOLD_BYTES = 100_000_000;
2
11
 
3
12
  export class Zip {
13
+ @measureFnc
4
14
  public static async gzip(buffer: Buffer, level?: number): Promise<Buffer> {
5
- return await doStream(new CompressionStream("gzip"), 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
+ public static gunzip(buffer: Buffer): MaybePromise<Buffer> {
28
+ // Switch to the synchronous version if the buffer is small. This is a lot faster in Node.js and clientside.
29
+ // - 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...
30
+ if (buffer.length < SYNC_THRESHOLD_BYTES) {
31
+ let time = Date.now();
32
+ let result = Zip.gunzipSync(buffer);
33
+ let duration = Date.now() - time;
34
+ if (duration > 50) {
35
+ // 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.
36
+ return ((async () => {
37
+ await new Promise(resolve => setTimeout(resolve, 0));
38
+ return result;
39
+ }))();
40
+ }
41
+ return result;
42
+ }
43
+ return Zip.gunzipAsyncBase(buffer);
44
+ }
45
+ @measureFnc
46
+ public static async gunzipAsyncBase(buffer: Buffer): Promise<Buffer> {
47
+ return Zip.gunzipUntracked(buffer);
6
48
  }
7
- public static async gunzip(buffer: Buffer): Promise<Buffer> {
49
+ // A base function, so we can avoid instrumentation for batch calls
50
+ public static async gunzipUntracked(buffer: Buffer): Promise<Buffer> {
51
+ if (isNode()) {
52
+ return await new Promise<Buffer>((resolve, reject) => {
53
+ zlib.gunzip(buffer, (err: any, result: Buffer) => {
54
+ if (err) reject(err);
55
+ else resolve(result);
56
+ });
57
+ });
58
+ }
8
59
  return await doStream(new DecompressionStream("gzip"), buffer);
9
60
  }
10
61
 
62
+ @measureFnc
63
+ public static gunzipSync(buffer: Buffer): Buffer {
64
+ return this.gunzipUntrackedSync(buffer);
65
+ }
66
+ private static gunzipUntrackedSync(buffer: Buffer): Buffer {
67
+ if (isNode()) {
68
+ return Buffer.from(zlib.gunzipSync(buffer));
69
+ }
70
+ return Buffer.from(pako.inflate(buffer));
71
+ }
72
+
73
+ @measureFnc
11
74
  public static async gunzipBatch(buffers: Buffer[]): Promise<Buffer[]> {
12
75
  let time = Date.now();
13
- buffers = await Promise.all(buffers.map(Zip.gunzip));
76
+ buffers = await Promise.all(buffers.map(x => {
77
+ if (x.length < SYNC_THRESHOLD_BYTES) {
78
+ return this.gunzipUntrackedSync(x);
79
+ }
80
+ return this.gunzipUntracked(x);
81
+ }));
14
82
  time = Date.now() - time;
15
- //let totalSize = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
83
+ let totalSize = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
16
84
  //console.log(`Gunzip ${formatNumber(totalSize)}B at ${formatNumber(totalSize / time * 1000)}B/s`);
17
85
  return buffers;
18
86
  }
@@ -34,4 +102,5 @@ async function doStream(stream: GenericTransformStream, buffer: Buffer): Promise
34
102
  }
35
103
  outputBuffers.push(Buffer.from(value));
36
104
  }
105
+
37
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -47,7 +47,6 @@ const FILE_MAX_LIFETIME = timeInMinute * 30;
47
47
 
48
48
  const FILE_ZIP_THRESHOLD = 16 * 1024 * 1024;
49
49
 
50
- const ZIP_THRESHOLD = 4096;
51
50
  const START_BYTES = Buffer.from([236, 49, 112, 121, 27, 127, 227, 63]);
52
51
  const END_BYTES = Buffer.from([220, 111, 243, 202, 200, 79, 213, 63]);
53
52
  // Delay writes, so we batch better, and thrash the disk less
@@ -143,9 +142,8 @@ export class TransactionStorage implements IStorage<Buffer> {
143
142
  public async get(key: string): Promise<Buffer | undefined> {
144
143
  await this.init;
145
144
  const value = this.cache.get(key);
146
- if (value && value.isZipped && value.value) {
147
- value.value = await Zip.gunzip(value.value);
148
- value.isZipped = false;
145
+ if (value && value.isZipped) {
146
+ throw new Error(`Transactions should be unzipped on file ready, so this state should be impossible. Key: ${key}`);
149
147
  }
150
148
  return value?.value;
151
149
  }
@@ -157,11 +155,6 @@ export class TransactionStorage implements IStorage<Buffer> {
157
155
  let entry: TransactionEntry = { key, value, isZipped: false, time: 0 };
158
156
  this.cache.set(key, entry);
159
157
 
160
- if (value.length >= ZIP_THRESHOLD) {
161
- value = await Zip.gzip(value);
162
- entry.value = value;
163
- entry.isZipped = true;
164
- }
165
158
  await this.pushAppend(entry);
166
159
  }
167
160
 
@@ -307,7 +300,7 @@ export class TransactionStorage implements IStorage<Buffer> {
307
300
  entryList.push(await this.parseTransactionFile(file));
308
301
  }
309
302
  let entries = entryList.flat();
310
- this.applyTransactionEntries(entries);
303
+ this.applyTransactionEntries(entries, initialLoad);
311
304
 
312
305
  time = Date.now() - time;
313
306
  if (time > 50) {
@@ -375,7 +368,7 @@ export class TransactionStorage implements IStorage<Buffer> {
375
368
  }
376
369
  return entries;
377
370
  }
378
- private applyTransactionEntries(entries: TransactionEntry[]): void {
371
+ private applyTransactionEntries(entries: TransactionEntry[], initialLoad?: boolean): void {
379
372
  let pendingWriteTimes = new Map<string, number>();
380
373
  for (const entry of this.pendingAppends) {
381
374
  let prevTime = pendingWriteTimes.get(entry.key);
@@ -404,9 +397,9 @@ export class TransactionStorage implements IStorage<Buffer> {
404
397
  anyChanged = true;
405
398
  this.cache.delete(entry.key);
406
399
  } else {
407
- if (!anyChanged) {
400
+ if (!anyChanged && !initialLoad) {
408
401
  let prev = this.cache.get(entry.key);
409
- if (!prev || prev.isZipped !== entry.isZipped) {
402
+ if (!prev) {
410
403
  anyChanged = true;
411
404
  } else {
412
405
  if (!prev.value) {
@@ -429,7 +422,8 @@ export class TransactionStorage implements IStorage<Buffer> {
429
422
  }
430
423
  }
431
424
 
432
- if (anyChanged) {
425
+ if (anyChanged && !initialLoad) {
426
+ console.warn(`Transaction storage ${this.debugName} changed on disk, triggering full download from disk`);
433
427
  for (const callback of this.resyncCallbacks) {
434
428
  try {
435
429
  callback();
@@ -467,6 +461,11 @@ export class TransactionStorage implements IStorage<Buffer> {
467
461
  let isZipped = (flags & 1) === 1;
468
462
  let isDelete = (flags & 2) === 2;
469
463
 
464
+ if (isZipped) {
465
+ value = Zip.gunzipSync(value);
466
+ isZipped = false;
467
+ }
468
+
470
469
  let entry: TransactionEntry = { key, value, isZipped, time };
471
470
  if (isDelete) {
472
471
  entry.value = undefined;
package/web/Page.tsx CHANGED
@@ -18,7 +18,6 @@ export class Page extends preact.Component {
18
18
  );
19
19
  if (ignore) return;
20
20
 
21
- console.log("Checking hotkey", e.key, e);
22
21
  let key = e.key;
23
22
  if (e.ctrlKey) key = "Ctrl+" + key;
24
23
  if (e.shiftKey) key = "Shift+" + key;
package/misc/objects.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function entries<T extends object>(object: T): [keyof T, T[keyof T]][];
package/misc/objects.ts DELETED
@@ -1,3 +0,0 @@
1
- export function entries<T extends object>(object: T): [keyof T, T[keyof T]][] {
2
- return Object.entries(object) as [keyof T, T[keyof T]][];
3
- }