sliftutils 1.1.4 → 1.1.41

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.
Files changed (55) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.cursor/rules/api-calls.mdc +16 -0
  3. package/.cursor/rules/coding-styles.mdc +50 -0
  4. package/.cursor/rules/components.mdc +44 -0
  5. package/.cursor/rules/disk-collection.mdc +31 -0
  6. package/.cursor/rules/general-guidelines.mdc +11 -0
  7. package/.cursor/rules/mobx-state.mdc +33 -0
  8. package/.cursor/rules/styling-css.mdc +99 -0
  9. package/CLAUDE.md +211 -0
  10. package/builders/hotReload.ts +23 -20
  11. package/builders/setup.ts +2 -2
  12. package/dist/test.ts.cache +13 -0
  13. package/index.d.ts +167 -14
  14. package/misc/apiKeys.d.ts +1 -0
  15. package/misc/apiKeys.tsx +7 -3
  16. package/misc/dist/apiKeys.tsx.cache +145 -0
  17. package/misc/dist/openrouter.ts.cache +119 -0
  18. package/misc/dist/yaml.ts.cache +45 -0
  19. package/misc/dist/yamlBase.ts.cache +2406 -0
  20. package/misc/getSecret.d.ts +1 -0
  21. package/misc/getSecret.ts +7 -0
  22. package/misc/https/httpsCerts.d.ts +17 -0
  23. package/misc/https/httpsCerts.ts +4 -4
  24. package/misc/openrouter.d.ts +1 -0
  25. package/misc/openrouter.ts +32 -4
  26. package/misc/zip.d.ts +2 -12
  27. package/misc/zip.ts +2 -97
  28. package/package.json +4 -3
  29. package/render-utils/InputLabel.d.ts +1 -2
  30. package/render-utils/InputLabel.tsx +1 -1
  31. package/render-utils/autoMeasure.d.ts +1 -0
  32. package/render-utils/autoMeasure.ts +43 -0
  33. package/render-utils/dist/Input.tsx.cache +319 -0
  34. package/render-utils/dist/InputLabel.tsx.cache +220 -0
  35. package/render-utils/dist/modal.tsx.cache +69 -0
  36. package/render-utils/dist/observer.tsx.cache +6 -3
  37. package/render-utils/modal.tsx +1 -1
  38. package/storage/DiskCollection.d.ts +17 -0
  39. package/storage/DiskCollection.ts +57 -5
  40. package/storage/FileFolderAPI.d.ts +3 -0
  41. package/storage/FileFolderAPI.tsx +104 -36
  42. package/storage/IStorage.d.ts +4 -0
  43. package/storage/IStorage.ts +3 -0
  44. package/storage/IndexedDBFileFolderAPI.ts +6 -0
  45. package/storage/PrivateFileSystemStorage.d.ts +4 -0
  46. package/storage/PrivateFileSystemStorage.ts +11 -0
  47. package/storage/StorageObservable.d.ts +4 -0
  48. package/storage/StorageObservable.ts +27 -2
  49. package/storage/StorageObservableAsync.d.ts +2 -0
  50. package/storage/StorageObservableAsync.ts +20 -0
  51. package/storage/backblaze.d.ts +97 -0
  52. package/storage/backblaze.ts +915 -0
  53. package/test.ts +10 -0
  54. package/yarn.lock +139 -4
  55. package/.cursorrules +0 -251
@@ -7,3 +7,4 @@ export declare const getSecret: {
7
7
  getAllKeys(): string[];
8
8
  get(key: string): Promise<string> | undefined;
9
9
  };
10
+ export declare function setSecret(key: string, value: string): void;
package/misc/getSecret.ts CHANGED
@@ -96,3 +96,10 @@ export const getSecret = cache(async function getSecret(key: string): Promise<st
96
96
  });
97
97
  });
98
98
  });
99
+
100
+ export function setSecret(key: string, value: string) {
101
+ if (isNode()) {
102
+ throw new Error("setSecret is only supported in the browser. We don't want to break the user's file system with setSecret in NodeJS.");
103
+ }
104
+ localStorage.setItem(getStorageKey(key), value);
105
+ }
@@ -1,3 +1,8 @@
1
+ /// <reference path="node-forge-ed25519.d.ts" />
2
+ /// <reference path="../../storage/storage.d.ts" />
3
+ /// <reference types="node" />
4
+ /// <reference types="node" />
5
+ import * as forge from "node-forge";
1
6
  /** NOTE: We also generate the domain *.domain */
