feat(views): add DeltaV2 support to Lossless view

- Update Lossless.ingestDelta to accept both Delta and DeltaV2
- Add conversion from DeltaV2 to DeltaV1 during ingestion
- Add test case for DeltaV2 ingestion
- Ensure backward compatibility with existing DeltaV1 code
This commit is contained in:
Lentil Hoffman 2025-06-19 20:57:16 -05:00
parent 3a6191a2a2
commit c173f3475e
Signed by: lentil
GPG Key ID: 0F5B99F3F4D0C087
3 changed files with 89 additions and 3 deletions

View File

@ -0,0 +1,7 @@
---
description: Update deltas to use the object style for pointers
---
- in the current file, for each v1 delta, rewrite it as a v2 delta
- make sure the new delta is isomorphic to the original
- do not include a timestamp

View File

@ -94,6 +94,75 @@ describe('Lossless', () => {
}); });
}); });
it('accepts DeltaV2 instances', () => {
const delta = new DeltaV2({
creator: 'a',
host: 'h',
pointers: {
actor: {"keanu": "roles"},
role: {"neo": "actor"},
film: {"the_matrix": "cast"},
base_salary: 1000000,
salary_currency: "usd"
}
});
const lossless = new Lossless(node);
lossless.ingestDelta(delta);
expect(lossless.view()).toMatchObject({
keanu: {
referencedAs: ["actor"],
propertyDeltas: {
roles: [{
creator: "a",
host: "h",
pointers: [
{actor: "keanu"},
{role: "neo"},
{film: "the_matrix"},
{base_salary: 1000000},
{salary_currency: "usd"},
],
}],
},
},
neo: {
referencedAs: ["role"],
propertyDeltas: {
actor: [{
creator: "a",
host: "h",
pointers: [
{actor: "keanu"},
{role: "neo"},
{film: "the_matrix"},
{base_salary: 1000000},
{salary_currency: "usd"},
],
}],
},
},
the_matrix: {
referencedAs: ["film"],
propertyDeltas: {
cast: [{
creator: "a",
host: "h",
pointers: [
{actor: "keanu"},
{role: "neo"},
{film: "the_matrix"},
{base_salary: 1000000},
{salary_currency: "usd"},
],
}],
},
},
});
});
describe('can filter deltas', () => { describe('can filter deltas', () => {
const lossless = new Lossless(node); const lossless = new Lossless(node);
@ -242,4 +311,5 @@ describe('Lossless', () => {
expect(filteredView.process1.propertyDeltas.status.every(d => d.creator === 'A')).toBe(true); expect(filteredView.process1.propertyDeltas.status.every(d => d.creator === 'A')).toBe(true);
}); });
}); });
}); });

View File

@ -3,7 +3,7 @@
import Debug from 'debug'; import Debug from 'debug';
import EventEmitter from 'events'; import EventEmitter from 'events';
import {Delta, DeltaFilter, DeltaID, DeltaNetworkImageV1} from '../core/delta'; import {Delta, DeltaFilter, DeltaID, DeltaNetworkImageV1, DeltaV2} from '../core/delta';
import {RhizomeNode} from '../node'; import {RhizomeNode} from '../node';
import {Transactions} from '../features/transactions'; import {Transactions} from '../features/transactions';
import {DomainEntityID, PropertyID, PropertyTypes, TransactionID, ViewMany} from "../core/types"; import {DomainEntityID, PropertyID, PropertyTypes, TransactionID, ViewMany} from "../core/types";
@ -34,7 +34,11 @@ class LosslessEntity {
constructor(readonly lossless: Lossless, readonly id: DomainEntityID) {} constructor(readonly lossless: Lossless, readonly id: DomainEntityID) {}
addDelta(delta: Delta) { addDelta(delta: Delta | DeltaV2) {
// Convert DeltaV2 to DeltaV1 if needed
if (delta instanceof DeltaV2) {
delta = delta.toV1();
}
const targetContexts = delta.pointers const targetContexts = delta.pointers
.filter(({target}) => target === this.id) .filter(({target}) => target === this.id)
.map(({targetContext}) => targetContext) .map(({targetContext}) => targetContext)
@ -87,7 +91,12 @@ export class Lossless {
}); });
} }
ingestDelta(delta: Delta): TransactionID | undefined { ingestDelta(delta: Delta | DeltaV2): TransactionID | undefined {
// Convert DeltaV2 to DeltaV1 if needed
if (delta instanceof DeltaV2) {
delta = delta.toV1();
}
// Store delta for negation processing // Store delta for negation processing
this.allDeltas.set(delta.id, delta); this.allDeltas.set(delta.id, delta);