@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.
- package/dist/core/VerificationSession.d.ts +62 -0
- package/dist/core/VerificationSession.js +404 -0
- package/dist/core/errors.d.ts +49 -0
- package/dist/core/errors.js +81 -0
- package/dist/core/htppClient.d.ts +17 -0
- package/dist/core/htppClient.js +160 -0
- package/dist/{main.d.ts → core/keyringConnectExtension.d.ts} +9 -7
- package/dist/{main.js → core/keyringConnectExtension.js} +53 -46
- package/dist/core/websocketClient.d.ts +23 -0
- package/dist/core/websocketClient.js +220 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/types/api.d.ts +24 -0
- package/dist/types/api.js +2 -0
- package/dist/types/core.d.ts +78 -0
- package/dist/types/core.js +2 -0
- package/dist/types/extension.d.ts +59 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +20 -0
- package/dist/types/websocket.d.ts +68 -0
- package/dist/types/websocket.js +3 -0
- package/dist/ui/UIManager.d.ts +52 -0
- package/dist/ui/UIManager.js +257 -0
- package/dist/ui/components/keyring-button.d.ts +12 -0
- package/dist/ui/components/keyring-button.js +140 -0
- package/dist/ui/components/keyring-text.d.ts +12 -0
- package/dist/ui/components/keyring-text.js +169 -0
- package/dist/ui/composites/keyring-complete-modal.d.ts +19 -0
- package/dist/ui/composites/keyring-complete-modal.js +200 -0
- package/dist/ui/composites/keyring-mobile-modal.d.ts +25 -0
- package/dist/ui/composites/keyring-mobile-modal.js +308 -0
- package/dist/ui/composites/keyring-qr-modal.d.ts +32 -0
- package/dist/ui/composites/keyring-qr-modal.js +464 -0
- package/dist/ui/composites/keyring-selection-modal.d.ts +25 -0
- package/dist/ui/composites/keyring-selection-modal.js +342 -0
- package/dist/ui/composites/keyring-terminated-modal.d.ts +14 -0
- package/dist/ui/composites/keyring-terminated-modal.js +121 -0
- package/dist/ui/icons/apple-icon.d.ts +4 -0
- package/dist/ui/icons/apple-icon.js +47 -0
- package/dist/ui/icons/check-circle.d.ts +4 -0
- package/dist/ui/icons/check-circle.js +35 -0
- package/dist/ui/icons/checkmark.d.ts +4 -0
- package/dist/ui/icons/checkmark.js +37 -0
- package/dist/ui/icons/close-circle.d.ts +4 -0
- package/dist/ui/icons/close-circle.js +35 -0
- package/dist/ui/icons/error-circle.d.ts +4 -0
- package/dist/ui/icons/error-circle.js +48 -0
- package/dist/ui/icons/extension-grid.d.ts +4 -0
- package/dist/ui/icons/extension-grid.js +79 -0
- package/dist/ui/icons/google-icon.d.ts +4 -0
- package/dist/ui/icons/google-icon.js +55 -0
- package/dist/ui/icons/gradient-donut.d.ts +8 -0
- package/dist/ui/icons/gradient-donut.js +85 -0
- package/dist/ui/icons/keyring.d.ts +4 -0
- package/dist/ui/icons/keyring.js +71 -0
- package/dist/ui/icons/mobile-grid.d.ts +4 -0
- package/dist/ui/icons/mobile-grid.js +68 -0
- package/dist/ui/icons/success.d.ts +4 -0
- package/dist/ui/icons/success.js +54 -0
- package/dist/utils/environment.d.ts +34 -0
- package/dist/utils/environment.js +67 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +40 -0
- package/dist/utils/platformUtils.d.ts +24 -0
- package/dist/utils/platformUtils.js +64 -0
- package/package.json +32 -9
- package/readme.md +57 -105
- package/dist/types.d.ts +0 -159
- /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 {
|
|
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
|
|
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:
|
|
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.
|
|
13
|
-
const types_1 = require("
|
|
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
|
|
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.
|
|
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.
|
|
64
|
-
|
|
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 (!
|
|
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.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
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.
|
|
17
|
+
exports.VerificationSession = void 0;
|
|
18
18
|
__exportStar(require("@keyringnetwork/contracts-abi"), exports);
|
|
19
|
-
var
|
|
20
|
-
Object.defineProperty(exports, "
|
|
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
|
+
}
|