@certik/skynet 0.20.1 → 0.21.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.21.0
4
+
5
+ - BREAKING: Remove support for web3.js.
6
+ - Add `debug` log level to `log` library. They will only be printed when `NODE_ENV` is set to `development`.
7
+ - Hide all logs from `log` library when `NODE_ENV` is set to `test`.
8
+ - Add `throttle` and `memoize` to `availability` library.
9
+ - Add `object-hash` library for getting hash of an object. Based on xxhash algorithm.
10
+
11
+ ## 0.20.2
12
+
13
+ - Make `options` in `withRetry` function optional
14
+
3
15
  ## 0.20.1
4
16
 
5
17
  - Update dynamodb `scanWholeTable` types
package/availability.ts CHANGED
@@ -1,3 +1,12 @@
1
+ import pThrottle from "p-throttle";
2
+ import pMemoize, { type CacheStorage } from "p-memoize";
3
+ import type { AsyncReturnType } from "type-fest";
4
+ import QuickLRU from "quick-lru";
5
+ import { getHash } from "./object-hash";
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ export declare type AnyAsyncFunction = (...arguments_: readonly any[]) => Promise<any>;
9
+
1
10
  export async function wait(time: number) {
2
11
  return new Promise((resolve) => {
3
12
  setTimeout(resolve, time);
@@ -47,28 +56,49 @@ export async function exponentialRetry<T>(
47
56
  return result;
48
57
  }
49
58
 
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- export function withRetry<F extends (...args: any[]) => any>(
59
+ export function withRetry<F extends AnyAsyncFunction>(
52
60
  func: F,
53
- options: { maxRetry: number; initialDuration?: number; growFactor?: number },
54
- ) {
55
- let retries = options.maxRetry;
56
- let duration = options.initialDuration || 500;
57
- const growFactorFinal = options.growFactor || 2;
61
+ options?: { maxRetry?: number; initialDuration?: number; growFactor?: number },
62
+ ): F {
63
+ let retries = options?.maxRetry || 3;
64
+ let duration = options?.initialDuration || 500;
65
+ const growFactorFinal = options?.growFactor || 2;
58
66
 
59
- return async (...args: Parameters<F>): Promise<ReturnType<F>> => {
67
+ return (async (...args: Parameters<F>): Promise<AsyncReturnType<F>> => {
60
68
  do {
61
69
  try {
62
70
  return await func(...args);
63
71
  } catch (error) {
64
72
  retries = retries - 1;
65
- if (retries === 0) {
73
+ if (retries <= 0) {
66
74
  throw error;
67
75
  }
68
76
  await wait(duration);
69
77
  duration = duration * growFactorFinal;
70
78
  }
71
- } while (retries >= 0);
79
+ } while (retries > 0);
72
80
  throw new Error("unreachable");
73
- };
81
+ }) as F;
82
+ }
83
+
84
+ export function memoize<F extends AnyAsyncFunction>(
85
+ func: F,
86
+ options?: {
87
+ cache?: CacheStorage<string, AsyncReturnType<F>> | false;
88
+ cacheKey?: (args: Parameters<F>) => string;
89
+ lruMaxSize?: number;
90
+ },
91
+ ) {
92
+ if (!options) {
93
+ options = {};
94
+ }
95
+ if (!options.cache) {
96
+ options.cache = new QuickLRU({ maxSize: options.lruMaxSize || 10000 });
97
+ }
98
+ if (!options.cacheKey) {
99
+ options.cacheKey = (args: Parameters<F>) => getHash(args);
100
+ }
101
+ return pMemoize<F, string>(func, options);
74
102
  }
103
+
104
+ export { pThrottle as throttle };
package/log.ts CHANGED
@@ -34,20 +34,38 @@ function timestamp() {
34
34
  }
35
35
 
36
36
  const inline = {
37
+ debug: function (...args: unknown[]) {
38
+ if (process.env.NODE_ENV === "development") {
39
+ console.log(`${timestamp()} ${getLine(args)}`);
40
+ }
41
+ },
37
42
  log: function (...args: unknown[]) {
38
- console.log(`${timestamp()} ${getLine(args)}`);
43
+ if (process.env.NODE_ENV !== "test") {
44
+ console.log(`${timestamp()} ${getLine(args)}`);
45
+ }
39
46
  },
40
47
  error: function (...args: unknown[]) {
41
- console.error(`${timestamp()} ${getLine(args)}`);
48
+ if (process.env.NODE_ENV !== "test") {
49
+ console.error(`${timestamp()} ${getLine(args)}`);
50
+ }
42
51
  },
43
52
  };
44
53
 
45
54
  const logger = {
55
+ debug: function (...args: unknown[]) {
56
+ if (process.env.NODE_ENV === "development") {
57
+ console.log(`[${timestamp()}]`, ...args);
58
+ }
59
+ },
46
60
  log: function (...args: unknown[]) {
47
- console.log(`[${timestamp()}]`, ...args);
61
+ if (process.env.NODE_ENV !== "test") {
62
+ console.log(`[${timestamp()}]`, ...args);
63
+ }
48
64
  },
49
65
  error: function (...args: unknown[]) {
50
- console.error(`[${timestamp()}]`, ...args);
66
+ if (process.env.NODE_ENV !== "test") {
67
+ console.error(`[${timestamp()}]`, ...args);
68
+ }
51
69
  },
52
70
  };
53
71
 
package/object-hash.ts ADDED
@@ -0,0 +1,66 @@
1
+ import xh from "@node-rs/xxhash";
2
+
3
+ export function getHash(obj: unknown) {
4
+ const xxh3 = xh.xxh3.Xxh3.withSeed();
5
+ hash(obj, xxh3);
6
+ return xxh3.digest().toString(16);
7
+ }
8
+
9
+ function hash(obj: unknown, xxh3: xh.xxh3.Xxh3) {
10
+ if (obj === null) {
11
+ xxh3.update("null");
12
+ } else if (obj === undefined) {
13
+ xxh3.update("undefined");
14
+ } else if (typeof obj === "string") {
15
+ xxh3.update(obj);
16
+ } else if (typeof obj === "number") {
17
+ xxh3.update(obj.toString());
18
+ } else if (typeof obj === "boolean") {
19
+ xxh3.update(obj.toString());
20
+ } else if (typeof obj === "bigint") {
21
+ xxh3.update(obj.toString());
22
+ } else if (obj instanceof Date) {
23
+ xxh3.update(obj.toISOString());
24
+ } else if (Array.isArray(obj)) {
25
+ arrayHash(obj, xxh3);
26
+ } else if (obj instanceof Set) {
27
+ setHash(obj, xxh3);
28
+ } else if (obj instanceof Map) {
29
+ mapHash(obj, xxh3);
30
+ } else if (typeof obj === "object") {
31
+ objectHash(obj, xxh3);
32
+ } else {
33
+ throw new Error(`Unsupported type: ${obj}`);
34
+ }
35
+ }
36
+
37
+ function arrayHash(array: unknown[], xxh3: xh.xxh3.Xxh3) {
38
+ xxh3.update("[");
39
+ for (const obj of array) {
40
+ hash(obj, xxh3);
41
+ }
42
+ xxh3.update("]");
43
+ }
44
+
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ function setHash(_set: Set<unknown>, _xxh3: xh.xxh3.Xxh3) {
47
+ throw new Error("Set hashing not implemented");
48
+ }
49
+
50
+ function mapHash(map: Map<unknown, unknown>, xxh3: xh.xxh3.Xxh3) {
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ const array = Array.from(map.entries()).sort(([aKey], [bKey]) => (aKey as any).localeCompare(bKey));
53
+ for (const [key, value] of array) {
54
+ hash(key, xxh3);
55
+ hash(value, xxh3);
56
+ }
57
+ }
58
+
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ function objectHash(obj: any, xxh3: xh.xxh3.Xxh3) {
61
+ const array = Object.entries(obj).sort(([aKey], [bKey]) => aKey.localeCompare(bKey));
62
+ for (const [key, value] of array) {
63
+ hash(key, xxh3);
64
+ hash(value, xxh3);
65
+ }
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@certik/skynet",
3
- "version": "0.20.1",
3
+ "version": "0.21.0",
4
4
  "description": "Skynet Shared JS library",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -21,14 +21,17 @@
21
21
  "@aws-sdk/lib-dynamodb": "^3.758.0",
22
22
  "@databricks/sql": "^1.9.0",
23
23
  "@elastic/elasticsearch": "^8.17.1",
24
+ "@node-rs/xxhash": "^1.7.6",
24
25
  "@slack/web-api": "^6.13.0",
25
26
  "chalk": "^5.4.1",
26
27
  "execa": "^9.5.2",
27
28
  "express": "^4.21.2",
28
29
  "md5": "^2.3.0",
29
30
  "meow": "^13.2.0",
31
+ "p-memoize": "^7.1.1",
32
+ "p-throttle": "^7.0.0",
33
+ "quick-lru": "^7.0.0",
30
34
  "type-fest": "^4.35.0",
31
- "web3": "^4.16.0",
32
35
  "which": "^5.0.0"
33
36
  },
34
37
  "devDependencies": {
package/web3.ts DELETED
@@ -1,41 +0,0 @@
1
- import { Web3 } from "web3";
2
- import type { AbiFunctionFragment } from "web3";
3
- import { PROTOCOLS } from "./const.ts";
4
-
5
- function newWeb3ByProtocol(protocol: string) {
6
- if (!Object.keys(PROTOCOLS).includes(protocol)) {
7
- return null;
8
- }
9
-
10
- return new Web3(PROTOCOLS[protocol].endpoint);
11
- }
12
-
13
- function normalizeCallParams<T>(params?: T) {
14
- if (params === undefined) {
15
- return [] as T[];
16
- }
17
-
18
- if (Array.isArray(params)) {
19
- return params as T[];
20
- }
21
-
22
- return [params];
23
- }
24
-
25
- async function singleCall<TData, TParams>(protocol: string, abi: AbiFunctionFragment, target: string, params: TParams) {
26
- const web3 = newWeb3ByProtocol(protocol);
27
-
28
- if (!web3) {
29
- throw new Error(`unsupported protocol`);
30
- }
31
-
32
- const contract = new web3.eth.Contract([abi], target);
33
- const functionSignature = web3.eth.abi.encodeFunctionSignature(abi);
34
- const method = contract.methods[functionSignature];
35
-
36
- const result = await method(...normalizeCallParams(params)).call<TData>();
37
-
38
- return result;
39
- }
40
-
41
- export { singleCall };