@certik/skynet 0.20.2 → 0.22.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/abi.ts +2 -4
  3. package/api.ts +7 -7
  4. package/app.ts +6 -12
  5. package/availability.ts +37 -7
  6. package/bun.lockb +0 -0
  7. package/const.ts +1 -1
  8. package/date.ts +1 -1
  9. package/deploy.ts +4 -4
  10. package/dist/abi.d.ts +112 -0
  11. package/dist/abi.d.ts.map +1 -0
  12. package/dist/abi.js +616 -0
  13. package/dist/address.d.ts +3 -0
  14. package/dist/address.d.ts.map +1 -0
  15. package/dist/address.js +69 -0
  16. package/dist/api.d.ts +32 -0
  17. package/dist/api.d.ts.map +1 -0
  18. package/dist/api.js +35174 -0
  19. package/dist/app.d.ts +102 -0
  20. package/dist/app.d.ts.map +1 -0
  21. package/dist/app.js +71714 -0
  22. package/dist/availability.d.ts +24 -0
  23. package/dist/availability.d.ts.map +1 -0
  24. package/dist/availability.js +926 -0
  25. package/dist/cli.d.ts +6 -0
  26. package/dist/cli.d.ts.map +1 -0
  27. package/dist/cli.js +86 -0
  28. package/dist/const.d.ts +35 -0
  29. package/dist/const.d.ts.map +1 -0
  30. package/dist/const.js +207 -0
  31. package/dist/databricks.d.ts +4 -0
  32. package/dist/databricks.d.ts.map +1 -0
  33. package/dist/databricks.js +84920 -0
  34. package/dist/date.d.ts +6 -0
  35. package/dist/date.d.ts.map +1 -0
  36. package/dist/date.js +101 -0
  37. package/dist/deploy.d.ts +76 -0
  38. package/dist/deploy.d.ts.map +1 -0
  39. package/dist/deploy.js +17506 -0
  40. package/dist/dynamodb.d.ts +17 -0
  41. package/dist/dynamodb.d.ts.map +1 -0
  42. package/dist/dynamodb.js +27365 -0
  43. package/dist/env.d.ts +7 -0
  44. package/dist/env.d.ts.map +1 -0
  45. package/dist/env.js +71 -0
  46. package/dist/graphql.d.ts +6 -0
  47. package/dist/graphql.d.ts.map +1 -0
  48. package/dist/graphql.js +73 -0
  49. package/dist/indexer.d.ts +70 -0
  50. package/dist/indexer.d.ts.map +1 -0
  51. package/dist/indexer.js +36979 -0
  52. package/dist/log.d.ts +14 -0
  53. package/dist/log.d.ts.map +1 -0
  54. package/dist/log.js +108 -0
  55. package/dist/object-hash.d.ts +2 -0
  56. package/dist/object-hash.d.ts.map +1 -0
  57. package/dist/object-hash.js +446 -0
  58. package/dist/opsgenie.d.ts +21 -0
  59. package/dist/opsgenie.d.ts.map +1 -0
  60. package/dist/opsgenie.js +320 -0
  61. package/dist/por.d.ts +38 -0
  62. package/dist/por.d.ts.map +1 -0
  63. package/dist/por.js +164 -0
  64. package/dist/s3.d.ts +21 -0
  65. package/dist/s3.d.ts.map +1 -0
  66. package/dist/s3.js +28878 -0
  67. package/dist/search.d.ts +6 -0
  68. package/dist/search.d.ts.map +1 -0
  69. package/dist/search.js +37754 -0
  70. package/dist/selector.d.ts +18 -0
  71. package/dist/selector.d.ts.map +1 -0
  72. package/dist/selector.js +89 -0
  73. package/dist/slack.d.ts +9 -0
  74. package/dist/slack.d.ts.map +1 -0
  75. package/dist/slack.js +16844 -0
  76. package/dist/util.d.ts +5 -0
  77. package/dist/util.d.ts.map +1 -0
  78. package/dist/util.js +72 -0
  79. package/dist/xxhash.win32-x64-msvc-hrdz34v7.node +0 -0
  80. package/examples/api.ts +1 -1
  81. package/examples/indexer.ts +1 -1
  82. package/examples/mode-indexer.ts +1 -1
  83. package/indexer.ts +11 -11
  84. package/log.ts +22 -4
  85. package/object-hash.ts +66 -0
  86. package/package.json +102 -4
  87. package/por.ts +181 -0
  88. package/search.ts +2 -2
  89. package/tsconfig.build.json +24 -0
  90. package/tsconfig.json +5 -5
  91. package/web3.ts +0 -41
