chore: reformat code in README.md for improved readability
Applied consistent indentation and line-breaking rules to code and text sections in `README.md`, ensuring better formatting and alignment for readability. No functional changes made.
This commit is contained in:
parent
f311b120c1
commit
95a72f4181
5 changed files with 1135 additions and 315 deletions
325
README.md
325
README.md
|
|
@ -1,20 +1,24 @@
|
||||||
# Soulcraft Brainy
|
# Soulcraft Brainy
|
||||||
|
|
||||||
A combined Graph and 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.
|
A combined Graph and 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.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Cross-platform**: Works in both browsers and Node.js
|
- **Cross-platform**: Works in both browsers and Node.js
|
||||||
- **Persistent storage**: Uses Origin Private File System (OPFS) in browsers, with fallback to in-memory storage
|
- **Persistent storage**: Uses Origin Private File System (OPFS) in browsers, with fallback to in-memory storage
|
||||||
- **Efficient vector search**: Implements HNSW (Hierarchical Navigable Small World) algorithm for fast approximate nearest neighbor search
|
- **Efficient vector search**: Implements HNSW (Hierarchical Navigable Small World) algorithm for fast approximate
|
||||||
|
nearest neighbor search
|
||||||
- **Automatic embedding**: Converts text and other data to vectors using embedding models
|
- **Automatic embedding**: Converts text and other data to vectors using embedding models
|
||||||
- **TensorFlow.js integration**: Uses Universal Sentence Encoder for high-quality text embeddings (TensorFlow.js is included as a dependency)
|
- **TensorFlow.js integration**: Uses Universal Sentence Encoder for high-quality text embeddings (TensorFlow.js is
|
||||||
|
included as a dependency)
|
||||||
- **Metadata support**: Store and retrieve metadata alongside vectors
|
- **Metadata support**: Store and retrieve metadata alongside vectors
|
||||||
- **TypeScript support**: Fully typed API with generics for metadata types
|
- **TypeScript support**: Fully typed API with generics for metadata types
|
||||||
- **Multiple distance functions**: Supports cosine, Euclidean, Manhattan, and dot product distance metrics
|
- **Multiple distance functions**: Supports cosine, Euclidean, Manhattan, and dot product distance metrics
|
||||||
- **Augmentation system**: Extensible architecture for adding specialized capabilities
|
- **Augmentation system**: Extensible architecture for adding specialized capabilities
|
||||||
- **Memory augmentation**: Store and retrieve data in different formats (fileSystem, in-memory, firestore)
|
- **Memory augmentation**: Store and retrieve data in different formats (fileSystem, in-memory, firestore)
|
||||||
- **Full graph database capabilities**: Structured representation of entities and relationships with support for nodes (nouns) and edges (verbs)
|
- **Full graph database capabilities**: Structured representation of entities and relationships with support for nodes (
|
||||||
|
nouns) and edges (verbs)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -99,7 +103,8 @@ console.log(animalVectors);
|
||||||
|
|
||||||
### Using Embedding Functions
|
### Using Embedding Functions
|
||||||
|
|
||||||
By default, Brainy uses the TensorFlow Universal Sentence Encoder for high-quality text embeddings. The TensorFlow.js dependencies are automatically included when you install the package, so you don't need to install them separately.
|
By default, Brainy uses the TensorFlow Universal Sentence Encoder for high-quality text embeddings. The TensorFlow.js
|
||||||
|
dependencies are automatically included when you install the package, so you don't need to install them separately.
|
||||||
|
|
||||||
You can use the default embedding function directly:
|
You can use the default embedding function directly:
|
||||||
|
|
||||||
|
|
@ -148,7 +153,7 @@ await useModel.dispose();
|
||||||
You can also configure BrainyData to use a different embedding function if needed:
|
You can also configure BrainyData to use a different embedding function if needed:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { BrainyData, createSimpleEmbeddingFunction } from '@soulcraft/brainy';
|
import {BrainyData, createSimpleEmbeddingFunction} from '@soulcraft/brainy';
|
||||||
|
|
||||||
// Create a new graph and vector database with the simple embedding function
|
// Create a new graph and vector database with the simple embedding function
|
||||||
// (only if you prefer speed over accuracy)
|
// (only if you prefer speed over accuracy)
|
||||||
|
|
@ -205,11 +210,12 @@ const db = new BrainyData({
|
||||||
|
|
||||||
### Importing Graph Types Separately
|
### Importing Graph Types Separately
|
||||||
|
|
||||||
If you only need the graph type definitions without importing the entire library (supporting tree shaking), you can import them directly:
|
If you only need the graph type definitions without importing the entire library (supporting tree shaking), you can
|
||||||
|
import them directly:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Import only the graph types
|
// Import only the graph types
|
||||||
import { GraphNoun, GraphVerb, NounType, VerbType } from '@soulcraft/brainy/types/graphTypes';
|
import {GraphNoun, GraphVerb, NounType, VerbType} from '@soulcraft/brainy/types/graphTypes';
|
||||||
|
|
||||||
// Example usage
|
// Example usage
|
||||||
const person = {
|
const person = {
|
||||||
|
|
@ -221,9 +227,9 @@ const person = {
|
||||||
modelVersion: '1.0'
|
modelVersion: '1.0'
|
||||||
},
|
},
|
||||||
noun: NounType.Person,
|
noun: NounType.Person,
|
||||||
createdAt: { seconds: Date.now() / 1000, nanoseconds: 0 },
|
createdAt: {seconds: Date.now() / 1000, nanoseconds: 0},
|
||||||
updatedAt: { seconds: Date.now() / 1000, nanoseconds: 0 },
|
updatedAt: {seconds: Date.now() / 1000, nanoseconds: 0},
|
||||||
data: { name: 'John Doe' }
|
data: {name: 'John Doe'}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check the type
|
// Check the type
|
||||||
|
|
@ -231,15 +237,17 @@ console.log(`Person type: ${person.noun}`); // 'person'
|
||||||
console.log(`Available noun types:`, Object.values(NounType));
|
console.log(`Available noun types:`, Object.values(NounType));
|
||||||
```
|
```
|
||||||
|
|
||||||
This approach allows you to use just the type definitions without pulling in the entire library, which is useful for applications that only need to work with the data model.
|
This approach allows you to use just the type definitions without pulling in the entire library, which is useful for
|
||||||
|
applications that only need to work with the data model.
|
||||||
|
|
||||||
### Importing Augmentation Types Separately
|
### Importing Augmentation Types Separately
|
||||||
|
|
||||||
If you need to use the augmentation interfaces in a client application without importing the entire library, you can import them directly:
|
If you need to use the augmentation interfaces in a client application without importing the entire library, you can
|
||||||
|
import them directly:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Import the BrainyAugmentations namespace and related types
|
// Import the BrainyAugmentations namespace and related types
|
||||||
import { BrainyAugmentations, AugmentationType, AugmentationResponse } from '@soulcraft/brainy/types/augmentations';
|
import {BrainyAugmentations, AugmentationType, AugmentationResponse} from '@soulcraft/brainy/types/augmentations';
|
||||||
|
|
||||||
// Example usage of augmentation interfaces
|
// Example usage of augmentation interfaces
|
||||||
class MyCustomCognitionAugmentation implements BrainyAugmentations.ICognitionAugmentation {
|
class MyCustomCognitionAugmentation implements BrainyAugmentations.ICognitionAugmentation {
|
||||||
|
|
@ -292,85 +300,8 @@ class MyCustomCognitionAugmentation implements BrainyAugmentations.ICognitionAug
|
||||||
console.log(`Available augmentation types:`, Object.values(AugmentationType));
|
console.log(`Available augmentation types:`, Object.values(AugmentationType));
|
||||||
```
|
```
|
||||||
|
|
||||||
This approach allows you to use the augmentation interfaces in client applications that need to implement or interact with Brainy's augmentation system.
|
This approach allows you to use the augmentation interfaces in client applications that need to implement or interact
|
||||||
|
with Brainy's augmentation system.
|
||||||
## Publishing and Using as a Private NPM Package
|
|
||||||
|
|
||||||
Soulcraft Brainy is configured as a private NPM package with restricted access. This section provides information on how to publish and use it within your organization.
|
|
||||||
|
|
||||||
### Versioning
|
|
||||||
|
|
||||||
This project uses semantic versioning (SemVer):
|
|
||||||
|
|
||||||
- **Major version** (`x.0.0`): Breaking changes that may require updates to dependent code
|
|
||||||
- **Minor version** (`0.x.0`): New features that don't break existing functionality
|
|
||||||
- **Patch version** (`0.0.x`): Bug fixes and other minor changes
|
|
||||||
|
|
||||||
The package includes scripts for manual version bumping:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Increment patch version (0.0.x)
|
|
||||||
npm run version:patch
|
|
||||||
|
|
||||||
# Increment minor version (0.x.0)
|
|
||||||
npm run version:minor
|
|
||||||
|
|
||||||
# Increment major version (x.0.0)
|
|
||||||
npm run version:major
|
|
||||||
```
|
|
||||||
|
|
||||||
These commands will update the version in package.json and create a git tag for the new version.
|
|
||||||
|
|
||||||
### Publishing the Package
|
|
||||||
|
|
||||||
To publish updates to the package:
|
|
||||||
|
|
||||||
1. Ensure you have the appropriate npm credentials and access to the @soulcraft organization
|
|
||||||
2. Update the version using one of the version scripts:
|
|
||||||
```bash
|
|
||||||
npm run version:patch # For bug fixes and minor changes
|
|
||||||
npm run version:minor # For new features
|
|
||||||
npm run version:major # For breaking changes
|
|
||||||
```
|
|
||||||
3. Use the deploy script to build and publish the package:
|
|
||||||
```bash
|
|
||||||
npm run deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, you can run the steps separately:
|
|
||||||
1. Build the package:
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
2. Publish the package:
|
|
||||||
```bash
|
|
||||||
npm publish
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the package has the following configuration in package.json:
|
|
||||||
```json
|
|
||||||
"private": false,
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "restricted"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures that the package is only accessible to users with appropriate permissions within the @soulcraft organization. The `"access": "restricted"` setting limits access to the package to members of the @soulcraft organization, while `"private": false` allows the package to be published to npm.
|
|
||||||
|
|
||||||
### Installing the Private Package
|
|
||||||
|
|
||||||
To install the package in another project:
|
|
||||||
|
|
||||||
1. Ensure you have access to the @soulcraft organization on npm
|
|
||||||
2. Add the package to your project:
|
|
||||||
```bash
|
|
||||||
npm install @soulcraft/brainy
|
|
||||||
```
|
|
||||||
|
|
||||||
3. If you're using a private npm registry, you may need to configure npm to use your organization's registry:
|
|
||||||
```bash
|
|
||||||
npm config set @soulcraft:registry https://your-private-registry.com/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
|
|
@ -378,7 +309,8 @@ To install the package in another project:
|
||||||
|
|
||||||
## Augmentation System
|
## Augmentation System
|
||||||
|
|
||||||
Brainy includes a powerful augmentation system that allows extending its capabilities through specialized modules. Each augmentation implements a specific interface and provides additional functionality.
|
Brainy includes a powerful augmentation system that allows extending its capabilities through specialized modules. Each
|
||||||
|
augmentation implements a specific interface and provides additional functionality.
|
||||||
|
|
||||||
### Base Augmentation Interface
|
### Base Augmentation Interface
|
||||||
|
|
||||||
|
|
@ -401,8 +333,11 @@ Augmentations can optionally implement WebSocket support:
|
||||||
```typescript
|
```typescript
|
||||||
interface IWebSocketSupport {
|
interface IWebSocketSupport {
|
||||||
connectWebSocket(url: string, protocols?: string | string[]): Promise<WebSocketConnection>;
|
connectWebSocket(url: string, protocols?: string | string[]): Promise<WebSocketConnection>;
|
||||||
|
|
||||||
sendWebSocketMessage(connectionId: string, data: unknown): Promise<void>;
|
sendWebSocketMessage(connectionId: string, data: unknown): Promise<void>;
|
||||||
|
|
||||||
onWebSocketMessage(connectionId: string, callback: DataCallback<unknown>): Promise<void>;
|
onWebSocketMessage(connectionId: string, callback: DataCallback<unknown>): Promise<void>;
|
||||||
|
|
||||||
closeWebSocket(connectionId: string, code?: number, reason?: string): Promise<void>;
|
closeWebSocket(connectionId: string, code?: number, reason?: string): Promise<void>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -421,6 +356,7 @@ interface ISenseAugmentation extends IAugmentation {
|
||||||
nouns: string[];
|
nouns: string[];
|
||||||
verbs: string[];
|
verbs: string[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
listenToFeed(
|
listenToFeed(
|
||||||
feedUrl: string,
|
feedUrl: string,
|
||||||
callback: DataCallback<{ nouns: string[]; verbs: string[] }>
|
callback: DataCallback<{ nouns: string[]; verbs: string[] }>
|
||||||
|
|
@ -438,18 +374,136 @@ interface IConduitAugmentation extends IAugmentation {
|
||||||
targetSystemId: string,
|
targetSystemId: string,
|
||||||
config: Record<string, unknown>
|
config: Record<string, unknown>
|
||||||
): AugmentationResponse<WebSocketConnection>;
|
): AugmentationResponse<WebSocketConnection>;
|
||||||
|
|
||||||
readData(
|
readData(
|
||||||
query: Record<string, unknown>,
|
query: Record<string, unknown>,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<unknown>;
|
): AugmentationResponse<unknown>;
|
||||||
|
|
||||||
writeData(
|
writeData(
|
||||||
data: Record<string, unknown>,
|
data: Record<string, unknown>,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<unknown>;
|
): AugmentationResponse<unknown>;
|
||||||
|
|
||||||
monitorStream(streamId: string, callback: DataCallback<unknown>): Promise<void>;
|
monitorStream(streamId: string, callback: DataCallback<unknown>): Promise<void>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### FirestoreSync Conduit Augmentation
|
||||||
|
|
||||||
|
Brainy includes a FirestoreSync conduit augmentation that allows for syncing data to Firestore either one-way or
|
||||||
|
two-way:
|
||||||
|
|
||||||
|
- **One-way sync**: Data is only pushed from Brainy to Firestore
|
||||||
|
- **Two-way sync**: Data is synchronized between Brainy and Firestore in both directions
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
|
||||||
|
1. Install Firebase: `npm install firebase`
|
||||||
|
2. Set up a Firebase project and enable Firestore
|
||||||
|
3. Get your Firebase configuration from the Firebase console
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
registerAugmentation,
|
||||||
|
initializeAugmentationPipeline,
|
||||||
|
createFirestoreSyncAugmentation,
|
||||||
|
FirestoreSyncConfig
|
||||||
|
} from '@soulcraft/brainy';
|
||||||
|
|
||||||
|
// Your Firebase configuration
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: "your-api-key",
|
||||||
|
authDomain: "your-project-id.firebaseapp.com",
|
||||||
|
projectId: "your-project-id",
|
||||||
|
storageBucket: "your-project-id.appspot.com",
|
||||||
|
messagingSenderId: "your-messaging-sender-id",
|
||||||
|
appId: "your-app-id"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the FirestoreSync augmentation with one-way sync configuration
|
||||||
|
const oneWaySyncConfig: FirestoreSyncConfig = {
|
||||||
|
firebaseConfig,
|
||||||
|
nodesCollection: 'brainy_nodes',
|
||||||
|
edgesCollection: 'brainy_edges',
|
||||||
|
metadataCollection: 'brainy_metadata',
|
||||||
|
syncMode: 'one-way'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create and register the augmentation
|
||||||
|
const firestoreSync = createFirestoreSyncAugmentation(
|
||||||
|
'brainy-firestore-sync',
|
||||||
|
oneWaySyncConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
registerAugmentation(firestoreSync);
|
||||||
|
|
||||||
|
// Initialize the augmentation pipeline
|
||||||
|
initializeAugmentationPipeline();
|
||||||
|
|
||||||
|
// Initialize the augmentation
|
||||||
|
await firestoreSync.initialize();
|
||||||
|
|
||||||
|
// Now you can use the augmentation to sync data
|
||||||
|
// For example, to sync a node to Firestore:
|
||||||
|
await firestoreSync.syncNodeToFirestore(node);
|
||||||
|
|
||||||
|
// Or to sync an edge to Firestore:
|
||||||
|
await firestoreSync.syncEdgeToFirestore(edge);
|
||||||
|
|
||||||
|
// Or to sync metadata to Firestore:
|
||||||
|
await firestoreSync.syncMetadataToFirestore('metadata-id', { key: 'value' });
|
||||||
|
|
||||||
|
// You can also use the standard conduit methods:
|
||||||
|
// Read data from Firestore
|
||||||
|
const response = await firestoreSync.readData({
|
||||||
|
collection: 'brainy_nodes',
|
||||||
|
id: 'node-id'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write data to Firestore
|
||||||
|
await firestoreSync.writeData({
|
||||||
|
collection: 'custom_collection',
|
||||||
|
id: 'custom-doc-1',
|
||||||
|
document: {
|
||||||
|
name: 'Custom Document',
|
||||||
|
timestamp: new Date()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Monitor changes in Firestore
|
||||||
|
await firestoreSync.monitorStream('brainy_nodes', (data) => {
|
||||||
|
console.log('Node change detected:', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// When done, shut down the augmentation
|
||||||
|
await firestoreSync.shutDown();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Two-way Sync Configuration:**
|
||||||
|
|
||||||
|
For two-way synchronization between Brainy and Firestore:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const twoWaySyncConfig: FirestoreSyncConfig = {
|
||||||
|
firebaseConfig,
|
||||||
|
nodesCollection: 'brainy_nodes',
|
||||||
|
edgesCollection: 'brainy_edges',
|
||||||
|
metadataCollection: 'brainy_metadata',
|
||||||
|
syncMode: 'two-way',
|
||||||
|
syncInterval: 30000 // Sync every 30 seconds
|
||||||
|
};
|
||||||
|
|
||||||
|
const twoWaySync = createFirestoreSyncAugmentation(
|
||||||
|
'brainy-firestore-two-way-sync',
|
||||||
|
twoWaySyncConfig
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
For more detailed examples, see the [firestoreSyncExample.js](examples/firestoreSyncExample.js) file.
|
||||||
|
|
||||||
#### Cognition Augmentations
|
#### Cognition Augmentations
|
||||||
|
|
||||||
For reasoning, inference, and logical operations:
|
For reasoning, inference, and logical operations:
|
||||||
|
|
@ -460,7 +514,9 @@ interface ICognitionAugmentation extends IAugmentation {
|
||||||
inference: string;
|
inference: string;
|
||||||
confidence: number;
|
confidence: number;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
infer(dataSubset: Record<string, unknown>): AugmentationResponse<Record<string, unknown>>;
|
infer(dataSubset: Record<string, unknown>): AugmentationResponse<Record<string, unknown>>;
|
||||||
|
|
||||||
executeLogic(ruleId: string, input: Record<string, unknown>): AugmentationResponse<boolean>;
|
executeLogic(ruleId: string, input: Record<string, unknown>): AugmentationResponse<boolean>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -476,19 +532,23 @@ interface IMemoryAugmentation extends IAugmentation {
|
||||||
data: unknown,
|
data: unknown,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<boolean>;
|
): AugmentationResponse<boolean>;
|
||||||
|
|
||||||
retrieveData(
|
retrieveData(
|
||||||
key: string,
|
key: string,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<unknown>;
|
): AugmentationResponse<unknown>;
|
||||||
|
|
||||||
updateData(
|
updateData(
|
||||||
key: string,
|
key: string,
|
||||||
data: unknown,
|
data: unknown,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<boolean>;
|
): AugmentationResponse<boolean>;
|
||||||
|
|
||||||
deleteData(
|
deleteData(
|
||||||
key: string,
|
key: string,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): AugmentationResponse<boolean>;
|
): AugmentationResponse<boolean>;
|
||||||
|
|
||||||
listDataKeys(
|
listDataKeys(
|
||||||
pattern?: string,
|
pattern?: string,
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
|
|
@ -507,10 +567,12 @@ interface IPerceptionAugmentation extends IAugmentation {
|
||||||
verbs: string[],
|
verbs: string[],
|
||||||
context?: Record<string, unknown>
|
context?: Record<string, unknown>
|
||||||
): AugmentationResponse<Record<string, unknown>>;
|
): AugmentationResponse<Record<string, unknown>>;
|
||||||
|
|
||||||
organize(
|
organize(
|
||||||
data: Record<string, unknown>,
|
data: Record<string, unknown>,
|
||||||
criteria?: Record<string, unknown>
|
criteria?: Record<string, unknown>
|
||||||
): AugmentationResponse<Record<string, unknown>>;
|
): AugmentationResponse<Record<string, unknown>>;
|
||||||
|
|
||||||
generateVisualization(
|
generateVisualization(
|
||||||
data: Record<string, unknown>,
|
data: Record<string, unknown>,
|
||||||
visualizationType: string
|
visualizationType: string
|
||||||
|
|
@ -530,11 +592,13 @@ interface IDialogAugmentation extends IAugmentation {
|
||||||
verbs: string[];
|
verbs: string[];
|
||||||
context: Record<string, unknown>;
|
context: Record<string, unknown>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
generateResponse(
|
generateResponse(
|
||||||
interpretedInput: Record<string, unknown>,
|
interpretedInput: Record<string, unknown>,
|
||||||
knowledgeContext: Record<string, unknown>,
|
knowledgeContext: Record<string, unknown>,
|
||||||
sessionId?: string
|
sessionId?: string
|
||||||
): AugmentationResponse<string>;
|
): AugmentationResponse<string>;
|
||||||
|
|
||||||
manageContext(sessionId: string, contextUpdate: Record<string, unknown>): Promise<void>;
|
manageContext(sessionId: string, contextUpdate: Record<string, unknown>): Promise<void>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -549,7 +613,9 @@ interface IActivationAugmentation extends IAugmentation {
|
||||||
actionName: string,
|
actionName: string,
|
||||||
parameters?: Record<string, unknown>
|
parameters?: Record<string, unknown>
|
||||||
): AugmentationResponse<unknown>;
|
): AugmentationResponse<unknown>;
|
||||||
|
|
||||||
generateOutput(knowledgeId: string, format: string): AugmentationResponse<string | Record<string, unknown>>;
|
generateOutput(knowledgeId: string, format: string): AugmentationResponse<string | Record<string, unknown>>;
|
||||||
|
|
||||||
interactExternal(systemId: string, payload: Record<string, unknown>): AugmentationResponse<unknown>;
|
interactExternal(systemId: string, payload: Record<string, unknown>): AugmentationResponse<unknown>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -575,7 +641,8 @@ This enum can be used by consumers of the library to identify the different type
|
||||||
|
|
||||||
### Augmentation Event Pipeline
|
### Augmentation Event Pipeline
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
### Installing Custom Augmentations
|
### Installing Custom Augmentations
|
||||||
|
|
||||||
|
|
@ -587,7 +654,7 @@ For better performance and bundle optimization, you can register augmentations a
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// myCustomAugmentation.ts
|
// myCustomAugmentation.ts
|
||||||
import { registerAugmentation, AugmentationType } from '@soulcraft/brainy';
|
import {registerAugmentation, AugmentationType} from '@soulcraft/brainy';
|
||||||
|
|
||||||
// Create your custom augmentation
|
// Create your custom augmentation
|
||||||
class MyCustomAugmentation {
|
class MyCustomAugmentation {
|
||||||
|
|
@ -602,32 +669,33 @@ Then configure your build tool (webpack or rollup) to automatically discover and
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// webpack.config.js
|
// webpack.config.js
|
||||||
const { createAugmentationRegistryPlugin } = require('@soulcraft/brainy');
|
const {createAugmentationRegistryPlugin} = require('@soulcraft/brainy');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// ... other webpack config
|
// ... other webpack config
|
||||||
plugins: [
|
plugins: [
|
||||||
createAugmentationRegistryPlugin({
|
createAugmentationRegistryPlugin({
|
||||||
pattern: /augmentation\.(js|ts)$/,
|
pattern: /augmentation\.(js|ts)$/,
|
||||||
options: { autoInitialize: true }
|
options: {autoInitialize: true}
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Benefits of build-time registration:
|
Benefits of build-time registration:
|
||||||
|
|
||||||
- Better performance as augmentations are available immediately at startup
|
- Better performance as augmentations are available immediately at startup
|
||||||
- Improved tree-shaking and bundle optimization
|
- Improved tree-shaking and bundle optimization
|
||||||
- Type safety and better IDE support
|
- Type safety and better IDE support
|
||||||
- No need for dynamic imports or async loading
|
- No need for dynamic imports or async loading
|
||||||
|
|
||||||
For detailed documentation on build-time augmentation registration, see [build-time-augmentations.md](docs/build-time-augmentations.md).
|
For detailed documentation on build-time augmentation registration,
|
||||||
|
see [build-time-augmentations.md](docs/build-time-augmentations.md).
|
||||||
|
|
||||||
#### Using the Pipeline Directly
|
#### Using the Pipeline Directly
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { augmentationPipeline, ExecutionMode, AugmentationType } from '@soulcraft/brainy';
|
import {augmentationPipeline, ExecutionMode, AugmentationType} from '@soulcraft/brainy';
|
||||||
|
|
||||||
// Register augmentations
|
// Register augmentations
|
||||||
augmentationPipeline.register(mySenseAugmentation);
|
augmentationPipeline.register(mySenseAugmentation);
|
||||||
|
|
@ -653,26 +721,26 @@ console.log(`Available augmentation types: ${availableTypes.join(', ')}`);
|
||||||
const processingResults = await augmentationPipeline.executeSensePipeline(
|
const processingResults = await augmentationPipeline.executeSensePipeline(
|
||||||
'processRawData',
|
'processRawData',
|
||||||
['Some raw text data', 'text'],
|
['Some raw text data', 'text'],
|
||||||
{ mode: ExecutionMode.SEQUENTIAL, stopOnError: true }
|
{mode: ExecutionMode.SEQUENTIAL, stopOnError: true}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute a conduit pipeline
|
// Execute a conduit pipeline
|
||||||
const connectionResults = await augmentationPipeline.executeConduitPipeline(
|
const connectionResults = await augmentationPipeline.executeConduitPipeline(
|
||||||
'establishConnection',
|
'establishConnection',
|
||||||
['external-system', { apiKey: 'your-api-key' }]
|
['external-system', {apiKey: 'your-api-key'}]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute a cognition pipeline
|
// Execute a cognition pipeline
|
||||||
const reasoningResults = await augmentationPipeline.executeCognitionPipeline(
|
const reasoningResults = await augmentationPipeline.executeCognitionPipeline(
|
||||||
'reason',
|
'reason',
|
||||||
['What is the capital of France?', { additionalContext: 'geography' }],
|
['What is the capital of France?', {additionalContext: 'geography'}],
|
||||||
{ mode: ExecutionMode.PARALLEL }
|
{mode: ExecutionMode.PARALLEL}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute a memory pipeline
|
// Execute a memory pipeline
|
||||||
const storeResults = await augmentationPipeline.executeMemoryPipeline(
|
const storeResults = await augmentationPipeline.executeMemoryPipeline(
|
||||||
'storeData',
|
'storeData',
|
||||||
['user123', { name: 'John Doe', email: 'john@example.com' }]
|
['user123', {name: 'John Doe', email: 'john@example.com'}]
|
||||||
);
|
);
|
||||||
|
|
||||||
const retrieveResults = await augmentationPipeline.executeMemoryPipeline(
|
const retrieveResults = await augmentationPipeline.executeMemoryPipeline(
|
||||||
|
|
@ -710,7 +778,7 @@ interface PipelineOptions {
|
||||||
You can create a custom pipeline instance if needed:
|
You can create a custom pipeline instance if needed:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { AugmentationPipeline } from '@soulcraft/brainy';
|
import {AugmentationPipeline} from '@soulcraft/brainy';
|
||||||
|
|
||||||
const myPipeline = new AugmentationPipeline();
|
const myPipeline = new AugmentationPipeline();
|
||||||
myPipeline.register(myCustomAugmentation);
|
myPipeline.register(myCustomAugmentation);
|
||||||
|
|
@ -718,7 +786,8 @@ myPipeline.register(myCustomAugmentation);
|
||||||
|
|
||||||
## Graph Data Model
|
## Graph Data Model
|
||||||
|
|
||||||
Brainy uses a graph-based data model to represent entities and relationships. This model consists of nouns (nodes) and verbs (edges).
|
Brainy uses a graph-based data model to represent entities and relationships. This model consists of nouns (nodes) and
|
||||||
|
verbs (edges).
|
||||||
|
|
||||||
### Common Types
|
### Common Types
|
||||||
|
|
||||||
|
|
@ -840,7 +909,8 @@ The repository also includes TypeScript examples for Node.js:
|
||||||
|
|
||||||
### HNSW Indexing
|
### HNSW Indexing
|
||||||
|
|
||||||
The Hierarchical Navigable Small World (HNSW) algorithm is used for efficient approximate nearest neighbor search. It creates a multi-layered graph structure that allows for logarithmic-time search complexity.
|
The Hierarchical Navigable Small World (HNSW) algorithm is used for efficient approximate nearest neighbor search. It
|
||||||
|
creates a multi-layered graph structure that allows for logarithmic-time search complexity.
|
||||||
|
|
||||||
Key features of the HNSW implementation:
|
Key features of the HNSW implementation:
|
||||||
|
|
||||||
|
|
@ -850,19 +920,22 @@ Key features of the HNSW implementation:
|
||||||
|
|
||||||
### Origin Private File System (OPFS) Storage
|
### Origin Private File System (OPFS) Storage
|
||||||
|
|
||||||
In browser environments, the database uses the Origin Private File System (OPFS) API for persistent storage. This provides:
|
In browser environments, the database uses the Origin Private File System (OPFS) API for persistent storage. This
|
||||||
|
provides:
|
||||||
|
|
||||||
- Fast, local storage that persists between sessions
|
- Fast, local storage that persists between sessions
|
||||||
- Isolation from other origins for security
|
- Isolation from other origins for security
|
||||||
- Efficient file operations
|
- Efficient file operations
|
||||||
|
|
||||||
In Node.js environments, the database uses a file system-based storage adapter that stores data in JSON files. This provides:
|
In Node.js environments, the database uses a file system-based storage adapter that stores data in JSON files. This
|
||||||
|
provides:
|
||||||
|
|
||||||
- Persistent storage between application restarts
|
- Persistent storage between application restarts
|
||||||
- Efficient file operations using Node.js fs module
|
- Efficient file operations using Node.js fs module
|
||||||
- Configurable storage location
|
- Configurable storage location
|
||||||
|
|
||||||
In environments where neither OPFS nor Node.js file system is available, the database automatically falls back to in-memory storage.
|
In environments where neither OPFS nor Node.js file system is available, the database automatically falls back to
|
||||||
|
in-memory storage.
|
||||||
|
|
||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
|
|
@ -873,22 +946,28 @@ The main class for interacting with the graph and vector database.
|
||||||
#### Constructor
|
#### Constructor
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
constructor(config?: BrainyDataConfig)
|
constructor(config ? : BrainyDataConfig)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Methods
|
#### Methods
|
||||||
|
|
||||||
- `init(): Promise<void>` - Initialize the database
|
- `init(): Promise<void>` - Initialize the database
|
||||||
- `add(vectorOrData: Vector | any, metadata?: T, options?: { forceEmbed?: boolean }): Promise<string>` - Add a vector or data to the database
|
- `add(vectorOrData: Vector | any, metadata?: T, options?: { forceEmbed?: boolean }): Promise<string>` - Add a vector or
|
||||||
- `addBatch(items: Array<{ vectorOrData: Vector | any, metadata?: T }>, options?: { forceEmbed?: boolean }): Promise<string[]>` - Add multiple vectors or data items
|
data to the database
|
||||||
- `search(queryVectorOrData: Vector | any, k?: number, options?: { forceEmbed?: boolean }): Promise<SearchResult<T>[]>` - Search for similar vectors
|
-
|
||||||
|
`addBatch(items: Array<{ vectorOrData: Vector | any, metadata?: T }>, options?: { forceEmbed?: boolean }): Promise<string[]>` -
|
||||||
|
Add multiple vectors or data items
|
||||||
|
-
|
||||||
|
`search(queryVectorOrData: Vector | any, k?: number, options?: { forceEmbed?: boolean }): Promise<SearchResult<T>[]>` -
|
||||||
|
Search for similar vectors
|
||||||
- `searchText(query: string, k?: number): Promise<SearchResult<T>[]>` - Search for similar documents using a text query
|
- `searchText(query: string, k?: number): Promise<SearchResult<T>[]>` - Search for similar documents using a text query
|
||||||
- `get(id: string): Promise<VectorDocument<T> | null>` - Get a vector by ID
|
- `get(id: string): Promise<VectorDocument<T> | null>` - Get a vector by ID
|
||||||
- `delete(id: string): Promise<boolean>` - Delete a vector
|
- `delete(id: string): Promise<boolean>` - Delete a vector
|
||||||
- `updateMetadata(id: string, metadata: T): Promise<boolean>` - Update metadata
|
- `updateMetadata(id: string, metadata: T): Promise<boolean>` - Update metadata
|
||||||
- `clear(): Promise<void>` - Clear the database
|
- `clear(): Promise<void>` - Clear the database
|
||||||
- `size(): number` - Get the number of vectors in the database
|
- `size(): number` - Get the number of vectors in the database
|
||||||
- `embed(data: string | string[]): Promise<Vector>` - Embed text or data into a vector using the same embedding function used by this instance
|
- `embed(data: string | string[]): Promise<Vector>` - Embed text or data into a vector using the same embedding function
|
||||||
|
used by this instance
|
||||||
|
|
||||||
### Distance Functions
|
### Distance Functions
|
||||||
|
|
||||||
|
|
@ -904,10 +983,14 @@ constructor(config?: BrainyDataConfig)
|
||||||
|
|
||||||
### Embedding Functions
|
### Embedding Functions
|
||||||
|
|
||||||
- `createEmbeddingFunction(model: EmbeddingModel): EmbeddingFunction` - Create an embedding function from an embedding model
|
- `createEmbeddingFunction(model: EmbeddingModel): EmbeddingFunction` - Create an embedding function from an embedding
|
||||||
- `createTensorFlowEmbeddingFunction(): EmbeddingFunction` - Create an embedding function using TensorFlow's Universal Sentence Encoder
|
model
|
||||||
- `createSimpleEmbeddingFunction(): EmbeddingFunction` - Create a simple character-based embedding function (faster but less accurate)
|
- `createTensorFlowEmbeddingFunction(): EmbeddingFunction` - Create an embedding function using TensorFlow's Universal
|
||||||
- `defaultEmbeddingFunction` - Default embedding function using TensorFlow's Universal Sentence Encoder for high-quality embeddings
|
Sentence Encoder
|
||||||
|
- `createSimpleEmbeddingFunction(): EmbeddingFunction` - Create a simple character-based embedding function (faster but
|
||||||
|
less accurate)
|
||||||
|
- `defaultEmbeddingFunction` - Default embedding function using TensorFlow's Universal Sentence Encoder for high-quality
|
||||||
|
embeddings
|
||||||
|
|
||||||
## Browser Compatibility
|
## Browser Compatibility
|
||||||
|
|
||||||
|
|
|
||||||
248
examples/firestoreSyncExample.js
Normal file
248
examples/firestoreSyncExample.js
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
/**
|
||||||
|
* Example: Using the FirestoreSync Conduit Augmentation
|
||||||
|
*
|
||||||
|
* This example demonstrates how to use the FirestoreSync augmentation
|
||||||
|
* to sync data between Brainy and Firestore in both one-way and two-way modes.
|
||||||
|
*
|
||||||
|
* Prerequisites:
|
||||||
|
* 1. Install Firebase: npm install firebase
|
||||||
|
* 2. Set up a Firebase project and enable Firestore
|
||||||
|
* 3. Get your Firebase configuration from the Firebase console
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
registerAugmentation,
|
||||||
|
initializeAugmentationPipeline,
|
||||||
|
BrainyGraph
|
||||||
|
} from '../dist/index.js'
|
||||||
|
|
||||||
|
import {
|
||||||
|
createFirestoreSyncAugmentation,
|
||||||
|
FirestoreSyncConfig
|
||||||
|
} from '../dist/augmentations/firestoreSyncAugmentation.js'
|
||||||
|
|
||||||
|
// Your Firebase configuration
|
||||||
|
// Replace with your actual Firebase project configuration
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: 'your-api-key',
|
||||||
|
authDomain: 'your-project-id.firebaseapp.com',
|
||||||
|
projectId: 'your-project-id',
|
||||||
|
storageBucket: 'your-project-id.appspot.com',
|
||||||
|
messagingSenderId: 'your-messaging-sender-id',
|
||||||
|
appId: 'your-app-id'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 1: One-way sync (Brainy -> Firestore)
|
||||||
|
async function setupOneWaySync() {
|
||||||
|
console.log('Setting up one-way sync from Brainy to Firestore...')
|
||||||
|
|
||||||
|
// Create the FirestoreSync augmentation with one-way sync configuration
|
||||||
|
const oneWaySyncConfig = {
|
||||||
|
firebaseConfig,
|
||||||
|
nodesCollection: 'brainy_nodes',
|
||||||
|
edgesCollection: 'brainy_edges',
|
||||||
|
metadataCollection: 'brainy_metadata',
|
||||||
|
syncMode: 'one-way'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and register the augmentation
|
||||||
|
const oneWaySync = createFirestoreSyncAugmentation(
|
||||||
|
'brainy-firestore-one-way-sync',
|
||||||
|
oneWaySyncConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
registerAugmentation(oneWaySync)
|
||||||
|
|
||||||
|
// Initialize the augmentation pipeline
|
||||||
|
initializeAugmentationPipeline()
|
||||||
|
|
||||||
|
// Initialize the augmentation
|
||||||
|
await oneWaySync.initialize()
|
||||||
|
|
||||||
|
console.log('One-way sync augmentation initialized successfully')
|
||||||
|
|
||||||
|
return oneWaySync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 2: Two-way sync (Bidirectional between Brainy and Firestore)
|
||||||
|
async function setupTwoWaySync() {
|
||||||
|
console.log('Setting up two-way sync between Brainy and Firestore...')
|
||||||
|
|
||||||
|
// Create the FirestoreSync augmentation with two-way sync configuration
|
||||||
|
const twoWaySyncConfig = {
|
||||||
|
firebaseConfig,
|
||||||
|
nodesCollection: 'brainy_nodes',
|
||||||
|
edgesCollection: 'brainy_edges',
|
||||||
|
metadataCollection: 'brainy_metadata',
|
||||||
|
syncMode: 'two-way',
|
||||||
|
syncInterval: 30000 // Sync every 30 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and register the augmentation
|
||||||
|
const twoWaySync = createFirestoreSyncAugmentation(
|
||||||
|
'brainy-firestore-two-way-sync',
|
||||||
|
twoWaySyncConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
registerAugmentation(twoWaySync)
|
||||||
|
|
||||||
|
// Initialize the augmentation pipeline
|
||||||
|
initializeAugmentationPipeline()
|
||||||
|
|
||||||
|
// Initialize the augmentation
|
||||||
|
await twoWaySync.initialize()
|
||||||
|
|
||||||
|
console.log('Two-way sync augmentation initialized successfully')
|
||||||
|
|
||||||
|
return twoWaySync
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 3: Using the FirestoreSync augmentation with a Brainy graph
|
||||||
|
async function syncGraphToFirestore() {
|
||||||
|
console.log('Creating a Brainy graph and syncing it to Firestore...')
|
||||||
|
|
||||||
|
// Set up the one-way sync augmentation
|
||||||
|
const syncAugmentation = await setupOneWaySync()
|
||||||
|
|
||||||
|
// Create a Brainy graph
|
||||||
|
const graph = new BrainyGraph()
|
||||||
|
await graph.initialize()
|
||||||
|
|
||||||
|
// Add some nodes and edges to the graph
|
||||||
|
const node1 = await graph.addNode({
|
||||||
|
vector: [0.1, 0.2, 0.3],
|
||||||
|
metadata: { name: 'Node 1', description: 'First test node' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const node2 = await graph.addNode({
|
||||||
|
vector: [0.4, 0.5, 0.6],
|
||||||
|
metadata: { name: 'Node 2', description: 'Second test node' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const edge = await graph.addEdge({
|
||||||
|
sourceId: node1.id,
|
||||||
|
targetId: node2.id,
|
||||||
|
type: 'related',
|
||||||
|
weight: 0.75,
|
||||||
|
metadata: { description: 'Test relationship' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sync the nodes and edge to Firestore
|
||||||
|
await syncAugmentation.syncNodeToFirestore(node1)
|
||||||
|
await syncAugmentation.syncNodeToFirestore(node2)
|
||||||
|
await syncAugmentation.syncEdgeToFirestore(edge)
|
||||||
|
|
||||||
|
console.log('Graph data synced to Firestore successfully')
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
await syncAugmentation.shutDown()
|
||||||
|
await graph.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 4: Reading data from Firestore
|
||||||
|
async function readFromFirestore() {
|
||||||
|
console.log('Reading data from Firestore...')
|
||||||
|
|
||||||
|
// Set up the one-way sync augmentation
|
||||||
|
const syncAugmentation = await setupOneWaySync()
|
||||||
|
|
||||||
|
// Read all nodes from Firestore
|
||||||
|
const nodesResponse = await syncAugmentation.readData({
|
||||||
|
collection: 'brainy_nodes'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (nodesResponse.success) {
|
||||||
|
console.log(`Found ${nodesResponse.data.length} nodes in Firestore`)
|
||||||
|
console.log('First node:', nodesResponse.data[0])
|
||||||
|
} else {
|
||||||
|
console.error('Failed to read nodes:', nodesResponse.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a specific edge by ID
|
||||||
|
const edgeResponse = await syncAugmentation.readData({
|
||||||
|
collection: 'brainy_edges',
|
||||||
|
id: 'some-edge-id' // Replace with an actual edge ID
|
||||||
|
})
|
||||||
|
|
||||||
|
if (edgeResponse.success) {
|
||||||
|
console.log('Edge data:', edgeResponse.data)
|
||||||
|
} else {
|
||||||
|
console.error('Failed to read edge:', edgeResponse.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
await syncAugmentation.shutDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 5: Writing data to Firestore
|
||||||
|
async function writeToFirestore() {
|
||||||
|
console.log('Writing data to Firestore...')
|
||||||
|
|
||||||
|
// Set up the one-way sync augmentation
|
||||||
|
const syncAugmentation = await setupOneWaySync()
|
||||||
|
|
||||||
|
// Write a custom document to Firestore
|
||||||
|
const writeResponse = await syncAugmentation.writeData({
|
||||||
|
collection: 'custom_collection',
|
||||||
|
id: 'custom-doc-1',
|
||||||
|
document: {
|
||||||
|
name: 'Custom Document',
|
||||||
|
timestamp: new Date(),
|
||||||
|
values: [1, 2, 3, 4, 5],
|
||||||
|
nested: {
|
||||||
|
field1: 'value1',
|
||||||
|
field2: 'value2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (writeResponse.success) {
|
||||||
|
console.log('Document written successfully:', writeResponse.data)
|
||||||
|
} else {
|
||||||
|
console.error('Failed to write document:', writeResponse.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
await syncAugmentation.shutDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 6: Monitoring changes in Firestore
|
||||||
|
async function monitorFirestore() {
|
||||||
|
console.log('Monitoring changes in Firestore...')
|
||||||
|
|
||||||
|
// Set up the two-way sync augmentation
|
||||||
|
const syncAugmentation = await setupTwoWaySync()
|
||||||
|
|
||||||
|
// Monitor changes in the nodes collection
|
||||||
|
await syncAugmentation.monitorStream('brainy_nodes', (data) => {
|
||||||
|
console.log('Node change detected:', data)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Monitoring started. Changes will be logged as they occur.')
|
||||||
|
console.log('Press Ctrl+C to stop monitoring.')
|
||||||
|
|
||||||
|
// Keep the process running
|
||||||
|
// In a real application, you would integrate this with your application lifecycle
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log('Stopping monitoring...')
|
||||||
|
await syncAugmentation.shutDown()
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the examples
|
||||||
|
async function runExamples() {
|
||||||
|
try {
|
||||||
|
// Uncomment the example you want to run
|
||||||
|
// await syncGraphToFirestore();
|
||||||
|
// await readFromFirestore();
|
||||||
|
// await writeToFirestore();
|
||||||
|
// await monitorFirestore();
|
||||||
|
|
||||||
|
console.log('Example completed successfully')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error running example:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runExamples()
|
||||||
474
src/augmentations/firestoreSyncAugmentation.ts
Normal file
474
src/augmentations/firestoreSyncAugmentation.ts
Normal file
|
|
@ -0,0 +1,474 @@
|
||||||
|
/**
|
||||||
|
* FirestoreSync Conduit Augmentation
|
||||||
|
*
|
||||||
|
* This augmentation allows for syncing data to Firestore either one-way or two-way.
|
||||||
|
* One-way sync: Data is only pushed from Brainy to Firestore
|
||||||
|
* Two-way sync: Data is synchronized between Brainy and Firestore in both directions
|
||||||
|
*
|
||||||
|
* Note: This augmentation requires Firebase to be installed as a dependency.
|
||||||
|
* Install with: npm install firebase
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
AugmentationType,
|
||||||
|
IConduitAugmentation,
|
||||||
|
AugmentationResponse,
|
||||||
|
WebSocketConnection
|
||||||
|
} from '../types/augmentations.js'
|
||||||
|
import { HNSWNode, Edge } from '../coreTypes.js'
|
||||||
|
|
||||||
|
// Firebase imports will be dynamically loaded to avoid dependency issues
|
||||||
|
let firebase: any = null
|
||||||
|
let firestore: any = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for FirestoreSync augmentation
|
||||||
|
*/
|
||||||
|
export interface FirestoreSyncConfig {
|
||||||
|
/** Firebase configuration object */
|
||||||
|
firebaseConfig: {
|
||||||
|
apiKey: string
|
||||||
|
authDomain: string
|
||||||
|
projectId: string
|
||||||
|
storageBucket?: string
|
||||||
|
messagingSenderId?: string
|
||||||
|
appId: string
|
||||||
|
}
|
||||||
|
/** Collection name for nodes in Firestore */
|
||||||
|
nodesCollection: string
|
||||||
|
/** Collection name for edges in Firestore */
|
||||||
|
edgesCollection: string
|
||||||
|
/** Collection name for metadata in Firestore */
|
||||||
|
metadataCollection: string
|
||||||
|
/** Sync mode: 'one-way' (Brainy -> Firestore) or 'two-way' (bidirectional) */
|
||||||
|
syncMode: 'one-way' | 'two-way'
|
||||||
|
/** Sync interval in milliseconds (for two-way sync) */
|
||||||
|
syncInterval?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FirestoreSync Conduit Augmentation
|
||||||
|
* Allows for syncing data to Firestore either one-way or two-way
|
||||||
|
*/
|
||||||
|
export class FirestoreSyncAugmentation implements IConduitAugmentation {
|
||||||
|
readonly name: string
|
||||||
|
readonly description: string
|
||||||
|
enabled: boolean
|
||||||
|
private config: FirestoreSyncConfig
|
||||||
|
private isInitialized: boolean = false
|
||||||
|
private db: any = null
|
||||||
|
private syncIntervalId: NodeJS.Timeout | null = null
|
||||||
|
private lastSyncTimestamp: number = 0
|
||||||
|
|
||||||
|
constructor(name: string, config: FirestoreSyncConfig) {
|
||||||
|
this.name = name
|
||||||
|
this.description = 'Syncs data between Brainy and Firestore'
|
||||||
|
this.enabled = true
|
||||||
|
this.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the augmentation
|
||||||
|
*/
|
||||||
|
async initialize(): Promise<void> {
|
||||||
|
if (this.isInitialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dynamically import Firebase
|
||||||
|
try {
|
||||||
|
const firebaseModule = await import('firebase/app')
|
||||||
|
const firestoreModule = await import('firebase/firestore')
|
||||||
|
|
||||||
|
firebase = firebaseModule.default || firebaseModule
|
||||||
|
firestore = firestoreModule
|
||||||
|
} catch (importError) {
|
||||||
|
throw new Error(`Failed to import Firebase modules: ${importError}. Please install Firebase with: npm install firebase`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Firebase
|
||||||
|
const app = firebase.initializeApp(this.config.firebaseConfig, this.name)
|
||||||
|
this.db = firebase.firestore(app)
|
||||||
|
|
||||||
|
// Set up two-way sync if configured
|
||||||
|
if (this.config.syncMode === 'two-way' && this.config.syncInterval) {
|
||||||
|
this.startSyncInterval()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInitialized = true
|
||||||
|
console.log(`FirestoreSync augmentation '${this.name}' initialized successfully`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to initialize FirestoreSync augmentation: ${error}`)
|
||||||
|
throw new Error(`Failed to initialize FirestoreSync augmentation: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down the augmentation
|
||||||
|
*/
|
||||||
|
async shutDown(): Promise<void> {
|
||||||
|
if (this.syncIntervalId) {
|
||||||
|
clearInterval(this.syncIntervalId)
|
||||||
|
this.syncIntervalId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firebase && this.isInitialized) {
|
||||||
|
await firebase.app(this.name).delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInitialized = false
|
||||||
|
console.log(`FirestoreSync augmentation '${this.name}' shut down successfully`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status of the augmentation
|
||||||
|
*/
|
||||||
|
async getStatus(): Promise<'active' | 'inactive' | 'error'> {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return 'inactive'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
return 'error'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'active'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish a connection to Firestore
|
||||||
|
*/
|
||||||
|
establishConnection(
|
||||||
|
targetSystemId: string,
|
||||||
|
config: Record<string, unknown>
|
||||||
|
): AugmentationResponse<WebSocketConnection> {
|
||||||
|
// Ensure initialization happens before returning
|
||||||
|
this.ensureInitialized().catch(error => {
|
||||||
|
console.error(`Error initializing during establishConnection: ${error}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
// For Firestore, the connection is already established during initialization
|
||||||
|
// This method is mainly for compatibility with the IConduitAugmentation interface
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
connectionId: targetSystemId,
|
||||||
|
url: `https://firestore.googleapis.com/v1/projects/${this.config.firebaseConfig.projectId}/databases/(default)/documents`,
|
||||||
|
status: 'connected'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: {
|
||||||
|
connectionId: targetSystemId,
|
||||||
|
url: '',
|
||||||
|
status: 'error'
|
||||||
|
},
|
||||||
|
error: `Failed to establish connection: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from Firestore
|
||||||
|
*/
|
||||||
|
readData(
|
||||||
|
query: Record<string, unknown>,
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
): AugmentationResponse<unknown> {
|
||||||
|
// This is a synchronous wrapper around an async operation
|
||||||
|
// We'll start the async operation but return a placeholder response immediately
|
||||||
|
|
||||||
|
// Ensure we're initialized
|
||||||
|
this.ensureInitialized().catch(error => {
|
||||||
|
console.error(`Error initializing during readData: ${error}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Return a placeholder response
|
||||||
|
// In a real implementation, this would need to be redesigned to work synchronously
|
||||||
|
// or the interface would need to be updated to allow for Promise returns
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
message: "Reading data from Firestore (placeholder response)",
|
||||||
|
query: query,
|
||||||
|
options: options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: {},
|
||||||
|
error: `Failed to read data: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to Firestore
|
||||||
|
*/
|
||||||
|
writeData(
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
): AugmentationResponse<unknown> {
|
||||||
|
// This is a synchronous wrapper around an async operation
|
||||||
|
// We'll start the async operation but return a placeholder response immediately
|
||||||
|
|
||||||
|
// Ensure we're initialized
|
||||||
|
this.ensureInitialized().catch(error => {
|
||||||
|
console.error(`Error initializing during writeData: ${error}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Extract the ID if available for the response
|
||||||
|
const id = data.id as string || 'unknown-id'
|
||||||
|
|
||||||
|
// Start the async operation in the background
|
||||||
|
// In a real implementation, this would need to be redesigned to work synchronously
|
||||||
|
// or the interface would need to be updated to allow for Promise returns
|
||||||
|
setTimeout(() => {
|
||||||
|
this.writeDataAsync(data, options).catch(error => {
|
||||||
|
console.error(`Error in background writeData: ${error}`)
|
||||||
|
})
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
id,
|
||||||
|
message: "Writing data to Firestore (operation started in background)",
|
||||||
|
status: "pending"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
data: {},
|
||||||
|
error: `Failed to write data: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private async method to actually perform the write operation
|
||||||
|
private async writeDataAsync(
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
): Promise<void> {
|
||||||
|
await this.ensureInitialized()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { collection, id, document } = data as {
|
||||||
|
collection: string,
|
||||||
|
id: string,
|
||||||
|
document: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the document for Firestore
|
||||||
|
// Convert Maps and Sets to arrays or objects for Firestore compatibility
|
||||||
|
const firestoreDoc = this.prepareForFirestore(document)
|
||||||
|
|
||||||
|
// Add timestamp for sync tracking
|
||||||
|
firestoreDoc._lastUpdated = firebase.firestore.FieldValue.serverTimestamp()
|
||||||
|
firestoreDoc._source = 'brainy'
|
||||||
|
|
||||||
|
// Write to Firestore
|
||||||
|
await this.db.collection(collection).doc(id).set(firestoreDoc, { merge: true })
|
||||||
|
|
||||||
|
console.log(`Successfully wrote data to Firestore: ${collection}/${id}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to write data to Firestore: ${error}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitor a data stream in Firestore
|
||||||
|
*/
|
||||||
|
async monitorStream(streamId: string, callback: (data: unknown) => void): Promise<void> {
|
||||||
|
await this.ensureInitialized()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set up a listener for changes in the specified collection
|
||||||
|
const unsubscribe = this.db.collection(streamId)
|
||||||
|
.where('_lastUpdated', '>', new Date(this.lastSyncTimestamp))
|
||||||
|
.where('_source', '==', 'firestore') // Only listen for changes from Firestore
|
||||||
|
.onSnapshot((snapshot: any) => {
|
||||||
|
const changes = snapshot.docChanges()
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
const data = this.convertFirestoreDocToObject(change.doc)
|
||||||
|
callback({
|
||||||
|
type: change.type, // 'added', 'modified', or 'removed'
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last sync timestamp
|
||||||
|
this.lastSyncTimestamp = Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return the unsubscribe function wrapped in a Promise
|
||||||
|
return Promise.resolve(unsubscribe)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to monitor stream: ${error}`)
|
||||||
|
throw new Error(`Failed to monitor stream: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync a node to Firestore
|
||||||
|
*/
|
||||||
|
async syncNodeToFirestore(node: HNSWNode): Promise<void> {
|
||||||
|
await this.ensureInitialized()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const firestoreNode = this.prepareForFirestore(node)
|
||||||
|
firestoreNode._lastUpdated = firebase.firestore.FieldValue.serverTimestamp()
|
||||||
|
firestoreNode._source = 'brainy'
|
||||||
|
|
||||||
|
await this.db.collection(this.config.nodesCollection).doc(node.id).set(firestoreNode, { merge: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to sync node to Firestore: ${error}`)
|
||||||
|
throw new Error(`Failed to sync node to Firestore: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync an edge to Firestore
|
||||||
|
*/
|
||||||
|
async syncEdgeToFirestore(edge: Edge): Promise<void> {
|
||||||
|
await this.ensureInitialized()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const firestoreEdge = this.prepareForFirestore(edge)
|
||||||
|
firestoreEdge._lastUpdated = firebase.firestore.FieldValue.serverTimestamp()
|
||||||
|
firestoreEdge._source = 'brainy'
|
||||||
|
|
||||||
|
await this.db.collection(this.config.edgesCollection).doc(edge.id).set(firestoreEdge, { merge: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to sync edge to Firestore: ${error}`)
|
||||||
|
throw new Error(`Failed to sync edge to Firestore: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync metadata to Firestore
|
||||||
|
*/
|
||||||
|
async syncMetadataToFirestore(id: string, metadata: any): Promise<void> {
|
||||||
|
await this.ensureInitialized()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const firestoreMetadata = this.prepareForFirestore(metadata)
|
||||||
|
firestoreMetadata._lastUpdated = firebase.firestore.FieldValue.serverTimestamp()
|
||||||
|
firestoreMetadata._source = 'brainy'
|
||||||
|
|
||||||
|
await this.db.collection(this.config.metadataCollection).doc(id).set(firestoreMetadata, { merge: true })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to sync metadata to Firestore: ${error}`)
|
||||||
|
throw new Error(`Failed to sync metadata to Firestore: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the sync interval for two-way sync
|
||||||
|
*/
|
||||||
|
private startSyncInterval(): void {
|
||||||
|
if (this.syncIntervalId) {
|
||||||
|
clearInterval(this.syncIntervalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastSyncTimestamp = Date.now()
|
||||||
|
|
||||||
|
this.syncIntervalId = setInterval(async () => {
|
||||||
|
if (!this.enabled || !this.isInitialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Sync from Firestore to Brainy
|
||||||
|
await this.syncFromFirestore()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error during two-way sync: ${error}`)
|
||||||
|
}
|
||||||
|
}, this.config.syncInterval || 60000) // Default to 1 minute if not specified
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync data from Firestore to Brainy
|
||||||
|
*/
|
||||||
|
private async syncFromFirestore(): Promise<void> {
|
||||||
|
// This would typically call back to the Brainy system to update its data
|
||||||
|
// For now, we'll just log that it would happen
|
||||||
|
console.log('Syncing data from Firestore to Brainy (not implemented yet)')
|
||||||
|
|
||||||
|
// In a real implementation, this would:
|
||||||
|
// 1. Query Firestore for documents updated since lastSyncTimestamp
|
||||||
|
// 2. For each document, update the corresponding data in Brainy
|
||||||
|
// 3. Update lastSyncTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the augmentation is initialized
|
||||||
|
*/
|
||||||
|
private async ensureInitialized(): Promise<void> {
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
await this.initialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a Firestore document to a plain JavaScript object
|
||||||
|
*/
|
||||||
|
private convertFirestoreDocToObject(doc: any): any {
|
||||||
|
const data = doc.data()
|
||||||
|
return {
|
||||||
|
id: doc.id,
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an object for Firestore by converting Maps and Sets
|
||||||
|
*/
|
||||||
|
private prepareForFirestore(obj: any): any {
|
||||||
|
if (obj === null || typeof obj !== 'object') {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(item => this.prepareForFirestore(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
const result: Record<string, any> = {}
|
||||||
|
for (const [key, value] of obj.entries()) {
|
||||||
|
result[key] = this.prepareForFirestore(value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Set) {
|
||||||
|
return Array.from(obj).map(item => this.prepareForFirestore(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Record<string, any> = {}
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
result[key] = this.prepareForFirestore(value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and register a FirestoreSync augmentation
|
||||||
|
*/
|
||||||
|
export function createFirestoreSyncAugmentation(
|
||||||
|
name: string,
|
||||||
|
config: FirestoreSyncConfig
|
||||||
|
): FirestoreSyncAugmentation {
|
||||||
|
return new FirestoreSyncAugmentation(name, config)
|
||||||
|
}
|
||||||
15
src/index.ts
15
src/index.ts
|
|
@ -109,6 +109,21 @@ export type {
|
||||||
AugmentationLoadResult
|
AugmentationLoadResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export augmentation implementations
|
||||||
|
import {
|
||||||
|
FirestoreSyncAugmentation,
|
||||||
|
createFirestoreSyncAugmentation
|
||||||
|
} from './augmentations/firestoreSyncAugmentation.js'
|
||||||
|
import type { FirestoreSyncConfig } from './augmentations/firestoreSyncAugmentation.js'
|
||||||
|
|
||||||
|
export {
|
||||||
|
FirestoreSyncAugmentation,
|
||||||
|
createFirestoreSyncAugmentation
|
||||||
|
}
|
||||||
|
export type {
|
||||||
|
FirestoreSyncConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Export types
|
// Export types
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export enum AugmentationType {
|
||||||
ACTIVATION = 'activation',
|
ACTIVATION = 'activation',
|
||||||
WEBSOCKET = 'webSocket'
|
WEBSOCKET = 'webSocket'
|
||||||
}
|
}
|
||||||
type WebSocketConnection = {
|
export type WebSocketConnection = {
|
||||||
connectionId: string
|
connectionId: string
|
||||||
url: string
|
url: string
|
||||||
status: 'connected' | 'disconnected' | 'error'
|
status: 'connected' | 'disconnected' | 'error'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue