diff --git a/examples/app.ts b/examples/app.ts index d7d38ac..6d3e116 100644 --- a/examples/app.ts +++ b/examples/app.ts @@ -1,5 +1,5 @@ import Debug from 'debug'; -import {Collection} from "../src/collection"; +import {BasicCollection} from '../src/collection-basic'; import {Entity} from "../src/entity"; import {RhizomeNode} from "../src/node"; const debug = Debug('example-app'); @@ -23,7 +23,7 @@ type User = { // Enable API to read lossless view rhizomeNode.httpServer.httpApi.serveLossless(); - const users = new Collection("user"); + const users = new BasicCollection("user"); users.rhizomeConnect(rhizomeNode); users.onUpdate((u: Entity) => { diff --git a/src/collection.ts b/src/collection-abstract.ts similarity index 81% rename from src/collection.ts rename to src/collection-abstract.ts index 49a54d2..d422e3d 100644 --- a/src/collection.ts +++ b/src/collection-abstract.ts @@ -1,38 +1,31 @@ -// A basic collection of entities -// This may be extended to house a collection of objects that all follow a common schema. -// It should enable operations like removing a property removes the value from the entities in the collection -// It could then be further extended with e.g. table semantics like filter, sort, join - import Debug from 'debug'; import {randomUUID} from "node:crypto"; import EventEmitter from "node:events"; import {Delta} from "./delta"; import {Entity, EntityProperties} from "./entity"; -import {LastWriteWins, ResolvedViewOne} from './last-write-wins'; +import {ResolvedViewOne} from './last-write-wins'; import {RhizomeNode} from "./node"; import {DomainEntityID} from "./types"; -const debug = Debug('rz:collection'); +const debug = Debug('rz:abstract-collection'); -export class Collection { +export abstract class Collection { rhizomeNode?: RhizomeNode; name: string; eventStream = new EventEmitter(); - lossy?: LastWriteWins; + lossy?: T; constructor(name: string) { this.name = name; } - // Instead of trying to update our final view of the entity with every incoming delta, - // let's try this: - // - keep a lossless view (of everything) - // - build a lossy view when needed - // This approach is simplistic, but can then be optimized and enhanced. + abstract initializeView(): void; + + abstract resolve(id: string): ResolvedViewOne | undefined; rhizomeConnect(rhizomeNode: RhizomeNode) { this.rhizomeNode = rhizomeNode; - this.lossy = new LastWriteWins(this.rhizomeNode.lossless); + this.initializeView(); // Listen for completed transactions, and emit updates to event stream this.rhizomeNode.lossless.eventStream.on("updated", (id) => { @@ -43,13 +36,12 @@ export class Collection { this.eventStream.emit("update", res); }); - rhizomeNode.httpServer.httpApi.serveCollection(this); + // TODO: Fix this + rhizomeNode.httpServer.httpApi.serveCollection(this); debug(`[${this.rhizomeNode.config.peerId}]`, `Connected ${this.name} to rhizome`); } - // Applies the javascript rules for updating object values, - // e.g. set to `undefined` to delete a property. // This function is here instead of Entity so that it can: // - read the current state in order to build its delta // - include the collection name in the delta it produces @@ -206,14 +198,4 @@ export class Collection { return res; } - resolve( - id: string - ): ResolvedViewOne | undefined { - if (!this.rhizomeNode) throw new Error('collection not connected to rhizome'); - if (!this.lossy) throw new Error('lossy view not initialized'); - - const res = this.lossy.resolve([id]) || {}; - - return res[id]; - } } diff --git a/src/collection-basic.ts b/src/collection-basic.ts new file mode 100644 index 0000000..da1a639 --- /dev/null +++ b/src/collection-basic.ts @@ -0,0 +1,27 @@ +// A basic collection of entities +// This may be extended to house a collection of objects that all follow a common schema. +// It should enable operations like removing a property removes the value from the entities in the collection +// It could then be further extended with e.g. table semantics like filter, sort, join + +import {Collection} from './collection-abstract'; +import {LastWriteWins, ResolvedViewOne} from './last-write-wins'; + +export class BasicCollection extends Collection { + lossy?: LastWriteWins; + + initializeView() { + if (!this.rhizomeNode) throw new Error('not connected to rhizome'); + this.lossy = new LastWriteWins(this.rhizomeNode.lossless); + } + + resolve( + id: string + ): ResolvedViewOne | undefined { + if (!this.rhizomeNode) throw new Error('collection not connected to rhizome'); + if (!this.lossy) throw new Error('lossy view not initialized'); + + const res = this.lossy.resolve([id]) || {}; + + return res[id]; + } +} diff --git a/src/collection-relational.ts b/src/collection-relational.ts new file mode 100644 index 0000000..078b17f --- /dev/null +++ b/src/collection-relational.ts @@ -0,0 +1,26 @@ +import {Collection} from "./collection-abstract"; +import {LastWriteWins, ResolvedViewOne} from "./last-write-wins"; + +class RelationalView extends LastWriteWins { +} + +export class RelationalCollection extends Collection { + lossy?: RelationalView; + + initializeView() { + if (!this.rhizomeNode) throw new Error('not connected to rhizome'); + this.lossy = new RelationalView(this.rhizomeNode.lossless); + } + + resolve( + id: string + ): ResolvedViewOne | undefined { + if (!this.rhizomeNode) throw new Error('collection not connected to rhizome'); + if (!this.lossy) throw new Error('lossy view not initialized'); + + const res = this.lossy.resolve([id]) || {}; + + return res[id]; + } + +} diff --git a/src/entity.ts b/src/entity.ts index 616da1b..37b4c71 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -7,7 +7,6 @@ // - As typescript interfaces? // - As typescript classes? -import {Collection} from "./collection"; import {PropertyTypes} from "./types"; export type EntityProperties = { @@ -20,11 +19,5 @@ export class Entity { constructor( readonly id: string, - readonly collection?: Collection ) {} - - async save() { - if (!this.collection) throw new Error('to save this entity you must specify the collection'); - return this.collection.put(this.id, this.properties); - } } diff --git a/src/http/api.ts b/src/http/api.ts index e9dbb80..bc69868 100644 --- a/src/http/api.ts +++ b/src/http/api.ts @@ -1,5 +1,5 @@ import express, {Router} from "express"; -import {Collection} from "../collection"; +import {Collection} from "../collection-abstract"; import {Delta} from "../delta"; import {RhizomeNode} from "../node"; @@ -57,7 +57,8 @@ export class HttpApi { }); } - serveCollection(collection: Collection) { + // serveCollection(collection: T) { + serveCollection(collection: Collection) { const {name} = collection; // Get the ID of all domain entities diff --git a/src/relational.ts b/src/relational.ts deleted file mode 100644 index d2061c7..0000000 --- a/src/relational.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Collection} from "./collection"; - - - - -export class RelationalCollection extends Collection { - // lossy?: - -} diff --git a/util/app.ts b/util/app.ts index 5bbf7e6..2713bcd 100644 --- a/util/app.ts +++ b/util/app.ts @@ -1,4 +1,4 @@ -import {Collection} from "../src/collection"; +import {BasicCollection} from "../src/collection-basic"; import {RhizomeNode, RhizomeNodeConfig} from "../src/node"; const start = 5000; @@ -17,7 +17,7 @@ export class App extends RhizomeNode { ...config, }); - const users = new Collection("user"); + const users = new BasicCollection("user"); users.rhizomeConnect(this); const {httpAddr, httpPort} = this.config;