windkit 0.2.1 → 0.2.3
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 +91 -0
- package/README.md +265 -46
- package/index.js +3 -0
- package/package.json +39 -41
- package/src/StoreSession.js +103 -0
- package/src/WalletSession.js +375 -0
- package/src/WindConnector.js +294 -0
- package/dist/index.cjs +0 -365
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -70
- package/dist/index.d.ts +0 -70
- package/dist/index.js +0 -342
- package/dist/index.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
This project follows [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## [0.2.3] - 2026-03-02
|
|
10
|
+
|
|
11
|
+
Published: 2026-03-02T14:07:04Z
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Session storage key standardized to `"vex-session"`.
|
|
16
|
+
- Login identity payload compacted with standardized keys:
|
|
17
|
+
- `pi` (peer id)
|
|
18
|
+
- `na` (app name)
|
|
19
|
+
- `ic` (icon)
|
|
20
|
+
- `do` (origin)
|
|
21
|
+
- optional `auth` (cached identity proof)
|
|
22
|
+
- RPC method names standardized:
|
|
23
|
+
- `signRequest`
|
|
24
|
+
- `signMessage`
|
|
25
|
+
- `sharedSecret`
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `clearSession()` helper for manual session reset.
|
|
30
|
+
- Smart session reuse:
|
|
31
|
+
- Reuses stored `peerID`
|
|
32
|
+
- Reuses permission hints (`account@permission`) when available.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Hardened session loader:
|
|
37
|
+
- Automatically clears expired or malformed session payloads.
|
|
38
|
+
- Improved disconnect cleanup:
|
|
39
|
+
- Safe destroy/disconnect (best-effort, non-throwing).
|
|
40
|
+
|
|
41
|
+
### Improved
|
|
42
|
+
|
|
43
|
+
- Optimized for low-memory environments:
|
|
44
|
+
- Lightweight heartbeat with jitter
|
|
45
|
+
- Reduced message routing allocations
|
|
46
|
+
- Lower background CPU usage on mobile devices
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## [0.2.1] - 2025-10-13
|
|
51
|
+
|
|
52
|
+
Published: 2025-10-13T16:28:38Z
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- PeerJS signaling support.
|
|
57
|
+
- Session persistence via `sessionStorage`.
|
|
58
|
+
- Transaction signing via Vexanium Signing Request (VSR).
|
|
59
|
+
- Message signing support.
|
|
60
|
+
- Shared secret derivation (ECDH).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## [0.2.0] - 2025-10-06
|
|
65
|
+
|
|
66
|
+
Published: 2025-10-06T14:44:23Z
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- Structured WebRTC connection lifecycle.
|
|
71
|
+
- WalletSession abstraction layer.
|
|
72
|
+
- Basic login flow via embedded PeerID inside VSR.
|
|
73
|
+
- Request/response routing via internal request IDs.
|
|
74
|
+
|
|
75
|
+
### Improved
|
|
76
|
+
|
|
77
|
+
- More predictable session initialization flow.
|
|
78
|
+
- Internal protocol normalization groundwork.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## [0.1.1] - 2025-08-12
|
|
83
|
+
|
|
84
|
+
Published: 2025-08-12T01:33:25Z
|
|
85
|
+
|
|
86
|
+
### Added
|
|
87
|
+
|
|
88
|
+
- Initial public release of WindKit.
|
|
89
|
+
- Basic VSR identity login.
|
|
90
|
+
- WebRTC DataConnection integration.
|
|
91
|
+
- Minimal transaction signing flow.
|
package/README.md
CHANGED
|
@@ -2,82 +2,301 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/windkit)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-
A protocol to connect Vexanium DApps to the Wind wallet.
|
|
6
5
|
|
|
7
|
-
|
|
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
|
+
Designed for production environments, including low-RAM mobile devices.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
8
16
|
- Cross-device login via VSR (Vexanium Signing Request)
|
|
9
|
-
- Transaction signing (single
|
|
17
|
+
- Transaction signing (single action, multiple actions, or full transaction)
|
|
18
|
+
- Optional broadcast control (sign-only or broadcast)
|
|
10
19
|
- Message signing
|
|
11
|
-
- Shared secret (ECDH)
|
|
20
|
+
- Shared secret derivation (ECDH)
|
|
21
|
+
- Session persistence via `sessionStorage`
|
|
22
|
+
- Low-memory heartbeat strategy
|
|
23
|
+
- Pure ESM module (JavaScript-only)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 📦 Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install windkit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
WindKit is **ESM-only**.
|
|
34
|
+
|
|
35
|
+
Your project must include:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"type": "module"
|
|
40
|
+
}
|
|
41
|
+
```
|
|
12
42
|
|
|
13
|
-
|
|
14
|
-
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 🚀 Quick Start
|
|
46
|
+
|
|
47
|
+
### Create Connector
|
|
48
|
+
|
|
49
|
+
```js
|
|
15
50
|
import { WindConnector } from "windkit";
|
|
16
51
|
|
|
17
52
|
const connector = new WindConnector();
|
|
18
|
-
|
|
53
|
+
|
|
54
|
+
connector.on("session", (session, proof) => {
|
|
55
|
+
console.log("Connected as:", session.permissionLevel?.toString());
|
|
56
|
+
});
|
|
57
|
+
|
|
19
58
|
await connector.connect();
|
|
20
59
|
```
|
|
21
60
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
61
|
+
By default, WindKit uses PeerJS default signaling.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🌐 Custom PeerJS Server (Optional)
|
|
27
66
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
window.open(walletUrl, "Wind Wallet");
|
|
67
|
+
```js
|
|
68
|
+
connector.setServer("peer.yourdomain.com", 443, "/", true);
|
|
31
69
|
```
|
|
32
70
|
|
|
33
|
-
|
|
34
|
-
```javascript
|
|
35
|
-
import { ABICache } from "@wharfkit/abicache";
|
|
71
|
+
Signature:
|
|
36
72
|
|
|
37
|
-
|
|
73
|
+
```js
|
|
74
|
+
setServer(host, port, path, secure);
|
|
75
|
+
```
|
|
38
76
|
|
|
39
|
-
|
|
40
|
-
// Optional on first login: verify IdentityProof as needed
|
|
41
|
-
const account = proof?.signer?.toString?.(); // e.g., "userxyz@active"
|
|
77
|
+
Add custom ICE server:
|
|
42
78
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Store.session = session;
|
|
48
|
-
}
|
|
79
|
+
```js
|
|
80
|
+
connector.addIceServer({
|
|
81
|
+
urls: "stun:stun.cloudflare.com:3478"
|
|
82
|
+
});
|
|
49
83
|
```
|
|
50
84
|
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 🔐 Login Flow (VSR)
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
const vsr = connector.createLoginRequest(
|
|
91
|
+
"My Vexanium DApp",
|
|
92
|
+
"https://example.com/icon.png"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const payload = vsr.startsWith("vsr:") ? vsr.slice(4) : vsr;
|
|
96
|
+
|
|
97
|
+
window.open(
|
|
98
|
+
`https://wallet.windcrypto.com/login?vsr=${encodeURIComponent(payload)}`,
|
|
99
|
+
"Wind Wallet"
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Wallet flow:
|
|
104
|
+
|
|
105
|
+
1. Decode VSR
|
|
106
|
+
2. Connect to embedded PeerID
|
|
107
|
+
3. Send `LOGIN_OK`
|
|
108
|
+
4. Emit session
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🔄 Session Handling
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
connector.on("session", (session, proof) => {
|
|
116
|
+
session.onClose(() => {
|
|
117
|
+
console.log("Wallet disconnected");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
session.onError((error) => {
|
|
121
|
+
console.error("Session error:", error);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
window.appSession = session;
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## ✍️ Send Transaction
|
|
131
|
+
|
|
132
|
+
### With ABI Cache (Recommended)
|
|
133
|
+
|
|
134
|
+
```js
|
|
53
135
|
import { Action } from "@wharfkit/antelope";
|
|
136
|
+
import { ABICache } from "@wharfkit/abicache";
|
|
137
|
+
|
|
138
|
+
const abiCache = new ABICache();
|
|
139
|
+
appSession.setABICache(abiCache);
|
|
54
140
|
|
|
55
|
-
// Example: transfer VEX
|
|
56
141
|
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
142
|
|
|
64
143
|
const action = Action.from(
|
|
65
144
|
{
|
|
66
145
|
account: "vex.token",
|
|
67
146
|
name: "transfer",
|
|
68
|
-
data
|
|
69
|
-
|
|
147
|
+
data: {
|
|
148
|
+
from: "alice",
|
|
149
|
+
to: "bob",
|
|
150
|
+
quantity: "1.0000 VEX",
|
|
151
|
+
memo: "WindKit test"
|
|
152
|
+
},
|
|
153
|
+
authorization: [appSession.permissionLevel]
|
|
70
154
|
},
|
|
71
155
|
abi
|
|
72
156
|
);
|
|
73
157
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// result is either SendTransactionResponse (broadcast) or SignedTransaction (sign-only)
|
|
158
|
+
const result = await appSession.transact({ action });
|
|
159
|
+
|
|
77
160
|
console.log(result.transaction_id ?? result.id);
|
|
78
161
|
```
|
|
79
162
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### Sign Only (No Broadcast)
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
await appSession.transact(
|
|
169
|
+
{ action },
|
|
170
|
+
{ broadcast: false }
|
|
171
|
+
);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 📝 Sign Message
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const signature = await appSession.signMessage("Hello Wind!");
|
|
180
|
+
console.log(signature.toString());
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 🔑 Shared Secret (ECDH)
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
import { PublicKey } from "@wharfkit/antelope";
|
|
189
|
+
|
|
190
|
+
const pub = PublicKey.from("PUB_K1_...");
|
|
191
|
+
const secret = await appSession.sharedSecret(pub);
|
|
192
|
+
|
|
193
|
+
console.log(secret.toString());
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 💾 Session Storage
|
|
199
|
+
|
|
200
|
+
WindKit stores session data in:
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
sessionStorage["vex-session"]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Example structure:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"peerID": "VEX-xxxx",
|
|
211
|
+
"permission": "account@active",
|
|
212
|
+
"expiration": "2026-03-01T12:00:00",
|
|
213
|
+
"auth": "base64u_identity_proof"
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Clear session manually:
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
import { clearSession } from "windkit";
|
|
221
|
+
|
|
222
|
+
clearSession();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🏗 Architecture
|
|
228
|
+
|
|
229
|
+
### WindConnector
|
|
230
|
+
|
|
231
|
+
- Creates VSR identity login
|
|
232
|
+
- Hosts PeerJS PeerID (DApp-side)
|
|
233
|
+
- Waits for wallet connection
|
|
234
|
+
- Emits session
|
|
235
|
+
|
|
236
|
+
### WalletSession
|
|
237
|
+
|
|
238
|
+
- Sends:
|
|
239
|
+
- `signRequest`
|
|
240
|
+
- `signMessage`
|
|
241
|
+
- `sharedSecret`
|
|
242
|
+
- Routes replies via request IDs
|
|
243
|
+
- Lightweight heartbeat ping
|
|
244
|
+
- Handles account change events
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 🔎 Protocol Notes
|
|
249
|
+
|
|
250
|
+
Transaction signing method:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
signRequest
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Wallet push events handled:
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
LOGIN_OK
|
|
260
|
+
ACTIVE_ACCOUNT_CHANGED
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
All communication occurs over PeerJS `DataConnection`.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## ⚙ Technical Details
|
|
268
|
+
|
|
269
|
+
Chain ID is internally fixed via:
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
WalletSession.ChainID
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Dependencies:
|
|
276
|
+
|
|
277
|
+
- `@wharfkit/signing-request`
|
|
278
|
+
- `@wharfkit/antelope`
|
|
279
|
+
- `peerjs`
|
|
280
|
+
- `pako`
|
|
281
|
+
|
|
282
|
+
Optimized for:
|
|
283
|
+
|
|
284
|
+
- Low-RAM mobile devices
|
|
285
|
+
- Background browser tabs
|
|
286
|
+
- Unstable WebRTC networks
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 🔐 Security Model
|
|
291
|
+
|
|
292
|
+
- IdentityProof can be verified by the DApp (recommended).
|
|
293
|
+
- Private keys never leave the wallet.
|
|
294
|
+
- VSR ensures transaction integrity.
|
|
295
|
+
- PeerID is embedded inside the VSR payload to prevent misrouting.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 📄 License
|
|
300
|
+
|
|
301
|
+
MIT License
|
|
302
|
+
© Wind Stack
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,61 +1,59 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "windkit",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.3",
|
|
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
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
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
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
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.
|
|
51
|
+
"@wharfkit/antelope": "^1.1.1",
|
|
52
52
|
"@wharfkit/signing-request": "^3.2.0",
|
|
53
53
|
"pako": "^2.1.0",
|
|
54
|
-
"peerjs": "^1.5.
|
|
54
|
+
"peerjs": "^1.5.5"
|
|
55
55
|
},
|
|
56
|
-
"
|
|
57
|
-
"
|
|
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
|
+
}
|