@tomo-inc/wallet-utils 0.0.17 → 0.0.19

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/dist/index.cjs CHANGED
@@ -11,6 +11,7 @@ var cache_exports = {};
11
11
  __export(cache_exports, {
12
12
  clear: () => clear,
13
13
  get: () => get,
14
+ isKeyExist: () => isKeyExist,
14
15
  remove: () => remove,
15
16
  set: () => set,
16
17
  setKeyNS: () => setKeyNS
@@ -323,21 +324,25 @@ function calcCtrLength(str) {
323
324
 
324
325
  // src/index.ts
325
326
  var CubeStages = {
327
+ "prod-dogeos": "prod",
326
328
  prod: "prod",
327
329
  pre: "prod",
328
330
  dev: "gamma"
329
331
  };
330
332
  var CubeOrgIds = {
333
+ "prod-dogeos": "Org#49944bce-9daf-423f-a982-269f6d0d301b",
331
334
  prod: "Org#49944bce-9daf-423f-a982-269f6d0d301b",
332
335
  pre: "Org#71c13f6d-b992-4660-874d-2ae0fadc789f",
333
336
  dev: "Org#3d07a75a-1188-4bd0-acfa-671a198b83eb"
334
337
  };
335
338
  var RelayOrigins = {
339
+ "prod-dogeos": "https://social-relay.tomo.inc",
336
340
  prod: "https://social-relay.tomo.inc",
337
341
  pre: "https://social-relay.tomo.inc",
338
342
  dev: "https://social-relay-dev.tomo.inc"
339
343
  };
340
344
  var TomoApiDomains = {
345
+ "prod-dogeos": "https://mydoge-wallet.tomo.inc",
341
346
  prod: "https://wallet-pro.tomo.inc",
342
347
  pre: "https://wallet-pre-wlfi.tomo.inc",
343
348
  dev: "https://wallet-test.tomo.inc"
package/dist/index.d.cts CHANGED
@@ -2,15 +2,17 @@ declare function get(key: string): any;
2
2
  declare function set(key: string, val: any, isTemp: boolean): boolean;
3
3
  declare function remove(key: string): void;
4
4
  declare function clear(): void;
5
+ declare function isKeyExist(key: string): boolean;
5
6
  declare function setKeyNS(NS: string): void;
6
7
 
7
8
  declare const cache_clear: typeof clear;
8
9
  declare const cache_get: typeof get;
10
+ declare const cache_isKeyExist: typeof isKeyExist;
9
11
  declare const cache_remove: typeof remove;
10
12
  declare const cache_set: typeof set;
11
13
  declare const cache_setKeyNS: typeof setKeyNS;
12
14
  declare namespace cache {
13
- export { cache_clear as clear, cache_get as get, cache_remove as remove, cache_set as set, cache_setKeyNS as setKeyNS };
15
+ export { cache_clear as clear, cache_get as get, cache_isKeyExist as isKeyExist, cache_remove as remove, cache_set as set, cache_setKeyNS as setKeyNS };
14
16
  }
15
17
 
16
18
  declare const getBrowserName: () => string;
@@ -66,24 +68,28 @@ declare function calcCtrLength(str: string): {
66
68
  originLen: number;
67
69
  };
68
70
 
69
- type TomoStage = "prod" | "pre" | "dev";
71
+ type TomoStage = "prod" | "pre" | "dev" | "prod-dogeos";
70
72
  type CubeStage = "gamma" | "prod";
71
73
  declare const CubeStages: {
74
+ "prod-dogeos": string;
72
75
  prod: string;
73
76
  pre: string;
74
77
  dev: string;
75
78
  };
76
79
  declare const CubeOrgIds: {
80
+ "prod-dogeos": string;
77
81
  prod: string;
78
82
  pre: string;
79
83
  dev: string;
80
84
  };
81
85
  declare const RelayOrigins: {
86
+ "prod-dogeos": string;
82
87
  prod: string;
83
88
  pre: string;
84
89
  dev: string;
85
90
  };
86
91
  declare const TomoApiDomains: {
92
+ "prod-dogeos": string;
87
93
  prod: string;
88
94
  pre: string;
89
95
  dev: string;
package/dist/index.d.ts CHANGED
@@ -2,15 +2,17 @@ declare function get(key: string): any;
2
2
  declare function set(key: string, val: any, isTemp: boolean): boolean;
3
3
  declare function remove(key: string): void;
4
4
  declare function clear(): void;
5
+ declare function isKeyExist(key: string): boolean;
5
6
  declare function setKeyNS(NS: string): void;
6
7
 
7
8
  declare const cache_clear: typeof clear;
8
9
  declare const cache_get: typeof get;
10
+ declare const cache_isKeyExist: typeof isKeyExist;
9
11
  declare const cache_remove: typeof remove;
10
12
  declare const cache_set: typeof set;
11
13
  declare const cache_setKeyNS: typeof setKeyNS;
12
14
  declare namespace cache {
13
- export { cache_clear as clear, cache_get as get, cache_remove as remove, cache_set as set, cache_setKeyNS as setKeyNS };
15
+ export { cache_clear as clear, cache_get as get, cache_isKeyExist as isKeyExist, cache_remove as remove, cache_set as set, cache_setKeyNS as setKeyNS };
14
16
  }
15
17
 
16
18
  declare const getBrowserName: () => string;
@@ -66,24 +68,28 @@ declare function calcCtrLength(str: string): {
66
68
  originLen: number;
67
69
  };
68
70
 
69
- type TomoStage = "prod" | "pre" | "dev";
71
+ type TomoStage = "prod" | "pre" | "dev" | "prod-dogeos";
70
72
  type CubeStage = "gamma" | "prod";
71
73
  declare const CubeStages: {
74
+ "prod-dogeos": string;
72
75
  prod: string;
73
76
  pre: string;
74
77
  dev: string;
75
78
  };
76
79
  declare const CubeOrgIds: {
80
+ "prod-dogeos": string;
77
81
  prod: string;
78
82
  pre: string;
79
83
  dev: string;
80
84
  };
81
85
  declare const RelayOrigins: {
86
+ "prod-dogeos": string;
82
87
  prod: string;
83
88
  pre: string;
84
89
  dev: string;
85
90
  };
86
91
  declare const TomoApiDomains: {
92
+ "prod-dogeos": string;
87
93
  prod: string;
88
94
  pre: string;
89
95
  dev: string;
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ var cache_exports = {};
9
9
  __export(cache_exports, {
10
10
  clear: () => clear,
11
11
  get: () => get,
12
+ isKeyExist: () => isKeyExist,
12
13
  remove: () => remove,
13
14
  set: () => set,
14
15
  setKeyNS: () => setKeyNS
@@ -321,21 +322,25 @@ function calcCtrLength(str) {
321
322
 
322
323
  // src/index.ts
323
324
  var CubeStages = {
325
+ "prod-dogeos": "prod",
324
326
  prod: "prod",
325
327
  pre: "prod",
326
328
  dev: "gamma"
327
329
  };
328
330
  var CubeOrgIds = {
331
+ "prod-dogeos": "Org#49944bce-9daf-423f-a982-269f6d0d301b",
329
332
  prod: "Org#49944bce-9daf-423f-a982-269f6d0d301b",
330
333
  pre: "Org#71c13f6d-b992-4660-874d-2ae0fadc789f",
331
334
  dev: "Org#3d07a75a-1188-4bd0-acfa-671a198b83eb"
332
335
  };
333
336
  var RelayOrigins = {
337
+ "prod-dogeos": "https://social-relay.tomo.inc",
334
338
  prod: "https://social-relay.tomo.inc",
335
339
  pre: "https://social-relay.tomo.inc",
336
340
  dev: "https://social-relay-dev.tomo.inc"
337
341
  };
338
342
  var TomoApiDomains = {
343
+ "prod-dogeos": "https://mydoge-wallet.tomo.inc",
339
344
  prod: "https://wallet-pro.tomo.inc",
340
345
  pre: "https://wallet-pre-wlfi.tomo.inc",
341
346
  dev: "https://wallet-test.tomo.inc"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomo-inc/wallet-utils",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "author": "tomo.inc",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -20,9 +20,10 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^20.0.0",
23
+ "@vitest/coverage-v8": "4.0.14",
23
24
  "tsup": "^8.0.0",
24
25
  "typescript": "^5.0.0",
25
- "vitest": "^3.2.4"
26
+ "vitest": "^4.0.14"
26
27
  },
27
28
  "scripts": {
28
29
  "build": "tsup"
package/project.json CHANGED
@@ -50,7 +50,7 @@
50
50
  "executor": "nx:run-commands",
51
51
  "outputs": ["{projectRoot}/coverage"],
52
52
  "options": {
53
- "command": "vitest run",
53
+ "command": "vitest run --coverage",
54
54
  "cwd": "packages/wallet-utils"
55
55
  }
56
56
  },
@@ -0,0 +1,130 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import { getBrowserName } from "../broswer";
3
+
4
+ describe("browser utilities", () => {
5
+ const originalWindow = global.window;
6
+ const originalNavigator = global.navigator;
7
+
8
+ beforeEach(() => {
9
+ // Reset window and navigator
10
+ delete (global as any).window;
11
+ delete (global as any).navigator;
12
+ });
13
+
14
+ afterEach(() => {
15
+ global.window = originalWindow;
16
+ global.navigator = originalNavigator;
17
+ });
18
+
19
+ describe("getBrowserName", () => {
20
+ it("should return empty string in Node.js environment", () => {
21
+ expect(getBrowserName()).toBe("");
22
+ });
23
+
24
+ it("should detect Chrome", () => {
25
+ (global as any).window = {
26
+ navigator: {
27
+ userAgent:
28
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
29
+ },
30
+ };
31
+ expect(getBrowserName()).toBe("Chrome");
32
+ });
33
+
34
+ it("should detect Safari", () => {
35
+ (global as any).window = {
36
+ navigator: {
37
+ userAgent:
38
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
39
+ },
40
+ };
41
+ expect(getBrowserName()).toBe("Safari");
42
+ });
43
+
44
+ it("should detect Firefox", () => {
45
+ (global as any).window = {
46
+ navigator: {
47
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
48
+ },
49
+ };
50
+ expect(getBrowserName()).toBe("Firefox");
51
+ });
52
+
53
+ it("should detect Edge", () => {
54
+ (global as any).window = {
55
+ navigator: {
56
+ userAgent:
57
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
58
+ },
59
+ };
60
+ expect(getBrowserName()).toBe("Edge");
61
+ });
62
+
63
+ it("should detect Opera", () => {
64
+ // According to the code, Chrome detection (line 22) comes before Opera detection (line 32)
65
+ // So if userAgent contains "Chrome/", it will be detected as Chrome
66
+ // We test with Opera/ prefix (older Opera versions that don't include Chrome/)
67
+ (global as any).window = {
68
+ navigator: {
69
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Opera/12.0",
70
+ },
71
+ };
72
+ expect(getBrowserName()).toBe("Opera");
73
+ });
74
+
75
+ it("should detect Opera with OPR (but will be detected as Chrome due to code order)", () => {
76
+ // Modern Opera includes "Chrome/" in userAgent, so it will be detected as Chrome
77
+ // This test documents the actual behavior
78
+ (global as any).window = {
79
+ navigator: {
80
+ userAgent:
81
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OPR/106.0.0.0",
82
+ },
83
+ };
84
+ // Due to code order, Chrome detection comes first, so this returns "Chrome"
85
+ expect(getBrowserName()).toBe("Chrome");
86
+ });
87
+
88
+ it("should detect Brave when userAgent does not contain Chrome/ or Safari/", () => {
89
+ // Brave detection relies on navigator.brave
90
+ // But Chrome detection (line 22) comes before Brave detection (line 26)
91
+ // Also Safari detection (line 9) comes before Brave
92
+ // So we need userAgent that doesn't match Safari or Chrome
93
+ // We test with userAgent that doesn't have Chrome/ or Safari/ but has brave property
94
+ (global as any).window = {
95
+ navigator: {
96
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)",
97
+ brave: {
98
+ isBrave: vi.fn(() => true),
99
+ },
100
+ },
101
+ };
102
+ expect(getBrowserName()).toBe("Brave");
103
+ });
104
+
105
+ it("should detect Chrome even with brave property when userAgent contains Chrome/", () => {
106
+ // This documents the actual behavior: Chrome detection comes before Brave
107
+ // So even if navigator.brave exists, if userAgent has "Chrome/", it returns "Chrome"
108
+ (global as any).window = {
109
+ navigator: {
110
+ userAgent:
111
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
112
+ brave: {
113
+ isBrave: vi.fn(() => true),
114
+ },
115
+ },
116
+ };
117
+ // Due to code order, Chrome detection comes first
118
+ expect(getBrowserName()).toBe("Chrome");
119
+ });
120
+
121
+ it("should return empty string for unknown browser", () => {
122
+ (global as any).window = {
123
+ navigator: {
124
+ userAgent: "Unknown Browser/1.0",
125
+ },
126
+ };
127
+ expect(getBrowserName()).toBe("");
128
+ });
129
+ });
130
+ });
@@ -0,0 +1,215 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { get, set, remove, clear, isKeyExist, setKeyNS } from "../cache";
3
+
4
+ // Mock localStorage and sessionStorage
5
+ const createStorageMock = () => {
6
+ const store: Record<string, string> = {};
7
+ const mock: any = {
8
+ getItem: vi.fn((key: string) => store[key] || null),
9
+ setItem: vi.fn((key: string, value: string) => {
10
+ store[key] = value.toString();
11
+ // Also set as property for hasOwnProperty check
12
+ mock[key] = value.toString();
13
+ }),
14
+ removeItem: vi.fn((key: string) => {
15
+ delete store[key];
16
+ delete mock[key];
17
+ }),
18
+ clear: vi.fn(() => {
19
+ Object.keys(store).forEach((key) => {
20
+ delete store[key];
21
+ delete mock[key];
22
+ });
23
+ }),
24
+ get length() {
25
+ return Object.keys(store).length;
26
+ },
27
+ key: vi.fn((index: number) => Object.keys(store)[index] || null),
28
+ };
29
+
30
+ return mock;
31
+ };
32
+
33
+ const localStorageMock = createStorageMock();
34
+ const sessionStorageMock = createStorageMock();
35
+
36
+ describe("cache utilities", () => {
37
+ beforeEach(() => {
38
+ // Reset stores
39
+ localStorageMock.clear();
40
+ sessionStorageMock.clear();
41
+ // Reset mocks
42
+ vi.clearAllMocks();
43
+ // Set up window object
44
+ (global as any).window = {
45
+ localStorage: localStorageMock,
46
+ sessionStorage: sessionStorageMock,
47
+ };
48
+ // Reset key namespace
49
+ setKeyNS("tomo-");
50
+ });
51
+
52
+ describe("set and get", () => {
53
+ it("should set and get value from localStorage", () => {
54
+ const result = set("test-key", "test-value", false);
55
+ expect(result).toBe(true);
56
+ expect(localStorageMock.setItem).toHaveBeenCalled();
57
+
58
+ const value = get("test-key");
59
+ expect(value).toBe("test-value");
60
+ });
61
+
62
+ it("should set and get value from sessionStorage", () => {
63
+ const result = set("test-key", "test-value", true);
64
+ expect(result).toBe(true);
65
+ expect(sessionStorageMock.setItem).toHaveBeenCalled();
66
+
67
+ const value = get("test-key");
68
+ expect(value).toBe("test-value");
69
+ });
70
+
71
+ it("should set and get complex objects", () => {
72
+ const obj = { name: "test", count: 123 };
73
+ set("obj-key", obj, false);
74
+ const value = get("obj-key");
75
+ expect(value).toEqual(obj);
76
+ });
77
+
78
+ it("should set and get arrays", () => {
79
+ const arr = [1, 2, 3];
80
+ set("arr-key", arr, false);
81
+ const value = get("arr-key");
82
+ expect(value).toEqual(arr);
83
+ });
84
+ });
85
+
86
+ describe("remove", () => {
87
+ it("should remove key from both storages", () => {
88
+ set("test-key", "value", false);
89
+ remove("test-key");
90
+ expect(localStorageMock.removeItem).toHaveBeenCalled();
91
+ expect(sessionStorageMock.removeItem).toHaveBeenCalled();
92
+
93
+ const value = get("test-key");
94
+ expect(value).toBeNull();
95
+ });
96
+ });
97
+
98
+ describe("clear", () => {
99
+ it("should clear both storages", () => {
100
+ set("key1", "value1", false);
101
+ set("key2", "value2", true);
102
+ clear();
103
+ expect(localStorageMock.clear).toHaveBeenCalled();
104
+ expect(sessionStorageMock.clear).toHaveBeenCalled();
105
+ });
106
+ });
107
+
108
+ describe("setKeyNS", () => {
109
+ it("should change key namespace", () => {
110
+ setKeyNS("custom-");
111
+ set("test-key", "value", false);
112
+ expect(localStorageMock.setItem).toHaveBeenCalledWith("custom-test-key", expect.any(String));
113
+ });
114
+
115
+ it("should not change namespace if empty string", () => {
116
+ setKeyNS("custom-");
117
+ setKeyNS("");
118
+ set("test-key", "value", false);
119
+ // Should still use the previous namespace
120
+ expect(localStorageMock.setItem).toHaveBeenCalledWith("custom-test-key", expect.any(String));
121
+ });
122
+
123
+ it("should not change namespace if not a string", () => {
124
+ setKeyNS("prefix-");
125
+ setKeyNS(123 as any);
126
+ set("k", "v", false);
127
+ expect(localStorageMock.setItem).toHaveBeenCalledWith("prefix-k", expect.any(String));
128
+ });
129
+ });
130
+
131
+ describe("edge cases", () => {
132
+ it("should return null for non-existent key", () => {
133
+ const value = get("non-existent");
134
+ expect(value).toBeNull();
135
+ });
136
+
137
+ it("should handle invalid JSON gracefully", () => {
138
+ // Key must exist so get() proceeds to getItem and JSON.parse (hits catch)
139
+ set("corrupted-key", "x", false);
140
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
141
+ localStorageMock.getItem = vi.fn(() => "invalid json");
142
+ const value = get("corrupted-key");
143
+ expect(value).toBeNull();
144
+ expect(errSpy).toHaveBeenCalled();
145
+ errSpy.mockRestore();
146
+ });
147
+
148
+ it("should return null when val has wrong format (no type/data)", () => {
149
+ // Key must exist so get() reaches getItem and the format check (lines 39-46)
150
+ set("bad-format", "x", false);
151
+ localStorageMock.getItem = vi.fn(() => JSON.stringify({ foo: "bar" }));
152
+ const value = get("bad-format");
153
+ expect(value).toBeNull();
154
+ });
155
+ });
156
+
157
+ describe("SSR / no window", () => {
158
+ it("get should return null when window is undefined", () => {
159
+ delete (global as any).window;
160
+ const value = get("any-key");
161
+ expect(value).toBeNull();
162
+ });
163
+
164
+ it("set should return false when window is undefined", () => {
165
+ delete (global as any).window;
166
+ const result = set("key", "value", false);
167
+ expect(result).toBe(false);
168
+ });
169
+
170
+ it("remove should no-op when window is undefined", () => {
171
+ delete (global as any).window;
172
+ expect(() => remove("key")).not.toThrow();
173
+ });
174
+
175
+ it("clear should no-op when window is undefined", () => {
176
+ delete (global as any).window;
177
+ expect(() => clear()).not.toThrow();
178
+ });
179
+
180
+ it("isKeyExist should return false when window is undefined", () => {
181
+ delete (global as any).window;
182
+ expect(isKeyExist("any-key")).toBe(false);
183
+ });
184
+ });
185
+
186
+ describe("set QUOTA exceeded", () => {
187
+ it("should clear and retry setItem when QUOTA error", () => {
188
+ const clearSpy = vi.spyOn(localStorageMock, "clear");
189
+ let callCount = 0;
190
+ localStorageMock.setItem = vi.fn((key: string, value: string) => {
191
+ callCount++;
192
+ if (callCount === 1) {
193
+ const err = new Error("QuotaExceededError") as Error & { name?: string };
194
+ err.name = "QuotaExceededError";
195
+ throw err;
196
+ }
197
+ });
198
+ const result = set("quota-key", "value", false);
199
+ expect(clearSpy).toHaveBeenCalled();
200
+ expect(localStorageMock.setItem).toHaveBeenCalledTimes(2);
201
+ });
202
+
203
+ it("should return false when setItem throws non-QUOTA error (catch branch)", () => {
204
+ localStorageMock.setItem = vi.fn(() => {
205
+ const err = new Error("SecurityError") as Error & { name?: string };
206
+ err.name = "SecurityError";
207
+ throw err;
208
+ });
209
+ const result = set("sec-key", "value", false);
210
+ expect(result).toBe(false);
211
+ expect(localStorageMock.setItem).toHaveBeenCalledTimes(1);
212
+ expect(localStorageMock.clear).not.toHaveBeenCalled();
213
+ });
214
+ });
215
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { maskEmail, isEmail } from "../email";
3
+
4
+ describe("email utilities", () => {
5
+ describe("maskEmail", () => {
6
+ it("should mask email with username length >= 4", () => {
7
+ expect(maskEmail("alice@example.com")).toBe("al***@example.com");
8
+ // "testuser" has 8 chars, slice(0, -3) = "testu", so result is "testu***"
9
+ expect(maskEmail("testuser@example.com")).toBe("testu***@example.com");
10
+ });
11
+
12
+ it("should mask email with username length < 4", () => {
13
+ expect(maskEmail("abc@example.com")).toBe("abc*@example.com");
14
+ expect(maskEmail("ab@example.com")).toBe("ab**@example.com");
15
+ expect(maskEmail("a@example.com")).toBe("a***@example.com");
16
+ });
17
+
18
+ it("should return undefined for empty email", () => {
19
+ expect(maskEmail("")).toBeUndefined();
20
+ });
21
+
22
+ it("should return null for invalid email format", () => {
23
+ expect(maskEmail("invalid")).toBeNull();
24
+ expect(maskEmail("no-at-sign")).toBeNull();
25
+ });
26
+ });
27
+
28
+ describe("isEmail", () => {
29
+ it("should validate correct email addresses", () => {
30
+ expect(isEmail("test@example.com")).toBe(true);
31
+ expect(isEmail("user.name@example.co.uk")).toBe(true);
32
+ expect(isEmail("user+tag@example.com")).toBe(true);
33
+ });
34
+
35
+ it("should reject invalid email addresses", () => {
36
+ expect(isEmail("invalid")).toBe(false);
37
+ expect(isEmail("@example.com")).toBe(false);
38
+ expect(isEmail("test@")).toBe(false);
39
+ expect(isEmail("test@example")).toBe(false);
40
+ expect(isEmail("test @example.com")).toBe(false);
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,109 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ ChainTypeEnum,
4
+ SupportedChainTypes,
5
+ ProviderProtocol,
6
+ ProviderStandard,
7
+ } from "../global-config";
8
+
9
+ describe("global-config", () => {
10
+ describe("ChainTypeEnum", () => {
11
+ it("should have all chain types defined", () => {
12
+ expect(ChainTypeEnum.BITCOIN).toBe("bitcoin");
13
+ expect(ChainTypeEnum.DOGECOIN).toBe("dogecoin");
14
+ expect(ChainTypeEnum.EVM).toBe("evm");
15
+ expect(ChainTypeEnum.SOLANA).toBe("solana");
16
+ expect(ChainTypeEnum.SUI).toBe("sui");
17
+ expect(ChainTypeEnum.TON).toBe("ton");
18
+ expect(ChainTypeEnum.TRON).toBe("tron");
19
+ expect(ChainTypeEnum.COSMOS).toBe("cosmos");
20
+ expect(ChainTypeEnum.APTOS).toBe("aptos");
21
+ });
22
+ });
23
+
24
+ describe("SupportedChainTypes", () => {
25
+ it("should have configuration for DOGECOIN chain", () => {
26
+ const config = SupportedChainTypes[ChainTypeEnum.DOGECOIN];
27
+ expect(config.chainId).toBe("3");
28
+ expect(config.chainIndex).toBe(300);
29
+ expect(config.support).toBe(true);
30
+ expect(config.gasFee).toBe(0.5);
31
+ expect(config.dust).toBe(0.0001);
32
+ });
33
+
34
+ it("should have configuration for EVM chain", () => {
35
+ const config = SupportedChainTypes[ChainTypeEnum.EVM];
36
+ expect(config.chainId).toBe("1");
37
+ expect(config.chainIndex).toBe(100);
38
+ expect(config.support).toBe(true);
39
+ expect(config.gasFee).toBe(0.00001);
40
+ expect(config.dust).toBe(0);
41
+ });
42
+
43
+ it("should have configuration for SOLANA chain", () => {
44
+ const config = SupportedChainTypes[ChainTypeEnum.SOLANA];
45
+ expect(config.chainId).toBe("501");
46
+ expect(config.chainIndex).toBe(50100);
47
+ expect(config.support).toBe(true);
48
+ expect(config.gasFee).toBe(0.0001);
49
+ expect(config.dust).toBe(0);
50
+ expect(config.reserve).toBe(0.0009);
51
+ });
52
+
53
+ it("should have configuration for TRON chain", () => {
54
+ const config = SupportedChainTypes[ChainTypeEnum.TRON];
55
+ expect(config.chainId).toBe("19484");
56
+ expect(config.chainIndex).toBe(1948400);
57
+ expect(config.support).toBe(true);
58
+ expect(config.gasFee).toBe(0.5);
59
+ expect(config.dust).toBe(0);
60
+ });
61
+
62
+ it("should have configuration for BITCOIN chain", () => {
63
+ const config = SupportedChainTypes[ChainTypeEnum.BITCOIN];
64
+ expect(config.chainId).toBe("0");
65
+ expect(config.chainIndex).toBe(0);
66
+ expect(config.support).toBe(false);
67
+ expect(config.gasFee).toBe(0.00001);
68
+ expect(config.dust).toBe(0.0001);
69
+ });
70
+
71
+ it("should have configuration for unsupported chains", () => {
72
+ const unsupportedChains = [
73
+ ChainTypeEnum.SUI,
74
+ ChainTypeEnum.TON,
75
+ ChainTypeEnum.COSMOS,
76
+ ChainTypeEnum.APTOS,
77
+ ];
78
+ unsupportedChains.forEach((chainType) => {
79
+ const config = SupportedChainTypes[chainType];
80
+ expect(config.support).toBe(false);
81
+ expect(config.gasFee).toBe(0);
82
+ expect(config.dust).toBe(0);
83
+ });
84
+ });
85
+
86
+ it("should have all ChainTypeEnum in SupportedChainTypes", () => {
87
+ const allChainTypes = Object.values(ChainTypeEnum);
88
+ allChainTypes.forEach((chainType) => {
89
+ expect(SupportedChainTypes[chainType]).toBeDefined();
90
+ });
91
+ });
92
+ });
93
+
94
+ describe("ProviderProtocol", () => {
95
+ it("should have expected values", () => {
96
+ expect(ProviderProtocol.INJECT).toBe("inject");
97
+ expect(ProviderProtocol.DEEPLINK).toBe("deeplink");
98
+ expect(ProviderProtocol.WALLET_CONNECT).toBe("wallet-connect");
99
+ });
100
+ });
101
+
102
+ describe("ProviderStandard", () => {
103
+ it("should have expected values", () => {
104
+ expect(ProviderStandard.NORMAL).toBe("normal");
105
+ expect(ProviderStandard.EIP1193).toBe("eip1193");
106
+ expect(ProviderStandard.WALLET_STANDARD).toBe("wallet-standard");
107
+ });
108
+ });
109
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ cache,
4
+ CubeStages,
5
+ CubeOrgIds,
6
+ RelayOrigins,
7
+ TomoApiDomains,
8
+ } from "../index";
9
+
10
+ describe("index", () => {
11
+ it("should export cache module", () => {
12
+ expect(cache).toBeDefined();
13
+ expect(cache.get).toBeDefined();
14
+ expect(cache.set).toBeDefined();
15
+ expect(cache.remove).toBeDefined();
16
+ expect(cache.clear).toBeDefined();
17
+ expect(cache.setKeyNS).toBeDefined();
18
+ });
19
+
20
+ it("CubeStages should map stage to cube stage", () => {
21
+ expect(CubeStages.prod).toBe("prod");
22
+ expect(CubeStages.pre).toBe("prod");
23
+ expect(CubeStages.dev).toBe("gamma");
24
+ });
25
+
26
+ it("CubeOrgIds should have org ids per stage", () => {
27
+ expect(CubeOrgIds.prod).toMatch(/^Org#/);
28
+ expect(CubeOrgIds.pre).toMatch(/^Org#/);
29
+ expect(CubeOrgIds.dev).toMatch(/^Org#/);
30
+ });
31
+
32
+ it("RelayOrigins should have relay URLs", () => {
33
+ expect(RelayOrigins.prod).toBe("https://social-relay.tomo.inc");
34
+ expect(RelayOrigins.pre).toBe("https://social-relay.tomo.inc");
35
+ expect(RelayOrigins.dev).toBe("https://social-relay-dev.tomo.inc");
36
+ });
37
+
38
+ it("TomoApiDomains should have wallet API URLs", () => {
39
+ expect(TomoApiDomains.prod).toContain("tomo.inc");
40
+ expect(TomoApiDomains.pre).toContain("tomo.inc");
41
+ expect(TomoApiDomains.dev).toContain("tomo.inc");
42
+ });
43
+ });
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validatePassword } from "../password";
3
+
4
+ describe("password validation", () => {
5
+ it("should validate password with letters and numbers", () => {
6
+ expect(validatePassword("password123")).toBe(true);
7
+ expect(validatePassword("Password123")).toBe(true);
8
+ expect(validatePassword("PASSWORD123")).toBe(true);
9
+ });
10
+
11
+ it("should reject password without letters", () => {
12
+ expect(validatePassword("12345678")).toBe(false);
13
+ });
14
+
15
+ it("should reject password without numbers", () => {
16
+ expect(validatePassword("password")).toBe(false);
17
+ });
18
+
19
+ it("should reject password too short", () => {
20
+ expect(validatePassword("pass123")).toBe(false);
21
+ });
22
+
23
+ it("should reject password too long", () => {
24
+ expect(validatePassword("a".repeat(33) + "123")).toBe(false);
25
+ });
26
+
27
+ it("should accept password with exactly 8 characters", () => {
28
+ expect(validatePassword("pass1234")).toBe(true);
29
+ });
30
+
31
+ it("should accept password with exactly 32 characters", () => {
32
+ expect(validatePassword("a".repeat(30) + "12")).toBe(true);
33
+ });
34
+ });
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { stripHexPrefix, compareString, subs, getExplorerUrl, calcCtrLength } from "../string";
3
+
4
+ describe("string utilities", () => {
5
+ describe("stripHexPrefix", () => {
6
+ it("should strip 0x prefix", () => {
7
+ expect(stripHexPrefix("0x123abc")).toBe("123abc");
8
+ });
9
+
10
+ it("should return original string if no prefix", () => {
11
+ expect(stripHexPrefix("123abc")).toBe("123abc");
12
+ });
13
+
14
+ it("should handle non-string input", () => {
15
+ expect(stripHexPrefix(123 as any)).toBe(123);
16
+ });
17
+ });
18
+
19
+ describe("compareString", () => {
20
+ it("should compare strings case-insensitively", () => {
21
+ expect(compareString("Hello", "hello")).toBe(true);
22
+ expect(compareString("WORLD", "world")).toBe(true);
23
+ expect(compareString("Test", "test")).toBe(true);
24
+ });
25
+
26
+ it("should return false for different strings", () => {
27
+ expect(compareString("Hello", "World")).toBe(false);
28
+ });
29
+ });
30
+
31
+ describe("subs", () => {
32
+ it("should substitute template variables", () => {
33
+ const template = "Hello {name}, you are {age} years old";
34
+ const data = { name: "Alice", age: 30 };
35
+ const result = subs(template, data);
36
+ expect(result).toBe("Hello Alice, you are 30 years old");
37
+ });
38
+
39
+ it("should handle array of objects", () => {
40
+ const template = "{name}: {value}";
41
+ const data = [
42
+ { name: "key1", value: "value1" },
43
+ { name: "key2", value: "value2" },
44
+ ];
45
+ const result = subs(template, data);
46
+ expect(result).toBe("key1: value1key2: value2");
47
+ });
48
+
49
+ it("should handle escaped braces", () => {
50
+ // In the string "\\{literal\\}", the backslashes escape the braces
51
+ // The regex /\\?\{([^}]+)\}/g matches \{literal\}, and since match.charAt(0) == "\\",
52
+ // it returns match.slice(1) which is "{literal\}"
53
+ const template = "\\{literal\\} {name}";
54
+ const data = { name: "Alice" };
55
+ const result = subs(template, data);
56
+ // The actual behavior: escaped braces become "{literal\}" (backslash is preserved)
57
+ expect(result).toBe("{literal\\} Alice");
58
+ });
59
+
60
+ it("should handle missing variables", () => {
61
+ const template = "Hello {name}, {missing}";
62
+ const data = { name: "Alice" };
63
+ const result = subs(template, data);
64
+ expect(result).toBe("Hello Alice, ");
65
+ });
66
+
67
+ it("should use custom regexp when provided", () => {
68
+ const template = "Hello {{name}}";
69
+ const data = { name: "Bob" };
70
+ const result = subs(template, data, /\{\{([^}]+)\}\}/g);
71
+ expect(result).toBe("Hello Bob");
72
+ });
73
+ });
74
+
75
+ describe("getExplorerUrl", () => {
76
+ it("should return correct explorer URL for Ethereum", () => {
77
+ const url = getExplorerUrl(100, { txId: "0x123" });
78
+ expect(url).toBe("https://etherscan.io/tx/0x123");
79
+ });
80
+
81
+ it("should return correct explorer URL for Solana", () => {
82
+ const url = getExplorerUrl(50100, { txId: "abc123" });
83
+ expect(url).toBe("https://solscan.io/tx/abc123");
84
+ });
85
+
86
+ it("should return correct explorer URL for BSC", () => {
87
+ const url = getExplorerUrl(5600, { txId: "0x456" });
88
+ expect(url).toBe("https://bscscan.com/tx/0x456");
89
+ });
90
+
91
+ it("should throw error for unsupported chain", () => {
92
+ expect(() => getExplorerUrl(99999, { txId: "0x123" })).toThrow();
93
+ });
94
+
95
+ it("should throw when chainIndex has no explorer tpl", () => {
96
+ expect(() => getExplorerUrl(0, { txId: "0x123" })).toThrow("chainIndex 0 tpl not found");
97
+ });
98
+ });
99
+
100
+ describe("calcCtrLength", () => {
101
+ it("should calculate length for English text", () => {
102
+ const result = calcCtrLength("Hello");
103
+ expect(result.len).toBe(5);
104
+ expect(result.originLen).toBe(5);
105
+ });
106
+
107
+ it("should calculate length for Chinese text", () => {
108
+ const result = calcCtrLength("你好");
109
+ expect(result.len).toBe(4); // Chinese characters count as 2
110
+ expect(result.originLen).toBe(2);
111
+ });
112
+
113
+ it("should handle mixed text", () => {
114
+ const result = calcCtrLength("Hello你好");
115
+ expect(result.len).toBe(9); // 5 + 4
116
+ expect(result.originLen).toBe(7); // 5 + 2
117
+ });
118
+
119
+ it("should trim whitespace", () => {
120
+ const result = calcCtrLength(" Hello ");
121
+ expect(result.len).toBe(5);
122
+ expect(result.originLen).toBe(5);
123
+ });
124
+ });
125
+ });
package/src/cache.ts CHANGED
@@ -117,4 +117,4 @@ function setKeyNS(NS: string) {
117
117
  }
118
118
  }
119
119
 
120
- export { clear, get, remove, set, setKeyNS };
120
+ export { clear, get, isKeyExist, remove, set, setKeyNS };
package/src/index.ts CHANGED
@@ -7,27 +7,31 @@ export * from "./global-config";
7
7
  export * from "./password";
8
8
  export * from "./string";
9
9
 
10
- export type TomoStage = "prod" | "pre" | "dev";
10
+ export type TomoStage = "prod" | "pre" | "dev" | "prod-dogeos";
11
11
  export type CubeStage = "gamma" | "prod";
12
12
  export const CubeStages = {
13
+ "prod-dogeos": "prod",
13
14
  prod: "prod",
14
15
  pre: "prod",
15
16
  dev: "gamma",
16
17
  };
17
18
 
18
19
  export const CubeOrgIds = {
20
+ "prod-dogeos": "Org#49944bce-9daf-423f-a982-269f6d0d301b",
19
21
  prod: "Org#49944bce-9daf-423f-a982-269f6d0d301b",
20
22
  pre: "Org#71c13f6d-b992-4660-874d-2ae0fadc789f",
21
23
  dev: "Org#3d07a75a-1188-4bd0-acfa-671a198b83eb",
22
24
  };
23
25
 
24
26
  export const RelayOrigins = {
27
+ "prod-dogeos": "https://social-relay.tomo.inc",
25
28
  prod: "https://social-relay.tomo.inc",
26
29
  pre: "https://social-relay.tomo.inc",
27
30
  dev: "https://social-relay-dev.tomo.inc",
28
31
  };
29
32
 
30
33
  export const TomoApiDomains = {
34
+ "prod-dogeos": "https://mydoge-wallet.tomo.inc",
31
35
  prod: "https://wallet-pro.tomo.inc",
32
36
  pre: "https://wallet-pre-wlfi.tomo.inc",
33
37
  dev: "https://wallet-test.tomo.inc",
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: "node",
7
+ coverage: {
8
+ provider: "v8",
9
+ reporter: ["text", "json", "html"],
10
+ exclude: ["node_modules/", "dist/", "**/*.config.*", "**/__tests__/**", "**/*.test.ts", "**/*.spec.ts"],
11
+ },
12
+ },
13
+ });