windkit 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,54 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ This project follows Semantic Versioning.
6
+
7
+ ---
8
+
9
+ ## [0.2.2] - 2026-03-02
10
+
11
+ ### Changed
12
+
13
+ - Session storage key standardized to `"vex-session"`.
14
+ - Login identity request now stores compact app info keys:
15
+ - `pi` (peer id), `na` (app name), `ic` (icon), `do` (origin), optional `auth` (cached proof).
16
+ - Request method names standardized:
17
+ - `signRequest`, `signMessage`, `sharedSecret`.
18
+
19
+ ### Added
20
+
21
+ - `clearSession()` helper.
22
+ - Session reuse:
23
+ - Reuses saved `peerID` and hints (`account@permission`) when present.
24
+
25
+ ### Fixed
26
+
27
+ - Safer session loading (auto-clears invalid or expired payloads).
28
+ - Improved cleanup on disconnect (best-effort destroy/disconnect).
29
+
30
+ ### Improved
31
+
32
+ - Low-memory friendly behavior:
33
+ - Lightweight heartbeat/ping with jitter (best-effort).
34
+ - Minimal allocations on message routing.
35
+
36
+ ---
37
+
38
+ ## [0.2.1]
39
+
40
+ ### Added
41
+
42
+ - Initial PeerJS signaling support.
43
+ - Session persistence via sessionStorage.
44
+ - Transaction signing via VSR.
45
+ - Message signing.
46
+ - Shared secret (ECDH).
47
+
48
+ ---
49
+
50
+ ## [0.1.1]
51
+
52
+ ### Added
53
+
54
+ - Initial public release of WindKit.
package/README.md CHANGED
@@ -2,82 +2,257 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/windkit.svg)](https://www.npmjs.com/package/windkit)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
5
- A protocol to connect Vexanium DApps to the Wind wallet.
5
+
6
+ WindKit is a lightweight WebRTC protocol for connecting Vexanium DApps to Wind Wallet using PeerJS and WharfKit Signing Requests (VSR).
7
+
8
+ It enables secure cross-device login and transaction signing without requiring browser extensions.
9
+
10
+ ---
6
11
 
7
12
  ## Features
13
+
8
14
  - Cross-device login via VSR (Vexanium Signing Request)
9
- - Transaction signing (single or multiple actions)
15
+ - Transaction signing (single action, multiple actions, or full transaction)
16
+ - Optional broadcast (sign-only or broadcast)
10
17
  - Message signing
11
- - Shared secret (ECDH) for encryption
18
+ - Shared secret derivation (ECDH)
19
+ - Session persistence via sessionStorage
20
+ - Lightweight keepalive (low memory footprint)
21
+ - Pure ESM module
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ npm install windkit
28
+
29
+ WindKit is ESM-only.
30
+
31
+ Your project must use:
32
+
33
+ {
34
+ "type": "module"
35
+ }
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ### Create Connector
12
42
 
13
- ## Initialize the Connector
14
- ```javascript
15
43
  import { WindConnector } from "windkit";
16
44
 
17
45
  const connector = new WindConnector();
18
- connector.on("session", onSession); // fires after wallet approves login
46
+
47
+ connector.on("session", (session, proof) => {
48
+ console.log("Connected as:", session.permissionLevel?.toString());
49
+ });
50
+
19
51
  await connector.connect();
20
- ```
21
52
 
22
- ## Create a Login Request and Open the Wallet
23
- ```javascript
24
- // Build a VSR and deep-link / QR it to the wallet
25
- const vsr = connector.createLoginRequest("Vexanium DApp", "https://example.com/icon.png");
26
- const request = vsr.split(":")[1];
53
+ By default, WindKit relies on PeerJS default signaling behavior (no host/port/path/secure configured).
27
54
 
28
- // Open Wind Wallet login (adjust the URL/host as needed)
29
- const walletUrl = `https://windwallet.app/login?vsr=${request}`;
30
- window.open(walletUrl, "Wind Wallet");
31
- ```
55
+ ---
32
56
 
33
- ## Receive a WalletSession
34
- ```javascript
35
- import { ABICache } from "@wharfkit/abicache";
57
+ ### Use Your Own PeerJS Server (Optional)
36
58
 
37
- const abiCache = new ABICache({/* optional custom fetch */});
59
+ connector.setServer("peer.yourdomain.com", 443, "/", true);
38
60
 
39
- function onSession(session, proof) {
40
- // Optional on first login: verify IdentityProof as needed
41
- const account = proof?.signer?.toString?.(); // e.g., "userxyz@active"
61
+ Signature:
42
62
 
43
- session.setABICache(abiCache); // faster ABI (de)serialization
44
- session.onClose(() => console.log("Disconnected from wallet"));
45
-
46
- // Store for later use
47
- Store.session = session;
48
- }
49
- ```
63
+ setServer(host, port, path, secure)
64
+
65
+ Add custom STUN/TURN server:
66
+
67
+ connector.addIceServer({
68
+ urls: "stun:stun.cloudflare.com:3478"
69
+ });
70
+
71
+ ---
72
+
73
+ ## Login Flow (VSR)
74
+
75
+ const vsr = connector.createLoginRequest(
76
+ "My Vexanium DApp",
77
+ "https://example.com/icon.png"
78
+ );
79
+
80
+ const payload = vsr.startsWith("vsr:") ? vsr.slice(4) : vsr;
81
+
82
+ window.open(
83
+ `https://wallet.windcrypto.com/login?vsr=${encodeURIComponent(payload)}`,
84
+ "Wind Wallet"
85
+ );
86
+
87
+ Wallet flow:
88
+
89
+ 1. Decode VSR
90
+ 2. Connect to embedded PeerID
91
+ 3. Send LOGIN_OK
92
+ 4. Emit session
93
+
94
+ ---
95
+
96
+ ## Session Handling
97
+
98
+ connector.on("session", (session, proof) => {
99
+
100
+ session.onClose(() => {
101
+ console.log("Wallet disconnected");
102
+ });
103
+
104
+ session.onError((error) => {
105
+ console.error("Session error:", error);
106
+ });
107
+
108
+ window.appSession = session;
109
+ });
110
+
111
+ ---
112
+
113
+ ## Send Transaction
114
+
115
+ ### With ABI Cache (Recommended)
50
116
 