package/dist/util.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare function range(startAt: number, endAt: number, step: number): [number, number][];
2
+ declare function arrayGroup<T>(array: T[], groupSize: number): T[][];
3
+ declare function fillRange(start: number, end: number): number[];
4
+ export { arrayGroup, range, fillRange };
5
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.ts"],"names":[],"mappings":"AACA,iBAAS,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,sBAQ1D;AAED,iBAAS,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,SAQnD;AAKD,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,YAQ5C;AAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC"}
package/dist/util.js ADDED
@@ -0,0 +1,72 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __toESM = (mod, isNodeMode, target) => {
9
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
10
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
+ for (let key of __getOwnPropNames(mod))
12
+ if (!__hasOwnProp.call(to, key))
13
+ __defProp(to, key, {
14
+ get: () => mod[key],
15
+ enumerable: true
16
+ });
17
+ return to;
18
+ };
19
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
20
+ var __toCommonJS = (from) => {
21
+ var entry = __moduleCache.get(from), desc;
22
+ if (entry)
23
+ return entry;
24
+ entry = __defProp({}, "__esModule", { value: true });
25
+ if (from && typeof from === "object" || typeof from === "function")
26
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
+ get: () => from[key],
28
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
+ }));
30
+ __moduleCache.set(from, entry);
31
+ return entry;
32
+ };
33
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __export = (target, all) => {
35
+ for (var name in all)
36
+ __defProp(target, name, {
37
+ get: all[name],
38
+ enumerable: true,
39
+ configurable: true,
40
+ set: (newValue) => all[name] = () => newValue
41
+ });
42
+ };
43
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
44
+ var __require = import.meta.require;
45
+
46
+ // util.ts
47
+ function range(startAt, endAt, step) {
48
+ const arr = [];
49
+ for (let i = startAt;i <= endAt; i += step) {
50
+ arr.push([i, Math.min(endAt, i + step - 1)]);
51
+ }
52
+ return arr;
53
+ }
54
+ function arrayGroup(array, groupSize) {
55
+ const groups = [];
56
+ for (let i = 0;i < array.length; i += groupSize) {
57
+ groups.push(array.slice(i, i + groupSize));
58
+ }
59
+ return groups;
60
+ }
61
+ function fillRange(start, end) {
62
+ const result = [];
63
+ for (let i = start;i <= end; i++) {
64
+ result.push(i);
65
+ }
66
+ return result;
67
+ }
68
+ export {
69
+ range,
70
+ fillRange,
71
+ arrayGroup
72
+ };
package/examples/api.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  // $ examples/api --help
9
9
 
10
10
  import type { Request, Response, NextFunction } from "express";
11
- import { api, SENSITIVE_VALUE } from "../app.ts";
11
+ import { api, SENSITIVE_VALUE } from "../app";
12
12
 
13
13
  // an example middleware
