@keyringnetwork/keyring-connect-sdk 3.1.0 → 4.1.0-alpha.1

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 (69) hide show
  1. package/dist/core/VerificationSession.d.ts +62 -0
  2. package/dist/core/VerificationSession.js +404 -0
  3. package/dist/core/errors.d.ts +49 -0
  4. package/dist/core/errors.js +81 -0
  5. package/dist/core/htppClient.d.ts +17 -0
  6. package/dist/core/htppClient.js +160 -0
  7. package/dist/{main.d.ts → core/keyringConnectExtension.d.ts} +9 -7
  8. package/dist/{main.js → core/keyringConnectExtension.js} +53 -46
  9. package/dist/core/websocketClient.d.ts +23 -0
  10. package/dist/core/websocketClient.js +220 -0
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +3 -3
  13. package/dist/types/api.d.ts +24 -0
  14. package/dist/types/api.js +2 -0
  15. package/dist/types/core.d.ts +78 -0
  16. package/dist/types/core.js +2 -0
  17. package/dist/types/extension.d.ts +59 -0
  18. package/dist/types/index.d.ts +4 -0
  19. package/dist/types/index.js +20 -0
  20. package/dist/types/websocket.d.ts +68 -0
  21. package/dist/types/websocket.js +3 -0
  22. package/dist/ui/UIManager.d.ts +52 -0
  23. package/dist/ui/UIManager.js +257 -0
  24. package/dist/ui/components/keyring-button.d.ts +12 -0
  25. package/dist/ui/components/keyring-button.js +140 -0
  26. package/dist/ui/components/keyring-text.d.ts +12 -0
  27. package/dist/ui/components/keyring-text.js +169 -0
  28. package/dist/ui/composites/keyring-complete-modal.d.ts +19 -0
  29. package/dist/ui/composites/keyring-complete-modal.js +200 -0
  30. package/dist/ui/composites/keyring-mobile-modal.d.ts +25 -0
  31. package/dist/ui/composites/keyring-mobile-modal.js +308 -0
  32. package/dist/ui/composites/keyring-qr-modal.d.ts +32 -0
  33. package/dist/ui/composites/keyring-qr-modal.js +464 -0
  34. package/dist/ui/composites/keyring-selection-modal.d.ts +25 -0
  35. package/dist/ui/composites/keyring-selection-modal.js +342 -0
  36. package/dist/ui/composites/keyring-terminated-modal.d.ts +14 -0
  37. package/dist/ui/composites/keyring-terminated-modal.js +121 -0
  38. package/dist/ui/icons/apple-icon.d.ts +4 -0
  39. package/dist/ui/icons/apple-icon.js +47 -0
  40. package/dist/ui/icons/check-circle.d.ts +4 -0
  41. package/dist/ui/icons/check-circle.js +35 -0
  42. package/dist/ui/icons/checkmark.d.ts +4 -0
  43. package/dist/ui/icons/checkmark.js +37 -0
  44. package/dist/ui/icons/close-circle.d.ts +4 -0
  45. package/dist/ui/icons/close-circle.js +35 -0
  46. package/dist/ui/icons/error-circle.d.ts +4 -0
  47. package/dist/ui/icons/error-circle.js +48 -0
  48. package/dist/ui/icons/extension-grid.d.ts +4 -0
  49. package/dist/ui/icons/extension-grid.js +79 -0
  50. package/dist/ui/icons/google-icon.d.ts +4 -0
  51. package/dist/ui/icons/google-icon.js +55 -0
  52. package/dist/ui/icons/gradient-donut.d.ts +8 -0
  53. package/dist/ui/icons/gradient-donut.js +85 -0
  54. package/dist/ui/icons/keyring.d.ts +4 -0
  55. package/dist/ui/icons/keyring.js +71 -0
  56. package/dist/ui/icons/mobile-grid.d.ts +4 -0
  57. package/dist/ui/icons/mobile-grid.js +68 -0
  58. package/dist/ui/icons/success.d.ts +4 -0
  59. package/dist/ui/icons/success.js +54 -0
  60. package/dist/utils/environment.d.ts +34 -0
  61. package/dist/utils/environment.js +67 -0
  62. package/dist/utils/logger.d.ts +7 -0
  63. package/dist/utils/logger.js +40 -0
  64. package/dist/utils/platformUtils.d.ts +24 -0
  65. package/dist/utils/platformUtils.js +64 -0
  66. package/package.json +32 -9
  67. package/readme.md +57 -105
  68. package/dist/types.d.ts +0 -159
  69. /package/dist/{types.js → types/extension.js} +0 -0
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.HttpClient = void 0;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const environment_1 = require("../utils/environment");
18
+ const logger_1 = require("../utils/logger");
19
+ const platformUtils_1 = require("../utils/platformUtils");
20
+ const errors_1 = require("./errors");
21
+ class HttpClient {
22
+ constructor(apiKey) {
23
+ this.logger = logger_1.KeyringLogger.init("HttpClient");
24
+ this.httpClient = axios_1.default.create({
25
+ baseURL: environment_1.Environment.getConfig().apiUrl,
26
+ timeout: environment_1.Environment.getConfig().timeout,
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ "X-API-KEY": apiKey,
30
+ },
31
+ });
32
+ this.setupAxiosInterceptors();
33
+ this.logger.debug({ apiUrl: environment_1.Environment.getConfig().apiUrl }, "HTTP client initialized");
34
+ HttpClient.instance = this;
35
+ }
36
+ setupAxiosInterceptors() {
37
+ this.httpClient.interceptors.request.use((config) => {
38
+ var _a;
39
+ this.logger.debug({ method: (_a = config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase(), url: config.url }, "HTTP Request");
40
+ return config;
41
+ }, (error) => {
42
+ this.logger.error("HTTP Request failed", error);
43
+ return Promise.reject(errors_1.KeyringError.fromUnknown(error));
44
+ });
45
+ this.httpClient.interceptors.response.use((response) => {
46
+ this.logger.debug({ status: response.status, url: response.config.url }, "HTTP Response");
47
+ return response;
48
+ }, (error) => {
49
+ var _a, _b, _c;
50
+ this.logger.error({
51
+ status: (_a = error.response) === null || _a === void 0 ? void 0 : _a.status,
52
+ url: (_b = error.config) === null || _b === void 0 ? void 0 : _b.url,
53
+ message: error.message || ((_c = error.response) === null || _c === void 0 ? void 0 : _c.data),
54
+ }, "HTTP Response error");
55
+ return Promise.reject(errors_1.KeyringError.fromUnknown(error));
56
+ });
57
+ }
58
+ static initializeHttpClient(apiKey) {
59
+ if (!this.instance) {
60
+ this.instance = new HttpClient(apiKey);
61
+ }
62
+ return this.instance;
63
+ }
64
+ createSession(sessionConfig) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ this.logger.debug("Creating session on backend");
67
+ const response = yield this.httpClient.post("/api/v1/connect/sessions/create", {
68
+ config: sessionConfig,
69
+ origin_url: platformUtils_1.PlatformUtils.isBrowser() ? window.location.href : "server",
70
+ });
71
+ this.logger.debug({ sessionId: response.data.session_id, expiresAt: response.data.expires_at }, "Session creation response received");
72
+ return response.data;
73
+ });
74
+ }
75
+ closeSession(sessionId, sessionToken) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ yield this.httpClient.delete(`/api/v1/connect/sessions/${sessionId}`, {
78
+ headers: {
79
+ "X-SESSION-TOKEN": sessionToken,
80
+ },
81
+ });
82
+ });
83
+ }
84
+ getSession(sessionId, sessionToken) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ var _a, _b, _c;
87
+ this.logger.debug({ sessionId }, "Getting session from backend");
88
+ try {
89
+ const response = yield this.httpClient.get(`/api/v1/connect/sessions/${sessionId}`, {
90
+ headers: {
91
+ "x-session-token": sessionToken,
92
+ },
93
+ });
94
+ // Handle 204 No Content (session deleted/expired)
95
+ if (response.status === 204 || !response.data) {
96
+ this.logger.debug({ sessionId }, "Session not found (204 or empty response)");
97
+ return null;
98
+ }
99
+ this.logger.debug({ sessionId: response.data.session_id, status: response.data.status }, "Session retrieved");
100
+ return response.data;
101
+ }
102
+ catch (error) {
103
+ // Return null for 404 or 401 (session not found or expired)
104
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404 || ((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 401) {
105
+ this.logger.debug({ sessionId, status: (_c = error.response) === null || _c === void 0 ? void 0 : _c.status }, "Session not found");
106
+ return null;
107
+ }
108
+ // Re-throw other errors
109
+ throw error;
110
+ }
111
+ });
112
+ }
113
+ /**
114
+ * Start polling for session updates
115
+ * Returns a function to stop polling
116
+ */
117
+ startPolling(sessionId, sessionToken, onUpdate, onError, interval = 2000) {
118
+ this.logger.debug({ sessionId, interval }, "Starting session polling");
119
+ let isPolling = true;
120
+ let timeoutId = null;
121
+ const poll = () => __awaiter(this, void 0, void 0, function* () {
122
+ if (!isPolling)
123
+ return;
124
+ try {
125
+ const session = yield this.getSession(sessionId, sessionToken);
126
+ // Stop polling if session not found (expired or deleted)
127
+ if (!session) {
128
+ this.logger.debug({ sessionId }, "Session not found, stopping polling");
129
+ isPolling = false;
130
+ onError(new Error("Session not found or expired"));
131
+ return;
132
+ }
133
+ onUpdate(session);
134
+ // Continue polling if still active
135
+ if (isPolling) {
136
+ timeoutId = setTimeout(poll, interval);
137
+ }
138
+ }
139
+ catch (error) {
140
+ this.logger.error(error, "Polling error");
141
+ onError(error);
142
+ // Continue polling despite errors (network might be temporarily down)
143
+ if (isPolling) {
144
+ timeoutId = setTimeout(poll, interval);
145
+ }
146
+ }
147
+ });
148
+ // Start polling
149
+ poll();
150
+ // Return stop function
151
+ return () => {
152
+ this.logger.debug({ sessionId }, "Stopping session polling");
153
+ isPolling = false;
154
+ if (timeoutId) {
155
+ clearTimeout(timeoutId);
156
+ }
157
+ };
158
+ }
159
+ }
160
+ exports.HttpClient = HttpClient;
@@ -1,17 +1,23 @@
1
- import { ExtensionSDKConfig, ExtensionState } from "./types";
1
+ import { CredentialData, ExtensionState, SessionConfig } from "../types";
2
2
  /**
3
3
  * Class for interacting with the Keyring Connect browser extension
4
4
  */
