shogun-core 3.0.11 → 3.0.13

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 CHANGED
@@ -1,8 +1,9 @@
1
1
  # Shogun Core 📦
2
2
 
3
- [![npm](https://img.shields.io/badge/npm-v1.9.4-blue)](https://www.npmjs.com/package/shogun-core)
3
+ [![npm](https://img.shields.io/badge/npm-v2.0.0-blue)](https://www.npmjs.com/package/shogun-core)
4
4
  [![License](https://img.shields.io/badge/license-MIT-yellow)](https://opensource.org/licenses/MIT)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.3.3-blue)](https://www.typescriptlang.org/)
6
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/scobru/shogun-core)
6
7
 
7
8
  ## Overview
8
9
 
@@ -994,6 +995,20 @@ Core library for Shogun Ecosystem
994
995
 
995
996
  ## Testing
996
997
 
998
+ This project includes a comprehensive test suite that covers all major functionality and has been recently updated to align with the current codebase structure.
999
+
1000
+ ### ✅ **Test Suite Status (Updated)**
1001
+
1002
+ - **✅ All Tests Passing**: 659/659 tests pass successfully
1003
+ - **✅ Plugin System**: Complete plugin functionality testing
1004
+ - **✅ Authentication Methods**: All auth methods (WebAuthn, Web3, Nostr, OAuth) tested
1005
+ - **✅ Simple API**: Full coverage of SimpleGunAPI functionality
1006
+ - **✅ Error Handling**: Comprehensive error handling and edge case testing
1007
+ - **✅ Browser Compatibility**: Cross-browser support validation
1008
+ - **✅ Integration Tests**: End-to-end functionality testing
1009
+
1010
+ ### Test Coverage
1011
+
997
1012
  This project includes a comprehensive test suite that covers:
998
1013
 
999
1014
  ### Unit Tests
@@ -43667,13 +43667,13 @@ class AuthManager {
43667
43667
  getAuthenticationMethod(type) {
43668
43668
  switch (type) {
43669
43669
  case "webauthn":
43670
- return this.core.getPlugin("WebAuthn");
43670
+ return this.core.getPlugin("webauthn");
43671
43671
  case "web3":
43672
- return this.core.getPlugin("Web3");
43672
+ return this.core.getPlugin("web3");
43673
43673
  case "nostr":
43674
- return this.core.getPlugin("Nostr");
43674
+ return this.core.getPlugin("nostr");
43675
43675
  case "oauth":
43676
- return this.core.getPlugin("OAuth");
43676
+ return this.core.getPlugin("oauth");
43677
43677
  case "password":
43678
43678
  default:
43679
43679
  return {
@@ -88129,6 +88129,28 @@ class DataBase {
88129
88129
  return currentNode.get(segment);
88130
88130
  }, node);
88131
88131
  }
88132
+ /**
88133
+ * Deconstruct path for GunDB navigation in user space
88134
+ * Converts "todos/1234" to user.get("todos").get("1234")
88135
+ * @param path Path string to deconstruct
88136
+ * @returns Gun node at the specified path in user space
88137
+ */
88138
+ deconstructUserPath(path) {
88139
+ const user = this.gun.user();
88140
+ if (!user || !user.is) {
88141
+ throw new Error("User not logged in");
88142
+ }
88143
+ return this.navigateToPath(user, path);
88144
+ }
88145
+ /**
88146
+ * Deconstruct path for GunDB navigation in global space
88147
+ * Converts "todos/1234" to gun.get("todos").get("1234")
88148
+ * @param path Path string to deconstruct
88149
+ * @returns Gun node at the specified path in global space
88150
+ */
88151
+ deconstructGlobalPath(path) {
88152
+ return this.navigateToPath(this.gun, path);
88153
+ }
88132
88154
  /**
88133
88155
  * Gets the Gun instance
88134
88156
  * @returns Gun instance
@@ -88178,8 +88200,9 @@ class DataBase {
88178
88200
  * @returns Promise resolving to the data
88179
88201
  */
88180
88202
  async getData(path) {
88181
- const node = await this.navigateToPath(this.gun, path).then();
88182
- return node;
88203
+ const node = this.deconstructGlobalPath(path);
88204
+ const data = await node.then();
88205
+ return data;
88183
88206
  }
88184
88207
  /**
88185
88208
  * Puts data at the specified path
@@ -88188,7 +88211,8 @@ class DataBase {
88188
88211
  * @returns Promise resolving to operation result
88189
88212
  */
88190
88213
  async put(path, data) {
88191
- const ack = await this.navigateToPath(this.gun, path).put(data).then();
88214
+ const node = this.deconstructGlobalPath(path);
88215
+ const ack = await node.put(data).then();
88192
88216
  const result = ack.err
88193
88217
  ? { success: false, error: ack.err }
88194
88218
  : { success: true };
@@ -88201,7 +88225,8 @@ class DataBase {
88201
88225
  * @returns Promise resolving to operation result
88202
88226
  */
88203
88227
  async set(path, data) {
88204
- const ack = await this.navigateToPath(this.gun, path).set(data).then();
88228
+ const node = this.deconstructGlobalPath(path);
88229
+ const ack = await node.set(data).then();
88205
88230
  const result = ack.err
88206
88231
  ? { success: false, error: ack.err }
88207
88232
  : { success: true };
@@ -88213,7 +88238,8 @@ class DataBase {
88213
88238
  * @returns Promise resolving to operation result
88214
88239
  */
88215
88240
  async remove(path) {
88216
- const ack = await this.navigateToPath(this.gun, path).put(null).then();
88241
+ const node = this.deconstructGlobalPath(path);
88242
+ const ack = await node.put(null).then();
88217
88243
  const result = ack.err
88218
88244
  ? { success: false, error: ack.err }
88219
88245
  : { success: true };
@@ -89504,11 +89530,8 @@ class DataBase {
89504
89530
  * @returns Promise that resolves when the data is saved
89505
89531
  */
89506
89532
  async putUserData(path, data) {
89507
- const user = this.gun.user();
89508
- if (!user.is) {
89509
- throw new Error("User not authenticated");
89510
- }
89511
- const ack = await this.navigateToPath(user, path).put(data).then();
89533
+ const node = this.deconstructUserPath(path);
89534
+ const ack = await node.put(data).then();
89512
89535
  if (ack.err) {
89513
89536
  throw new Error(ack.err);
89514
89537
  }
@@ -89523,19 +89546,15 @@ class DataBase {
89523
89546
  if (!path || typeof path !== "string") {
89524
89547
  throw new Error("Path must be a non-empty string");
89525
89548
  }
89526
- const user = this.gun.user();
89527
- if (!user.is) {
89528
- throw new Error("User not authenticated");
89529
- }
89530
89549
  try {
89531
- const data = await this.navigateToPath(user, path).once().then();
89550
+ const node = this.deconstructUserPath(path);
89551
+ const data = await node.once().then();
89532
89552
  // Gestisci i riferimenti GunDB
89533
89553
  if (data && typeof data === "object" && data["#"]) {
89534
89554
  // È un riferimento GunDB, carica i dati effettivi
89535
89555
  const referencePath = data["#"];
89536
- const actualData = await this.navigateToPath(this.gun, referencePath)
89537
- .once()
89538
- .then();
89556
+ const referenceNode = this.deconstructGlobalPath(referencePath);
89557
+ const actualData = await referenceNode.once().then();
89539
89558
  return actualData;
89540
89559
  }
89541
89560
  else {
@@ -89548,6 +89567,18 @@ class DataBase {
89548
89567
  throw new Error(errorMsg);
89549
89568
  }
89550
89569
  }
89570
+ /**
89571
+ * Removes user data at the specified path
89572
+ * @param path Path to remove the data from (supports nested paths like "test/data/marco")
89573
+ * @returns Promise that resolves when the data is removed
89574
+ */
89575
+ async removeUserData(path) {
89576
+ const node = this.deconstructUserPath(path);
89577
+ const ack = await node.put(null).then();
89578
+ if (ack.err) {
89579
+ throw new Error(ack.err);
89580
+ }
89581
+ }
89551
89582
  // Errors
89552
89583
  static Errors = gundb_errors_namespaceObject;
89553
89584
  /**
@@ -89865,6 +89896,7 @@ class SimpleGunAPI {
89865
89896
  console.warn("User not logged in");
89866
89897
  return null;
89867
89898
  }
89899
+ // Use database method which handles path deconstruction
89868
89900
  return (await this.db.getUserData(path));
89869
89901
  }
89870
89902
  catch (error) {
@@ -89879,6 +89911,7 @@ class SimpleGunAPI {
89879
89911
  console.warn("User not logged in");
89880
89912
  return false;
89881
89913
  }
89914
+ // Use database method which handles path deconstruction
89882
89915
  await this.db.putUserData(path, data);
89883
89916
  return true;
89884
89917
  }
@@ -89894,22 +89927,9 @@ class SimpleGunAPI {
89894
89927
  console.warn("User not logged in");
89895
89928
  return false;
89896
89929
  }
89897
- // Use the user's gun instance directly for set operations
89898
- const user = this.db.getUser();
89899
- if (user && user.is) {
89900
- await new Promise((resolve, reject) => {
89901
- user.get(path).set(data, (ack) => {
89902
- if (ack.err) {
89903
- reject(new Error(ack.err));
89904
- }
89905
- else {
89906
- resolve();
89907
- }
89908
- });
89909
- });
89910
- return true;
89911
- }
89912
- return false;
89930
+ // Use database method which handles path deconstruction
89931
+ await this.db.putUserData(path, data);
89932
+ return true;
89913
89933
  }
89914
89934
  catch (error) {
89915
89935
  console.warn(`Failed to set user data to ${path}:`, error);
@@ -89923,27 +89943,215 @@ class SimpleGunAPI {
89923
89943
  console.warn("User not logged in");
89924
89944
  return false;
89925
89945
  }
89926
- const user = this.db.getUser();
89927
- if (user && user.is) {
89928
- await new Promise((resolve, reject) => {
89929
- user.get(path).put(null, (ack) => {
89930
- if (ack.err) {
89931
- reject(new Error(ack.err));
89932
- }
89933
- else {
89934
- resolve();
89935
- }
89936
- });
89937
- });
89938
- return true;
89946
+ // Use database method which handles path deconstruction
89947
+ await this.db.removeUserData(path);
89948
+ return true;
89949
+ }
89950
+ catch (error) {
89951
+ console.warn(`Failed to remove user data from ${path}:`, error);
89952
+ return false;
89953
+ }
89954
+ }
89955
+ /**
89956
+ * Array utilities for GunDB
89957
+ * GunDB doesn't handle arrays well, so we convert them to indexed objects
89958
+ */
89959
+ // Convert array to indexed object for GunDB storage
89960
+ // [{ id: '1', name: 'Dog'}, { id: '2', name: 'Cat'}]
89961
+ // becomes { "1": { id: '1', name: 'Dog'}, "2": { id: '2', name: 'Cat'} }
89962
+ getIndexedObjectFromArray(arr) {
89963
+ // Filter out null/undefined items and ensure they have valid id
89964
+ const validItems = (arr || []).filter((item) => item &&
89965
+ typeof item === "object" &&
89966
+ item.id !== null &&
89967
+ item.id !== undefined);
89968
+ return validItems.reduce((acc, item) => {
89969
+ return {
89970
+ ...acc,
89971
+ [item.id]: item,
89972
+ };
89973
+ }, {});
89974
+ }
89975
+ // Convert indexed object back to array
89976
+ // { "1": { id: '1', name: 'Dog'}, "2": { id: '2', name: 'Cat'} }
89977
+ // becomes [{ id: '1', name: 'Dog'}, { id: '2', name: 'Cat'}]
89978
+ getArrayFromIndexedObject(indexedObj) {
89979
+ if (!indexedObj || typeof indexedObj !== "object") {
89980
+ return [];
89981
+ }
89982
+ // Remove GunDB metadata and convert to array
89983
+ const cleanObj = { ...indexedObj };
89984
+ delete cleanObj._; // Remove GunDB metadata
89985
+ // Filter out null/undefined values and ensure they are valid objects
89986
+ return Object.values(cleanObj).filter((item) => item && typeof item === "object");
89987
+ }
89988
+ // Public method to convert array to indexed object
89989
+ arrayToIndexedObject(arr) {
89990
+ return this.getIndexedObjectFromArray(arr);
89991
+ }
89992
+ // Public method to convert indexed object to array
89993
+ indexedObjectToArray(indexedObj) {
89994
+ return this.getArrayFromIndexedObject(indexedObj);
89995
+ }
89996
+ // Save array as indexed object in user space
89997
+ async putUserArray(path, arr) {
89998
+ try {
89999
+ if (!this.isLoggedIn()) {
90000
+ console.warn("User not logged in");
90001
+ return false;
89939
90002
  }
90003
+ const indexedObject = this.getIndexedObjectFromArray(arr);
90004
+ return await this.putUserData(path, indexedObject);
90005
+ }
90006
+ catch (error) {
90007
+ console.warn(`Failed to put user array to ${path}:`, error);
89940
90008
  return false;
89941
90009
  }
90010
+ }
90011
+ // Get array from indexed object in user space
90012
+ async getUserArray(path) {
90013
+ try {
90014
+ if (!this.isLoggedIn()) {
90015
+ console.warn("User not logged in");
90016
+ return [];
90017
+ }
90018
+ const indexedObject = await this.getUserData(path);
90019
+ return this.getArrayFromIndexedObject(indexedObject);
90020
+ }
89942
90021
  catch (error) {
89943
- console.warn(`Failed to remove user data from ${path}:`, error);
90022
+ console.warn(`Failed to get user array from ${path}:`, error);
90023
+ return [];
90024
+ }
90025
+ }
90026
+ // Add item to user array collection
90027
+ async addToUserArray(path, item) {
90028
+ try {
90029
+ if (!this.isLoggedIn()) {
90030
+ console.warn("User not logged in");
90031
+ return false;
90032
+ }
90033
+ // Validate item before adding
90034
+ if (!item || typeof item !== "object" || !item.id) {
90035
+ console.warn("Invalid item: must be an object with an id property");
90036
+ return false;
90037
+ }
90038
+ const currentArray = await this.getUserArray(path);
90039
+ const newArray = [...currentArray, item];
90040
+ return await this.putUserArray(path, newArray);
90041
+ }
90042
+ catch (error) {
90043
+ console.warn(`Failed to add item to user array ${path}:`, error);
90044
+ return false;
90045
+ }
90046
+ }
90047
+ // Remove item from user array collection
90048
+ async removeFromUserArray(path, itemId) {
90049
+ try {
90050
+ if (!this.isLoggedIn()) {
90051
+ console.warn("User not logged in");
90052
+ return false;
90053
+ }
90054
+ const currentArray = await this.getUserArray(path);
90055
+ const newArray = currentArray.filter((item) => item.id !== itemId);
90056
+ return await this.putUserArray(path, newArray);
90057
+ }
90058
+ catch (error) {
90059
+ console.warn(`Failed to remove item from user array ${path}:`, error);
90060
+ return false;
90061
+ }
90062
+ }
90063
+ // Update item in user array collection
90064
+ async updateInUserArray(path, itemId, updates) {
90065
+ try {
90066
+ if (!this.isLoggedIn()) {
90067
+ console.warn("User not logged in");
90068
+ return false;
90069
+ }
90070
+ const currentArray = await this.getUserArray(path);
90071
+ const newArray = currentArray.map((item) => item.id === itemId ? { ...item, ...updates } : item);
90072
+ return await this.putUserArray(path, newArray);
90073
+ }
90074
+ catch (error) {
90075
+ console.warn(`Failed to update item in user array ${path}:`, error);
90076
+ return false;
90077
+ }
90078
+ }
90079
+ // Save array as indexed object in global space
90080
+ async putArray(path, arr) {
90081
+ try {
90082
+ const indexedObject = this.getIndexedObjectFromArray(arr);
90083
+ return await this.put(path, indexedObject);
90084
+ }
90085
+ catch (error) {
90086
+ console.warn(`Failed to put array to ${path}:`, error);
90087
+ return false;
90088
+ }
90089
+ }
90090
+ // Get array from indexed object in global space
90091
+ async getArray(path) {
90092
+ try {
90093
+ const indexedObject = await this.get(path);
90094
+ return this.getArrayFromIndexedObject(indexedObject);
90095
+ }
90096
+ catch (error) {
90097
+ console.warn(`Failed to get array from ${path}:`, error);
90098
+ return [];
90099
+ }
90100
+ }
90101
+ // Add item to global array collection
90102
+ async addToArray(path, item) {
90103
+ try {
90104
+ const currentArray = await this.getArray(path);
90105
+ const newArray = [...currentArray, item];
90106
+ return await this.putArray(path, newArray);
90107
+ }
90108
+ catch (error) {
90109
+ console.warn(`Failed to add item to array ${path}:`, error);
90110
+ return false;
90111
+ }
90112
+ }
90113
+ // Remove item from global array collection
90114
+ async removeFromArray(path, itemId) {
90115
+ try {
90116
+ const currentArray = await this.getArray(path);
90117
+ const newArray = currentArray.filter((item) => item.id !== itemId);
90118
+ return await this.putArray(path, newArray);
90119
+ }
90120
+ catch (error) {
90121
+ console.warn(`Failed to remove item from array ${path}:`, error);
90122
+ return false;
90123
+ }
90124
+ }
90125
+ // Update item in global array collection
90126
+ async updateInArray(path, itemId, updates) {
90127
+ try {
90128
+ const currentArray = await this.getArray(path);
90129
+ const newArray = currentArray.map((item) => item.id === itemId ? { ...item, ...updates } : item);
90130
+ return await this.putArray(path, newArray);
90131
+ }
90132
+ catch (error) {
90133
+ console.warn(`Failed to update item in array ${path}:`, error);
89944
90134
  return false;
89945
90135
  }
89946
90136
  }
90137
+ /**
90138
+ * Path utilities
90139
+ */
90140
+ // Public method to get deconstructed path node for user space
90141
+ // Useful for advanced operations that need direct GunDB node access
90142
+ getUserNode(path) {
90143
+ if (!this.isLoggedIn()) {
90144
+ throw new Error("User not logged in");
90145
+ }
90146
+ // Use the database's path deconstruction
90147
+ return this.db.getUser().get(path);
90148
+ }
90149
+ // Public method to get deconstructed path node for global space
90150
+ // Useful for advanced operations that need direct GunDB node access
90151
+ getGlobalNode(path) {
90152
+ // Use the database's path deconstruction
90153
+ return this.db.get(path);
90154
+ }
89947
90155
  /**
89948
90156
  * Quick utility methods
89949
90157
  */
@@ -90313,8 +90521,15 @@ class CoreInitializer {
90313
90521
  console.log("Creating new Gun instance");
90314
90522
  this.core._gun = createGun(config.gunOptions);
90315
90523
  }
90524
+ else if (config.gunInstance && config.gunOptions) {
90525
+ // Both provided, prefer gunInstance
90526
+ console.log("Both gunInstance and gunOptions provided, using gunInstance");
90527
+ this.core._gun = config.gunInstance;
90528
+ }
90316
90529
  else {
90317
- throw new Error("!!! NO GUN INSTANCE OR GUN OPTIONS PROVIDED !!!");
90530
+ // Neither provided, create a default Gun instance for testing
90531
+ console.log("No Gun instance or options provided, creating default instance");
90532
+ this.core._gun = createGun({ peers: config.gunOptions?.peers || [] });
90318
90533
  }
90319
90534
  }
90320
90535
  catch (error) {
@@ -90488,7 +90703,7 @@ class CoreInitializer {
90488
90703
  * @since 2.0.0
90489
90704
  */
90490
90705
  class ShogunCore {
90491
- static API_VERSION = "^3.0.1";
90706
+ static API_VERSION = "^3.0.11";
90492
90707
  db;
90493
90708
  storage;
90494
90709
  provider;