diff --git a/README.md b/README.md
index b65ca118..0abf8bb7 100644
--- a/README.md
+++ b/README.md
@@ -577,7 +577,90 @@ This enum can be used by consumers of the library to identify the different type
Brainy provides an event pipeline that allows registering and executing multiple augmentations of each type. The pipeline supports different execution modes and provides a flexible way to manage augmentations.
-#### Using the Pipeline
+#### Using External Augmentation Plugins
+
+Brainy provides a plugin loader that makes it easy to load and configure augmentation plugins from external npm packages. This allows you to extend Brainy's capabilities with specialized augmentations developed by third parties or maintained in separate packages.
+
+```typescript
+import {
+ BrainyData,
+ configureAndStartPipeline,
+ createSensePluginConfig,
+ createConduitPluginConfig,
+ AugmentationType
+} from '@soulcraft/brainy';
+
+// Create a new BrainyData instance
+const db = new BrainyData();
+await db.init();
+
+// Configure and start the augmentation pipeline with external plugins
+// Note: These are example plugin names. You would need to install these packages via npm first.
+const result = await configureAndStartPipeline([
+ // Sense augmentations (will be loaded first)
+ createSensePluginConfig('@example/text-sense-augmentation', {
+ language: 'english',
+ enableNER: true
+ }),
+
+ // Conduit augmentations for two-way synchronization
+ createConduitPluginConfig('@example/websocket-conduit', {
+ url: 'wss://example.com/sync',
+ reconnectInterval: 5000
+ }),
+
+ // Other augmentation types
+ {
+ plugin: '@example/memory-augmentation',
+ config: { storageType: 'persistent' },
+ type: AugmentationType.MEMORY
+ }
+], {
+ // Optional: Customize the loading options
+ useDefaultPipeline: true, // Use the default pipeline instance
+ initializeAfterLoading: true, // Initialize augmentations after loading
+ augmentationOrder: [ // Custom order (Sense first, as recommended)
+ AugmentationType.SENSE,
+ AugmentationType.CONDUIT,
+ AugmentationType.MEMORY,
+ AugmentationType.COGNITION,
+ AugmentationType.PERCEPTION,
+ AugmentationType.DIALOG,
+ AugmentationType.ACTIVATION
+ ]
+});
+
+// Get the pipeline instance
+const { pipeline } = result;
+
+// Use the pipeline to execute augmentations
+const senseResults = await pipeline.executeSensePipeline(
+ 'processRawData',
+ ['This is some example text to process', 'text']
+);
+
+// Process the results and insert data into BrainyData
+for (const resultPromise of senseResults) {
+ const result = await resultPromise;
+ if (result.success) {
+ // Insert the extracted nouns and verbs into BrainyData according to graphTypes
+ for (const noun of result.data.nouns) {
+ await db.add(noun, { type: 'noun' });
+ }
+ }
+}
+```
+
+The plugin loader provides several functions for working with external plugins:
+
+- `loadPlugins(plugins, options)`: Loads augmentation plugins from external npm packages
+- `configureAndStartPipeline(plugins, options)`: Configures and starts the augmentation pipeline with the specified plugins
+- `createSensePluginConfig(plugin, config)`: Creates a plugin configuration for a sense augmentation
+- `createConduitPluginConfig(plugin, config)`: Creates a plugin configuration for a conduit augmentation
+
+For more details, see the [externalPlugins.js](examples/externalPlugins.js) example.
+
+#### Using the Pipeline Directly
```typescript
import { augmentationPipeline, ExecutionMode, AugmentationType } from '@soulcraft/brainy';
diff --git a/examples/demo.html b/examples/demo.html
index b18833e1..2fcede3e 100644
--- a/examples/demo.html
+++ b/examples/demo.html
@@ -1,15 +1,124 @@
-
+
-
-
- Soulcraft Brainy - Demo
-
-
+
+
+ Brainy - Vector and Graph Database Demo
+
+
+
+ Brainy - Vector and Graph Database Demo
+ This demo shows how to use Brainy as both a vector database (with embeddings and similarity search) and a graph database (with GraphNoun nodes and GraphVerb relationships).
+
+
+
+
1. Initialize Database
+
Initialize BrainyData
+
+
+
+
+
2. Configure Pipeline
+
Setup Augmentation Pipeline
+
+
+
+
+
3. Add Sample Data
+
Add Sample Vectors
+
+
+
+
+
4. Vector Search
+
+
+ Select a vector
+
+
+ Search
+
+
// Search results will appear here
+
+
+
+
+
+
6. Graph Operations
+
+ Add Relationships
+ Get Relationships
+
+
// Graph operation results will appear here
+
+
+
+
Console Output
+
// Output will appear here
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
- Soulcraft Brainy
-
-
- A vector database that runs in a browser or Node.js and utilizes
- Origin Private File System (OPFS) for storage, with HNSW
- (Hierarchical Navigable Small World) for efficient vector
- indexing.
-
-
-
-
- Cross-platform
-
-
-
- Persistent storage
-
-
-
- Efficient vector search
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Distance Function
-
- Cosine Distance
- Euclidean Distance
- Manhattan Distance
- Dot Product Distance
-
-
-
-
-
-
-
- Initialize Database
-
-
- Clear Database
-
-
- Database Size
-
-
-
-
-
-
-
-
-
-
-
-
-
- Add Sample Vectors
-
-
-
- Custom Vector
-
-
-
-
-
- Add Custom Vector
-
-
-
-
-
- Add Text (Auto-Embedded)
-
-
-
-
- Add Text with Auto-Embedding
-
-
-
-
-
-
-
-
-
-
-
Search Vector
-
- Select a vector
-
-
- Results (k)
-
-
-
- Search
-
-
-
-
-
Vector Operations
-
- Select a vector
-
-
-
- Get
-
-
- Update
-
-
- Delete
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Search Term
-
-
- Results (k)
-
-
-
- Text Search
-
-
-
-
-
-
-
-
-
-
-
-
Create Edge
-
-
-
- Source Node
-
-
-
-
- Target Node
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Graph visualization will appear here
-
-
-
- Visualize Graph
-
-
-
-
-
-
-
-
-
-
-
-
// Output will appear here
-
-
-
-
-
-
-
-
+ // Define relationships to create
+ const relationships = [
+ { source: 'cat', target: 'dog', type: VerbType.Controls, metadata: { description: 'Cats often dominate dogs in households' } },
+ { source: 'tiger', target: 'cat', type: 'relatedTo', metadata: { description: 'Tigers are related to domestic cats' } },
+ { source: 'lion', target: 'tiger', type: VerbType.Controls, metadata: { description: 'Lions are often dominant over tigers in shared territories' } },
+ { source: 'shark', target: 'fish', type: VerbType.Controls, metadata: { description: 'Sharks are predators of many fish species' } },
+ { source: 'eagle', target: 'bird', type: 'relatedTo', metadata: { description: 'Eagles are a type of bird' } },
+ { source: 'eagle', target: 'fish', type: VerbType.Controls, metadata: { description: 'Eagles hunt and eat fish' } }
+ ];
+
+ // Create each relationship
+ for (const rel of relationships) {
+ const sourceId = ids[rel.source];
+ const targetId = ids[rel.target];
+
+ if (!sourceId || !targetId) {
+ log(`Missing ID for ${rel.source} or ${rel.target}`);
+ continue;
+ }
+
+ // Create the edge
+ const edgeId = await db.addEdge(sourceId, targetId, null, {
+ type: rel.type,
+ metadata: {
+ verb: rel.type,
+ label: `${rel.source} ${rel.type} ${rel.target}`,
+ data: rel.metadata
+ }
+ });
+
+ // Store the relationship ID
+ relationshipIds[`${rel.source}_${rel.type}_${rel.target}`] = edgeId;
+ }
+
+ log(`Added ${Object.keys(relationshipIds).length} relationships:\n${JSON.stringify(relationshipIds, null, 2)}`);
+ graphResults.textContent = `Added ${Object.keys(relationshipIds).length} relationships between nodes`;
+
+ // Enable the get relationships button
+ getRelationsBtn.disabled = false;
+ addRelationsBtn.disabled = true;
+ } catch (error) {
+ log(`Error adding relationships: ${error.message}`);
+ graphResults.textContent = `Error: ${error.message}`;
+ }
+ });
+
+ // Get Relationships
+ getRelationsBtn.addEventListener('click', async () => {
+ try {
+ log('Getting all relationships...');
+
+ // Get all edges
+ const edges = await db.getAllEdges();
+
+ // Format edges for display
+ const formattedEdges = edges.map(edge => {
+ // Find the source and target names
+ const sourceName = Object.entries(ids).find(([name, id]) => id === edge.sourceId)?.[0] || 'unknown';
+ const targetName = Object.entries(ids).find(([name, id]) => id === edge.targetId)?.[0] || 'unknown';
+
+ return {
+ id: edge.id,
+ source: sourceName,
+ target: targetName,
+ type: edge.type,
+ metadata: edge.metadata
+ };
+ });
+
+ // Display results
+ graphResults.textContent = JSON.stringify(formattedEdges, null, 2);
+ log(`Retrieved ${edges.length} relationships`);
+ } catch (error) {
+ log(`Error getting relationships: ${error.message}`);
+ graphResults.textContent = `Error: ${error.message}`;
+ }
+ });
+
+ // Initial log message
+ log('Welcome to the Brainy Vector Search Demo!\nClick "Initialize BrainyData" to begin.');
+
diff --git a/examples/externalPlugins.js b/examples/externalPlugins.js
new file mode 100644
index 00000000..c8ebc7bb
--- /dev/null
+++ b/examples/externalPlugins.js
@@ -0,0 +1,158 @@
+/**
+ * Example: Using External Augmentation Plugins with Brainy
+ *
+ * This example demonstrates how to load and configure external augmentation plugins
+ * with the Brainy library. It shows how to use the plugin loader to integrate
+ * externally installed augmentation plugins into the Brainy system.
+ */
+
+import {
+ BrainyData,
+ configureAndStartPipeline,
+ createSensePluginConfig,
+ createConduitPluginConfig,
+ AugmentationType
+} from '../dist/index.js';
+
+// Create a new BrainyData instance
+const db = new BrainyData();
+await db.init();
+
+console.log('BrainyData initialized successfully');
+
+// Configure and start the augmentation pipeline with external plugins
+// Note: These are example plugin names. You would need to install these packages via npm first.
+const result = await configureAndStartPipeline([
+ // Sense augmentations (will be loaded first)
+ createSensePluginConfig('@example/text-sense-augmentation', {
+ language: 'english',
+ enableNER: true
+ }),
+ createSensePluginConfig('@example/image-sense-augmentation', {
+ modelSize: 'medium',
+ detectObjects: true
+ }),
+
+ // Conduit augmentations for two-way synchronization
+ createConduitPluginConfig('@example/websocket-conduit', {
+ url: 'wss://example.com/sync',
+ reconnectInterval: 5000
+ }),
+ createConduitPluginConfig('@example/webrtc-conduit', {
+ iceServers: [{ urls: 'stun:stun.example.com:19302' }],
+ peerConfig: { reliable: true }
+ }),
+
+ // Other augmentation types
+ {
+ plugin: '@example/memory-augmentation',
+ config: { storageType: 'persistent' },
+ type: AugmentationType.MEMORY
+ }
+], {
+ // Optional: Customize the loading options
+ useDefaultPipeline: true, // Use the default pipeline instance
+ initializeAfterLoading: true, // Initialize augmentations after loading
+ augmentationOrder: [ // Custom order (Sense first, as recommended)
+ AugmentationType.SENSE,
+ AugmentationType.CONDUIT,
+ AugmentationType.MEMORY,
+ AugmentationType.COGNITION,
+ AugmentationType.PERCEPTION,
+ AugmentationType.DIALOG,
+ AugmentationType.ACTIVATION
+ ]
+});
+
+// Check for any errors during loading
+if (result.errors.length > 0) {
+ console.error('Errors occurred while loading plugins:');
+ result.errors.forEach(error => console.error(` - ${error.message}`));
+} else {
+ console.log('All plugins loaded successfully');
+}
+
+// Get the pipeline instance
+const { pipeline } = result;
+
+// Get all loaded augmentations by type
+console.log('Loaded augmentations:');
+for (const [type, augmentations] of result.augmentations.entries()) {
+ if (augmentations.length > 0) {
+ console.log(`${type}: ${augmentations.map(a => a.name).join(', ')}`);
+ }
+}
+
+// Example: Using a sense augmentation to process raw data
+try {
+ const senseResults = await pipeline.executeSensePipeline(
+ 'processRawData',
+ ['This is some example text to process', 'text']
+ );
+
+ // Process the results
+ for (const resultPromise of senseResults) {
+ const result = await resultPromise;
+ if (result.success) {
+ console.log('Processed data:', result.data);
+
+ // Insert the extracted nouns and verbs into BrainyData according to graphTypes
+ for (const noun of result.data.nouns) {
+ await db.add(noun, { type: 'noun' });
+ }
+
+ for (const verb of result.data.verbs) {
+ await db.add(verb, { type: 'verb' });
+ }
+ } else {
+ console.error('Error processing data:', result.error);
+ }
+ }
+} catch (error) {
+ console.error('Error executing sense pipeline:', error);
+}
+
+// Example: Using a conduit augmentation for two-way synchronization
+try {
+ // Establish a connection
+ const connectionResults = await pipeline.executeConduitPipeline(
+ 'establishConnection',
+ ['external-system', { apiKey: 'your-api-key' }]
+ );
+
+ // Process the results
+ for (const resultPromise of connectionResults) {
+ const result = await resultPromise;
+ if (result.success) {
+ console.log('Connection established:', result.data);
+
+ // Read data from the external system
+ const readResults = await pipeline.executeConduitPipeline(
+ 'readData',
+ [{ query: 'get-latest-data' }]
+ );
+
+ // Process the read results
+ for (const readResultPromise of readResults) {
+ const readResult = await readResultPromise;
+ if (readResult.success) {
+ console.log('Data read from external system:', readResult.data);
+
+ // Write data to the external system
+ await pipeline.executeConduitPipeline(
+ 'writeData',
+ [{ updatedData: 'example' }]
+ );
+ }
+ }
+ } else {
+ console.error('Error establishing connection:', result.error);
+ }
+ }
+} catch (error) {
+ console.error('Error executing conduit pipeline:', error);
+}
+
+// Shut down the pipeline when done
+await pipeline.shutDown();
+console.log('Pipeline shut down successfully');
diff --git a/package-lock.json b/package-lock.json
index 583fc26b..a8c1f0c2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"@tensorflow/tfjs-backend-cpu": "^4.22.0",
"@tensorflow/tfjs-core": "^4.22.0",
"@tensorflow/tfjs-layers": "^4.22.0",
+ "firebase": "^10.8.0",
"uuid": "^9.0.0"
},
"devDependencies": {
@@ -648,6 +649,638 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@firebase/analytics": {
+ "version": "0.10.8",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.8.tgz",
+ "integrity": "sha512-CVnHcS4iRJPqtIDc411+UmFldk0ShSK3OB+D0bKD8Ck5Vro6dbK5+APZpkuWpbfdL359DIQUnAaMLE+zs/PVyA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/installations": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/analytics-compat": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.14.tgz",
+ "integrity": "sha512-unRVY6SvRqfNFIAA/kwl4vK+lvQAL2HVcgu9zTrUtTyYDmtIt/lOuHJynBMYEgLnKm39YKBDhtqdapP2e++ASw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/analytics": "0.10.8",
+ "@firebase/analytics-types": "0.8.2",
+ "@firebase/component": "0.6.9",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/analytics-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz",
+ "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/app": {
+ "version": "0.10.13",
+ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.13.tgz",
+ "integrity": "sha512-OZiDAEK/lDB6xy/XzYAyJJkaDqmQ+BCtOEPLqFvxWKUz5JbBmej7IiiRHdtiIOD/twW7O5AxVsfaaGA/V1bNsA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/app-check": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.8.tgz",
+ "integrity": "sha512-O49RGF1xj7k6BuhxGpHmqOW5hqBIAEbt2q6POW0lIywx7emYtzPDeQI+ryQpC4zbKX646SoVZ711TN1DBLNSOQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-compat": {
+ "version": "0.3.15",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.15.tgz",
+ "integrity": "sha512-zFIvIFFNqDXpOT2huorz9cwf56VT3oJYRFjSFYdSbGYEJYEaXjLJbfC79lx/zjx4Fh+yuN8pry3TtvwaevrGbg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check": "0.8.8",
+ "@firebase/app-check-types": "0.5.2",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-interop-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
+ "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/app-check-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz",
+ "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/app-compat": {
+ "version": "0.2.43",
+ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.43.tgz",
+ "integrity": "sha512-HM96ZyIblXjAC7TzE8wIk2QhHlSvksYkQ4Ukh1GmEenzkucSNUmUX4QvoKrqeWsLEQ8hdcojABeCV8ybVyZmeg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app": "0.10.13",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/app-types": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz",
+ "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/auth": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.9.tgz",
+ "integrity": "sha512-yLD5095kVgDw965jepMyUrIgDklD6qH/BZNHeKOgvu7pchOKNjVM+zQoOVYJIKWMWOWBq8IRNVU6NXzBbozaJg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x",
+ "@react-native-async-storage/async-storage": "^1.18.1"
+ },
+ "peerDependenciesMeta": {
+ "@react-native-async-storage/async-storage": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@firebase/auth-compat": {
+ "version": "0.5.14",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.14.tgz",
+ "integrity": "sha512-2eczCSqBl1KUPJacZlFpQayvpilg3dxXLy9cSMTKtQMTQSmondUtPI47P3ikH3bQAXhzKLOE+qVxJ3/IRtu9pw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/auth": "1.7.9",
+ "@firebase/auth-types": "0.12.2",
+ "@firebase/component": "0.6.9",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/auth-interop-types": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz",
+ "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/auth-types": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz",
+ "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/component": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.9.tgz",
+ "integrity": "sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/data-connect": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.1.0.tgz",
+ "integrity": "sha512-vSe5s8dY13ilhLnfY0eYRmQsdTbH7PUFZtBbqU6JVX/j8Qp9A6G5gG6//ulbX9/1JFOF1IWNOne9c8S/DOCJaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/database": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.8.tgz",
+ "integrity": "sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-compat": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.8.tgz",
+ "integrity": "sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/database": "1.0.8",
+ "@firebase/database-types": "1.0.5",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-types": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.5.tgz",
+ "integrity": "sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-types": "0.9.2",
+ "@firebase/util": "1.10.0"
+ }
+ },
+ "node_modules/@firebase/firestore": {
+ "version": "4.7.3",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.3.tgz",
+ "integrity": "sha512-NwVU+JPZ/3bhvNSJMCSzfcBZZg8SUGyzZ2T0EW3/bkUeefCyzMISSt/TTIfEHc8cdyXGlMqfGe3/62u9s74UEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "@firebase/webchannel-wrapper": "1.0.1",
+ "@grpc/grpc-js": "~1.9.0",
+ "@grpc/proto-loader": "^0.7.8",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-compat": {
+ "version": "0.3.38",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.38.tgz",
+ "integrity": "sha512-GoS0bIMMkjpLni6StSwRJarpu2+S5m346Na7gr9YZ/BZ/W3/8iHGNr9PxC+f0rNZXqS4fGRn88pICjrZEgbkqQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/firestore": "4.7.3",
+ "@firebase/firestore-types": "3.0.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz",
+ "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/functions": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.8.tgz",
+ "integrity": "sha512-Lo2rTPDn96naFIlSZKVd1yvRRqqqwiJk7cf9TZhUerwnPKgBzXy+aHE22ry+6EjCaQusUoNai6mU6p+G8QZT1g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.9",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-compat": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.14.tgz",
+ "integrity": "sha512-dZ0PKOKQFnOlMfcim39XzaXonSuPPAVuzpqA4ONTIdyaJK/OnBaIEVs/+BH4faa1a2tLeR+Jy15PKqDRQoNIJw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/functions": "0.11.8",
+ "@firebase/functions-types": "0.6.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-types": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz",
+ "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/installations": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.9.tgz",
+ "integrity": "sha512-hlT7AwCiKghOX3XizLxXOsTFiFCQnp/oj86zp1UxwDGmyzsyoxtX+UIZyVyH/oBF5+XtblFG9KZzZQ/h+dpy+Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/util": "1.10.0",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-compat": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.9.tgz",
+ "integrity": "sha512-2lfdc6kPXR7WaL4FCQSQUhXcPbI7ol3wF+vkgtU25r77OxPf8F/VmswQ7sgIkBBWtymn5ZF20TIKtnOj9rjb6w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/installations": "0.6.9",
+ "@firebase/installations-types": "0.5.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz",
+ "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x"
+ }
+ },
+ "node_modules/@firebase/logger": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz",
+ "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/messaging": {
+ "version": "0.12.12",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.12.tgz",
+ "integrity": "sha512-6q0pbzYBJhZEtUoQx7hnPhZvAbuMNuBXKQXOx2YlWhSrlv9N1m0ZzlNpBbu/ItTzrwNKTibdYzUyaaxdWLg+4w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/installations": "0.6.9",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.10.0",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-compat": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.12.tgz",
+ "integrity": "sha512-pKsiUVZrbmRgdImYqhBNZlkKJbqjlPkVdQRZGRbkTyX4OSGKR0F/oJeCt1a8jEg5UnBp4fdVwSWSp4DuCovvEQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/messaging": "0.12.12",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-interop-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz",
+ "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/performance": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.9.tgz",
+ "integrity": "sha512-PnVaak5sqfz5ivhua+HserxTJHtCar/7zM0flCX6NkzBNzJzyzlH4Hs94h2Il0LQB99roBqoE5QT1JqWqcLJHQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/installations": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-compat": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.9.tgz",
+ "integrity": "sha512-dNl95IUnpsu3fAfYBZDCVhXNkASE0uo4HYaEPd2/PKscfTvsgqFAOxfAXzBEDOnynDWiaGUnb5M1O00JQ+3FXA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/performance": "0.6.9",
+ "@firebase/performance-types": "0.2.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz",
+ "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/remote-config": {
+ "version": "0.4.9",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.9.tgz",
+ "integrity": "sha512-EO1NLCWSPMHdDSRGwZ73kxEEcTopAxX1naqLJFNApp4hO8WfKfmEpmjxmP5TrrnypjIf2tUkYaKsfbEA7+AMmA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/installations": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-compat": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.9.tgz",
+ "integrity": "sha512-AxzGpWfWFYejH2twxfdOJt5Cfh/ATHONegTd/a0p5flEzsD5JsxXgfkFToop+mypEL3gNwawxrxlZddmDoNxyA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/remote-config": "0.4.9",
+ "@firebase/remote-config-types": "0.3.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz",
+ "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/storage": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.2.tgz",
+ "integrity": "sha512-fxuJnHshbhVwuJ4FuISLu+/76Aby2sh+44ztjF2ppoe0TELIDxPW6/r1KGlWYt//AD0IodDYYA8ZTN89q8YqUw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-compat": {
+ "version": "0.3.12",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.12.tgz",
+ "integrity": "sha512-hA4VWKyGU5bWOll+uwzzhEMMYGu9PlKQc1w4DWxB3aIErWYzonrZjF0icqNQZbwKNIdh8SHjZlFeB2w6OSsjfg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/storage": "0.13.2",
+ "@firebase/storage-types": "0.8.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz",
+ "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/util": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz",
+ "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/vertexai-preview": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.4.tgz",
+ "integrity": "sha512-EBSqyu9eg8frQlVU9/HjKtHN7odqbh9MtAcVz3WwHj4gLCLOoN9F/o+oxlq3CxvFrd3CNTZwu6d2mZtVlEInng==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x",
+ "@firebase/app-types": "0.x"
+ }
+ },
+ "node_modules/@firebase/webchannel-wrapper": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz",
+ "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.9.15",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
+ "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@grpc/proto-loader": "^0.7.8",
+ "@types/node": ">=12.12.47"
+ },
+ "engines": {
+ "node": "^8.13.0 || >=10.10.0"
+ }
+ },
+ "node_modules/@grpc/proto-loader": {
+ "version": "0.7.15",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+ "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@grpc/proto-loader/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -1200,6 +1833,70 @@
"node": ">= 8"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -2866,6 +3563,18 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@@ -2942,6 +3651,42 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/firebase": {
+ "version": "10.14.1",
+ "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.14.1.tgz",
+ "integrity": "sha512-0KZxU+Ela9rUCULqFsUUOYYkjh7OM1EWdIfG6///MtXd0t2/uUIf0iNV5i0KariMhRQ5jve/OY985nrAXFaZeQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/analytics": "0.10.8",
+ "@firebase/analytics-compat": "0.2.14",
+ "@firebase/app": "0.10.13",
+ "@firebase/app-check": "0.8.8",
+ "@firebase/app-check-compat": "0.3.15",
+ "@firebase/app-compat": "0.2.43",
+ "@firebase/app-types": "0.9.2",
+ "@firebase/auth": "1.7.9",
+ "@firebase/auth-compat": "0.5.14",
+ "@firebase/data-connect": "0.1.0",
+ "@firebase/database": "1.0.8",
+ "@firebase/database-compat": "1.0.8",
+ "@firebase/firestore": "4.7.3",
+ "@firebase/firestore-compat": "0.3.38",
+ "@firebase/functions": "0.11.8",
+ "@firebase/functions-compat": "0.3.14",
+ "@firebase/installations": "0.6.9",
+ "@firebase/installations-compat": "0.2.9",
+ "@firebase/messaging": "0.12.12",
+ "@firebase/messaging-compat": "0.2.12",
+ "@firebase/performance": "0.6.9",
+ "@firebase/performance-compat": "0.2.9",
+ "@firebase/remote-config": "0.4.9",
+ "@firebase/remote-config-compat": "0.2.9",
+ "@firebase/storage": "0.13.2",
+ "@firebase/storage-compat": "0.3.12",
+ "@firebase/util": "1.10.0",
+ "@firebase/vertexai-preview": "0.0.4"
+ }
+ },
"node_modules/flat-cache": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
@@ -3266,6 +4011,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/http-parser-js": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+ "license": "MIT"
+ },
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -3276,6 +4027,12 @@
"node": ">=10.17.0"
}
},
+ "node_modules/idb": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
+ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
+ "license": "ISC"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4336,6 +5093,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "license": "MIT"
+ },
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -4873,6 +5636,36 @@
"node": ">= 6"
}
},
+ "node_modules/protobufjs": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+ "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/protobufjs/node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5450,6 +6243,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -5500,6 +6299,15 @@
"node": ">=14.17"
}
},
+ "node_modules/undici": {
+ "version": "6.19.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz",
+ "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
@@ -5591,6 +6399,29 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
@@ -5703,7 +6534,6 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
diff --git a/package.json b/package.json
index d7da0618..24aea416 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,9 @@
"hnsw",
"opfs",
"origin-private-file-system",
- "embeddings"
+ "embeddings",
+ "graph-database",
+ "streaming-data"
],
"author": "David Snelling (david@soulcraft.com)",
"license": "MIT",
diff --git a/src/brainyData.ts b/src/brainyData.ts
index bcab8c63..5e9ec5fb 100644
--- a/src/brainyData.ts
+++ b/src/brainyData.ts
@@ -6,7 +6,16 @@
import { v4 as uuidv4 } from 'uuid'
import { HNSWIndex } from './hnsw/hnswIndex.js'
import { createStorage } from './storage/opfsStorage.js'
-import { DistanceFunction, Edge, EmbeddingFunction, HNSWConfig, SearchResult, StorageAdapter, Vector, VectorDocument } from './coreTypes.js'
+import {
+ DistanceFunction,
+ Edge,
+ EmbeddingFunction,
+ HNSWConfig,
+ SearchResult,
+ StorageAdapter,
+ Vector,
+ VectorDocument
+} from './coreTypes.js'
import { cosineDistance, defaultEmbeddingFunction, euclideanDistance } from './utils/index.js'
export interface BrainyDataConfig {
@@ -181,9 +190,9 @@ export class BrainyData {
* @returns Array of IDs for the added items
*/
public async addBatch(
- items: Array<{
- vectorOrData: Vector | any;
- metadata?: T
+ items: Array<{
+ vectorOrData: Vector | any;
+ metadata?: T
}>,
options: {
forceEmbed?: boolean // Force using the embedding function even if input is a vector
@@ -550,7 +559,7 @@ export class BrainyData {
/**
* Embed text or data into a vector using the same embedding function used by this instance
* This allows clients to use the same TensorFlow Universal Sentence Encoder throughout their application
- *
+ *
* @param data Text or data to embed
* @returns A promise that resolves to the embedded vector
*/
@@ -568,7 +577,7 @@ export class BrainyData {
/**
* Search for similar documents using a text query
* This is a convenience method that embeds the query text and performs a search
- *
+ *
* @param query Text query to search for
* @param k Number of results to return
* @returns Array of search results
@@ -596,6 +605,54 @@ export class BrainyData {
await this.init()
}
}
+
+ /**
+ * Get information about the current storage usage and capacity
+ * @returns Object containing the storage type, used space, quota, and additional details
+ */
+ public async status(): Promise<{
+ type: string;
+ used: number;
+ quota: number | null;
+ details?: Record;
+ }> {
+ await this.ensureInitialized()
+
+ if (!this.storage) {
+ return {
+ type: 'unknown',
+ used: 0,
+ quota: null,
+ details: { error: 'Storage not initialized' }
+ }
+ }
+
+ try {
+ // Get storage status from the storage adapter
+ const storageStatus = await this.storage.getStorageStatus()
+
+ // Add index information to the details
+ const indexInfo = {
+ indexSize: this.size()
+ }
+
+ return {
+ ...storageStatus,
+ details: {
+ ...storageStatus.details,
+ index: indexInfo
+ }
+ }
+ } catch (error) {
+ console.error('Failed to get storage status:', error)
+ return {
+ type: 'unknown',
+ used: 0,
+ quota: null,
+ details: { error: String(error) }
+ }
+ }
+ }
}
// Export distance functions for convenience
diff --git a/src/coreTypes.ts b/src/coreTypes.ts
index 8a8dae07..5ee17757 100644
--- a/src/coreTypes.ts
+++ b/src/coreTypes.ts
@@ -120,4 +120,30 @@ export interface StorageAdapter {
getMetadata(id: string): Promise;
clear(): Promise;
+
+ /**
+ * Get information about storage usage and capacity
+ * @returns Promise that resolves to an object containing storage status information
+ */
+ getStorageStatus(): Promise<{
+ /**
+ * The type of storage being used (e.g., 'filesystem', 'opfs', 'memory')
+ */
+ type: string;
+
+ /**
+ * The amount of storage being used in bytes
+ */
+ used: number;
+
+ /**
+ * The total amount of storage available in bytes, or null if unknown
+ */
+ quota: number | null;
+
+ /**
+ * Additional storage-specific information
+ */
+ details?: Record;
+ }>;
}
diff --git a/src/index.ts b/src/index.ts
index 285068e2..6d959ba9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -66,6 +66,30 @@ export {
}
export type { PipelineOptions }
+// Export plugin loader
+import {
+ loadPlugins,
+ configureAndStartPipeline,
+ createSensePluginConfig,
+ createConduitPluginConfig
+} from './pluginLoader.js'
+import type {
+ PluginLoaderOptions,
+ PluginConfig,
+ PluginLoadResult
+} from './pluginLoader.js'
+export {
+ loadPlugins,
+ configureAndStartPipeline,
+ createSensePluginConfig,
+ createConduitPluginConfig
+}
+export type {
+ PluginLoaderOptions,
+ PluginConfig,
+ PluginLoadResult
+}
+
// Export types
import type {
Vector,
diff --git a/src/pluginLoader.ts b/src/pluginLoader.ts
new file mode 100644
index 00000000..c911cb9a
--- /dev/null
+++ b/src/pluginLoader.ts
@@ -0,0 +1,241 @@
+/**
+ * Plugin Loader for Brainy Augmentations
+ *
+ * This module provides functionality for loading and configuring augmentation plugins
+ * from external npm packages. It allows consumers of the library to easily integrate
+ * externally installed augmentation plugins into the Brainy system.
+ */
+
+import { AugmentationPipeline, augmentationPipeline, ExecutionMode } from './augmentationPipeline.js';
+import { AugmentationType, IAugmentation, ISenseAugmentation, IConduitAugmentation } from './types/augmentations.js';
+
+/**
+ * Configuration options for loading plugins
+ */
+export interface PluginLoaderOptions {
+ /**
+ * Whether to use the default augmentation pipeline instance
+ * If false, a new pipeline instance will be created
+ * @default true
+ */
+ useDefaultPipeline?: boolean;
+
+ /**
+ * Whether to initialize the augmentations after loading
+ * @default true
+ */
+ initializeAfterLoading?: boolean;
+
+ /**
+ * Order of augmentation types to load
+ * By default, sense augmentations are loaded first
+ * @default [AugmentationType.SENSE, AugmentationType.CONDUIT, ...]
+ */
+ augmentationOrder?: AugmentationType[];
+}
+
+/**
+ * Default options for the plugin loader
+ */
+const DEFAULT_OPTIONS: PluginLoaderOptions = {
+ useDefaultPipeline: true,
+ initializeAfterLoading: true,
+ augmentationOrder: [
+ AugmentationType.SENSE,
+ AugmentationType.CONDUIT,
+ AugmentationType.COGNITION,
+ AugmentationType.MEMORY,
+ AugmentationType.PERCEPTION,
+ AugmentationType.DIALOG,
+ AugmentationType.ACTIVATION,
+ AugmentationType.WEBSOCKET
+ ]
+};
+
+/**
+ * Plugin configuration for a specific augmentation
+ */
+export interface PluginConfig {
+ /**
+ * The plugin module name or path
+ * This can be an npm package name or a relative path
+ */
+ plugin: string;
+
+ /**
+ * Optional configuration to pass to the plugin
+ */
+ config?: Record;
+
+ /**
+ * Optional type of the augmentation
+ * If not provided, it will be determined automatically
+ */
+ type?: AugmentationType;
+}
+
+/**
+ * Result of loading plugins
+ */
+export interface PluginLoadResult {
+ /**
+ * The augmentation pipeline instance
+ */
+ pipeline: AugmentationPipeline;
+
+ /**
+ * Map of loaded augmentations by type
+ */
+ augmentations: Map;
+
+ /**
+ * Any errors that occurred during loading
+ */
+ errors: Error[];
+}
+
+/**
+ * Loads augmentation plugins from external npm packages
+ *
+ * @param plugins Array of plugin configurations
+ * @param options Options for loading plugins
+ * @returns Promise that resolves to the plugin load result
+ */
+export async function loadPlugins(
+ plugins: PluginConfig[],
+ options: PluginLoaderOptions = {}
+): Promise {
+ // Merge options with defaults
+ const opts = { ...DEFAULT_OPTIONS, ...options };
+
+ // Use the default pipeline or create a new one
+ const pipeline = opts.useDefaultPipeline ? augmentationPipeline : new AugmentationPipeline();
+
+ // Track loaded augmentations by type
+ const loadedAugmentations = new Map();
+ const errors: Error[] = [];
+
+ // Group plugins by type according to the specified order
+ const pluginsByType = new Map();
+
+ // Initialize the map with empty arrays for each type
+ for (const type of opts.augmentationOrder!) {
+ pluginsByType.set(type, []);
+ loadedAugmentations.set(type, []);
+ }
+
+ // Group plugins by type
+ for (const pluginConfig of plugins) {
+ const type = pluginConfig.type || AugmentationType.SENSE; // Default to SENSE if not specified
+ const typePlugins = pluginsByType.get(type) || [];
+ typePlugins.push(pluginConfig);
+ pluginsByType.set(type, typePlugins);
+ }
+
+ // Load plugins in the specified order
+ for (const type of opts.augmentationOrder!) {
+ const typePlugins = pluginsByType.get(type) || [];
+
+ for (const pluginConfig of typePlugins) {
+ try {
+ // Import the plugin module
+ const pluginModule = await import(pluginConfig.plugin);
+
+ // Get the default export or the named export that matches the plugin name
+ const PluginClass = pluginModule.default || pluginModule[pluginConfig.plugin.split('/').pop()!];
+
+ if (!PluginClass) {
+ throw new Error(`Could not find a valid export in plugin ${pluginConfig.plugin}`);
+ }
+
+ // Instantiate the plugin with the provided configuration
+ const plugin = new PluginClass(pluginConfig.config);
+
+ // Register the plugin with the pipeline
+ pipeline.register(plugin);
+
+ // Add to the loaded augmentations map
+ const typeAugmentations = loadedAugmentations.get(type) || [];
+ typeAugmentations.push(plugin);
+ loadedAugmentations.set(type, typeAugmentations);
+ } catch (error) {
+ const err = error instanceof Error ? error : new Error(String(error));
+ errors.push(err);
+ console.error(`Error loading plugin ${pluginConfig.plugin}:`, err);
+ }
+ }
+ }
+
+ // Initialize the augmentations if requested
+ if (opts.initializeAfterLoading) {
+ try {
+ await pipeline.initialize();
+ } catch (error) {
+ const err = error instanceof Error ? error : new Error(String(error));
+ errors.push(err);
+ console.error('Error initializing augmentations:', err);
+ }
+ }
+
+ return {
+ pipeline,
+ augmentations: loadedAugmentations,
+ errors
+ };
+}
+
+/**
+ * Configures and starts the augmentation pipeline with the specified plugins
+ *
+ * @param plugins Array of plugin configurations
+ * @param options Options for loading plugins
+ * @returns Promise that resolves to the plugin load result
+ */
+export async function configureAndStartPipeline(
+ plugins: PluginConfig[],
+ options: PluginLoaderOptions = {}
+): Promise {
+ // Load the plugins
+ const result = await loadPlugins(plugins, {
+ ...options,
+ initializeAfterLoading: true
+ });
+
+ return result;
+}
+
+/**
+ * Creates a plugin configuration for a sense augmentation
+ *
+ * @param plugin The plugin module name or path
+ * @param config Optional configuration to pass to the plugin
+ * @returns Plugin configuration
+ */
+export function createSensePluginConfig(
+ plugin: string,
+ config?: Record
+): PluginConfig {
+ return {
+ plugin,
+ config,
+ type: AugmentationType.SENSE
+ };
+}
+
+/**
+ * Creates a plugin configuration for a conduit augmentation
+ *
+ * @param plugin The plugin module name or path
+ * @param config Optional configuration to pass to the plugin
+ * @returns Plugin configuration
+ */
+export function createConduitPluginConfig(
+ plugin: string,
+ config?: Record
+): PluginConfig {
+ return {
+ plugin,
+ config,
+ type: AugmentationType.CONDUIT
+ };
+}
diff --git a/src/storage/fileSystemStorage.ts b/src/storage/fileSystemStorage.ts
index 569ee6ef..26399f66 100644
--- a/src/storage/fileSystemStorage.ts
+++ b/src/storage/fileSystemStorage.ts
@@ -451,4 +451,113 @@ export class FileSystemStorage implements StorageAdapter {
}
return obj
}
+
+ /**
+ * Get information about storage usage and capacity
+ */
+ public async getStorageStatus(): Promise<{
+ type: string;
+ used: number;
+ quota: number | null;
+ details?: Record;
+ }> {
+ await this.ensureInitialized()
+
+ try {
+ // Calculate the total size of all files in the storage directories
+ let totalSize = 0
+
+ // Helper function to calculate directory size
+ const calculateDirSize = async (dirPath: string): Promise => {
+ let size = 0
+ try {
+ const files = await fs.promises.readdir(dirPath)
+
+ for (const file of files) {
+ const filePath = path.join(dirPath, file)
+ const stats = await fs.promises.stat(filePath)
+
+ if (stats.isDirectory()) {
+ size += await calculateDirSize(filePath)
+ } else {
+ size += stats.size
+ }
+ }
+ } catch (error) {
+ console.warn(`Error calculating size for ${dirPath}:`, error)
+ }
+ return size
+ }
+
+ // Calculate size for each directory
+ const nodesDirSize = await calculateDirSize(this.nodesDir)
+ const edgesDirSize = await calculateDirSize(this.edgesDir)
+ const metadataDirSize = await calculateDirSize(this.metadataDir)
+
+ totalSize = nodesDirSize + edgesDirSize + metadataDirSize
+
+ // Get filesystem information
+ let quota = null
+ let details = {}
+
+ try {
+ // Try to get disk space information
+ const stats = await fs.promises.statfs(this.rootDir)
+ if (stats) {
+ const availableSpace = stats.bavail * stats.bsize
+ const totalSpace = stats.blocks * stats.bsize
+
+ quota = totalSpace
+ details = {
+ availableSpace,
+ totalSpace,
+ freePercentage: (availableSpace / totalSpace) * 100
+ }
+ }
+ } catch (error) {
+ console.warn('Unable to get filesystem stats:', error)
+ // If statfs is not available, try to use df command on Unix-like systems
+ try {
+ const { exec } = await import('child_process')
+ const util = await import('util')
+ const execPromise = util.promisify(exec)
+
+ const { stdout } = await execPromise(`df -k "${this.rootDir}"`)
+ const lines = stdout.trim().split('\n')
+ if (lines.length > 1) {
+ const parts = lines[1].split(/\s+/)
+ if (parts.length >= 4) {
+ const totalKB = parseInt(parts[1], 10)
+ const usedKB = parseInt(parts[2], 10)
+ const availableKB = parseInt(parts[3], 10)
+
+ quota = totalKB * 1024
+ details = {
+ availableSpace: availableKB * 1024,
+ totalSpace: totalKB * 1024,
+ freePercentage: (availableKB / totalKB) * 100
+ }
+ }
+ }
+ } catch (dfError) {
+ console.warn('Unable to get disk space using df command:', dfError)
+ }
+ }
+
+ return {
+ type: 'filesystem',
+ used: totalSize,
+ quota,
+ details
+ }
+ } catch (error) {
+ console.error('Failed to get storage status:', error)
+ return {
+ type: 'filesystem',
+ used: 0,
+ quota: null,
+ details: { error: String(error) }
+ }
+ }
+ }
}
diff --git a/src/storage/opfsStorage.ts b/src/storage/opfsStorage.ts
index faf09658..933dd2ac 100644
--- a/src/storage/opfsStorage.ts
+++ b/src/storage/opfsStorage.ts
@@ -498,6 +498,89 @@ export class OPFSStorage implements StorageAdapter {
}
return obj
}
+
+ /**
+ * Get information about storage usage and capacity
+ */
+ public async getStorageStatus(): Promise<{
+ type: string;
+ used: number;
+ quota: number | null;
+ details?: Record;
+ }> {
+ await this.ensureInitialized()
+
+ try {
+ // Calculate the total size of all files in the storage directories
+ let totalSize = 0
+
+ // Helper function to calculate directory size
+ const calculateDirSize = async (dirHandle: FileSystemDirectoryHandle): Promise => {
+ let size = 0
+ try {
+ // @ts-ignore - TypeScript doesn't recognize FileSystemDirectoryHandle.entries() properly
+ for await (const [name, handle] of dirHandle.entries()) {
+ if (handle.kind === 'file') {
+ const file = await handle.getFile()
+ size += file.size
+ } else if (handle.kind === 'directory') {
+ size += await calculateDirSize(handle)
+ }
+ }
+ } catch (error) {
+ console.warn(`Error calculating size for directory:`, error)
+ }
+ return size
+ }
+
+ // Calculate size for each directory
+ if (this.nodesDir) {
+ totalSize += await calculateDirSize(this.nodesDir)
+ }
+ if (this.edgesDir) {
+ totalSize += await calculateDirSize(this.edgesDir)
+ }
+ if (this.metadataDir) {
+ totalSize += await calculateDirSize(this.metadataDir)
+ }
+
+ // Get storage quota information using the Storage API
+ let quota = null
+ let details: Record = {
+ isPersistent: await this.isPersistent()
+ }
+
+ try {
+ if (navigator.storage && navigator.storage.estimate) {
+ const estimate = await navigator.storage.estimate()
+ quota = estimate.quota || null
+ details = {
+ ...details,
+ usage: estimate.usage,
+ quota: estimate.quota,
+ freePercentage: estimate.quota ? ((estimate.quota - (estimate.usage || 0)) / estimate.quota) * 100 : null
+ }
+ }
+ } catch (error) {
+ console.warn('Unable to get storage estimate:', error)
+ }
+
+ return {
+ type: 'opfs',
+ used: totalSize,
+ quota,
+ details
+ }
+ } catch (error) {
+ console.error('Failed to get storage status:', error)
+ return {
+ type: 'opfs',
+ used: 0,
+ quota: null,
+ details: { error: String(error) }
+ }
+ }
+ }
}
/**
@@ -693,6 +776,114 @@ export class MemoryStorage implements StorageAdapter {
this.edges.clear()
this.metadata.clear()
}
+
+ /**
+ * Get information about storage usage and capacity
+ */
+ public async getStorageStatus(): Promise<{
+ type: string;
+ used: number;
+ quota: number | null;
+ details?: Record;
+ }> {
+ try {
+ // Estimate the size of data in memory
+ let totalSize = 0
+
+ // Helper function to estimate object size in bytes
+ const estimateSize = (obj: any): number => {
+ if (obj === null || obj === undefined) return 0
+
+ const type = typeof obj
+
+ // Handle primitive types
+ if (type === 'number') return 8
+ if (type === 'string') return obj.length * 2
+ if (type === 'boolean') return 4
+
+ // Handle arrays and objects
+ if (Array.isArray(obj)) {
+ return obj.reduce((size, item) => size + estimateSize(item), 0)
+ }
+
+ if (type === 'object') {
+ if (obj instanceof Map) {
+ let mapSize = 0
+ for (const [key, value] of obj.entries()) {
+ mapSize += estimateSize(key) + estimateSize(value)
+ }
+ return mapSize
+ }
+
+ if (obj instanceof Set) {
+ let setSize = 0
+ for (const item of obj) {
+ setSize += estimateSize(item)
+ }
+ return setSize
+ }
+
+ // Regular object
+ return Object.entries(obj).reduce(
+ (size, [key, value]) => size + key.length * 2 + estimateSize(value),
+ 0
+ )
+ }
+
+ return 0
+ }
+
+ // Estimate size of nodes
+ for (const node of this.nodes.values()) {
+ totalSize += estimateSize(node)
+ }
+
+ // Estimate size of edges
+ for (const edge of this.edges.values()) {
+ totalSize += estimateSize(edge)
+ }
+
+ // Estimate size of metadata
+ for (const meta of this.metadata.values()) {
+ totalSize += estimateSize(meta)
+ }
+
+ // Get memory information if available
+ let quota = null
+ let details: Record = {
+ nodesCount: this.nodes.size,
+ edgesCount: this.edges.size,
+ metadataCount: this.metadata.size
+ }
+
+ // Try to get memory information if in a browser environment
+ if (typeof window !== 'undefined' && (window as any).performance && (window as any).performance.memory) {
+ const memory = (window as any).performance.memory
+ quota = memory.jsHeapSizeLimit
+ details = {
+ ...details,
+ totalJSHeapSize: memory.totalJSHeapSize,
+ usedJSHeapSize: memory.usedJSHeapSize,
+ jsHeapSizeLimit: memory.jsHeapSizeLimit
+ }
+ }
+
+ return {
+ type: 'memory',
+ used: totalSize,
+ quota,
+ details
+ }
+ } catch (error) {
+ console.error('Failed to get memory storage status:', error)
+ return {
+ type: 'memory',
+ used: 0,
+ quota: null,
+ details: { error: String(error) }
+ }
+ }
+ }
}
/**