51
- ## Send a Transaction
52
- ```javascript
53
117
  import { Action } from "@wharfkit/antelope";
118
+ import { ABICache } from "@wharfkit/abicache";
119
+
120
+ const abiCache = new ABICache();
121
+ appSession.setABICache(abiCache);
54
122
 
55
- // Example: transfer VEX
56
123
  const abi = await abiCache.getAbi("vex.token");
57
- const data = {
58
- from: "aiueo",
59
- to: "babibu",
60
- quantity: "1.0000 VEX",
61
- memo: "test transfer"
62
- };
63
124
 
64
125
  const action = Action.from(
65
126
  {
66
127
  account: "vex.token",
67
128
  name: "transfer",
68
- data,
69
- authorization: [Store.session.permissionLevel]
129
+ data: {
130
+ from: "alice",
131
+ to: "bob",
132
+ quantity: "1.0000 VEX",
133
+ memo: "test"
134
+ },
135
+ authorization: [appSession.permissionLevel]
70
136
  },
71
137
  abi
72
138
  );
73
139
 
74
- // By default, transact() broadcasts. Set { broadcast: false } to sign only.
75
- const result = await Store.session.transact({ action });
76
- // result is either SendTransactionResponse (broadcast) or SignedTransaction (sign-only)
140
+ const result = await appSession.transact({ action });
141
+
77
142
  console.log(result.transaction_id ?? result.id);
78
- ```
79
143
 