14
14
  async function exampleMiddleware(req: Request, res: Response, next: NextFunction) {
@@ -7,7 +7,7 @@
7
7
  // $ examples/indexer deploy --protocol eth
8
8
  // $ examples/indexer --help
9
9
 
10
- import { indexer, every } from "../app.ts";
10
+ import { indexer, every } from "../app";
11
11
 
12
12
  const app = indexer({
13
13
  name: "example-indexer",
@@ -8,7 +8,7 @@
8
8
  // $ examples/mode-indexer deploy --protocol eth
9
9
  // $ examples/mode-indexer --help
10
10
 
11
- import { modeIndexer, every } from "../app.ts";
11
+ import { modeIndexer, every } from "../app";
12
12
 
13
13
  async function validate({
14
14
  protocol,
package/indexer.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import meow from "meow";
2
- import { createRecord, getRecordByKey } from "./dynamodb.ts";
3
- import { getEnvironment } from "./env.ts";
4
- import { exponentialRetry } from "./availability.ts";
5
- import { range as numberRange, fillRange as fillNumberRange } from "./util.ts";
6
- import { getSelectorDesc, getSelectorFlags, toSelectorString } from "./selector.ts";
7
- import type { SelectorFlags, Selector } from "./selector.ts";
8
- import { getBinaryName } from "./cli.ts";
9
- import { inline } from "./log.ts";
10
- import { findDateAfter, dateRange, daysInRange as fillDateRange } from "./date.ts";
2
+ import { createRecord, getRecordByKey } from "./dynamodb";
3
+ import { getEnvironment } from "./env";
4
+ import { exponentialRetry } from "./availability";
5
+ import { range as numberRange, fillRange as fillNumberRange } from "./util";
6
+ import { getSelectorDesc, getSelectorFlags, toSelectorString } from "./selector";
7
+ import type { SelectorFlags, Selector } from "./selector";
8
+ import { getBinaryName } from "./cli";
9
+ import { inline } from "./log";
10
+ import { findDateAfter, dateRange, daysInRange as fillDateRange } from "./date";
11
11
 
12
12
  const STATE_TABLE_NAME = "skynet-" + getEnvironment() + "-indexer-state";
13
13
 
@@ -643,7 +643,7 @@ ${selector ? getSelectorDesc(selector) : ""}
643
643
  );
644
644
 
645
645
  try {
646
- return runMode(cli.flags);
646
+ return runMode(cli.flags as ModeIndexerFlags<TSelector>);
647
647
  } catch (err) {
648
648
  inline.error(err);
649
649
  process.exit(1);
@@ -735,7 +735,7 @@ ${selector ? getSelectorDesc(selector) : ""}
735
735
  inline.log(`[INDEXER] build successfully in ${Date.now() - startTime}ms`);
736
736
  }
737
737
 
738
- return runBuild(cli.flags).catch((err) => {
738
+ return runBuild(cli.flags as ModeIndexerFlags<TSelector>).catch((err) => {
739
739
  inline.error(err);
740
740
  process.exit(1);
741
741
  });
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,11 +1,106 @@
1
1
  {
2
2
  "name": "@certik/skynet",
3
- "version": "0.20.2",
3
+ "version": "0.22.0",
4
4
  "description": "Skynet Shared JS library",
5
5
  "type": "module",
6
- "main": "index.ts",
6
+ "exports": {
7
+ "./abi": {
8
+ "import": "./dist/abi.js",
9
+ "types": "./dist/abi.d.ts"
10
+ },
11
+ "./address": {
12
+ "import": "./dist/address.js",
13
+ "types": "./dist/address.d.ts"
14
+ },
15
+ "./api": {
16
+ "import": "./dist/api.js",
17
+ "types": "./dist/api.d.ts"
18
+ },
19
+ "./app": {
20
+ "import": "./dist/app.js",
21
+ "types": "./dist/app.d.ts"
22
+ },
23
+ "./availability": {
24
+ "import": "./dist/availability.js",
25
+ "types": "./dist/availability.d.ts"
26
+ },
27
+ "./cli": {
28
+ "import": "./dist/cli.js",
29
+ "types": "./dist/cli.d.ts"
30
+ },
31
+ "./const": {
32
+ "import": "./dist/const.js",
33
+ "types": "./dist/const.d.ts"
34
+ },
35
+ "./databricks": {
36
+ "import": "./dist/databricks.js",
37
+ "types": "./dist/databricks.d.ts"
38
+ },
39
+ "./date": {
40
+ "import": "./dist/date.js",
41
+ "types": "./dist/date.d.ts"
42
+ },
43
+ "./deploy": {
44
+ "import": "./dist/deploy.js",
45
+ "types": "./dist/deploy.d.ts"
46
+ },
47
+ "./dynamodb": {
48
+ "import": "./dist/dynamodb.js",
49
+ "types": "./dist/dynamodb.d.ts"
50
+ },
51
+ "./env": {
52
+ "import": "./dist/env.js",
53
+ "types": "./dist/env.d.ts"
54
+ },
55
+ "./graphql": {
56
+ "import": "./dist/graphql.js",
57
+ "types": "./dist/graphql.d.ts"
58
+ },
59
+ "./indexer": {
60
+ "import": "./dist/indexer.js",
61
+ "types": "./dist/indexer.d.ts"
62
+ },
63
+ "./log": {
64
+ "import": "./dist/log.js",
65
+ "types": "./dist/log.d.ts"
66
+ },
67
+ "./object-hash": {
68
+ "import": "./dist/object-hash.js",
69
+ "types": "./dist/object-hash.d.ts"
70
+ },
71
+ "./opsgenie": {
72
+ "import": "./dist/opsgenie.js",
73
+ "types": "./dist/opsgenie.d.ts"
74
+ },
75
+ "./por": {
76
+ "import": "./dist/por.js",
77
+ "types": "./dist/por.d.ts"
78
+ },
79
+ "./s3": {
80
+ "import": "./dist/s3.js",
81
+ "types": "./dist/s3.d.ts"
82
+ },
83
+ "./search": {
84
+ "import": "./dist/search.js",
85
+ "types": "./dist/search.d.ts"
86
+ },
87
+ "./selector": {
88
+ "import": "./dist/selector.js",
89
+ "types": "./dist/selector.d.ts"
90
+ },
91
+ "./slack": {
92
+ "import": "./dist/slack.js",
93
+ "types": "./dist/slack.d.ts"
94
+ },
95
+ "./util": {
96
+ "import": "./dist/util.js",
97
+ "types": "./dist/util.d.ts"
98
+ }
99
+ },
7
100
  "author": "CertiK Engineering",
8
101
  "scripts": {
102
+ "build": "bun build --outdir dist --target bun --format esm *.ts && tsc --project tsconfig.build.json --emitDeclarationOnly",
103
+ "clean": "rimraf dist",
9
104
  "lint": "eslint '*.ts' test/*.ts",
10
105
  "test": "doppler run -- bun test",
11
106
  "format": "prettier --write '*.ts' '**/*.ts'",
@@ -21,14 +116,17 @@
21
116
  "@aws-sdk/lib-dynamodb": "^3.758.0",
22
117
  "@databricks/sql": "^1.9.0",
23
118
  "@elastic/elasticsearch": "^8.17.1",
119
+ "@node-rs/xxhash": "^1.7.6",
24
120
  "@slack/web-api": "^6.13.0",
25
121
  "chalk": "^5.4.1",
26
122
  "execa": "^9.5.2",
27
123
  "express": "^4.21.2",
28
124
  "md5": "^2.3.0",
29
125
  "meow": "^13.2.0",
126
+ "p-memoize": "^7.1.1",
127
+ "p-throttle": "^7.0.0",
128
+ "quick-lru": "^7.0.0",
30
129
  "type-fest": "^4.35.0",
31
- "web3": "^4.16.0",
32
130
  "which": "^5.0.0"
33
131
  },
34
132
  "devDependencies": {
@@ -43,7 +141,7 @@
43
141
  "eslint-plugin-prettier": "^5.2.3",
44
142
  "prettier": "^3.5.2",
45
143
  "typescript": "^5.8.2",
46
- "typescript-eslint": "^8.25.0"
144
+ "rimraf": "^6.0.1"
47
145
  },
48
146
  "license": "MIT",
49
147
  "publishConfig": {
package/por.ts ADDED
@@ -0,0 +1,181 @@
1
+ import { CryptoHasher } from "bun";
2
+
3
+ export interface UserLiability {
4
+ userHash: string;
5
+ balance: number;
6
+ token?: string;
7
+ }
8
+
9
+ export interface MerkleProof {
10
+ userHash: string;
11
+ balance: number;
12
+ token?: string;
13
+ proof: string[];
14
+ positions: ('left' | 'right')[];
15
+ rootHash: string;
16
+ leafIndex: number;
17
+ hashFunction?: (data: string) => string;
18
+ }
19
+
20
+ export interface MerkleNode {
21
+ hash: string;
22
+ left?: MerkleNode;
23
+ right?: MerkleNode;
24
+ data?: UserLiability;
25
+ }
26
+
27
+ export class MerkleTreePoR {
28
+ private leaves: string[] = [];
29
+ private tree: MerkleNode[][] = [];
30
+ private userMap: Map<string, number> = new Map();
31
+ private userData: UserLiability[] = [];
32
+ private hashFunction: (data: string) => string;
33
+
34
+ constructor(users: UserLiability[], hashFunction?: (data: string) => string) {
35
+ this.userData = [...users];
36
+ this.hashFunction = hashFunction || this.defaultSHA256Hash;
37
+ this.buildTree();
38
+ }
39
+
40
+ // Default SHA-256 hash function
41
+ private static defaultSHA256Hash(data: string): string {
42
+ const hasher = new CryptoHasher("sha256");
43
+ hasher.update(data);
44
+ return hasher.digest("hex");
45
+ }
46
+
47
+ private defaultSHA256Hash(data: string): string {
48
+ return MerkleTreePoR.defaultSHA256Hash(data);
49
+ }
50
+
51
+ // Configurable hash function
52
+ private hash(data: string): string {
53
+ return this.hashFunction(data);
54
+ }
55
+
56
+
57
+ // hash user data
58
+ private createLeafHash(user: UserLiability): string {
59
+ const leafData = `${user.userHash}:${user.balance}:${user.token || ''}`;
60
+ return this.hash(leafData);
61
+ }
62
+
63
+ private buildTree(): void {
64
+ if (this.userData.length === 0) {
65
+ throw new Error('Cannot build tree with empty user data');
66
+ }
67
+
68
+ // Create leaf hashes and store user mapping
69
+ this.leaves = this.userData.map((user, index) => {
70
+ const leafHash = this.createLeafHash(user);
71
+ this.userMap.set(`${user.userHash}:${user.token || ''}`, index);
72
+ return leafHash;
73
+ });
74
+
75
+ // Start with leaf level
76
+ let currentLevel = this.leaves;
77
+ this.tree.push(currentLevel.map(hash => ({ hash })));
78
+
79
+ // Build tree bottom-up
80
+ while (currentLevel.length > 1) {
81
+ const nextLevel: string[] = [];
82
+ const nextLevelNodes: MerkleNode[] = [];
83
+
84
+ for (let i = 0; i < currentLevel.length; i += 2) {
85
+ const left = currentLevel[i];
86
+ const right = i + 1 < currentLevel.length
87
+ ? currentLevel[i + 1]
88
+ : left; // Duplicate if odd number
89
+
90
+ const parentHash = this.hash(left + right);
91
+ nextLevel.push(parentHash);
92
+
93
+ const leftNode = this.tree[this.tree.length - 1][i];
94
+ const rightNode = i + 1 < currentLevel.length
95
+ ? this.tree[this.tree.length - 1][i + 1]
96
+ : leftNode;
97
+
98
+ nextLevelNodes.push({
99
+ hash: parentHash,
100
+ left: leftNode,
101
+ right: rightNode
102
+ });
103
+ }
104
+
105
+ currentLevel = nextLevel;
106
+ this.tree.push(nextLevelNodes);
107
+ }
108
+ }
109
+
110
+ // get root hash of created tree
111
+ getRootHash(): string {
112
+ if (this.tree.length === 0) {
113
+ throw new Error('Merkle tree not built');
114
+ }
115
+ return this.tree[this.tree.length - 1][0].hash;
116
+ }
117
+
118
+ // generate merkle proof
119
+ generateProof(userHash: string, token?: string): MerkleProof {
120
+ const key = `${userHash}:${token || ''}`;
121
+ const leafIndex = this.userMap.get(key);
122
+ if (leafIndex === undefined) {
123
+ throw new Error(`${userHash} not found in merkle tree for token ${token}`);
124
+ }
125
+
126
+ const user = this.userData[leafIndex];
127
+ const proof: string[] = [];
128
+ const positions: ('left' | 'right')[] = [];
129
+
130
+ let currentIndex = leafIndex;
131
+
132
+ // Walk up the tree collecting sibling hashes
133
+ for (let level = 0; level < this.tree.length - 1; level++) {
134
+ const currentLevelSize = this.tree[level].length;
135
+ const isRightNode = currentIndex % 2 === 1;
136
+ const siblingIndex = isRightNode ? currentIndex - 1 : currentIndex + 1;
137
+
138
+ if (siblingIndex < currentLevelSize) {
139
+ proof.push(this.tree[level][siblingIndex].hash);
140
+ positions.push(isRightNode ? 'left' : 'right');
141
+ } else {
142
+ // When odd number, the last node is duplicated
143
+ proof.push(this.tree[level][currentIndex].hash);
144
+ positions.push(isRightNode ? 'left' : 'right');
145
+ }
146
+
147
+ currentIndex = Math.floor(currentIndex / 2);
148
+ }
149
+
150
+ return {
151
+ userHash: user.userHash,
152
+ balance: user.balance,
153
+ token: user.token,
154
+ proof,
155
+ positions,
156
+ rootHash: this.getRootHash(),
157
+ leafIndex,
158
+ hashFunction: this.hashFunction !== this.defaultSHA256Hash ? this.hashFunction : undefined
159
+ };
160
+ }
161
+
162
+ // verify inclusion using proof
163
+ static verifyProof(merkleProof: MerkleProof): boolean {
164
+ const leafData = `${merkleProof.userHash}:${merkleProof.balance}:${merkleProof.token || ''}`;
165
+ const hashFunction = merkleProof.hashFunction || MerkleTreePoR.defaultSHA256Hash;
166
+ let currentHash = hashFunction(leafData);
167
+
168
+ for (let i = 0; i < merkleProof.proof.length; i++) {
169
+ const siblingHash = merkleProof.proof[i];
170
+ const position = merkleProof.positions[i];
171
+
172
+ if (position === 'left') {
173
+ currentHash = hashFunction(siblingHash + currentHash);
174
+ } else {
175
+ currentHash = hashFunction(currentHash + siblingHash);
176
+ }
177
+ }
178
+
179
+ return currentHash === merkleProof.rootHash;
180
+ }
181
+ }
package/search.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { isProduction } from "./env.ts";
2
- import { inline } from "./log.ts";
1
+ import { isProduction } from "./env";
2
+ import { inline } from "./log";
3
3
  import { Client, HttpConnection } from "@elastic/elasticsearch";
4
4
  import osModule from "os";
5
5
 
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "emitDeclarationOnly": true,
6
+ "outDir": "dist",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "target": "ES2020",
10
+ "module": "ES2020",
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": false,
13
+ "verbatimModuleSyntax": false
14
+ },
15
+ "include": [
16
+ "*.ts"
17
+ ],
18
+ "exclude": [
19
+ "test/**/*",
20
+ "examples/**/*",
21
+ "dist",
22
+ "node_modules"
23
+ ]
24
+ }
package/tsconfig.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  // Enable latest features
4
- "lib": ["ESNext", "DOM"],
4
+ "lib": [
5
+ "ESNext",
6
+ "DOM"
7
+ ],
5
8
  "target": "ESNext",
6
9
  "module": "ESNext",
7
10
  "moduleDetection": "force",
8
11
  "allowJs": true,
9
-
10
12
  // Bundler mode
11
13
  "moduleResolution": "bundler",
12
14
  "allowImportingTsExtensions": true,
13
15
  "verbatimModuleSyntax": true,
14
16
  "noEmit": true,
15
-
16
17
  // Best practices
17
- "strict": true,
18
+ "strict": false,
18
19
  "skipLibCheck": true,
19
20
  "noFallthroughCasesInSwitch": true,
20
-
21
21
  // Some stricter flags
22
22
  "noUnusedLocals": true,
23
23
  "noUnusedParameters": true,