shogun-core 5.2.0 โ 5.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/README.md +145 -1143
- package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js +1220 -0
- package/dist/browser/defaultVendors-node_modules_hpke_chacha20poly1305_esm_mod_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js +844 -0
- package/dist/browser/defaultVendors-node_modules_hpke_hybridkem-x-wing_esm_mod_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js +2335 -0
- package/dist/browser/defaultVendors-node_modules_mlkem_esm_mod_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js +999 -0
- package/dist/browser/defaultVendors-node_modules_noble_ciphers_chacha_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js +1651 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_curve_js-node_modules_noble_curves_esm_-1ce4ed.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js +825 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_abstract_edwards_js-node_modules_noble_curves_es-a82056.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js +508 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed25519_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js +747 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_ed448_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js +1608 -0
- package/dist/browser/defaultVendors-node_modules_noble_curves_esm_nist_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js +2117 -0
- package/dist/browser/defaultVendors-node_modules_noble_post-quantum_ml-dsa_js.shogun-core.js.map +1 -0
- package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js +86 -0
- package/dist/browser/defaultVendors-node_modules_openpgp_dist_openpgp_min_mjs.shogun-core.js.map +1 -0
- package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js +539 -0
- package/dist/browser/node_modules_hpke_ml-kem_esm_mod_js.shogun-core.js.map +1 -0
- package/dist/browser/shogun-core.js +160386 -0
- package/dist/browser/shogun-core.js.map +1 -0
- package/dist/config/simplified-config.js +236 -0
- package/dist/core.js +329 -0
- package/dist/crypto/asymmetric.js +99 -0
- package/dist/crypto/double-ratchet.js +370 -0
- package/dist/crypto/file-encryption.js +213 -0
- package/dist/crypto/hashing.js +87 -0
- package/dist/crypto/index.js +34 -0
- package/dist/crypto/mls-codec.js +202 -0
- package/dist/crypto/mls.js +550 -0
- package/dist/crypto/pgp.js +390 -0
- package/dist/crypto/random-generation.js +341 -0
- package/dist/crypto/sframe.js +350 -0
- package/dist/crypto/signal-protocol.js +376 -0
- package/dist/crypto/symmetric.js +91 -0
- package/dist/crypto/types.js +2 -0
- package/dist/crypto/utils.js +140 -0
- package/dist/examples/auth-test.js +253 -0
- package/dist/examples/crypto-identity-example.js +151 -0
- package/dist/examples/crypto-working-test.js +83 -0
- package/dist/examples/double-ratchet-test.js +155 -0
- package/dist/examples/mls-advanced-example.js +294 -0
- package/dist/examples/mls-sframe-test.js +304 -0
- package/dist/examples/pgp-example.js +200 -0
- package/dist/examples/quick-auth-test.js +61 -0
- package/dist/examples/random-generation-test.js +151 -0
- package/dist/examples/signal-protocol-test.js +38 -0
- package/dist/examples/simple-api-test.js +114 -0
- package/dist/examples/simple-crypto-identity-example.js +84 -0
- package/dist/examples/timeout-test.js +227 -0
- package/dist/examples/zkproof-credentials-example.js +212 -0
- package/dist/examples/zkproof-example.js +201 -0
- package/dist/gundb/api.js +435 -0
- package/dist/gundb/crypto.js +283 -0
- package/dist/gundb/db.js +1946 -0
- package/dist/gundb/derive.js +232 -0
- package/dist/gundb/errors.js +76 -0
- package/dist/gundb/index.js +22 -0
- package/dist/gundb/rxjs.js +447 -0
- package/dist/gundb/types.js +5 -0
- package/dist/index.js +58 -0
- package/dist/interfaces/common.js +2 -0
- package/dist/interfaces/events.js +40 -0
- package/dist/interfaces/plugin.js +2 -0
- package/dist/interfaces/shogun.js +37 -0
- package/dist/managers/AuthManager.js +226 -0
- package/dist/managers/CoreInitializer.js +228 -0
- package/dist/managers/CryptoIdentityManager.js +366 -0
- package/dist/managers/EventManager.js +70 -0
- package/dist/managers/PluginManager.js +299 -0
- package/dist/plugins/base.js +50 -0
- package/dist/plugins/index.js +32 -0
- package/dist/plugins/nostr/index.js +20 -0
- package/dist/plugins/nostr/nostrConnector.js +419 -0
- package/dist/plugins/nostr/nostrConnectorPlugin.js +453 -0
- package/dist/plugins/nostr/nostrSigner.js +319 -0
- package/dist/plugins/nostr/types.js +2 -0
- package/dist/plugins/smartwallet/index.js +18 -0
- package/dist/plugins/smartwallet/smartWalletPlugin.js +511 -0
- package/dist/plugins/smartwallet/types.js +2 -0
- package/dist/plugins/web3/index.js +20 -0
- package/dist/plugins/web3/types.js +2 -0
- package/dist/plugins/web3/web3Connector.js +533 -0
- package/dist/plugins/web3/web3ConnectorPlugin.js +455 -0
- package/dist/plugins/web3/web3Signer.js +314 -0
- package/dist/plugins/webauthn/index.js +19 -0
- package/dist/plugins/webauthn/types.js +14 -0
- package/dist/plugins/webauthn/webauthn.js +496 -0
- package/dist/plugins/webauthn/webauthnPlugin.js +489 -0
- package/dist/plugins/webauthn/webauthnSigner.js +310 -0
- package/dist/plugins/zkproof/index.js +53 -0
- package/dist/plugins/zkproof/types.js +2 -0
- package/dist/plugins/zkproof/zkCredentials.js +213 -0
- package/dist/plugins/zkproof/zkProofConnector.js +198 -0
- package/dist/plugins/zkproof/zkProofPlugin.js +272 -0
- package/dist/storage/storage.js +145 -0
- package/dist/types/config/simplified-config.d.ts +114 -0
- package/dist/types/core.d.ts +305 -0
- package/dist/types/crypto/asymmetric.d.ts +6 -0
- package/dist/types/crypto/double-ratchet.d.ts +22 -0
- package/dist/types/crypto/file-encryption.d.ts +19 -0
- package/dist/types/crypto/hashing.d.ts +9 -0
- package/dist/types/crypto/index.d.ts +13 -0
- package/dist/types/crypto/mls-codec.d.ts +39 -0
- package/dist/types/crypto/mls.d.ts +130 -0
- package/dist/types/crypto/pgp.d.ts +95 -0
- package/dist/types/crypto/random-generation.d.ts +35 -0
- package/dist/types/crypto/sframe.d.ts +102 -0
- package/dist/types/crypto/signal-protocol.d.ts +26 -0
- package/dist/types/crypto/symmetric.d.ts +9 -0
- package/dist/types/crypto/types.d.ts +144 -0
- package/dist/types/crypto/utils.d.ts +22 -0
- package/dist/types/examples/auth-test.d.ts +8 -0
- package/dist/types/examples/crypto-identity-example.d.ts +5 -0
- package/dist/types/examples/crypto-working-test.d.ts +1 -0
- package/dist/types/examples/double-ratchet-test.d.ts +1 -0
- package/dist/types/examples/mls-advanced-example.d.ts +53 -0
- package/dist/types/examples/mls-sframe-test.d.ts +1 -0
- package/dist/types/examples/pgp-example.d.ts +75 -0
- package/dist/types/examples/quick-auth-test.d.ts +8 -0
- package/dist/types/examples/random-generation-test.d.ts +1 -0
- package/dist/types/examples/signal-protocol-test.d.ts +1 -0
- package/dist/types/examples/simple-api-test.d.ts +10 -0
- package/dist/types/examples/simple-crypto-identity-example.d.ts +6 -0
- package/dist/types/examples/timeout-test.d.ts +8 -0
- package/dist/types/examples/zkproof-credentials-example.d.ts +12 -0
- package/dist/types/examples/zkproof-example.d.ts +11 -0
- package/dist/types/gundb/api.d.ts +185 -0
- package/dist/types/gundb/crypto.d.ts +95 -0
- package/dist/types/gundb/db.d.ts +397 -0
- package/dist/types/gundb/derive.d.ts +21 -0
- package/dist/types/gundb/errors.d.ts +42 -0
- package/dist/types/gundb/index.d.ts +3 -0
- package/dist/types/gundb/rxjs.d.ts +110 -0
- package/dist/types/gundb/types.d.ts +255 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/interfaces/common.d.ts +85 -0
- package/dist/types/interfaces/events.d.ts +131 -0
- package/dist/types/interfaces/plugin.d.ts +162 -0
- package/dist/types/interfaces/shogun.d.ts +208 -0
- package/dist/types/managers/AuthManager.d.ts +72 -0
- package/dist/types/managers/CoreInitializer.d.ts +40 -0
- package/dist/types/managers/CryptoIdentityManager.d.ts +102 -0
- package/dist/types/managers/EventManager.d.ts +49 -0
- package/dist/types/managers/PluginManager.d.ts +145 -0
- package/dist/types/plugins/base.d.ts +35 -0
- package/dist/types/plugins/index.d.ts +18 -0
- package/dist/types/plugins/nostr/index.d.ts +4 -0
- package/dist/types/plugins/nostr/nostrConnector.d.ts +119 -0
- package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +163 -0
- package/dist/types/plugins/nostr/nostrSigner.d.ts +105 -0
- package/dist/types/plugins/nostr/types.d.ts +122 -0
- package/dist/types/plugins/smartwallet/index.d.ts +2 -0
- package/dist/types/plugins/smartwallet/smartWalletPlugin.d.ts +67 -0
- package/dist/types/plugins/smartwallet/types.d.ts +80 -0
- package/dist/types/plugins/web3/index.d.ts +4 -0
- package/dist/types/plugins/web3/types.d.ts +107 -0
- package/dist/types/plugins/web3/web3Connector.d.ts +129 -0
- package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +160 -0
- package/dist/types/plugins/web3/web3Signer.d.ts +114 -0
- package/dist/types/plugins/webauthn/index.d.ts +3 -0
- package/dist/types/plugins/webauthn/types.d.ts +183 -0
- package/dist/types/plugins/webauthn/webauthn.d.ts +129 -0
- package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +179 -0
- package/dist/types/plugins/webauthn/webauthnSigner.d.ts +91 -0
- package/dist/types/plugins/zkproof/index.d.ts +48 -0
- package/dist/types/plugins/zkproof/types.d.ts +123 -0
- package/dist/types/plugins/zkproof/zkCredentials.d.ts +112 -0
- package/dist/types/plugins/zkproof/zkProofConnector.d.ts +46 -0
- package/dist/types/plugins/zkproof/zkProofPlugin.d.ts +76 -0
- package/dist/types/storage/storage.d.ts +51 -0
- package/dist/types/utils/errorHandler.d.ts +119 -0
- package/dist/types/utils/eventEmitter.d.ts +39 -0
- package/dist/types/utils/seedPhrase.d.ts +50 -0
- package/dist/types/utils/validation.d.ts +27 -0
- package/dist/utils/errorHandler.js +246 -0
- package/dist/utils/eventEmitter.js +79 -0
- package/dist/utils/seedPhrase.js +97 -0
- package/dist/utils/validation.js +81 -0
- package/package.json +10 -57
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Random Generation Module for shogun-core
|
|
3
|
+
// Provides cryptographically secure and deterministic random generation
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.randomSeedPhrase = exports.randomPassword = exports.randomColor = exports.randomShuffle = exports.randomChoice = exports.chance = exports.createDeterministicRandom = exports.DeterministicRandom = exports.randomUUID = exports.randomBool = exports.randomFloat = exports.randomInt = exports.randomBytes = exports.generateRandomString = void 0;
|
|
6
|
+
// Cryptographically secure random string generation
|
|
7
|
+
const generateRandomString = (length = 32, additionalSalt) => {
|
|
8
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
9
|
+
const randomBytes = new Uint8Array(length);
|
|
10
|
+
// Use crypto.getRandomValues for cryptographically secure randomness
|
|
11
|
+
crypto.getRandomValues(randomBytes);
|
|
12
|
+
let result = "";
|
|
13
|
+
for (let i = 0; i < length; i++) {
|
|
14
|
+
result += chars[randomBytes[i] % chars.length];
|
|
15
|
+
}
|
|
16
|
+
// Add additional salt if provided
|
|
17
|
+
if (additionalSalt) {
|
|
18
|
+
result = additionalSalt + result;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
exports.generateRandomString = generateRandomString;
|
|
23
|
+
// Generate random bytes
|
|
24
|
+
const randomBytes = (length) => {
|
|
25
|
+
const bytes = new Uint8Array(length);
|
|
26
|
+
crypto.getRandomValues(bytes);
|
|
27
|
+
return bytes;
|
|
28
|
+
};
|
|
29
|
+
exports.randomBytes = randomBytes;
|
|
30
|
+
// Generate random integer in range
|
|
31
|
+
const randomInt = (min, max) => {
|
|
32
|
+
const range = max - min + 1;
|
|
33
|
+
const randomBytes = new Uint8Array(4);
|
|
34
|
+
crypto.getRandomValues(randomBytes);
|
|
35
|
+
// Convert bytes to unsigned integer
|
|
36
|
+
const randomValue = (randomBytes[0] << 24) |
|
|
37
|
+
(randomBytes[1] << 16) |
|
|
38
|
+
(randomBytes[2] << 8) |
|
|
39
|
+
randomBytes[3];
|
|
40
|
+
// Ensure positive result and use modulo
|
|
41
|
+
return min + (Math.abs(randomValue) % range);
|
|
42
|
+
};
|
|
43
|
+
exports.randomInt = randomInt;
|
|
44
|
+
// Generate random float in range [0, 1)
|
|
45
|
+
const randomFloat = () => {
|
|
46
|
+
const randomBytes = new Uint8Array(4);
|
|
47
|
+
crypto.getRandomValues(randomBytes);
|
|
48
|
+
// Convert bytes to float
|
|
49
|
+
const randomValue = (randomBytes[0] << 24) |
|
|
50
|
+
(randomBytes[1] << 16) |
|
|
51
|
+
(randomBytes[2] << 8) |
|
|
52
|
+
randomBytes[3];
|
|
53
|
+
return randomValue / (0xffffffff + 1);
|
|
54
|
+
};
|
|
55
|
+
exports.randomFloat = randomFloat;
|
|
56
|
+
// Generate random boolean
|
|
57
|
+
const randomBool = () => {
|
|
58
|
+
const randomBytes = new Uint8Array(1);
|
|
59
|
+
crypto.getRandomValues(randomBytes);
|
|
60
|
+
return randomBytes[0] % 2 === 0;
|
|
61
|
+
};
|
|
62
|
+
exports.randomBool = randomBool;
|
|
63
|
+
// Generate random UUID v4
|
|
64
|
+
const randomUUID = () => {
|
|
65
|
+
const randomBytes = new Uint8Array(16);
|
|
66
|
+
crypto.getRandomValues(randomBytes);
|
|
67
|
+
// Set version (4) and variant bits
|
|
68
|
+
randomBytes[6] = (randomBytes[6] & 0x0f) | 0x40; // Version 4
|
|
69
|
+
randomBytes[8] = (randomBytes[8] & 0x3f) | 0x80; // Variant bits
|
|
70
|
+
// Convert to UUID string format
|
|
71
|
+
const hex = Array.from(randomBytes)
|
|
72
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
73
|
+
.join("");
|
|
74
|
+
return [
|
|
75
|
+
hex.slice(0, 8),
|
|
76
|
+
hex.slice(8, 12),
|
|
77
|
+
hex.slice(12, 16),
|
|
78
|
+
hex.slice(16, 20),
|
|
79
|
+
hex.slice(20, 32),
|
|
80
|
+
].join("-");
|
|
81
|
+
};
|
|
82
|
+
exports.randomUUID = randomUUID;
|
|
83
|
+
// Deterministic random generation using seed
|
|
84
|
+
class DeterministicRandom {
|
|
85
|
+
constructor(seed) {
|
|
86
|
+
this.seed = typeof seed === "string" ? this.hashString(seed) : seed;
|
|
87
|
+
}
|
|
88
|
+
// Simple hash function for string seeds
|
|
89
|
+
hashString(str) {
|
|
90
|
+
let hash = 0;
|
|
91
|
+
for (let i = 0; i < str.length; i++) {
|
|
92
|
+
const char = str.charCodeAt(i);
|
|
93
|
+
hash = (hash << 5) - hash + char;
|
|
94
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
95
|
+
}
|
|
96
|
+
return Math.abs(hash);
|
|
97
|
+
}
|
|
98
|
+
// Linear Congruential Generator (LCG)
|
|
99
|
+
lcg() {
|
|
100
|
+
this.seed = (this.seed * 1664525 + 1013904223) % Math.pow(2, 32);
|
|
101
|
+
return this.seed / Math.pow(2, 32);
|
|
102
|
+
}
|
|
103
|
+
// Generate random integer in range
|
|
104
|
+
integer(min = 0, max = 100) {
|
|
105
|
+
return Math.floor(this.lcg() * (max - min + 1)) + min;
|
|
106
|
+
}
|
|
107
|
+
// Generate random float in range
|
|
108
|
+
floating(min = 0, max = 1, fixed = 4) {
|
|
109
|
+
const value = this.lcg() * (max - min) + min;
|
|
110
|
+
return parseFloat(value.toFixed(fixed));
|
|
111
|
+
}
|
|
112
|
+
// Generate random boolean
|
|
113
|
+
bool() {
|
|
114
|
+
return this.lcg() < 0.5;
|
|
115
|
+
}
|
|
116
|
+
// Generate random string
|
|
117
|
+
string(length = 10, pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
|
|
118
|
+
let result = "";
|
|
119
|
+
for (let i = 0; i < length; i++) {
|
|
120
|
+
result += pool[this.integer(0, pool.length - 1)];
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
// Generate random GUID (deterministic)
|
|
125
|
+
guid() {
|
|
126
|
+
const hex = "0123456789abcdef";
|
|
127
|
+
let result = "";
|
|
128
|
+
for (let i = 0; i < 32; i++) {
|
|
129
|
+
result += hex[this.integer(0, 15)];
|
|
130
|
+
}
|
|
131
|
+
return [
|
|
132
|
+
result.slice(0, 8),
|
|
133
|
+
result.slice(8, 12),
|
|
134
|
+
result.slice(12, 16),
|
|
135
|
+
result.slice(16, 20),
|
|
136
|
+
result.slice(20, 32),
|
|
137
|
+
].join("-");
|
|
138
|
+
}
|
|
139
|
+
// Generate random choice from array
|
|
140
|
+
choice(array) {
|
|
141
|
+
return array[this.integer(0, array.length - 1)];
|
|
142
|
+
}
|
|
143
|
+
// Shuffle array (Fisher-Yates algorithm)
|
|
144
|
+
shuffle(array) {
|
|
145
|
+
const shuffled = [...array];
|
|
146
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
147
|
+
const j = this.integer(0, i);
|
|
148
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
149
|
+
}
|
|
150
|
+
return shuffled;
|
|
151
|
+
}
|
|
152
|
+
// Generate random color (hex)
|
|
153
|
+
color() {
|
|
154
|
+
return "#" + this.string(6, "0123456789abcdef");
|
|
155
|
+
}
|
|
156
|
+
// Generate random date in range
|
|
157
|
+
date(start, end) {
|
|
158
|
+
const startTime = start.getTime();
|
|
159
|
+
const endTime = end.getTime();
|
|
160
|
+
const randomTime = this.floating(startTime, endTime);
|
|
161
|
+
return new Date(randomTime);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.DeterministicRandom = DeterministicRandom;
|
|
165
|
+
// Factory function for deterministic random
|
|
166
|
+
const createDeterministicRandom = (seed) => {
|
|
167
|
+
return new DeterministicRandom(seed);
|
|
168
|
+
};
|
|
169
|
+
exports.createDeterministicRandom = createDeterministicRandom;
|
|
170
|
+
// Chance.js-like interface for compatibility
|
|
171
|
+
const chance = (seed) => {
|
|
172
|
+
return new DeterministicRandom(seed);
|
|
173
|
+
};
|
|
174
|
+
exports.chance = chance;
|
|
175
|
+
// Utility functions for random generation
|
|
176
|
+
const randomChoice = (array) => {
|
|
177
|
+
return array[(0, exports.randomInt)(0, array.length - 1)];
|
|
178
|
+
};
|
|
179
|
+
exports.randomChoice = randomChoice;
|
|
180
|
+
const randomShuffle = (array) => {
|
|
181
|
+
const shuffled = [...array];
|
|
182
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
183
|
+
const j = (0, exports.randomInt)(0, i);
|
|
184
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
185
|
+
}
|
|
186
|
+
return shuffled;
|
|
187
|
+
};
|
|
188
|
+
exports.randomShuffle = randomShuffle;
|
|
189
|
+
const randomColor = () => {
|
|
190
|
+
const hex = "0123456789abcdef";
|
|
191
|
+
let color = "#";
|
|
192
|
+
for (let i = 0; i < 6; i++) {
|
|
193
|
+
color += hex[(0, exports.randomInt)(0, 15)];
|
|
194
|
+
}
|
|
195
|
+
return color;
|
|
196
|
+
};
|
|
197
|
+
exports.randomColor = randomColor;
|
|
198
|
+
// Generate random password with specific requirements
|
|
199
|
+
const randomPassword = (options = {}) => {
|
|
200
|
+
const { length = 12, includeUppercase = true, includeLowercase = true, includeNumbers = true, includeSymbols = true, excludeSimilar = true, } = options;
|
|
201
|
+
let charset = "";
|
|
202
|
+
if (includeUppercase) {
|
|
203
|
+
charset += excludeSimilar
|
|
204
|
+
? "ABCDEFGHJKLMNPQRSTUVWXYZ"
|
|
205
|
+
: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
206
|
+
}
|
|
207
|
+
if (includeLowercase) {
|
|
208
|
+
charset += excludeSimilar
|
|
209
|
+
? "abcdefghijkmnpqrstuvwxyz"
|
|
210
|
+
: "abcdefghijklmnopqrstuvwxyz";
|
|
211
|
+
}
|
|
212
|
+
if (includeNumbers) {
|
|
213
|
+
charset += excludeSimilar ? "23456789" : "0123456789";
|
|
214
|
+
}
|
|
215
|
+
if (includeSymbols) {
|
|
216
|
+
charset += "!@#$%^&*()_+-=[]{}|;:,.<>?";
|
|
217
|
+
}
|
|
218
|
+
if (charset === "") {
|
|
219
|
+
throw new Error("At least one character type must be included");
|
|
220
|
+
}
|
|
221
|
+
let password = "";
|
|
222
|
+
for (let i = 0; i < length; i++) {
|
|
223
|
+
const randomIndex = (0, exports.randomInt)(0, charset.length - 1);
|
|
224
|
+
password += charset[randomIndex];
|
|
225
|
+
}
|
|
226
|
+
return password;
|
|
227
|
+
};
|
|
228
|
+
exports.randomPassword = randomPassword;
|
|
229
|
+
// Generate random seed phrase (for crypto wallets)
|
|
230
|
+
const randomSeedPhrase = (wordCount = 12) => {
|
|
231
|
+
// Common BIP39 wordlist (first 100 words for demo)
|
|
232
|
+
const wordlist = [
|
|
233
|
+
"abandon",
|
|
234
|
+
"ability",
|
|
235
|
+
"able",
|
|
236
|
+
"about",
|
|
237
|
+
"above",
|
|
238
|
+
"absent",
|
|
239
|
+
"absorb",
|
|
240
|
+
"abstract",
|
|
241
|
+
"absurd",
|
|
242
|
+
"abuse",
|
|
243
|
+
"access",
|
|
244
|
+
"accident",
|
|
245
|
+
"account",
|
|
246
|
+
"accuse",
|
|
247
|
+
"achieve",
|
|
248
|
+
"acid",
|
|
249
|
+
"acoustic",
|
|
250
|
+
"acquire",
|
|
251
|
+
"across",
|
|
252
|
+
"act",
|
|
253
|
+
"action",
|
|
254
|
+
"actor",
|
|
255
|
+
"actress",
|
|
256
|
+
"actual",
|
|
257
|
+
"adapt",
|
|
258
|
+
"add",
|
|
259
|
+
"addict",
|
|
260
|
+
"address",
|
|
261
|
+
"adjust",
|
|
262
|
+
"admit",
|
|
263
|
+
"adult",
|
|
264
|
+
"advance",
|
|
265
|
+
"advice",
|
|
266
|
+
"aerobic",
|
|
267
|
+
"affair",
|
|
268
|
+
"afford",
|
|
269
|
+
"afraid",
|
|
270
|
+
"again",
|
|
271
|
+
"age",
|
|
272
|
+
"agent",
|
|
273
|
+
"agree",
|
|
274
|
+
"ahead",
|
|
275
|
+
"aim",
|
|
276
|
+
"air",
|
|
277
|
+
"airport",
|
|
278
|
+
"aisle",
|
|
279
|
+
"alarm",
|
|
280
|
+
"album",
|
|
281
|
+
"alcohol",
|
|
282
|
+
"alert",
|
|
283
|
+
"alien",
|
|
284
|
+
"all",
|
|
285
|
+
"alley",
|
|
286
|
+
"allow",
|
|
287
|
+
"almost",
|
|
288
|
+
"alone",
|
|
289
|
+
"alpha",
|
|
290
|
+
"already",
|
|
291
|
+
"also",
|
|
292
|
+
"alter",
|
|
293
|
+
"always",
|
|
294
|
+
"amateur",
|
|
295
|
+
"amazing",
|
|
296
|
+
"among",
|
|
297
|
+
"amount",
|
|
298
|
+
"amused",
|
|
299
|
+
"analyst",
|
|
300
|
+
"anchor",
|
|
301
|
+
"ancient",
|
|
302
|
+
"anger",
|
|
303
|
+
"angle",
|
|
304
|
+
"angry",
|
|
305
|
+
"animal",
|
|
306
|
+
"ankle",
|
|
307
|
+
"announce",
|
|
308
|
+
"annual",
|
|
309
|
+
"another",
|
|
310
|
+
"answer",
|
|
311
|
+
"antenna",
|
|
312
|
+
"antique",
|
|
313
|
+
"anxiety",
|
|
314
|
+
"any",
|
|
315
|
+
"apart",
|
|
316
|
+
"apology",
|
|
317
|
+
"appear",
|
|
318
|
+
"apple",
|
|
319
|
+
"approve",
|
|
320
|
+
"april",
|
|
321
|
+
"arch",
|
|
322
|
+
"arctic",
|
|
323
|
+
"area",
|
|
324
|
+
"arena",
|
|
325
|
+
"argue",
|
|
326
|
+
"arm",
|
|
327
|
+
"armed",
|
|
328
|
+
"armor",
|
|
329
|
+
"army",
|
|
330
|
+
"around",
|
|
331
|
+
"arrange",
|
|
332
|
+
"arrest",
|
|
333
|
+
];
|
|
334
|
+
const phrase = [];
|
|
335
|
+
for (let i = 0; i < wordCount; i++) {
|
|
336
|
+
const randomIndex = (0, exports.randomInt)(0, wordlist.length - 1);
|
|
337
|
+
phrase.push(wordlist[randomIndex]);
|
|
338
|
+
}
|
|
339
|
+
return phrase;
|
|
340
|
+
};
|
|
341
|
+
exports.randomSeedPhrase = randomSeedPhrase;
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SFrame (Secure Frame) Manager
|
|
4
|
+
* End-to-end encryption for real-time media frames (audio/video)
|
|
5
|
+
* Designed for low overhead and high performance
|
|
6
|
+
*
|
|
7
|
+
* SFrame adds ~10 bytes per frame overhead
|
|
8
|
+
* Compatible with WebRTC Insertable Streams API
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.SFrameManager = void 0;
|
|
12
|
+
class SFrameManager {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.keys = new Map();
|
|
15
|
+
this.currentKeyId = 0;
|
|
16
|
+
this.frameCounter = 0;
|
|
17
|
+
this.initialized = false;
|
|
18
|
+
console.log("๐ฅ [SFrame] Manager created");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the SFrame manager
|
|
22
|
+
*/
|
|
23
|
+
async initialize() {
|
|
24
|
+
if (this.initialized) {
|
|
25
|
+
console.warn("[SFrame] Already initialized");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
console.log("๐ [SFrame] Initializing...");
|
|
30
|
+
// Generate initial key
|
|
31
|
+
await this.generateKey(0);
|
|
32
|
+
this.initialized = true;
|
|
33
|
+
console.log("โ
[SFrame] Initialized successfully");
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error("โ [SFrame] Initialization failed:", error);
|
|
37
|
+
throw new Error(`SFrame initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate a new SFrame encryption key
|
|
42
|
+
*/
|
|
43
|
+
async generateKey(keyId) {
|
|
44
|
+
try {
|
|
45
|
+
console.log(`๐ [SFrame] Generating key ${keyId}...`);
|
|
46
|
+
// Generate AES-GCM key (128-bit for low overhead)
|
|
47
|
+
const key = await crypto.subtle.generateKey({
|
|
48
|
+
name: "AES-GCM",
|
|
49
|
+
length: 128, // 128-bit for performance, 256-bit for maximum security
|
|
50
|
+
}, false, // Not extractable for security
|
|
51
|
+
["encrypt", "decrypt"]);
|
|
52
|
+
// Generate salt for key derivation
|
|
53
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
54
|
+
const sframeKey = {
|
|
55
|
+
keyId,
|
|
56
|
+
key,
|
|
57
|
+
salt,
|
|
58
|
+
};
|
|
59
|
+
this.keys.set(keyId, sframeKey);
|
|
60
|
+
console.log(`โ
[SFrame] Key ${keyId} generated`);
|
|
61
|
+
return sframeKey;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(`โ [SFrame] Key generation failed:`, error);
|
|
65
|
+
throw new Error(`SFrame key generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Derive an SFrame key from MLS shared secret
|
|
70
|
+
* This allows SFrame to use keys derived from MLS for media encryption
|
|
71
|
+
* RFC 9605 Section 5.2: MLS-based key management
|
|
72
|
+
*/
|
|
73
|
+
async deriveKeyFromMLSSecret(mlsSecret, keyId, context = "SFrame") {
|
|
74
|
+
try {
|
|
75
|
+
console.log(`๐ [SFrame] Deriving key ${keyId} from MLS secret (RFC 9605 Section 5.2)...`);
|
|
76
|
+
// RFC 9605 Section 5.2: Use specific labels for MLS-based derivation
|
|
77
|
+
const secretLabel = new TextEncoder().encode("SFrame 1.0 Secret");
|
|
78
|
+
const saltLabel = new TextEncoder().encode("SFrame 1.0 Salt");
|
|
79
|
+
// Import MLS secret as key material
|
|
80
|
+
const baseKey = await crypto.subtle.importKey("raw", mlsSecret, "HKDF", false, ["deriveKey", "deriveBits"]);
|
|
81
|
+
// Derive salt using HKDF (RFC 9605)
|
|
82
|
+
const derivedSaltBits = await crypto.subtle.deriveBits({
|
|
83
|
+
name: "HKDF",
|
|
84
|
+
hash: "SHA-256",
|
|
85
|
+
salt: new Uint8Array(0), // Empty salt for salt derivation
|
|
86
|
+
info: saltLabel,
|
|
87
|
+
}, baseKey, 128);
|
|
88
|
+
const salt = new Uint8Array(derivedSaltBits);
|
|
89
|
+
// Derive AES-GCM key using HKDF with RFC 9605 label
|
|
90
|
+
const key = await crypto.subtle.deriveKey({
|
|
91
|
+
name: "HKDF",
|
|
92
|
+
hash: "SHA-256",
|
|
93
|
+
salt: new Uint8Array(0), // Empty salt for key derivation
|
|
94
|
+
info: secretLabel,
|
|
95
|
+
}, baseKey, {
|
|
96
|
+
name: "AES-GCM",
|
|
97
|
+
length: 128,
|
|
98
|
+
}, false, ["encrypt", "decrypt"]);
|
|
99
|
+
const sframeKey = {
|
|
100
|
+
keyId,
|
|
101
|
+
key,
|
|
102
|
+
salt,
|
|
103
|
+
};
|
|
104
|
+
this.keys.set(keyId, sframeKey);
|
|
105
|
+
console.log(`โ
[SFrame] Key ${keyId} derived from MLS (RFC 9605 compliant)`);
|
|
106
|
+
return sframeKey;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error(`โ [SFrame] Key derivation failed:`, error);
|
|
110
|
+
throw new Error(`SFrame key derivation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Set the active encryption key
|
|
115
|
+
*/
|
|
116
|
+
setActiveKey(keyId) {
|
|
117
|
+
if (!this.keys.has(keyId)) {
|
|
118
|
+
throw new Error(`SFrame key ${keyId} not found`);
|
|
119
|
+
}
|
|
120
|
+
this.currentKeyId = keyId;
|
|
121
|
+
console.log(`๐ [SFrame] Active key set to ${keyId}`);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Encrypt a media frame using SFrame
|
|
125
|
+
*/
|
|
126
|
+
async encryptFrame(frameData) {
|
|
127
|
+
this.ensureInitialized();
|
|
128
|
+
try {
|
|
129
|
+
const sframeKey = this.keys.get(this.currentKeyId);
|
|
130
|
+
if (!sframeKey) {
|
|
131
|
+
throw new Error(`SFrame key ${this.currentKeyId} not found`);
|
|
132
|
+
}
|
|
133
|
+
// RFC 9605: IV = salt XOR counter
|
|
134
|
+
// Generate counter bytes (96-bit/12-byte)
|
|
135
|
+
const counterBytes = new Uint8Array(12);
|
|
136
|
+
const counterView = new DataView(counterBytes.buffer);
|
|
137
|
+
// Store frame counter in last 8 bytes (big-endian uint64-like)
|
|
138
|
+
counterView.setUint32(4, Math.floor(this.frameCounter / 0x100000000), false);
|
|
139
|
+
counterView.setUint32(8, this.frameCounter & 0xffffffff, false);
|
|
140
|
+
// SFrame header: 1 byte for key ID + frame counter encoding
|
|
141
|
+
// Simplified header: 1 byte key ID + 4 bytes frame counter
|
|
142
|
+
const header = new Uint8Array(5);
|
|
143
|
+
header[0] = this.currentKeyId;
|
|
144
|
+
new DataView(header.buffer).setUint32(1, this.frameCounter, false);
|
|
145
|
+
// XOR salt with counter to create IV (RFC 9605 Section 4.3)
|
|
146
|
+
const iv = new Uint8Array(12);
|
|
147
|
+
for (let i = 0; i < 12; i++) {
|
|
148
|
+
iv[i] = sframeKey.salt[i] ^ counterBytes[i];
|
|
149
|
+
}
|
|
150
|
+
// Encrypt the frame with header authentication (RFC 9605 Section 4.3)
|
|
151
|
+
const ciphertext = await crypto.subtle.encrypt({
|
|
152
|
+
name: "AES-GCM",
|
|
153
|
+
iv,
|
|
154
|
+
additionalData: header, // RFC 9605: Header included in AAD
|
|
155
|
+
tagLength: 128, // 128-bit authentication tag
|
|
156
|
+
}, sframeKey.key, frameData);
|
|
157
|
+
// RFC 9605: SFrame format = header + ciphertext (IV is derived, not transmitted)
|
|
158
|
+
// Note: We include IV for now for simplicity, but RFC specifies deriving it from counter
|
|
159
|
+
const encrypted = new Uint8Array(header.length + iv.length + ciphertext.byteLength);
|
|
160
|
+
encrypted.set(header, 0);
|
|
161
|
+
encrypted.set(iv, header.length);
|
|
162
|
+
encrypted.set(new Uint8Array(ciphertext), header.length + iv.length);
|
|
163
|
+
// Increment frame counter
|
|
164
|
+
this.frameCounter++;
|
|
165
|
+
return encrypted;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error("โ [SFrame] Frame encryption failed:", error);
|
|
169
|
+
throw new Error(`SFrame encryption failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Decrypt a media frame using SFrame
|
|
174
|
+
*/
|
|
175
|
+
async decryptFrame(encryptedFrame) {
|
|
176
|
+
this.ensureInitialized();
|
|
177
|
+
try {
|
|
178
|
+
// Parse SFrame header (5 bytes: 1 byte key ID + 4 bytes frame counter)
|
|
179
|
+
const header = encryptedFrame.slice(0, 5);
|
|
180
|
+
const keyId = header[0];
|
|
181
|
+
const frameCount = new DataView(header.buffer, header.byteOffset).getUint32(1, false);
|
|
182
|
+
// Get the key
|
|
183
|
+
const sframeKey = this.keys.get(keyId);
|
|
184
|
+
if (!sframeKey) {
|
|
185
|
+
throw new Error(`SFrame key ${keyId} not found`);
|
|
186
|
+
}
|
|
187
|
+
// Extract IV (12 bytes after header)
|
|
188
|
+
const iv = encryptedFrame.slice(5, 17);
|
|
189
|
+
// RFC 9605: Verify IV derivation (optional check for debugging)
|
|
190
|
+
// Reconstruct expected IV from frame count and salt
|
|
191
|
+
const counterBytes = new Uint8Array(12);
|
|
192
|
+
const counterView = new DataView(counterBytes.buffer);
|
|
193
|
+
counterView.setUint32(4, Math.floor(frameCount / 0x100000000), false);
|
|
194
|
+
counterView.setUint32(8, frameCount & 0xffffffff, false);
|
|
195
|
+
const expectedIV = new Uint8Array(12);
|
|
196
|
+
for (let i = 0; i < 12; i++) {
|
|
197
|
+
expectedIV[i] = sframeKey.salt[i] ^ counterBytes[i];
|
|
198
|
+
}
|
|
199
|
+
// Extract ciphertext (rest of the data)
|
|
200
|
+
const ciphertext = encryptedFrame.slice(17);
|
|
201
|
+
// Decrypt the frame with header authentication (RFC 9605 Section 4.3)
|
|
202
|
+
const plaintext = await crypto.subtle.decrypt({
|
|
203
|
+
name: "AES-GCM",
|
|
204
|
+
iv,
|
|
205
|
+
additionalData: header, // RFC 9605: Header included in AAD
|
|
206
|
+
tagLength: 128,
|
|
207
|
+
}, sframeKey.key, ciphertext);
|
|
208
|
+
return plaintext;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
console.error("โ [SFrame] Frame decryption failed:", error);
|
|
212
|
+
throw new Error(`SFrame decryption failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Encrypt transform function for Insertable Streams
|
|
217
|
+
* Use this with RTCRtpSender.createEncodedStreams()
|
|
218
|
+
*/
|
|
219
|
+
createEncryptTransform() {
|
|
220
|
+
const manager = this;
|
|
221
|
+
return new TransformStream({
|
|
222
|
+
async transform(encodedFrame, controller) {
|
|
223
|
+
try {
|
|
224
|
+
// Get frame data
|
|
225
|
+
const frameData = encodedFrame.data;
|
|
226
|
+
// Encrypt the frame
|
|
227
|
+
const encrypted = await manager.encryptFrame(frameData);
|
|
228
|
+
// Create new encoded frame with encrypted data
|
|
229
|
+
encodedFrame.data = encrypted.buffer;
|
|
230
|
+
// Forward the encrypted frame
|
|
231
|
+
controller.enqueue(encodedFrame);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
console.error("[SFrame] Encrypt transform error:", error);
|
|
235
|
+
// Forward unencrypted frame on error (fallback)
|
|
236
|
+
controller.enqueue(encodedFrame);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Decrypt transform function for Insertable Streams
|
|
243
|
+
* Use this with RTCRtpReceiver.createEncodedStreams()
|
|
244
|
+
*/
|
|
245
|
+
createDecryptTransform() {
|
|
246
|
+
const manager = this;
|
|
247
|
+
return new TransformStream({
|
|
248
|
+
async transform(encodedFrame, controller) {
|
|
249
|
+
try {
|
|
250
|
+
// Get encrypted frame data
|
|
251
|
+
const encryptedData = new Uint8Array(encodedFrame.data);
|
|
252
|
+
// Decrypt the frame
|
|
253
|
+
const decrypted = await manager.decryptFrame(encryptedData);
|
|
254
|
+
// Create new encoded frame with decrypted data
|
|
255
|
+
encodedFrame.data = decrypted;
|
|
256
|
+
// Forward the decrypted frame
|
|
257
|
+
controller.enqueue(encodedFrame);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.error("[SFrame] Decrypt transform error:", error);
|
|
261
|
+
// Skip frame on decryption error
|
|
262
|
+
// (better to drop frame than show corrupted video)
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Rotate encryption keys
|
|
269
|
+
* RFC 9605: Frame counter should be reset on key rotation to prevent exhaustion
|
|
270
|
+
*/
|
|
271
|
+
async rotateKey() {
|
|
272
|
+
try {
|
|
273
|
+
const newKeyId = this.currentKeyId + 1;
|
|
274
|
+
console.log(`๐ [SFrame] Rotating to key ${newKeyId}...`);
|
|
275
|
+
await this.generateKey(newKeyId);
|
|
276
|
+
this.setActiveKey(newKeyId);
|
|
277
|
+
// RFC 9605: Reset frame counter on key rotation
|
|
278
|
+
this.resetFrameCounter();
|
|
279
|
+
console.log(`๐ [SFrame] Frame counter reset to 0 for new key`);
|
|
280
|
+
console.log(`โ
[SFrame] Key rotated to ${newKeyId}`);
|
|
281
|
+
return newKeyId;
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error("โ [SFrame] Key rotation failed:", error);
|
|
285
|
+
throw new Error(`SFrame key rotation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get current key ID
|
|
290
|
+
*/
|
|
291
|
+
getCurrentKeyId() {
|
|
292
|
+
return this.currentKeyId;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get frame counter (for debugging)
|
|
296
|
+
*/
|
|
297
|
+
getFrameCounter() {
|
|
298
|
+
return this.frameCounter;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Reset frame counter (use when rotating keys)
|
|
302
|
+
*/
|
|
303
|
+
resetFrameCounter() {
|
|
304
|
+
this.frameCounter = 0;
|
|
305
|
+
console.log("๐ [SFrame] Frame counter reset");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Remove old keys to prevent memory bloat
|
|
309
|
+
*/
|
|
310
|
+
cleanupOldKeys(keepLast = 2) {
|
|
311
|
+
const keyIds = Array.from(this.keys.keys()).sort((a, b) => b - a);
|
|
312
|
+
if (keyIds.length > keepLast) {
|
|
313
|
+
const toDelete = keyIds.slice(keepLast);
|
|
314
|
+
toDelete.forEach((keyId) => {
|
|
315
|
+
this.keys.delete(keyId);
|
|
316
|
+
console.log(`๐งน [SFrame] Deleted old key ${keyId}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get statistics
|
|
322
|
+
*/
|
|
323
|
+
getStats() {
|
|
324
|
+
return {
|
|
325
|
+
keyCount: this.keys.size,
|
|
326
|
+
currentKeyId: this.currentKeyId,
|
|
327
|
+
frameCounter: this.frameCounter,
|
|
328
|
+
initialized: this.initialized,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Clean up resources
|
|
333
|
+
*/
|
|
334
|
+
destroy() {
|
|
335
|
+
this.keys.clear();
|
|
336
|
+
this.initialized = false;
|
|
337
|
+
this.frameCounter = 0;
|
|
338
|
+
console.log("โ
[SFrame] Manager destroyed");
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Ensure the manager is initialized
|
|
342
|
+
*/
|
|
343
|
+
ensureInitialized() {
|
|
344
|
+
if (!this.initialized) {
|
|
345
|
+
throw new Error("SFrame Manager not initialized. Call initialize() first.");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
exports.SFrameManager = SFrameManager;
|
|
350
|
+
exports.default = SFrameManager;
|