sliftutils 1.1.4 → 1.1.40
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/.claude/settings.local.json +7 -0
- package/.cursor/rules/api-calls.mdc +16 -0
- package/.cursor/rules/coding-styles.mdc +50 -0
- package/.cursor/rules/components.mdc +44 -0
- package/.cursor/rules/disk-collection.mdc +31 -0
- package/.cursor/rules/general-guidelines.mdc +11 -0
- package/.cursor/rules/mobx-state.mdc +33 -0
- package/.cursor/rules/styling-css.mdc +99 -0
- package/CLAUDE.md +211 -0
- package/builders/hotReload.ts +23 -20
- package/builders/setup.ts +2 -2
- package/dist/test.ts.cache +13 -0
- package/index.d.ts +166 -14
- package/misc/apiKeys.d.ts +1 -0
- package/misc/apiKeys.tsx +7 -3
- package/misc/dist/apiKeys.tsx.cache +145 -0
- package/misc/dist/openrouter.ts.cache +119 -0
- package/misc/dist/yaml.ts.cache +45 -0
- package/misc/dist/yamlBase.ts.cache +2406 -0
- package/misc/https/httpsCerts.d.ts +17 -0
- package/misc/https/httpsCerts.ts +4 -4
- package/misc/openrouter.d.ts +1 -0
- package/misc/openrouter.ts +32 -4
- package/misc/zip.d.ts +2 -12
- package/misc/zip.ts +2 -97
- package/package.json +4 -3
- package/render-utils/InputLabel.d.ts +1 -2
- package/render-utils/InputLabel.tsx +1 -1
- package/render-utils/autoMeasure.d.ts +1 -0
- package/render-utils/autoMeasure.ts +43 -0
- package/render-utils/dist/Input.tsx.cache +319 -0
- package/render-utils/dist/InputLabel.tsx.cache +220 -0
- package/render-utils/dist/modal.tsx.cache +69 -0
- package/render-utils/dist/observer.tsx.cache +6 -3
- package/render-utils/modal.tsx +1 -1
- package/storage/DiskCollection.d.ts +17 -0
- package/storage/DiskCollection.ts +57 -5
- package/storage/FileFolderAPI.d.ts +3 -0
- package/storage/FileFolderAPI.tsx +104 -36
- package/storage/IStorage.d.ts +4 -0
- package/storage/IStorage.ts +3 -0
- package/storage/IndexedDBFileFolderAPI.ts +6 -0
- package/storage/PrivateFileSystemStorage.d.ts +4 -0
- package/storage/PrivateFileSystemStorage.ts +11 -0
- package/storage/StorageObservable.d.ts +4 -0
- package/storage/StorageObservable.ts +27 -2
- package/storage/StorageObservableAsync.d.ts +2 -0
- package/storage/StorageObservableAsync.ts +20 -0
- package/storage/backblaze.d.ts +97 -0
- package/storage/backblaze.ts +915 -0
- package/test.ts +10 -0
- package/yarn.lock +139 -4
- package/.cursorrules +0 -251
|
@@ -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
|
+
}>;
|
package/misc/https/httpsCerts.ts
CHANGED
|
@@ -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[];
|
package/misc/openrouter.d.ts
CHANGED
package/misc/openrouter.ts
CHANGED
|
@@ -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
|
-
}, {
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.1.40",
|
|
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.
|
|
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
|
+
}
|