Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
dad9cff
feat: persistent history for SDS
danisharora099 Nov 14, 2025
454dc4a
refactor: simplify localHistory initialization by removing intermedia…
danisharora099 Nov 20, 2025
ca24c09
feat(history): introduce `ILocalHistory` interface and refactor `Pers…
danisharora099 Nov 20, 2025
032c3f5
refactor: rename `storageKey` to `storageKeyPrefix` and update storag…
danisharora099 Nov 20, 2025
b8ee363
refactor: rename persistence methods from `persist`/`restore` to `sav…
danisharora099 Nov 20, 2025
322240b
refactor: Remove silent error suppression for persistent history stor…
danisharora099 Nov 20, 2025
15f1a22
feat: remove `storageKeyPrefix` and `getDefaultHistoryStorage`, simpl…
danisharora099 Nov 20, 2025
581430a
feat: Conditionally set default history storage to localStorage for i…
danisharora099 Nov 21, 2025
d52ea9f
tests: use memLocalHistory for message_channel.spec.ts
danisharora099 Nov 27, 2025
d6412e5
test: add unit tests for localStorage persistence and error handling …
danisharora099 Nov 27, 2025
ad4973a
test: local storage specific test
danisharora099 Nov 27, 2025
503c348
feat: Introduce `PersistentStorage` for message history management an…
danisharora099 Nov 27, 2025
e54dacd
chore: remove unused exports
danisharora099 Nov 27, 2025
a3202b5
chore: interface abstractions
danisharora099 Nov 27, 2025
4757322
chore: update interface to type as we're not implementing it
danisharora099 Nov 27, 2025
ff8b4e5
fix: MemLocalHistory uses optional chaining
danisharora099 Nov 27, 2025
2ee8b08
chore: add logging to LocalHistory for storage init
danisharora099 Nov 27, 2025
41f8fdf
chore: address comments, reduce diff
danisharora099 Dec 9, 2025
721c494
chore: update word history to storage
danisharora099 Dec 9, 2025
8a749c4
refactor: implement browser and Node.js storage solutions for message…
danisharora099 Dec 10, 2025
e396c3b
refactor: cleaner API for storage in LocalHistoryOptions
danisharora099 Dec 10, 2025
ab6d3cf
minor fixes
danisharora099 Dec 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ CLAUDE.md
.env
postgres-data/
packages/rln/waku-rlnv2-contract/
/packages/**/allure-results
/packages/**/allure-results
17 changes: 15 additions & 2 deletions packages/sds/karma.conf.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
const config = require("../../karma.conf.cjs");
import path from "path";

module.exports = config;
import baseConfig from "../../karma.conf.cjs";

export default function (config) {
baseConfig(config);

const storageDir = path.resolve(__dirname, "src/message_channel/storage");

// Swap node storage for browser storage in webpack builds
config.webpack.resolve.alias = {
...config.webpack.resolve.alias,
[path.join(storageDir, "node.ts")]: path.join(storageDir, "browser.ts"),
[path.join(storageDir, "node.js")]: path.join(storageDir, "browser.ts")
};
}
3 changes: 3 additions & 0 deletions packages/sds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"description": "Scalable Data Sync implementation for the browser. Based on https://github.com/vacp2p/rfc-index/blob/main/vac/raw/sds.md",
"types": "./dist/index.d.ts",
"module": "./dist/index.js",
"browser": {
"./dist/message_channel/storage/index.js": "./dist/message_channel/storage/browser.js"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { expect } from "chai";

import { MemLocalHistory } from "./mem_local_history.js";
import { LocalHistory } from "./local_history.js";
import { ContentMessage } from "./message.js";

describe("MemLocalHistory", () => {
describe("LocalHistory", () => {
it("Cap max size when messages are pushed one at a time", () => {
const maxSize = 2;

const hist = new MemLocalHistory(maxSize);
const hist = new LocalHistory({ maxSize });

hist.push(
new ContentMessage("1", "c", "a", [], 1n, undefined, new Uint8Array([1]))
Expand All @@ -31,7 +31,7 @@ describe("MemLocalHistory", () => {
it("Cap max size when a pushed array is exceeding the cap", () => {
const maxSize = 2;

const hist = new MemLocalHistory(maxSize);
const hist = new LocalHistory({ maxSize });

hist.push(
new ContentMessage("1", "c", "a", [], 1n, undefined, new Uint8Array([1]))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { Logger } from "@waku/utils";
import _ from "lodash";

import { ContentMessage, isContentMessage } from "./message.js";
import { Storage } from "./storage/index.js";

export const DEFAULT_MAX_LENGTH = 10_000;

/**
* Options for the LocalHistory constructor.
* @param storage - The storage to use for the local history.
* - prefix - The prefix for the storage.
* - customInstance - The custom storage instance to use.
* @param maxSize - The maximum number of messages to store.
*/
export type LocalHistoryOptions = {
storage?: {
prefix?: string;
customInstance?: Storage;
};
maxSize?: number;
};

const log = new Logger("sds:local-history");

/**
* In-Memory implementation of a local history of messages.
*
Expand All @@ -17,15 +36,28 @@ export const DEFAULT_MAX_LENGTH = 10_000;
* If an array of items longer than `maxLength` is pushed, dropping will happen
* at next push.
*/
export class MemLocalHistory {
export class LocalHistory {
private items: ContentMessage[] = [];
private readonly storage?: Storage;
private readonly maxSize: number;

public constructor(opts: LocalHistoryOptions = {}) {
const { storage, maxSize } = opts;
const { prefix, customInstance } = storage ?? {};
this.maxSize = maxSize ?? DEFAULT_MAX_LENGTH;
if (customInstance) {
this.storage = customInstance;
log.info("Using custom storage instance", { customInstance });
} else if (prefix) {
this.storage = new Storage(prefix);
log.info("Creating storage with prefix", { prefix });
} else {
this.storage = undefined;
log.info("Using in-memory storage");
}

/**
* Construct a new in-memory local history
*
* @param maxLength The maximum number of message to store.
*/
public constructor(private maxLength: number = DEFAULT_MAX_LENGTH) {}
this.load();
}

public get length(): number {
return this.items.length;
Expand All @@ -47,11 +79,13 @@ export class MemLocalHistory {
this.items = _.uniqBy(combinedItems, "messageId");

// Let's drop older messages if max length is reached
if (this.length > this.maxLength) {
const numItemsToRemove = this.length - this.maxLength;
if (this.length > this.maxSize) {
const numItemsToRemove = this.length - this.maxSize;
this.items.splice(0, numItemsToRemove);
}

this.save();

return this.items.length;
}

Expand Down Expand Up @@ -99,4 +133,15 @@ export class MemLocalHistory {
);
}
}

private save(): void {
this.storage?.save(this.items);
}

private load(): void {
const messages = this.storage?.load() ?? [];
if (messages.length > 0) {
this.items = messages;
}
}
}
Loading
Loading