5
- export declare class KeyringConnect {
5
+ export declare class KeyringConnectExtension {
6
6
  private static readonly EXTENSION_TIMEOUT;
7
7
  private static readonly EXTENSION_ID;
8
8
  private static keyringConnectUrl;
9
+ private static resultPromise?;
10
+ private static resolveResult?;
11
+ private static rejectResult?;
9
12
  /**
10
13
  * Tries to launch the Keyring Connect Extension with the provided configuration.
11
14
  * If the extension is not installed, it will redirect to the Keyring Connect page.
15
+ * @throws If the extension is not installed and running in a non-browser environment,
16
+ * if required configuration fields are missing, if Chrome runtime is not available,
17
+ * if extension communication times out, or if the extension returns an error.
12
18
  * @param data - The configuration for the extension
13
19
  */
14
- static launchExtension(data: ExtensionSDKConfig): Promise<void>;
20
+ static launchExtension(data: SessionConfig): Promise<CredentialData | null>;
15
21
  /**
16
22
  * Check if Keyring Connect Extension is installed and ready to use
17
23
  * @returns Promise that resolves with true if extension is installed and ready to use, false otherwise
@@ -33,11 +39,7 @@ export declare class KeyringConnect {
33
39
  static subscribeToExtensionState(callback: (state: ExtensionState | null) => void): () => void;
34
40
  private static validateClientConfig;
35
41
  private static validateCredentialConfig;
36
- private static validateProofConfig;
37
42
  private static validateConfigData;
38
43
  private static convertToLaunchConfig;
39
- private static isChromeRuntimeAvailable;
40
44
  private static sendChromeMessage;
41
- private static launchExtensionViaChromeAPI;
42
- private static getExtensionStateViaChromeAPI;
43
45
  }
@@ -9,15 +9,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.KeyringConnect = void 0;
13
- const types_1 = require("./types");
12
+ exports.KeyringConnectExtension = void 0;
13
+ const types_1 = require("../types");
14
+ const platformUtils_1 = require("../utils/platformUtils");
14
15
  /**
15
16
  * Class for interacting with the Keyring Connect browser extension
16
17
  */
17
- class KeyringConnect {
18
+ class KeyringConnectExtension {
18
19
  /**
19
20
  * Tries to launch the Keyring Connect Extension with the provided configuration.
20
21
  * If the extension is not installed, it will redirect to the Keyring Connect page.
22
+ * @throws If the extension is not installed and running in a non-browser environment,
23
+ * if required configuration fields are missing, if Chrome runtime is not available,
24
+ * if extension communication times out, or if the extension returns an error.
21
25
  * @param data - The configuration for the extension
22
26
  */
23
27
  static launchExtension(data) {
@@ -32,11 +36,36 @@ class KeyringConnect {
32
36
  else {
33
37
  throw new Error("Keyring Connect is not installed");
34
38
  }
35
- return;
39
+ return null;
36
40
  }
41
+ // Create result promise that will be resolved when verification completes
42
+ this.resultPromise = new Promise((resolve, reject) => {
43
+ this.resolveResult = resolve;
44
+ this.rejectResult = reject;
45
+ });
37
46
  const validatedConfig = this.validateConfigData(data);
38
47
  const launchConfig = this.convertToLaunchConfig(validatedConfig);
39
- yield this.launchExtensionViaChromeAPI(launchConfig);
48
+ yield this.sendChromeMessage({
49
+ type: types_1.EVENT_ACTIONS.LAUNCH_KEYRING_CONNECT,
50
+ data: launchConfig,
51
+ });
52
+ const unsubscribe = this.subscribeToExtensionState((state) => {
53
+ var _a, _b, _c;
54
+ if ((state === null || state === void 0 ? void 0 : state.status) === "credential_success") {
55
+ if (!state.credentialData) {
56
+ (_a = this.rejectResult) === null || _a === void 0 ? void 0 : _a.call(this, new Error("No credential data found"));
57
+ }
58
+ else {
59
+ (_b = this.resolveResult) === null || _b === void 0 ? void 0 : _b.call(this, state.credentialData);
60
+ }
61
+ unsubscribe();
62
+ }
63
+ else if (state === null || state === void 0 ? void 0 : state.error) {
64
+ (_c = this.rejectResult) === null || _c === void 0 ? void 0 : _c.call(this, new Error(state.error));
65
+ unsubscribe();
66
+ }
67
+ });
68
+ return this.resultPromise;
40
69
  });
41
70
  }
42
71
  /**
@@ -59,9 +88,21 @@ class KeyringConnect {
59
88
  const timeout = new Promise((resolve) => {
60
89
  setTimeout(() => resolve(null), this.EXTENSION_TIMEOUT);
61
90
  });
91
+ // Check if Chrome is available first
92
+ if (!platformUtils_1.PlatformUtils.isChromeRuntimeAvailable()) {
93
+ return null;
94
+ }
62
95
  let extensionResponse;
63
- extensionResponse = this.getExtensionStateViaChromeAPI();
64
- return Promise.race([timeout, extensionResponse]);
96
+ extensionResponse = this.sendChromeMessage({
97
+ type: types_1.EVENT_ACTIONS.GET_STATUS,
98
+ });
99
+ try {
100
+ return yield Promise.race([timeout, extensionResponse]);
101
+ }
102
+ catch (error) {
103
+ // If there's an error (like Chrome runtime not available), return null
104
+ return null;
105
+ }
65
106
  });
66
107
  }
67
108
  /**
@@ -129,32 +170,18 @@ class KeyringConnect {
129
170
  }
130
171
  return config;
131
172
  }
132
- static validateProofConfig(config) {
133
- var _a;
134
- if ((_a = config.proof_config) === null || _a === void 0 ? void 0 : _a.datasource) {
135
- if (config.proof_config.datasource.proof_sources.length === 0) {
136
- throw new Error("At least one proof source is required");
137
- }
138
- if (!config.proof_config.datasource.proof_sources.some((source) => source.claims.length > 0)) {
139
- throw new Error("At least one claim is required per proof source");
140
- }
141
- }
142
- }
143
173
  static validateConfigData(config) {
144
174
  this.validateClientConfig(config);
145
- this.validateProofConfig(config);
146
175
  this.validateCredentialConfig(config);
147
176
  return config;
148
177
  }
149
178
  static convertToLaunchConfig(config) {
150
- var _a;
151
179
  const launchConfig = {
152
180
  client: {
153
181
  client_name: config.name,
154
182
  client_app_url: config.app_url,
155
183
  client_logo_url: config.logo_url,
156
184
  client_policy_id: config.policy_id,
157
- client_entity_id: (_a = config.krn_config) === null || _a === void 0 ? void 0 : _a.entity_id,
158
185
  credential_config: config.credential_config,
159
186
  },
160
187
  krn_config: config.krn_config
@@ -166,18 +193,13 @@ class KeyringConnect {
166
193
  websocketProxyUrl: config.krn_config.websocketProxyUrl,
167
194
  }
168
195
  : undefined,
169
- proof_config: config.proof_config,
170
196
  };
171
197
  return launchConfig;
172
198
  }
173
- static isChromeRuntimeAvailable() {
174
- var _a;
175
- return typeof ((_a = chrome === null || chrome === void 0 ? void 0 : chrome.runtime) === null || _a === void 0 ? void 0 : _a.sendMessage) === "function";
176
- }
177
199
  static sendChromeMessage(message) {
178
200
  return __awaiter(this, void 0, void 0, function* () {
179
201
  return new Promise((resolve, reject) => {
180
- if (!this.isChromeRuntimeAvailable()) {
202
+ if (!platformUtils_1.PlatformUtils.isChromeRuntimeAvailable()) {
181
203
  reject(new Error("Chrome runtime not available"));
182
204
  return;
183
205
  }
@@ -212,23 +234,8 @@ class KeyringConnect {
212
234
  });
213
235
  });
214
236
  }
215
- static launchExtensionViaChromeAPI(launchConfig) {
216
- return __awaiter(this, void 0, void 0, function* () {
217
- return this.sendChromeMessage({
218
- type: types_1.EVENT_ACTIONS.LAUNCH_KEYRING_CONNECT,
219
- data: launchConfig,
220
- });
221
- });
222
- }
223
- static getExtensionStateViaChromeAPI() {
224
- return __awaiter(this, void 0, void 0, function* () {
225
- return this.sendChromeMessage({
226
- type: types_1.EVENT_ACTIONS.GET_STATUS,
227
- });
228
- });
229
- }
230
237
  }
231
- exports.KeyringConnect = KeyringConnect;
232
- KeyringConnect.EXTENSION_TIMEOUT = 2000;
233
- KeyringConnect.EXTENSION_ID = "jgogeidclfccfoedhfjjaclnaojcllpi";
234
- KeyringConnect.keyringConnectUrl = "https://app.keyring.network/connect";
238
+ exports.KeyringConnectExtension = KeyringConnectExtension;
239
+ KeyringConnectExtension.EXTENSION_TIMEOUT = 2000;
240
+ KeyringConnectExtension.EXTENSION_ID = "jgogeidclfccfoedhfjjaclnaojcllpi";
241
+ KeyringConnectExtension.keyringConnectUrl = "https://app.keyring.network/connect";
@@ -0,0 +1,23 @@
1
+ import { SDKCallbacks, WebSocketMessage } from "../types";
2
+ export declare class KeyringWebSocketClient {
3
+ private logger;
4
+ private socket;
5
+ private sessionId;
6
+ private sessionToken;
7
+ private wsUrl;
8
+ private callbacks;
9
+ private reconnectAttempts;
10
+ private maxReconnectAttempts;
11
+ private heartbeatInterval;
12
+ private isConnected;
13
+ constructor(sessionId: string, sessionToken: string, callbacks: SDKCallbacks);
14
+ connect(): Promise<void>;
15
+ private handleMessage;
16
+ sendMessage(message: Partial<WebSocketMessage>): void;
17
+ private startHeartbeat;
18
+ private scheduleReconnect;
19
+ private cleanup;
20
+ disconnect(): void;
21
+ getConnectionState(): string;
22
+ private createError;
23
+ }
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KeyringWebSocketClient = void 0;
13
+ const environment_1 = require("../utils/environment");
14
+ const logger_1 = require("../utils/logger");
15
+ class KeyringWebSocketClient {
16
+ constructor(sessionId, sessionToken, callbacks) {
17
+ this.socket = null;
18
+ this.wsUrl = environment_1.Environment.getConfig().apiUrl;
19
+ this.reconnectAttempts = 0;
20
+ this.maxReconnectAttempts = 5;
21
+ this.heartbeatInterval = null;
22
+ this.isConnected = false;
23
+ this.sessionId = sessionId;
24
+ this.sessionToken = sessionToken;
25
+ this.callbacks = callbacks;
26
+ this.logger = logger_1.KeyringLogger.init("KeyringWebSocketClient").child({
27
+ sessionId,
28
+ wsUrl: this.wsUrl,
29
+ });
30
+ }
31
+ connect() {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ return new Promise((resolve, reject) => {
34
+ try {
35
+ // Convert HTTP/HTTPS URL to WS/WSS for native WebSocket
36
+ const wsUrl = this.wsUrl.replace("http://", "ws://").replace("https://", "wss://");
37
+ // Build WebSocket URL with sessionId and sessionToken
38
+ const url = `${wsUrl}/ws/connect/session?sessionId=${encodeURIComponent(this.sessionId)}&sessionToken=${encodeURIComponent(this.sessionToken)}`;
39
+ this.logger.debug({
40
+ wsUrl,
41
+ sessionId: this.sessionId,
42
+ hasSessionToken: !!this.sessionToken,
43
+ }, "Connecting to WebSocket");
44
+ this.socket = new WebSocket(url);
45
+ this.socket.onopen = () => {
46
+ var _a, _b;
47
+ this.logger.info("WebSocket connected successfully");
48
+ this.isConnected = true;
49
+ this.reconnectAttempts = 0;
50
+ this.startHeartbeat();
51
+ (_b = (_a = this.callbacks).onConnectionStatusChanged) === null || _b === void 0 ? void 0 : _b.call(_a, true);
52
+ resolve();
53
+ };
54
+ this.socket.onmessage = (event) => {
55
+ try {
56
+ const message = JSON.parse(event.data);
57
+ this.handleMessage(message);
58
+ }
59
+ catch (error) {
60
+ this.logger.error({ error, data: event.data }, "Failed to parse WebSocket message");
61
+ }
62
+ };
63
+ this.socket.onclose = (event) => {
64
+ var _a, _b;
65
+ if (!this.isConnected)
66
+ return;
67
+ this.logger.info({ code: event.code, reason: event.reason }, "WebSocket disconnected");
68
+ this.isConnected = false;
69
+ this.cleanup();
70
+ if (event.code !== 1000) {
71
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
72
+ this.scheduleReconnect();
73
+ }
74
+ }
75
+ else {
76
+ (_b = (_a = this.callbacks).onConnectionStatusChanged) === null || _b === void 0 ? void 0 : _b.call(_a, false);
77
+ }
78
+ };
79
+ this.socket.onerror = (error) => {
80
+ var _a, _b;
81
+ this.logger.error({ error, sessionId: this.sessionId }, "WebSocket error");
82
+ (_b = (_a = this.callbacks).onConnectionStatusChanged) === null || _b === void 0 ? void 0 : _b.call(_a, false);
83
+ reject(error);
84
+ };
85
+ // Set connection timeout
86
+ setTimeout(() => {
87
+ if (this.socket && !this.isConnected) {
88
+ this.socket.close();
89
+ reject(new Error("WebSocket connection timeout"));
90
+ }
91
+ }, 10000); // 10 second timeout
92
+ }
93
+ catch (error) {
94
+ reject(error);
95
+ }
96
+ });
97
+ });
98
+ }
99
+ handleMessage(message) {
100
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
101
+ try {
102
+ this.logger.debug({ message }, "Received message");
103
+ switch (message.type) {
104
+ case "connected":
105
+ this.logger.info("Connection confirmed by server");
106
+ break;
107
+ case "mobile_connected":
108
+ (_b = (_a = this.callbacks).onMobileConnected) === null || _b === void 0 ? void 0 : _b.call(_a, message.data);
109
+ break;
110
+ case "processing_started":
111
+ (_d = (_c = this.callbacks).onProcessingStarted) === null || _d === void 0 ? void 0 : _d.call(_c, message.data);
112
+ break;
113
+ case "processing_completed":
114
+ (_f = (_e = this.callbacks).onProcessingCompleted) === null || _f === void 0 ? void 0 : _f.call(_e, message.data);
115
+ break;
116
+ case "processing_failed":
117
+ (_h = (_g = this.callbacks).onProcessingFailed) === null || _h === void 0 ? void 0 : _h.call(_g, message.data);
118
+ break;
119
+ case "session_expired":
120
+ (_k = (_j = this.callbacks).onSessionExpired) === null || _k === void 0 ? void 0 : _k.call(_j, message.data);
121
+ this.disconnect();
122
+ break;
123
+ case "error":
124
+ (_m = (_l = this.callbacks).onError) === null || _m === void 0 ? void 0 : _m.call(_l, this.createError("MOBILE_PROCESSING_FAILED", message.data.message || "Mobile processing error", message.data));
125
+ break;
126
+ case "heartbeat_response":
127
+ this.logger.debug("Heartbeat response received");
128
+ break;
129
+ default:
130
+ this.logger.warn({ messageType: message.type }, "Unknown message type");
131
+ }
132
+ }
133
+ catch (error) {
134
+ this.logger.error({ error }, "Error handling WebSocket message");
135
+ (_p = (_o = this.callbacks).onError) === null || _p === void 0 ? void 0 : _p.call(_o, this.createError("NETWORK_ERROR", "Failed to handle WebSocket message", error));
136
+ }
137
+ }
138
+ sendMessage(message) {
139
+ var _a, _b;
140
+ if (!this.socket || !this.isConnected || this.socket.readyState !== WebSocket.OPEN) {
141
+ this.logger.warn("Cannot send message - WebSocket not connected");
142
+ return;
143
+ }
144
+ const fullMessage = Object.assign({ session_id: this.sessionId, timestamp: Date.now() }, message);
145
+ try {
146
+ this.socket.send(JSON.stringify(fullMessage));
147
+ this.logger.debug({ message: fullMessage }, "Sent message");
148
+ }
149
+ catch (error) {
150
+ this.logger.error({ error }, "Error sending message");
151
+ (_b = (_a = this.callbacks).onError) === null || _b === void 0 ? void 0 : _b.call(_a, this.createError("WEBSOCKET_CONNECTION_FAILED", "Failed to send WebSocket message", error));
152
+ }
153
+ }
154
+ startHeartbeat() {
155
+ this.heartbeatInterval = setInterval(() => {
156
+ this.sendMessage({
157
+ type: "heartbeat",
158
+ });
159
+ }, 30000); // 30 second heartbeat
160
+ }
161
+ scheduleReconnect() {
162
+ this.reconnectAttempts++;
163
+ const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000); // Exponential backoff, max 30s
164
+ this.logger.info({
165
+ attempt: this.reconnectAttempts,
166
+ maxAttempts: this.maxReconnectAttempts,
167
+ delay,
168
+ }, "Scheduling reconnection attempt");
169
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () {
170
+ try {
171
+ yield this.connect();
172
+ this.logger.info("Reconnection successful");
173
+ }
174
+ catch (error) {
175
+ this.logger.error({ error }, "Reconnection failed");
176
+ }
177
+ }), delay);
178
+ }
179
+ cleanup() {
180
+ if (this.heartbeatInterval) {
181
+ clearInterval(this.heartbeatInterval);
182
+ this.heartbeatInterval = null;
183
+ }
184
+ }
185
+ disconnect() {
186
+ var _a, _b;
187
+ this.logger.info("Disconnecting WebSocket");
188
+ this.cleanup();
189
+ this.isConnected = false;
190
+ if (this.socket) {
191
+ this.socket.close(1000, "Normal closure");
192
+ this.socket = null;
193
+ }
194
+ (_b = (_a = this.callbacks).onConnectionStatusChanged) === null || _b === void 0 ? void 0 : _b.call(_a, false);
195
+ }
196
+ getConnectionState() {
197
+ if (!this.socket)
198
+ return "CLOSED";
199
+ switch (this.socket.readyState) {
200
+ case WebSocket.CONNECTING:
201
+ return "CONNECTING";
202
+ case WebSocket.OPEN:
203
+ return "CONNECTED";
204
+ case WebSocket.CLOSING:
205
+ return "CLOSING";
206
+ case WebSocket.CLOSED:
207
+ return "CLOSED";
208
+ default:
209
+ return "UNKNOWN";
210
+ }
211
+ }
212
+ createError(code, message, details) {
213
+ return {
214
+ code,
215
+ message,
216
+ details,
217
+ };
218
+ }
219
+ }
220
+ exports.KeyringWebSocketClient = KeyringWebSocketClient;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from "@keyringnetwork/contracts-abi";
2
- export { KeyringConnect } from "./main";
2
+ export { VerificationSession } from "./core/VerificationSession";
3
3
  export * from "./types";
package/dist/index.js CHANGED
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.KeyringConnect = void 0;
17
+ exports.VerificationSession = void 0;
18
18
  __exportStar(require("@keyringnetwork/contracts-abi"), exports);
19
- var main_1 = require("./main");
20
- Object.defineProperty(exports, "KeyringConnect", { enumerable: true, get: function () { return main_1.KeyringConnect; } });
19
+ var VerificationSession_1 = require("./core/VerificationSession");
20
+ Object.defineProperty(exports, "VerificationSession", { enumerable: true, get: function () { return VerificationSession_1.VerificationSession; } });
21
21
  __exportStar(require("./types"), exports);
@@ -0,0 +1,24 @@
1
+ import { DataSource } from "@keyringnetwork/keyring-dtos";
2
+ import { CredentialData, SessionConfig } from "./core";
3
+ export type SessionStatus = "session_created" | "mobile_connected" | "processing_started" | "processing_completed" | "processing_failed" | "session_expired";
4
+ export interface CrossDeviceSession {
5
+ session_id: string;
6
+ session_token: string;
7
+ config: SessionConfig;
8
+ origin_url: string;
9
+ status: SessionStatus;
10
+ created_at: number | string;
11
+ expires_at: number | string;
12
+ result?: {
13
+ credential_data?: CredentialData;
14
+ entity_type?: string;
15
+ datasource?: DataSource;
16
+ };
17
+ error?: string;
18
+ platform: "web" | "mobile";
19
+ qr_code_data: string;
20
+ connected_clients: {
21
+ web?: string;
22
+ mobile?: string;
23
+ };
24
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });