Skip to content

Commit

Permalink
refactor: allow switching out the DB bindings (#7486)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone authored Dec 12, 2024
1 parent 89c8c34 commit 3f6229b
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 25 deletions.
16 changes: 16 additions & 0 deletions packages/core/src/bindings/db/jsonl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { JsonlDB } from "@alcalzone/jsonl-db";
import {
type Database,
type DatabaseFactory,
type DatabaseOptions,
} from "@zwave-js/shared/bindings";

/** An implementation of the Database bindings for Node.js based on JsonlDB */
export const db: DatabaseFactory = {
createInstance<V>(
filename: string,
options?: DatabaseOptions<V>,
): Database<V> {
return new JsonlDB(filename, options);
},
};
4 changes: 2 additions & 2 deletions packages/core/src/values/CacheBackedMap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { JsonlDB } from "@alcalzone/jsonl-db";
import { type Database } from "@zwave-js/shared/bindings";

export interface CacheBackedMapKeys<K extends string | number> {
/** The common prefix all keys start with */
Expand All @@ -12,7 +12,7 @@ export interface CacheBackedMapKeys<K extends string | number> {
/** Wrapper class which allows storing a Map as a subset of a JsonlDB */
export class CacheBackedMap<K extends string | number, V> implements Map<K, V> {
constructor(
private readonly cache: JsonlDB<any>,
private readonly cache: Database<any>,
private readonly cacheKeys: CacheBackedMapKeys<K>,
) {
this.map = new Map();
Expand Down
14 changes: 8 additions & 6 deletions packages/core/src/values/ValueDB.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { JsonlDB } from "@alcalzone/jsonl-db";
import { TypedEventTarget } from "@zwave-js/shared";
import { type Database } from "@zwave-js/shared/bindings";
import type { CommandClasses } from "../definitions/CommandClasses.js";
import {
ZWaveError,
Expand Down Expand Up @@ -106,8 +106,8 @@ export class ValueDB extends TypedEventTarget<ValueDBEventCallbacks> {
*/
public constructor(
nodeId: number,
valueDB: JsonlDB,
metadataDB: JsonlDB<ValueMetadata>,
valueDB: Database<unknown>,
metadataDB: Database<ValueMetadata>,
ownKeys?: Set<string>,
) {
super();
Expand All @@ -119,8 +119,8 @@ export class ValueDB extends TypedEventTarget<ValueDBEventCallbacks> {
}

private nodeId: number;
private _db: JsonlDB<unknown>;
private _metadata: JsonlDB<ValueMetadata>;
private _db: Database<unknown>;
private _metadata: Database<ValueMetadata>;
private _index: Set<string>;

private buildIndex(): Set<string> {
Expand Down Expand Up @@ -603,7 +603,9 @@ function compareDBKeyFast(
}

/** Extracts an index for each node from one or more JSONL DBs */
export function indexDBsByNode(databases: JsonlDB[]): Map<number, Set<string>> {
export function indexDBsByNode(
databases: Database<unknown>[],
): Map<number, Set<string>> {
const indexes = new Map<number, Set<string>>();
for (const db of databases) {
for (const key of db.keys()) {
Expand Down
39 changes: 39 additions & 0 deletions packages/shared/src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,42 @@ export interface FileSystem
{}

export type Platform = "linux" | "darwin" | "win32" | "browser" | "other";

export type DatabaseOptions<V> = {
/**
* An optional reviver function (similar to JSON.parse) to transform parsed values before they are accessible in the database.
* If this function is defined, it must always return a value.
*/
reviver?: (key: string, value: any) => V;
/**
* An optional serializer function (similar to JSON.serialize) to transform values before they are written to the database file.
* If this function is defined, it must always return a value.
*/
serializer?: (key: string, value: V) => any;
/** Whether timestamps should be recorded when setting values. Default: false */
enableTimestamps?: boolean;
};

export interface DatabaseFactory {
createInstance<V>(
filename: string,
options?: DatabaseOptions<V>,
): Database<V>;
}

export interface Database<V> {
open(): Promise<void>;
close(): Promise<void>;

has: Map<string, V>["has"];
get: Map<string, V>["get"];
set(key: string, value: V, updateTimestamp?: boolean): this;
delete(key: string): boolean;
clear(): void;

getTimestamp(key: string): number | undefined;
get size(): number;

keys: Map<string, V>["keys"];
entries: Map<string, V>["entries"];
}
26 changes: 16 additions & 10 deletions packages/zwave-js/src/lib/driver/Driver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JsonlDB, type JsonlDBOptions } from "@alcalzone/jsonl-db";
import { type JsonlDBOptions } from "@alcalzone/jsonl-db";
import {
type CCAPIHost,
type CCEncodingContext,
Expand Down Expand Up @@ -193,6 +193,7 @@ import {
pick,
} from "@zwave-js/shared";
import {
type Database,
type ReadFile,
type ReadFileSystemInfo,
} from "@zwave-js/shared/bindings";
Expand Down Expand Up @@ -894,19 +895,19 @@ export class Driver extends TypedEventTarget<DriverEventCallbacks>

public readonly cacheDir: string;

private _valueDB: JsonlDB | undefined;
private _valueDB: Database<unknown> | undefined;
/** @internal */
public get valueDB(): JsonlDB | undefined {
public get valueDB(): Database<unknown> | undefined {
return this._valueDB;
}
private _metadataDB: JsonlDB<ValueMetadata> | undefined;
private _metadataDB: Database<ValueMetadata> | undefined;
/** @internal */
public get metadataDB(): JsonlDB<ValueMetadata> | undefined {
public get metadataDB(): Database<ValueMetadata> | undefined {
return this._metadataDB;
}
private _networkCache: JsonlDB<any> | undefined;
private _networkCache: Database<any> | undefined;
/** @internal */
public get networkCache(): JsonlDB<any> {
public get networkCache(): Database<any> {
if (this._networkCache == undefined) {
throw new ZWaveError(
"The network cache was not yet initialized!",
Expand Down Expand Up @@ -1332,6 +1333,8 @@ export class Driver extends TypedEventTarget<DriverEventCallbacks>
?? (await import("@zwave-js/core/bindings/fs/node")).fs,
serial: this._options.host?.serial
?? (await import("@zwave-js/serial/bindings/node")).serial,
db: this._options.host?.db
?? (await import("@zwave-js/core/bindings/db/jsonl")).db,
};

const spOpenPromise = createDeferredPromise();
Expand Down Expand Up @@ -1593,7 +1596,7 @@ export class Driver extends TypedEventTarget<DriverEventCallbacks>
this.cacheDir,
`${homeId.toString(16)}.jsonl`,
);
this._networkCache = new JsonlDB(networkCacheFile, {
this._networkCache = this.bindings.db.createInstance(networkCacheFile, {
...options,
serializer: serializeNetworkCacheValue,
reviver: deserializeNetworkCacheValue,
Expand All @@ -1614,7 +1617,7 @@ export class Driver extends TypedEventTarget<DriverEventCallbacks>
this.cacheDir,
`${homeId.toString(16)}.values.jsonl`,
);
this._valueDB = new JsonlDB(valueDBFile, {
this._valueDB = this.bindings.db.createInstance(valueDBFile, {
...options,
enableTimestamps: true,
reviver: (_key, value) => deserializeCacheValue(value),
Expand All @@ -1626,7 +1629,10 @@ export class Driver extends TypedEventTarget<DriverEventCallbacks>
this.cacheDir,
`${homeId.toString(16)}.metadata.jsonl`,
);
this._metadataDB = new JsonlDB(metadataDBFile, options);
this._metadataDB = this.bindings.db.createInstance(
metadataDBFile,
options,
);
await this._metadataDB.open();

if (process.env.NO_CACHE === "true") {
Expand Down
7 changes: 6 additions & 1 deletion packages/zwave-js/src/lib/driver/Host.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// FIXME: This should eventually live in @zwave-js/host

import { type Serial } from "@zwave-js/serial";
import { type FileSystem, type Platform } from "@zwave-js/shared/bindings";
import {
type DatabaseFactory,
type FileSystem,
type Platform,
} from "@zwave-js/shared/bindings";

/** Abstractions for a host system Z-Wave JS is running on */
export interface Host {
fs: FileSystem;
platform: Platform;
serial: Serial;
db: DatabaseFactory;
}
11 changes: 7 additions & 4 deletions packages/zwave-js/src/lib/driver/NetworkCache.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { JsonlDB } from "@alcalzone/jsonl-db";
import { type AssociationAddress } from "@zwave-js/cc";
import {
type CommandClasses,
Expand All @@ -12,7 +11,11 @@ import {
securityClassOrder,
} from "@zwave-js/core";
import { Bytes, getEnumMemberName, num2hex, pickDeep } from "@zwave-js/shared";
import type { ReadFile, ReadFileSystemInfo } from "@zwave-js/shared/bindings";
import type {
Database,
ReadFile,
ReadFileSystemInfo,
} from "@zwave-js/shared/bindings";
import { isArray, isObject } from "alcalzone-shared/typeguards";
import path from "pathe";
import {
Expand Down Expand Up @@ -621,8 +624,8 @@ const legacyPaths = {

export async function migrateLegacyNetworkCache(
homeId: number,
networkCache: JsonlDB,
valueDB: JsonlDB,
networkCache: Database<any>,
valueDB: Database<unknown>,
fs: ReadFileSystemInfo & ReadFile,
cacheDir: string,
): Promise<void> {
Expand Down
7 changes: 6 additions & 1 deletion packages/zwave-js/src/lib/driver/ZWaveOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
} from "@zwave-js/core";
import { type Serial, type ZWaveSerialStream } from "@zwave-js/serial";
import { type DeepPartial, type Expand } from "@zwave-js/shared";
import type { FileSystem } from "@zwave-js/shared/bindings";
import type { DatabaseFactory, FileSystem } from "@zwave-js/shared/bindings";
import type {
InclusionUserCallbacks,
JoinNetworkUserCallbacks,
Expand Down Expand Up @@ -137,6 +137,11 @@ export interface ZWaveOptions {
* Specifies which bindings are used interact with serial ports.
*/
serial?: Serial;

/**
* Specifies which bindings are used to interact with the database used to store the cache.
*/
db?: DatabaseFactory;
};

storage: {
Expand Down
7 changes: 6 additions & 1 deletion packages/zwave-js/src/lib/zniffer/Zniffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,12 @@ export class Zniffer extends TypedEventTarget<ZnifferEventCallbacks> {
* The host bindings used to access file system etc.
*/
// This is set during `init()` and should not be accessed before
private bindings!: Required<NonNullable<ZWaveOptions["host"]>>;
private bindings!: Omit<
Required<
NonNullable<ZWaveOptions["host"]>
>,
"db"
>;

private serialFactory: ZnifferSerialStreamFactory | undefined;
/** The serial port instance */
Expand Down

0 comments on commit 3f6229b

Please sign in to comment.