feat: add external plugin loader and examples for Brainy

Introduced `pluginLoader.ts` to enable loading and configuring augmentation plugins from external packages. Added `examples/externalPlugins.js` to demonstrate usage. Enhanced storage status reporting in `opfsStorage` and `BrainyData` with detailed storage capacity and usage methods. Updated README with an external plugin usage guide.
This commit is contained in:
David Snelling 2025-05-28 15:39:14 -07:00
parent 767c349f63
commit b031a40b1c
11 changed files with 2220 additions and 1033 deletions

View file

@ -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';

File diff suppressed because it is too large Load diff

158
examples/externalPlugins.js Normal file
View file

@ -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');

832
package-lock.json generated
View file

@ -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"

View file

@ -38,7 +38,9 @@
"hnsw",
"opfs",
"origin-private-file-system",
"embeddings"
"embeddings",
"graph-database",
"streaming-data"
],
"author": "David Snelling (david@soulcraft.com)",
"license": "MIT",

View file

@ -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<T = any> {
* @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<T = any> {
/**
* 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<T = any> {
/**
* 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<T = any> {
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<string, any>;
}> {
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

View file

@ -120,4 +120,30 @@ export interface StorageAdapter {
getMetadata(id: string): Promise<any | null>;
clear(): Promise<void>;
/**
* 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<string, any>;
}>;
}

View file

@ -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,

241
src/pluginLoader.ts Normal file
View file

@ -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<string, any>;
/**
* 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<AugmentationType, IAugmentation[]>;
/**
* 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<PluginLoadResult> {
// 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<AugmentationType, IAugmentation[]>();
const errors: Error[] = [];
// Group plugins by type according to the specified order
const pluginsByType = new Map<AugmentationType, PluginConfig[]>();
// 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<PluginLoadResult> {
// 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<string, any>
): 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<string, any>
): PluginConfig {
return {
plugin,
config,
type: AugmentationType.CONDUIT
};
}

View file

@ -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<string, any>;
}> {
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<number> => {
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) }
}
}
}
}

View file

@ -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<string, any>;
}> {
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<number> => {
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<string, any> = {
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<string, any>;
}> {
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<string, any> = {
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) }
}
}
}
}
/**