2
7
  export declare const getHTTPSCert: {
3
8
  (key: string): Promise<{
@@ -16,3 +21,15 @@ export declare const getHTTPSCert: {
16
21
  cert: string;
17
22
  }> | undefined;
18
23
  };
24
+ export declare const getAccountKey: (domain: string) => Promise<string>;
25
+ export declare function parseCert(PEMorDER: string | Buffer): forge.pki.Certificate;
26
+ export declare function normalizeCertToPEM(PEMorDER: string | Buffer): string;
27
+ export declare function generateCert(config: {
28
+ accountKey: string;
29
+ domain: string;
30
+ altDomains?: string[];
31
+ }): Promise<{
32
+ domains: string[];
33
+ key: string;
34
+ cert: string;
35
+ }>;
@@ -91,7 +91,7 @@ export const getHTTPSCert = cache(async (domain: string): Promise<{ key: string;
91
91
  });
92
92
 
93
93
 
94
- const getAccountKey = async function getAccountKey(domain: string) {
94
+ export const getAccountKey = async function getAccountKey(domain: string) {
95
95
  let accountKey = getKeyStore<string>(domain, "letsEncryptAccountKey");
96
96
  let secret = await accountKey.get();
97
97
  if (!secret) {
@@ -105,11 +105,11 @@ const getAccountKey = async function getAccountKey(domain: string) {
105
105
  };
106
106
 
107
107
 
108
- function parseCert(PEMorDER: string | Buffer) {
108
+ export function parseCert(PEMorDER: string | Buffer) {
109
109
  return forge.pki.certificateFromPem(normalizeCertToPEM(PEMorDER));
110
110
  }
111
111
 
112
- function normalizeCertToPEM(PEMorDER: string | Buffer): string {
112
+ export function normalizeCertToPEM(PEMorDER: string | Buffer): string {
113
113
  if (PEMorDER.toString().startsWith("-----BEGIN CERTIFICATE-----")) {
114
114
  return PEMorDER.toString();
115
115
  }
@@ -118,7 +118,7 @@ function normalizeCertToPEM(PEMorDER: string | Buffer): string {
118
118
  }
119
119
 
120
120
 
121
- async function generateCert(config: {
121
+ export async function generateCert(config: {
122
122
  accountKey: string;
123
123
  domain: string;
124
124
  altDomains?: string[];
@@ -13,6 +13,7 @@ export type MessageHistory2 = {
13
13
  }[];
14
14
  export declare function getTotalCost(): number;
15
15
  type OpenRouterOptions = {
16
+ apiKey?: string;
16
17
  provider?: {
17
18
  sort?: "throughput" | "price" | "latency";
18
19
  order?: string[];
@@ -1,5 +1,7 @@
1
1
  import { parseYAML } from "./yaml";
2
2
  import { retryFunctional } from "socket-function/src/batching";
3
+
4
+ const CANNOT_RETRY = "(CANNOT RETRY)";
3
5
  import { formatNumber } from "socket-function/src/formatting/format";
4
6
  import { getAPIKey } from "./apiKeys";
5
7
 
@@ -22,6 +24,8 @@ export function getTotalCost() {
22
24
  return totalCost;
23
25
  }
24
26
  type OpenRouterOptions = {
27
+ // If not provided, we'll pop up a modal to ask the user for it, Or, if we're on the server, we'll try to read it from the user folder.
28
+ apiKey?: string;
25
29
  provider?: {
26
30
  sort?: "throughput" | "price" | "latency",
27
31
  // https://openrouter.ai/docs/features/provider-routing#ordering-specific-providers
@@ -90,12 +94,11 @@ export async function openRouterCallBase(config: {
90
94
  retries?: number;
91
95
  }): Promise<string> {
92
96
  let { model, messages, options, onCost } = config;
93
- let openrouterKey = await getAPIKey("openrouter.json");
97
+ let openrouterKey = options?.apiKey || await getAPIKey("openrouter.json");
94
98
  console.log(`Calling ${model} with ${messages.length} messages`);
95
99
  let time = Date.now();
96
100
  let stillRunning = true;
97
101
 
98
- // Spawn monitoring loop
99
102
  void (async () => {
100
103
  while (stillRunning) {
101
104
  await new Promise(resolve => setTimeout(resolve, 5000));
@@ -124,7 +127,6 @@ export async function openRouterCallBase(config: {
124
127
  }),
125
128
  });
126
129
 
127
- // If it failed, throw
128
130
  if (response.status !== 200) {
129
131
  let responseText = await response.text();
130
132
  throw new Error(`Failed to call OpenRouter: ${response.status} ${response.statusText} ${responseText}`);
@@ -134,11 +136,34 @@ export async function openRouterCallBase(config: {
134
136
  cost: number;
135
137
  };
136
138
  choices: {
139
+ finish_reason?: string;
137
140
  message: {
138
141
  content: string;
142
+ refusal?: string | null;
139
143
  };
140
144
  }[];
145
+ error?: {
146
+ code?: number | string;
147
+ message?: string;
148
+ metadata?: unknown;
149
+ };
141
150
  };
151
+ if (responseObj.error) {
152
+ throw new Error(`OpenRouter returned an error: ${responseObj.error.code} ${responseObj.error.message} ${JSON.stringify(responseObj.error.metadata)}`);
153
+ }
154
+ let choice = responseObj.choices?.[0];
155
+ if (!choice) {
156
+ throw new Error(`OpenRouter returned no choices: ${JSON.stringify(responseObj)}`);
157
+ }
158
+ if (choice.message.refusal) {
159
+ throw new Error(`OpenRouter model refused content: ${choice.message.refusal} ${CANNOT_RETRY}`);
160
+ }
161
+ if (choice.finish_reason === "content_filter") {
162
+ throw new Error(`OpenRouter content filter triggered (finish_reason=content_filter): ${choice.message.content ?? ""} ${CANNOT_RETRY}`);
163
+ }
164
+ if (choice.finish_reason === "error") {
165
+ throw new Error(`OpenRouter completion errored (finish_reason=error): ${choice.message.content ?? ""}`);
166
+ }
142
167
  let newCost = responseObj.usage.cost;
143
168
  totalCost += newCost;
144
169
  onCost?.(newCost);
@@ -159,7 +184,10 @@ export async function openRouterCallBase(config: {
159
184
  pendingLog.duration += Date.now() - time;
160
185
  pendingLog.cost += newCost;
161
186
  return responseObj.choices[0].message.content as string;
162
- }, { maxRetries: config.retries || 3 })();
187
+ }, {
188
+ maxRetries: config.retries || 3,
189
+ shouldRetry: message => !message.includes(CANNOT_RETRY),
190
+ })();
163
191
  } finally {
164
192
  stillRunning = false;
165
193
  }
package/misc/zip.d.ts CHANGED
@@ -1,12 +1,2 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import { MaybePromise } from "socket-function/src/types";
4
- export declare class Zip {
5
- static gzip(buffer: Buffer, level?: number): 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;
11
- static gunzipBatch(buffers: Buffer[]): Promise<Buffer[]>;
12
- }
1
+ import { Zip } from "socket-function/src/Zip";
2
+ export { Zip };
package/misc/zip.ts CHANGED
@@ -5,102 +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
- const SYNC_THRESHOLD_BYTES = 100_000_000;
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
- 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);
48
- }
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
- }
59
- return await doStream(new DecompressionStream("gzip"), buffer);
60
- }
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
74
- public static async gunzipBatch(buffers: Buffer[]): Promise<Buffer[]> {
75
- let time = Date.now();
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
- }));
82
- time = Date.now() - time;
83
- let totalSize = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
84
- //console.log(`Gunzip ${formatNumber(totalSize)}B at ${formatNumber(totalSize / time * 1000)}B/s`);
85
- return buffers;
86
- }
87
- }
88
-
89
- async function doStream(stream: GenericTransformStream, buffer: Buffer): Promise<Buffer> {
90
- let reader = stream.readable.getReader();
91
- let writer = stream.writable.getWriter();
92
- let writePromise = writer.write(buffer);
93
- let closePromise = writer.close();
94
-
95
- let outputBuffers: Buffer[] = [];
96
- while (true) {
97
- let { value, done } = await reader.read();
98
- if (done) {
99
- await writePromise;
100
- await closePromise;
101
- return Buffer.concat(outputBuffers);
102
- }
103
- outputBuffers.push(Buffer.from(value));
104
- }
105
-
106
- }
11
+ export { Zip };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.1.4",
3
+ "version": "1.1.41",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -25,7 +25,8 @@
25
25
  "watch-web": "node ./builders/watchRun.js --port 9877 \"web/*.ts\" \"web/*.tsx\" \"yarn build-web\"",
26
26
  "watch-extension": "node ./builders/watchRun.js --port 9878 \"extension/*.ts\" \"extension/*.tsx\" \"yarn build-extension\"",
27
27
  "watch-electron": "node ./builders/watchRun.js --port 9879 \"electron/*.ts\" \"electron/*.tsx\" \"yarn build-electron\"",
28
- "notes": "mobx, preact, socket-function, typenode SHOULD be peerDependencies. But we want to use yarn (better dependency deduplication), so we can't use peerDependencies (as they aren't installed by default, which makes them a nightmare to use). If you want to override the versions, feel free to use overrides/resolutions."
28
+ "notes": "mobx, preact, socket-function, typenode SHOULD be peerDependencies. But we want to use yarn (better dependency deduplication), so we can't use peerDependencies (as they aren't installed by default, which makes them a nightmare to use). If you want to override the versions, feel free to use overrides/resolutions.",
29
+ "test": "typenode ./test.ts"
29
30
  },
30
31
  "bin": {
31
32
  "build-nodejs": "./builders/nodeJSBuildRun.js",
@@ -50,7 +51,7 @@
50
51
  "mobx": "^6.13.3",
51
52
  "preact-old-types": "^10.28.1",
52
53
  "shell-quote": "^1.8.3",
53
- "socket-function": "^1.0.1",
54
+ "socket-function": "^1.1.30",
54
55
  "typenode": "*",
55
56
  "typesafecss": "*",
56
57
  "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 */
@@ -0,0 +1 @@
1
+ export declare function runAutoMeasure(): void;
@@ -0,0 +1,43 @@
1
+ import { runInfinitePoll } from "socket-function/src/batching";
2
+ import { timeInSecond } from "socket-function/src/misc";
3
+ import { startMeasure, logMeasureTable } from "socket-function/src/profiling/measure";
4
+
5
+ export function runAutoMeasure() {
6
+ let measureObj = startMeasure();
7
+
8
+ function logProfileMeasuresTimingsNow(force = false) {
9
+ let profile = measureObj.finish();
10
+ measureObj = startMeasure();
11
+ logMeasureTable(profile, {
12
+ name: `watchdog at ${new Date().toLocaleString()}`,
13
+ // NOTE: Much higher min log times, now that we are combining logs.
14
+ minTimeToLog: force ? 0 : 250,
15
+ thresholdInTable: 0,
16
+ setTitle: true
17
+ });
18
+ logMeasureTable(profile, {
19
+ name: `watchdog at ${new Date().toLocaleString()}`,
20
+ mergeDepth: 1,
21
+ minTimeToLog: force ? 0 : 250,
22
+ });
23
+ }
24
+ function logNow() {
25
+ logProfileMeasuresTimingsNow(true);
26
+ }
27
+ (globalThis as any).logProfileMeasuresNow = logNow;
28
+ (globalThis as any).logAll = logNow;
29
+ (globalThis as any).logNow = logNow;
30
+
31
+ (globalThis as any).logUnfiltered = function logUnfiltered(depth = 2) {
32
+ let profile = measureObj.finish();
33
+ measureObj = startMeasure();
34
+ logMeasureTable(profile, {
35
+ name: `all logs at ${new Date().toLocaleString()}`,
36
+ mergeDepth: depth,
37
+ minTimeToLog: 0,
38
+ maxTableEntries: 10000000,
39
+ thresholdInTable: 0
40
+ });
41
+ };
42
+ runInfinitePoll(timeInSecond * 60, logProfileMeasuresTimingsNow);
43
+ }