shogun-core 2.0.3 → 3.0.0
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 +150 -19
- package/dist/browser/shogun-core.js +3241 -1286
- package/dist/browser/shogun-core.js.map +1 -1
- package/dist/config/simplified-config.js +230 -0
- package/dist/core.js +49 -571
- package/dist/gundb/db.js +466 -237
- package/dist/gundb/improved-types.js +4 -0
- package/dist/gundb/index.js +4 -0
- package/dist/gundb/simple-api.js +438 -0
- package/dist/index.js +8 -2
- package/dist/managers/AuthManager.js +225 -0
- package/dist/managers/CoreInitializer.js +227 -0
- package/dist/managers/EventManager.js +67 -0
- package/dist/managers/PluginManager.js +296 -0
- package/dist/migration-test.js +91 -0
- package/dist/plugins/nostr/nostrConnectorPlugin.js +1 -1
- package/dist/plugins/oauth/oauthPlugin.js +1 -1
- package/dist/plugins/webauthn/webauthnPlugin.js +1 -1
- package/dist/types/config/simplified-config.d.ts +114 -0
- package/dist/types/core.d.ts +13 -46
- package/dist/types/gundb/db.d.ts +92 -14
- package/dist/types/gundb/improved-types.d.ts +123 -0
- package/dist/types/gundb/index.d.ts +2 -0
- package/dist/types/gundb/rxjs.d.ts +3 -3
- package/dist/types/gundb/simple-api.d.ts +90 -0
- package/dist/types/index.d.ts +6 -4
- package/dist/types/{types → interfaces}/shogun.d.ts +8 -10
- package/dist/types/managers/AuthManager.d.ts +69 -0
- package/dist/types/managers/CoreInitializer.d.ts +40 -0
- package/dist/types/managers/EventManager.d.ts +49 -0
- package/dist/types/managers/PluginManager.d.ts +145 -0
- package/dist/types/migration-test.d.ts +16 -0
- package/dist/types/plugins/base.d.ts +2 -2
- package/dist/types/plugins/index.d.ts +1 -1
- package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +1 -1
- package/dist/types/plugins/nostr/types.d.ts +2 -2
- package/dist/types/plugins/oauth/oauthPlugin.d.ts +1 -1
- package/dist/types/plugins/oauth/types.d.ts +2 -2
- package/dist/types/plugins/web3/types.d.ts +2 -2
- package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +1 -1
- package/dist/types/plugins/webauthn/types.d.ts +2 -2
- package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +1 -1
- package/dist/types/utils/errorHandler.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{types → interfaces}/common.js +0 -0
- /package/dist/{types → interfaces}/events.js +0 -0
- /package/dist/{types → interfaces}/plugin.js +0 -0
- /package/dist/{types → interfaces}/shogun.js +0 -0
- /package/dist/types/{types → interfaces}/common.d.ts +0 -0
- /package/dist/types/{types → interfaces}/events.d.ts +0 -0
- /package/dist/types/{types → interfaces}/plugin.d.ts +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { ShogunStorage } from "../storage/storage";
|
|
2
|
+
import { ErrorHandler } from "../utils/errorHandler";
|
|
3
|
+
import { WebauthnPlugin } from "../plugins/webauthn/webauthnPlugin";
|
|
4
|
+
import { Web3ConnectorPlugin } from "../plugins/web3/web3ConnectorPlugin";
|
|
5
|
+
import { NostrConnectorPlugin } from "../plugins/nostr/nostrConnectorPlugin";
|
|
6
|
+
import { OAuthPlugin } from "../plugins/oauth/oauthPlugin";
|
|
7
|
+
import { restrictedPut, DataBase, RxJS, createGun, Gun, derive, } from "../gundb";
|
|
8
|
+
/**
|
|
9
|
+
* Handles initialization of ShogunCore components
|
|
10
|
+
*/
|
|
11
|
+
export class CoreInitializer {
|
|
12
|
+
core;
|
|
13
|
+
constructor(core) {
|
|
14
|
+
this.core = core;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the Shogun SDK
|
|
18
|
+
* @param config - SDK Configuration object
|
|
19
|
+
* @description Creates a new instance of ShogunCore with the provided configuration.
|
|
20
|
+
* Initializes all required components including storage, event emitter, GunInstance connection,
|
|
21
|
+
* and plugin system.
|
|
22
|
+
*/
|
|
23
|
+
async initialize(config) {
|
|
24
|
+
// Polyfill console for environments where it might be missing
|
|
25
|
+
if (typeof console === "undefined") {
|
|
26
|
+
global.console = {
|
|
27
|
+
log: () => { },
|
|
28
|
+
warn: () => { },
|
|
29
|
+
error: () => { },
|
|
30
|
+
info: () => { },
|
|
31
|
+
debug: () => { },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Initialize storage
|
|
35
|
+
this.core.storage = new ShogunStorage();
|
|
36
|
+
// Setup error handler
|
|
37
|
+
ErrorHandler.addListener((error) => {
|
|
38
|
+
this.core.emit("error", {
|
|
39
|
+
action: error.code,
|
|
40
|
+
message: error.message,
|
|
41
|
+
type: error.type,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// Setup Gun instance
|
|
45
|
+
await this.initializeGun(config);
|
|
46
|
+
// Setup Gun user
|
|
47
|
+
await this.initializeGunUser();
|
|
48
|
+
// Setup Gun event forwarding
|
|
49
|
+
this.setupGunEventForwarding();
|
|
50
|
+
// Setup wallet derivation
|
|
51
|
+
this.setupWalletDerivation();
|
|
52
|
+
// Initialize RxJS
|
|
53
|
+
this.core.rx = new RxJS(this.core._gun);
|
|
54
|
+
// Register built-in plugins
|
|
55
|
+
this.registerBuiltinPlugins(config);
|
|
56
|
+
// Initialize async components
|
|
57
|
+
await this.initializeAsync();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Initialize Gun instance
|
|
61
|
+
*/
|
|
62
|
+
async initializeGun(config) {
|
|
63
|
+
if (config.gunOptions.authToken) {
|
|
64
|
+
restrictedPut(Gun, config.gunOptions.authToken);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
if (config.gunInstance) {
|
|
68
|
+
this.core._gun = config.gunInstance;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.core._gun = createGun(config.gunOptions);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (typeof console !== "undefined" && console.error) {
|
|
76
|
+
console.error("Error creating Gun instance:", error);
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Failed to create Gun instance: ${error}`);
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
this.core.db = new DataBase(this.core._gun, config.gunOptions.scope || "");
|
|
82
|
+
this.core._gun = this.core.db.gun;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (typeof console !== "undefined" && console.error) {
|
|
86
|
+
console.error("Error initializing GunInstance:", error);
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Failed to initialize GunInstance: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Initialize Gun user
|
|
93
|
+
*/
|
|
94
|
+
async initializeGunUser() {
|
|
95
|
+
try {
|
|
96
|
+
this.core._user = this.core._gun
|
|
97
|
+
.user()
|
|
98
|
+
.recall({ sessionStorage: true });
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (typeof console !== "undefined" && console.error) {
|
|
102
|
+
console.error("Error initializing Gun user:", error);
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`Failed to initialize Gun user: ${error}`);
|
|
105
|
+
}
|
|
106
|
+
this.core._gun.on("auth", (user) => {
|
|
107
|
+
this.core._user = this.core._gun
|
|
108
|
+
.user()
|
|
109
|
+
.recall({ sessionStorage: true });
|
|
110
|
+
this.core.emit("auth:login", {
|
|
111
|
+
userPub: user.pub,
|
|
112
|
+
method: "password",
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Setup Gun event forwarding
|
|
118
|
+
*/
|
|
119
|
+
setupGunEventForwarding() {
|
|
120
|
+
const gunEvents = ["gun:put", "gun:get", "gun:set", "gun:remove"];
|
|
121
|
+
gunEvents.forEach((eventName) => {
|
|
122
|
+
this.core.db.on(eventName, (data) => {
|
|
123
|
+
this.core.emit(eventName, data);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
const peerEvents = [
|
|
127
|
+
"gun:peer:add",
|
|
128
|
+
"gun:peer:remove",
|
|
129
|
+
"gun:peer:connect",
|
|
130
|
+
"gun:peer:disconnect",
|
|
131
|
+
];
|
|
132
|
+
peerEvents.forEach((eventName) => {
|
|
133
|
+
this.core.db.on(eventName, (data) => {
|
|
134
|
+
this.core.emit(eventName, data);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Setup wallet derivation
|
|
140
|
+
*/
|
|
141
|
+
setupWalletDerivation() {
|
|
142
|
+
this.core._gun.on("auth", async (user) => {
|
|
143
|
+
if (!user)
|
|
144
|
+
return;
|
|
145
|
+
const priv = user._?.sea?.epriv;
|
|
146
|
+
const pub = user._?.sea?.epub;
|
|
147
|
+
this.core.wallets = await derive(priv, pub, {
|
|
148
|
+
includeSecp256k1Bitcoin: true,
|
|
149
|
+
includeSecp256k1Ethereum: true,
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Register built-in plugins based on configuration
|
|
155
|
+
*/
|
|
156
|
+
registerBuiltinPlugins(config) {
|
|
157
|
+
try {
|
|
158
|
+
// Register OAuth plugin if configuration is provided
|
|
159
|
+
if (config.oauth) {
|
|
160
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
161
|
+
console.warn("OAuth plugin will be registered with provided configuration");
|
|
162
|
+
}
|
|
163
|
+
const oauthPlugin = new OAuthPlugin();
|
|
164
|
+
if (typeof oauthPlugin.configure === "function") {
|
|
165
|
+
oauthPlugin.configure(config.oauth);
|
|
166
|
+
}
|
|
167
|
+
this.core.pluginManager.register(oauthPlugin);
|
|
168
|
+
}
|
|
169
|
+
// Register WebAuthn plugin if configuration is provided
|
|
170
|
+
if (config.webauthn) {
|
|
171
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
172
|
+
console.warn("WebAuthn plugin will be registered with provided configuration");
|
|
173
|
+
}
|
|
174
|
+
const webauthnPlugin = new WebauthnPlugin();
|
|
175
|
+
if (typeof webauthnPlugin.configure === "function") {
|
|
176
|
+
webauthnPlugin.configure(config.webauthn);
|
|
177
|
+
}
|
|
178
|
+
this.core.pluginManager.register(webauthnPlugin);
|
|
179
|
+
}
|
|
180
|
+
// Register Web3 plugin if configuration is provided
|
|
181
|
+
if (config.web3) {
|
|
182
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
183
|
+
console.warn("Web3 plugin will be registered with provided configuration");
|
|
184
|
+
}
|
|
185
|
+
const web3Plugin = new Web3ConnectorPlugin();
|
|
186
|
+
if (typeof web3Plugin.configure === "function") {
|
|
187
|
+
web3Plugin.configure(config.web3);
|
|
188
|
+
}
|
|
189
|
+
this.core.pluginManager.register(web3Plugin);
|
|
190
|
+
}
|
|
191
|
+
// Register Nostr plugin if configuration is provided
|
|
192
|
+
if (config.nostr) {
|
|
193
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
194
|
+
console.warn("Nostr plugin will be registered with provided configuration");
|
|
195
|
+
}
|
|
196
|
+
const nostrPlugin = new NostrConnectorPlugin();
|
|
197
|
+
if (typeof nostrPlugin.configure === "function") {
|
|
198
|
+
nostrPlugin.configure(config.nostr);
|
|
199
|
+
}
|
|
200
|
+
this.core.pluginManager.register(nostrPlugin);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (typeof console !== "undefined" && console.error) {
|
|
205
|
+
console.error("Error registering builtin plugins:", error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Initialize async components
|
|
211
|
+
*/
|
|
212
|
+
async initializeAsync() {
|
|
213
|
+
try {
|
|
214
|
+
await this.core.db.initialize();
|
|
215
|
+
this.core.emit("debug", {
|
|
216
|
+
action: "core_initialized",
|
|
217
|
+
timestamp: Date.now(),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
if (typeof console !== "undefined" && console.error) {
|
|
222
|
+
console.error("Error during Shogun Core initialization:", error);
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ShogunEventEmitter } from "../interfaces/events";
|
|
2
|
+
/**
|
|
3
|
+
* Manages event operations for ShogunCore
|
|
4
|
+
*/
|
|
5
|
+
export class EventManager {
|
|
6
|
+
eventEmitter;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.eventEmitter = new ShogunEventEmitter();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Emits an event through the core's event emitter.
|
|
12
|
+
* Plugins should use this method to emit events instead of accessing the private eventEmitter directly.
|
|
13
|
+
* @param eventName The name of the event to emit.
|
|
14
|
+
* @param data The data to pass with the event.
|
|
15
|
+
* @returns {boolean} Indicates if the event had listeners.
|
|
16
|
+
*/
|
|
17
|
+
emit(eventName, data) {
|
|
18
|
+
return this.eventEmitter.emit(eventName, data);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Add an event listener
|
|
22
|
+
* @param eventName The name of the event to listen for
|
|
23
|
+
* @param listener The callback function to execute when the event is emitted
|
|
24
|
+
* @returns {this} Returns this instance for method chaining
|
|
25
|
+
*/
|
|
26
|
+
on(eventName, listener) {
|
|
27
|
+
this.eventEmitter.on(eventName, listener);
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a one-time event listener
|
|
32
|
+
* @param eventName The name of the event to listen for
|
|
33
|
+
* @param listener The callback function to execute when the event is emitted
|
|
34
|
+
* @returns {this} Returns this instance for method chaining
|
|
35
|
+
*/
|
|
36
|
+
once(eventName, listener) {
|
|
37
|
+
this.eventEmitter.once(eventName, listener);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Remove an event listener
|
|
42
|
+
* @param eventName The name of the event to stop listening for
|
|
43
|
+
* @param listener The callback function to remove
|
|
44
|
+
* @returns {this} Returns this instance for method chaining
|
|
45
|
+
*/
|
|
46
|
+
off(eventName, listener) {
|
|
47
|
+
this.eventEmitter.off(eventName, listener);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Remove all listeners for a specific event or all events
|
|
52
|
+
* @param eventName Optional. The name of the event to remove listeners for.
|
|
53
|
+
* If not provided, all listeners for all events are removed.
|
|
54
|
+
* @returns {this} Returns this instance for method chaining
|
|
55
|
+
*/
|
|
56
|
+
removeAllListeners(eventName) {
|
|
57
|
+
this.eventEmitter.removeAllListeners(eventName);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the underlying event emitter instance
|
|
62
|
+
* @returns The ShogunEventEmitter instance
|
|
63
|
+
*/
|
|
64
|
+
getEventEmitter() {
|
|
65
|
+
return this.eventEmitter;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages plugin registration, validation, and lifecycle
|
|
3
|
+
*/
|
|
4
|
+
export class PluginManager {
|
|
5
|
+
plugins = new Map();
|
|
6
|
+
core;
|
|
7
|
+
constructor(core) {
|
|
8
|
+
this.core = core;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Register a plugin with the Shogun SDK
|
|
12
|
+
* @param plugin Plugin instance to register
|
|
13
|
+
* @throws Error if a plugin with the same name is already registered
|
|
14
|
+
*/
|
|
15
|
+
register(plugin) {
|
|
16
|
+
try {
|
|
17
|
+
if (!plugin.name) {
|
|
18
|
+
if (typeof console !== "undefined" && console.error) {
|
|
19
|
+
console.error("Plugin registration failed: Plugin must have a name");
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (this.plugins.has(plugin.name)) {
|
|
24
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
25
|
+
console.warn(`Plugin "${plugin.name}" is already registered. Skipping.`);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Initialize plugin with core instance
|
|
30
|
+
plugin.initialize(this.core);
|
|
31
|
+
this.plugins.set(plugin.name, plugin);
|
|
32
|
+
this.core.emit("plugin:registered", {
|
|
33
|
+
name: plugin.name,
|
|
34
|
+
version: plugin.version || "unknown",
|
|
35
|
+
category: plugin._category || "unknown",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (typeof console !== "undefined" && console.error) {
|
|
40
|
+
console.error(`Error registering plugin "${plugin.name}":`, error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Unregister a plugin from the Shogun SDK
|
|
46
|
+
* @param name Name of the plugin to unregister
|
|
47
|
+
*/
|
|
48
|
+
unregister(name) {
|
|
49
|
+
try {
|
|
50
|
+
const plugin = this.plugins.get(name);
|
|
51
|
+
if (!plugin) {
|
|
52
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
53
|
+
console.warn(`Plugin "${name}" not found for unregistration`);
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Destroy plugin if it has a destroy method
|
|
58
|
+
if (typeof plugin.destroy === "function") {
|
|
59
|
+
try {
|
|
60
|
+
plugin.destroy();
|
|
61
|
+
}
|
|
62
|
+
catch (destroyError) {
|
|
63
|
+
if (typeof console !== "undefined" && console.error) {
|
|
64
|
+
console.error(`Error destroying plugin "${name}":`, destroyError);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.plugins.delete(name);
|
|
69
|
+
this.core.emit("plugin:unregistered", {
|
|
70
|
+
name: plugin.name,
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (typeof console !== "undefined" && console.error) {
|
|
76
|
+
console.error(`Error unregistering plugin "${name}":`, error);
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Retrieve a registered plugin by name
|
|
83
|
+
* @param name Name of the plugin
|
|
84
|
+
* @returns The requested plugin or undefined if not found
|
|
85
|
+
* @template T Type of the plugin or its public interface
|
|
86
|
+
*/
|
|
87
|
+
getPlugin(name) {
|
|
88
|
+
if (!name || typeof name !== "string") {
|
|
89
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
90
|
+
console.warn("Invalid plugin name provided to getPlugin");
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const plugin = this.plugins.get(name);
|
|
95
|
+
if (!plugin) {
|
|
96
|
+
if (typeof console !== "undefined" && console.warn) {
|
|
97
|
+
console.warn(`Plugin "${name}" not found`);
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return plugin;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get information about all registered plugins
|
|
105
|
+
* @returns Array of plugin information objects
|
|
106
|
+
*/
|
|
107
|
+
getPluginsInfo() {
|
|
108
|
+
const pluginsInfo = [];
|
|
109
|
+
this.plugins.forEach((plugin) => {
|
|
110
|
+
pluginsInfo.push({
|
|
111
|
+
name: plugin.name,
|
|
112
|
+
version: plugin.version || "unknown",
|
|
113
|
+
category: plugin._category,
|
|
114
|
+
description: plugin.description,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
return pluginsInfo;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the total number of registered plugins
|
|
121
|
+
* @returns Number of registered plugins
|
|
122
|
+
*/
|
|
123
|
+
getPluginCount() {
|
|
124
|
+
return this.plugins.size;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if all plugins are properly initialized
|
|
128
|
+
* @returns Object with initialization status for each plugin
|
|
129
|
+
*/
|
|
130
|
+
getPluginsInitializationStatus() {
|
|
131
|
+
const status = {};
|
|
132
|
+
this.plugins.forEach((plugin, name) => {
|
|
133
|
+
try {
|
|
134
|
+
// Verifica se il plugin ha un metodo per controllare l'inizializzazione
|
|
135
|
+
if (typeof plugin.assertInitialized === "function") {
|
|
136
|
+
plugin.assertInitialized();
|
|
137
|
+
status[name] = { initialized: true };
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Fallback: verifica se il plugin ha un riferimento al core
|
|
141
|
+
status[name] = {
|
|
142
|
+
initialized: !!plugin.core,
|
|
143
|
+
error: !plugin.core
|
|
144
|
+
? "No core reference found"
|
|
145
|
+
: undefined,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
status[name] = {
|
|
151
|
+
initialized: false,
|
|
152
|
+
error: error instanceof Error ? error.message : String(error),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return status;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Validate plugin system integrity
|
|
160
|
+
* @returns Object with validation results
|
|
161
|
+
*/
|
|
162
|
+
validatePluginSystem() {
|
|
163
|
+
const status = this.getPluginsInitializationStatus();
|
|
164
|
+
const totalPlugins = Object.keys(status).length;
|
|
165
|
+
const initializedPlugins = Object.values(status).filter((s) => s.initialized).length;
|
|
166
|
+
const failedPlugins = Object.entries(status)
|
|
167
|
+
.filter(([_, s]) => !s.initialized)
|
|
168
|
+
.map(([name, _]) => name);
|
|
169
|
+
const warnings = [];
|
|
170
|
+
if (totalPlugins === 0) {
|
|
171
|
+
warnings.push("No plugins registered");
|
|
172
|
+
}
|
|
173
|
+
if (failedPlugins.length > 0) {
|
|
174
|
+
warnings.push(`Failed plugins: ${failedPlugins.join(", ")}`);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
totalPlugins,
|
|
178
|
+
initializedPlugins,
|
|
179
|
+
failedPlugins,
|
|
180
|
+
warnings,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Attempt to reinitialize failed plugins
|
|
185
|
+
* @returns Object with reinitialization results
|
|
186
|
+
*/
|
|
187
|
+
reinitializeFailedPlugins() {
|
|
188
|
+
const status = this.getPluginsInitializationStatus();
|
|
189
|
+
const failedPlugins = Object.entries(status)
|
|
190
|
+
.filter(([_, s]) => !s.initialized)
|
|
191
|
+
.map(([name, _]) => name);
|
|
192
|
+
const success = [];
|
|
193
|
+
const failed = [];
|
|
194
|
+
failedPlugins.forEach((pluginName) => {
|
|
195
|
+
try {
|
|
196
|
+
const plugin = this.plugins.get(pluginName);
|
|
197
|
+
if (!plugin) {
|
|
198
|
+
failed.push({ name: pluginName, error: "Plugin not found" });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// Reinizializza il plugin
|
|
202
|
+
plugin.initialize(this.core);
|
|
203
|
+
success.push(pluginName);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
207
|
+
failed.push({ name: pluginName, error: errorMessage });
|
|
208
|
+
if (typeof console !== "undefined" && console.error) {
|
|
209
|
+
console.error(`[PluginManager] Failed to reinitialize plugin ${pluginName}:`, error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
return { success, failed };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Check plugin compatibility with current ShogunCore version
|
|
217
|
+
* @returns Object with compatibility information
|
|
218
|
+
*/
|
|
219
|
+
checkPluginCompatibility() {
|
|
220
|
+
const compatible = [];
|
|
221
|
+
const incompatible = [];
|
|
222
|
+
const unknown = [];
|
|
223
|
+
this.plugins.forEach((plugin) => {
|
|
224
|
+
const pluginInfo = {
|
|
225
|
+
name: plugin.name,
|
|
226
|
+
version: plugin.version || "unknown",
|
|
227
|
+
};
|
|
228
|
+
// Verifica se il plugin ha informazioni di compatibilità
|
|
229
|
+
if (typeof plugin.getCompatibilityInfo === "function") {
|
|
230
|
+
try {
|
|
231
|
+
const compatibilityInfo = plugin.getCompatibilityInfo();
|
|
232
|
+
if (compatibilityInfo && compatibilityInfo.compatible) {
|
|
233
|
+
compatible.push(pluginInfo);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
incompatible.push({
|
|
237
|
+
...pluginInfo,
|
|
238
|
+
reason: compatibilityInfo?.reason || "Unknown compatibility issue",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
unknown.push(pluginInfo);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
// Se non ha informazioni di compatibilità, considera sconosciuto
|
|
248
|
+
unknown.push(pluginInfo);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return { compatible, incompatible, unknown };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get comprehensive debug information about the plugin system
|
|
255
|
+
* @returns Complete plugin system debug information
|
|
256
|
+
*/
|
|
257
|
+
getPluginSystemDebugInfo() {
|
|
258
|
+
const pluginsInfo = this.getPluginsInfo();
|
|
259
|
+
const initializationStatus = this.getPluginsInitializationStatus();
|
|
260
|
+
const plugins = pluginsInfo.map((info) => ({
|
|
261
|
+
...info,
|
|
262
|
+
initialized: initializationStatus[info.name]?.initialized || false,
|
|
263
|
+
error: initializationStatus[info.name]?.error,
|
|
264
|
+
}));
|
|
265
|
+
return {
|
|
266
|
+
shogunCoreVersion: "^1.6.6",
|
|
267
|
+
totalPlugins: this.getPluginCount(),
|
|
268
|
+
plugins,
|
|
269
|
+
initializationStatus,
|
|
270
|
+
validation: this.validatePluginSystem(),
|
|
271
|
+
compatibility: this.checkPluginCompatibility(),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Check if a plugin is registered
|
|
276
|
+
* @param name Name of the plugin to check
|
|
277
|
+
* @returns true if the plugin is registered, false otherwise
|
|
278
|
+
*/
|
|
279
|
+
hasPlugin(name) {
|
|
280
|
+
return this.plugins.has(name);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get all plugins of a specific category
|
|
284
|
+
* @param category Category of plugins to filter
|
|
285
|
+
* @returns Array of plugins in the specified category
|
|
286
|
+
*/
|
|
287
|
+
getPluginsByCategory(category) {
|
|
288
|
+
const result = [];
|
|
289
|
+
this.plugins.forEach((plugin) => {
|
|
290
|
+
if (plugin._category === category) {
|
|
291
|
+
result.push(plugin);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration test file to verify that the refactored ShogunCore
|
|
3
|
+
* maintains the same public API as the original implementation
|
|
4
|
+
*/
|
|
5
|
+
import { ShogunCore } from "./core";
|
|
6
|
+
import { ShogunCore as OriginalShogunCore } from "./core";
|
|
7
|
+
/**
|
|
8
|
+
* Test function to verify API compatibility
|
|
9
|
+
*/
|
|
10
|
+
export function testApiCompatibility() {
|
|
11
|
+
const config = {
|
|
12
|
+
gunOptions: {
|
|
13
|
+
peers: ["https://gunjs.herokuapp.com/gun"],
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
// Test that both implementations can be instantiated with the same config
|
|
17
|
+
const originalCore = new OriginalShogunCore(config);
|
|
18
|
+
const refactoredCore = new ShogunCore(config);
|
|
19
|
+
// Test that all public methods exist on both implementations
|
|
20
|
+
const publicMethods = [
|
|
21
|
+
// Plugin management
|
|
22
|
+
"register",
|
|
23
|
+
"unregister",
|
|
24
|
+
"getPlugin",
|
|
25
|
+
"getPluginsInfo",
|
|
26
|
+
"getPluginCount",
|
|
27
|
+
"getPluginsInitializationStatus",
|
|
28
|
+
"validatePluginSystem",
|
|
29
|
+
"reinitializeFailedPlugins",
|
|
30
|
+
"checkPluginCompatibility",
|
|
31
|
+
"getPluginSystemDebugInfo",
|
|
32
|
+
"hasPlugin",
|
|
33
|
+
"getPluginsByCategory",
|
|
34
|
+
"getAuthenticationMethod",
|
|
35
|
+
// Error handling
|
|
36
|
+
"getRecentErrors",
|
|
37
|
+
// Authentication
|
|
38
|
+
"isLoggedIn",
|
|
39
|
+
"logout",
|
|
40
|
+
"login",
|
|
41
|
+
"loginWithPair",
|
|
42
|
+
"signUp",
|
|
43
|
+
// Event management
|
|
44
|
+
"emit",
|
|
45
|
+
"on",
|
|
46
|
+
"once",
|
|
47
|
+
"off",
|
|
48
|
+
"removeAllListeners",
|
|
49
|
+
// Auth method management
|
|
50
|
+
"setAuthMethod",
|
|
51
|
+
"getAuthMethod",
|
|
52
|
+
// Storage
|
|
53
|
+
"saveCredentials",
|
|
54
|
+
"getIsLoggedIn",
|
|
55
|
+
// Getters
|
|
56
|
+
"getCurrentUser",
|
|
57
|
+
];
|
|
58
|
+
const missingMethods = [];
|
|
59
|
+
publicMethods.forEach((method) => {
|
|
60
|
+
if (typeof refactoredCore[method] !== "function") {
|
|
61
|
+
missingMethods.push(method);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (missingMethods.length > 0) {
|
|
65
|
+
throw new Error(`Missing methods in refactored implementation: ${missingMethods.join(", ")}`);
|
|
66
|
+
}
|
|
67
|
+
console.log("✅ API compatibility test passed - all public methods are present");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Test that the refactored implementation maintains the same static properties
|
|
71
|
+
*/
|
|
72
|
+
export function testStaticProperties() {
|
|
73
|
+
if (ShogunCore.API_VERSION !== OriginalShogunCore.API_VERSION) {
|
|
74
|
+
throw new Error("API_VERSION mismatch between implementations");
|
|
75
|
+
}
|
|
76
|
+
console.log("✅ Static properties test passed");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Run all compatibility tests
|
|
80
|
+
*/
|
|
81
|
+
export function runCompatibilityTests() {
|
|
82
|
+
try {
|
|
83
|
+
testStaticProperties();
|
|
84
|
+
testApiCompatibility();
|
|
85
|
+
console.log("🎉 All compatibility tests passed! The refactored implementation is ready.");
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error("❌ Compatibility test failed:", error);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -394,7 +394,7 @@ export class NostrConnectorPlugin extends BasePlugin {
|
|
|
394
394
|
// Set authentication method to nostr before signup
|
|
395
395
|
core.setAuthMethod("nostr");
|
|
396
396
|
// Usa le chiavi derivate per signup
|
|
397
|
-
const signupResult = await core.signUp(credentials.username,
|
|
397
|
+
const signupResult = await core.signUp(credentials.username, undefined, k);
|
|
398
398
|
if (signupResult.success) {
|
|
399
399
|
// Dopo la creazione, autentica subito
|
|
400
400
|
const authResult = await core.login(credentials.username, "", k);
|
|
@@ -372,7 +372,7 @@ export class OAuthPlugin extends BasePlugin {
|
|
|
372
372
|
return loginResult;
|
|
373
373
|
}
|
|
374
374
|
// If login fails, try signup
|
|
375
|
-
const signupResult = await this.core.signUp(username,
|
|
375
|
+
const signupResult = await this.core.signUp(username, undefined, k);
|
|
376
376
|
if (signupResult.success) {
|
|
377
377
|
// Immediately login after signup
|
|
378
378
|
const postSignupLogin = await this.core.login(username, "", k);
|