80
- > Notes
81
- > - Class names: `WindConnector` (connector) and `WalletSession` (active session).
82
- > - Chain ID is handled internally by `WalletSession.ChainID` (Vexanium mainnet).
83
- > - For re-login, the `session` event may be called without `proof` (use `session.permissionLevel`).
144
+ ---
145
+
146
+ ### Sign Only (No Broadcast)
147
+
148
+ await appSession.transact(
149
+ { action },
150
+ { broadcast: false }
151
+ );
152
+
153
+ ---
154
+
155
+ ## Sign Message
156
+
157
+ const signature = await appSession.signMessage("Hello Wind!");
158
+ console.log(signature.toString());
159
+
160
+ ---
161
+
162
+ ## Shared Secret (ECDH)
163
+
164
+ import { PublicKey } from "@wharfkit/antelope";
165
+
166
+ const pub = PublicKey.from("PUB_K1_...");
167
+ const secret = await appSession.sharedSecret(pub);
168
+
169
+ console.log(secret.toString());
170
+
171
+ ---
172
+
173
+ ## Session Storage
174
+
175
+ WindKit stores session data in:
176
+
177
+ sessionStorage["vex-session"]
178
+
179
+ Example structure:
180
+
181
+ {
182
+ "peerID": "VEX-xxxx",
183
+ "permission": "account@active",
184
+ "expiration": "2026-03-01T12:00:00",
185
+ "auth": "base64u_identity_proof"
186
+ }
187
+
188
+ To clear session manually:
189
+
190
+ import { clearSession } from "windkit";
191
+
192
+ clearSession();
193
+
194
+ ---
195
+
196
+ ## Architecture
197
+
198
+ WindConnector
199
+ - Creates VSR identity login
200
+ - Hosts PeerJS PeerID (DApp-side)
201
+ - Waits for wallet connection
202
+ - Emits session
203
+
204
+ WalletSession
205
+ - Sends:
206
+ - signRequest
207
+ - signMessage
208
+ - sharedSecret
209
+ - Routes replies via request IDs
210
+ - Lightweight keepalive ping
211
+ - Handles account change events
212
+
213
+ ---
214
+
215
+ ## Protocol Notes
216
+
217
+ Transaction signing method name:
218
+ "signRequest"
219
+
220
+ Wallet push events handled:
221
+ LOGIN_OK
222
+ ACTIVE_ACCOUNT_CHANGED
223
+
224
+ All communication occurs via PeerJS DataConnection.
225
+
226
+ ---
227
+
228
+ ## Technical Details
229
+
230
+ Chain ID is fixed internally via:
231
+ WalletSession.ChainID
232
+
233
+ Uses:
234
+ - @wharfkit/signing-request
235
+ - @wharfkit/antelope
236
+ - peerjs
237
+ - pako (zlib compression)
238
+
239
+ Designed for:
240
+ - Low-RAM mobile devices
241
+ - Background tabs
242
+ - Unstable WebRTC environments
243
+
244
+ ---
245
+
246
+ ## Security Model
247
+
248
+ - IdentityProof can be verified by the DApp (recommended).
249
+ - Private keys never leave the wallet.
250
+ - VSR ensures transaction integrity.
251
+ - PeerID is embedded inside VSR to prevent misrouting.
252
+
253
+ ---
254
+
255
+ ## License
256
+
257
+ MIT License
258
+ © Wind Stack
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { WindConnector } from "./src/WindConnector.js";
2
+ export { WalletSession } from "./src/WalletSession.js";
3
+ export { saveSession, loadSession, clearSession } from "./src/StoreSession.js";
package/package.json CHANGED
@@ -1,61 +1,59 @@
1
1
  {
2
2
  "name": "windkit",
3
- "version": "0.2.0",
4
- "description": "A protocol for connecting Vexanium DApps to the Wind wallet, enabling secure communication and transaction signing.",
3
+ "version": "0.2.2",
4
+ "description": "Lightweight protocol to connect Vexanium DApps to Wind Wallet via PeerJS and VSR.",
5
+ "license": "MIT",
6
+ "author": "windstack",
7
+ "type": "module",
8
+ "main": "./index.js",
9
+ "exports": {
10
+ ".": "./index.js"
11
+ },
12
+ "files": [
13
+ "index.js",
14
+ "src/**",
15
+ "README.md",
16
+ "CHANGELOG.md",
17
+ "LICENSE"
18
+ ],
5
19
  "repository": {
6
20
  "type": "git",
7
21
  "url": "git+https://github.com/windvex/windkit.git"
8
22
  },
23
+ "bugs": {
24
+ "url": "https://github.com/windvex/windkit/issues"
25
+ },
26
+ "homepage": "https://github.com/windvex/windkit#readme",
9
27
  "keywords": [
10
- "cryptocurrency",
11
- "web3",
12
- "dapp",
13
- "walletconnect",
14
- "windkit",
15
- "web-rtc",
16
- "peerjs",
17
28
  "vexanium",
18
29
  "vex",
30
+ "wind",
31
+ "wallet",
32
+ "web3",
33
+ "peerjs",
34
+ "webrtc",
35
+ "wharfkit",
36
+ "signing-request",
37
+ "vsr",
19
38
  "blockchain"
20
39
  ],
21
- "license": "MIT",
22
- "type": "module",
23
- "main": "dist/index.cjs",
24
- "module": "dist/index.js",
25
- "types": "dist/index.d.ts",
26
- "exports": {
27
- ".": {
28
- "import": {
29
- "types": "./dist/index.d.ts",
30
- "default": "./dist/index.js"
31
- },
32
- "require": {
33
- "types": "./dist/index.d.ts",
34
- "default": "./dist/index.cjs"
35
- }
36
- }
40
+ "sideEffects": false,
41
+ "publishConfig": {
42
+ "access": "public"
37
43
  },
38
- "files": [
39
- "dist",
40
- "README.md",
41
- "LICENSE"
42
- ],
43
44
  "scripts": {
44
- "build": "tsup",
45
- "clean": "rm -rf dist",
46
- "prepublishOnly": "npm run clean && npm run build",
47
- "test": "echo \"(no tests yet)\""
45
+ "test": "node -e \"console.log('windkit ok')\"",
46
+ "pack:check": "npm pack --dry-run",
47
+ "publish:npm": "npm run pack:check && npm publish --access public"
48
48
  },
49
49
  "dependencies": {
50
50
  "@wharfkit/abicache": "^1.2.2",
51
- "@wharfkit/antelope": "^1.0.13",
51
+ "@wharfkit/antelope": "^1.1.1",
52
52
  "@wharfkit/signing-request": "^3.2.0",
53
53
  "pako": "^2.1.0",
54
- "peerjs": "^1.5.4"
54
+ "peerjs": "^1.5.5"
55
55
  },
56
- "devDependencies": {
57
- "@types/pako": "^2.0.4",
58
- "tsup": "^8.5.0",
59
- "typescript": "^5.9.3"
56
+ "engines": {
57
+ "node": ">=18"
60
58
  }
61
- }
59
+ }
@@ -0,0 +1,103 @@
1
+ // windkit/StoreSession.js
2
+ // Ultra Clean++ (single replace)
3
+ // ✅ sessionStorage SSOT for pairing
4
+ // ✅ auto-clear invalid/expired
5
+ // ✅ safe storage guards (private mode / SSR)
6
+
7
+ import { TimePointSec } from "@wharfkit/antelope";
8
+
9
+ const KEY = "vex-session";
10
+
11
+ function hasSessionStorage() {
12
+ try {
13
+ return typeof sessionStorage !== "undefined" && sessionStorage != null;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ /**
20
+ * @typedef {Object} StoredSession
21
+ * @property {string} peerID
22
+ * @property {string} permission // "account@active"
23
+ * @property {string|TimePointSec} expiration
24
+ * @property {string} auth // Base64u IdentityProof payload
25
+ */
26
+
27
+ export function saveSession(data) {
28
+ if (!hasSessionStorage()) return;
29
+
30
+ const exp =
31
+ typeof data?.expiration === "string"
32
+ ? data.expiration
33
+ : data?.expiration?.toString?.() ?? String(data?.expiration ?? "");
34
+
35
+ const payload = {
36
+ peerID: String(data?.peerID || ""),
37
+ permission: String(data?.permission || ""),
38
+ expiration: String(exp || ""),
39
+ auth: String(data?.auth || ""),
40
+ };
41
+
42
+ try {
43
+ sessionStorage.setItem(KEY, JSON.stringify(payload));
44
+ } catch {
45
+ // ignore (quota/private mode)
46
+ }
47
+ }
48
+
49
+ export function clearSession() {
50
+ if (!hasSessionStorage()) return;
51
+ try {
52
+ sessionStorage.removeItem(KEY);
53
+ } catch {
54
+ // ignore
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Load session from sessionStorage (auto-clears if invalid/expired).
60
+ * @returns {null | {peerID:string, permission:string, expiration:TimePointSec, auth:string}}
61
+ */
62
+ export function loadSession() {
63
+ if (!hasSessionStorage()) return null;
64
+
65
+ let raw = "";
66
+ try {
67
+ raw = sessionStorage.getItem(KEY) || "";
68
+ } catch {
69
+ return null;
70
+ }
71
+ if (!raw) return null;
72
+
73
+ try {
74
+ const data = JSON.parse(raw);
75
+ if (!data || typeof data !== "object") {
76
+ clearSession();
77
+ return null;
78
+ }
79
+
80
+ const peerID = String(data.peerID || "");
81
+ const permission = String(data.permission || "");
82
+ const auth = String(data.auth || "");
83
+ const expRaw = String(data.expiration || "");
84
+
85
+ if (!peerID || !permission || !auth || !expRaw) {
86
+ clearSession();
87
+ return null;
88
+ }
89
+
90
+ const expiration = TimePointSec.fromString(expRaw);
91
+ const expMs = expiration.toDate().getTime();
92
+
93
+ if (!Number.isFinite(expMs) || expMs <= Date.now()) {
94
+ clearSession();
95
+ return null;
96
+ }
97
+
98
+ return { peerID, permission, expiration, auth };
99
+ } catch {
100
+ clearSession();
101
+ return null;
102
+ }
103
+ }