Skip to content

Instantly share code, notes, and snippets.

@vedantroy
Last active December 31, 2024 09:15
Show Gist options
  • Save vedantroy/df6b18fa89bc24acfe89fc8493743378 to your computer and use it in GitHub Desktop.
Save vedantroy/df6b18fa89bc24acfe89fc8493743378 to your computer and use it in GitHub Desktop.
SQLite-backed key-value store with JS-like object manipulation and automatic JSON serialization.
import Database from 'better-sqlite3';
import { createDatabaseClient } from './proxy.ts';
// 1) Create an in-memory DB and your table(s).
const db = new Database(':memory:');
db.exec(`
CREATE TABLE users (
id TEXT PRIMARY KEY,
data JSON
);
`);
// 2) Create the parallel client with separate 'rdr' & 'wtr'.
const { rdr, wtr } = createDatabaseClient(db, { users: {} });
// 3) Insert a row in the 'users' table:
wtr.users['alice'] = { name: 'Alice', age: 30 };
// 4) Check it via the reader:
console.log(rdr.users['alice']);
// => { name: 'Alice', age: 30 }
// 5) Partial update: only set 'nested.foo' -> 42
wtr.users['alice'].nested = { foo: 42 };
// 6) Now the row has been updated in the DB:
console.log(rdr.users['alice']);
/*
{
name: 'Alice',
age: 30,
nested: { foo: 42 }
}
*/
// 7) Check presence:
console.log('alice' in rdr.users); // true
console.log(rdr.users.has('bob')); // false
// 9) Partial delete:
delete wtr.users['alice'].nested.foo;
// => Removes only nested.foo
// 10) Full delete:
delete wtr.users['alice'];
console.log(rdr.users['alice']);
// => undefined
import Database from 'better-sqlite3';
////////////////////////////////////////
// 1. Types
////////////////////////////////////////
export type SchemaDefinition = Record<string, any>;
export interface CreateDBOptions {
idColumn?: string;
jsonColumn?: string;
debugSql?: boolean;
}
////////////////////////////////////////
// 2. The shape of what we return
////////////////////////////////////////
export interface DBClient<TSchema extends SchemaDefinition> {
/** Reader: returns plain JS objects, no Proxy. */
rdr: { [TableName in keyof TSchema]: TableReader<TSchema[TableName]> };
/** Writer: partial updates (Proxies). */
wtr: { [TableName in keyof TSchema]: TableWriter<TSchema[TableName]> };
}
/** Reader interface: bracket-get returns plain objects from memory. */
export interface TableReader<TRow> {
[rowId: string]: TRow | undefined;
forEach(callback: (id: string, rowData: TRow) => void): void;
keys(): string[];
values(): TRow[];
entries(): Array<[string, TRow]>;
dict(): Record<string, TRow>;
has(id: string): boolean;
}
/** Writer interface: bracket-get returns a nested Proxy for partial JSON updates. */
export interface TableWriter<TRow> {
[rowId: string]: TRowProxy<TRow>;
forEach(callback: (id: string, rowProxy: TRowProxy<TRow>) => void): void;
keys(): string[];
entries(): Array<[string, TRowProxy<TRow>]>;
has(id: string): boolean;
}
/**
* A nested Proxy that allows partial updates to single fields.
* If you do `writer.users['bob'].nested.foo = 123`,
* it calls `json_set(..., '$.nested.foo', 123)` in the DB.
*/
export type TRowProxy<TRow> = TRow & {
[nestedKey: string]: any;
};
////////////////////////////////////////
// 3. Main entry point
////////////////////////////////////////
export function createDatabaseClient<TSchema extends SchemaDefinition>(
db: Database.Database,
schema: TSchema,
options: CreateDBOptions = {}
): DBClient<TSchema> {
const idColumn = options.idColumn ?? 'id';
const jsonColumn = options.jsonColumn ?? 'data';
const debugSql = !!options.debugSql;
////////////////////////////////////////
// A) In-memory cache: Map<tableName, Map<rowId, object>>
////////////////////////////////////////
const memoryCache = new Map<string, Map<string, any>>();
for (const tableName of Object.keys(schema)) {
memoryCache.set(tableName, new Map());
}
////////////////////////////////////////
// B) Precompiled statements for each table
////////////////////////////////////////
function wrapStmt(stmt: ReturnType<Database.Database['prepare']>, label: string) {
return {
get(...args: any[]) {
if (debugSql) {
console.log(`[SQL GET] ${label}, params: ${JSON.stringify(args)}`);
}
return stmt.get(...args);
},
run(...args: any[]) {
if (debugSql) {
console.log(`[SQL RUN] ${label}, params: ${JSON.stringify(args)}`);
}
return stmt.run(...args);
},
all(...args: any[]) {
if (debugSql) {
console.log(`[SQL ALL] ${label}, params: ${JSON.stringify(args)}`);
}
return stmt.all(...args);
},
};
}
const stmts = new Map<
string,
{
selectRow: ReturnType<typeof wrapStmt>;
upsertWholeRow: ReturnType<typeof wrapStmt>;
deleteRow: ReturnType<typeof wrapStmt>;
jsonSet: ReturnType<typeof wrapStmt>;
jsonRemove: ReturnType<typeof wrapStmt>;
checkExistence: ReturnType<typeof wrapStmt>;
selectAllIds: ReturnType<typeof wrapStmt>;
}
>();
function getStatementsForTable(tableName: string) {
if (stmts.has(tableName)) {
return stmts.get(tableName)!;
}
const selectRowSQL = `
SELECT ${jsonColumn} AS jsonData
FROM ${tableName}
WHERE ${idColumn} = ?`;
const upsertWholeRowSQL = `
INSERT OR REPLACE INTO ${tableName} (${idColumn}, ${jsonColumn})
VALUES (?, json(?))`;
const deleteRowSQL = `
DELETE FROM ${tableName}
WHERE ${idColumn} = ?`;
const jsonSetSQL = `
UPDATE ${tableName}
SET ${jsonColumn} = json_set(${jsonColumn}, ?, json(?))
WHERE ${idColumn} = ?`;
const jsonRemoveSQL = `
UPDATE ${tableName}
SET ${jsonColumn} = json_remove(${jsonColumn}, ?)
WHERE ${idColumn} = ?`;
const checkExistenceSQL = `
SELECT 1 FROM ${tableName}
WHERE ${idColumn} = ?`;
const selectAllIdsSQL = `
SELECT ${idColumn} AS id
FROM ${tableName}`;
const prepared = {
selectRow: wrapStmt(db.prepare(selectRowSQL), `${tableName}:selectRow`),
upsertWholeRow: wrapStmt(db.prepare(upsertWholeRowSQL), `${tableName}:upsertWholeRow`),
deleteRow: wrapStmt(db.prepare(deleteRowSQL), `${tableName}:deleteRow`),
jsonSet: wrapStmt(db.prepare(jsonSetSQL), `${tableName}:jsonSet`),
jsonRemove: wrapStmt(db.prepare(jsonRemoveSQL), `${tableName}:jsonRemove`),
checkExistence: wrapStmt(db.prepare(checkExistenceSQL), `${tableName}:checkExistence`),
selectAllIds: wrapStmt(db.prepare(selectAllIdsSQL), `${tableName}:selectAllIds`),
};
stmts.set(tableName, prepared);
return prepared;
}
////////////////////////////////////////
// C) Helper: load a row's JSON into memory cache if not loaded
////////////////////////////////////////
function loadRow(tableName: string, rowId: string) {
const cacheForTable = memoryCache.get(tableName)!;
if (cacheForTable.has(rowId)) {
return; // already in memory
}
const { selectRow } = getStatementsForTable(tableName);
const row = selectRow.get(rowId);
if (!row) return; // not found in DB
try {
cacheForTable.set(rowId, JSON.parse(row.jsonData));
} catch {
cacheForTable.set(rowId, null);
}
}
////////////////////////////////////////
// D) JSON path helpers for partial updates
////////////////////////////////////////
function pathToJsonPathString(path: string[]) {
if (!path.length) return '$';
return '$.' + path.map(escapeJsonKey).join('.');
}
function escapeJsonKey(k: string): string {
// naive
return k.replace(/"/g, '\\"');
}
////////////////////////////////////////
// E) Row-level Proxy for partial updates
////////////////////////////////////////
function createRowProxy(tableName: string, rowId: string, pathSoFar: string[] = []): any {
return new Proxy(
{},
{
get(_, propKey) {
if (typeof propKey === 'symbol') {
return Reflect.get(_, propKey);
}
loadRow(tableName, rowId);
const cacheForTable = memoryCache.get(tableName)!;
if (!cacheForTable.has(rowId)) {
throw new Error(`Row '${rowId}' not found in table '${tableName}' (read).`);
}
const rowData = cacheForTable.get(rowId);
const newPath = [...pathSoFar, propKey.toString()];
let current: any = rowData;
for (const p of newPath) {
if (current == null || typeof current !== 'object') {
return undefined;
}
current = current[p];
}
// If object or array, return deeper proxy so we can do partial updates
if (current && typeof current === 'object') {
return createRowProxy(tableName, rowId, newPath);
}
return current;
},
set(_, propKey, value) {
loadRow(tableName, rowId);
const cacheForTable = memoryCache.get(tableName)!;
if (!cacheForTable.has(rowId)) {
throw new Error(`Row '${rowId}' not found in table '${tableName}' (write).`);
}
const { jsonSet } = getStatementsForTable(tableName);
const newPath = [...pathSoFar, propKey.toString()];
const jsonPath = pathToJsonPathString(newPath);
jsonSet.run(jsonPath, JSON.stringify(value), rowId);
// Update local cache
const rowData = cacheForTable.get(rowId);
let cursor: any = rowData;
for (let i = 0; i < newPath.length - 1; i++) {
const seg = newPath[i];
if (cursor[seg] == null || typeof cursor[seg] !== 'object') {
cursor[seg] = {};
}
cursor = cursor[seg];
}
cursor[newPath[newPath.length - 1]] = value;
return true;
},
deleteProperty(_, propKey) {
loadRow(tableName, rowId);
const cacheForTable = memoryCache.get(tableName)!;
if (!cacheForTable.has(rowId)) {
throw new Error(`Row '${rowId}' not found in table '${tableName}' (delete).`);
}
// If it looks like a numeric index => forbid
const keyString = propKey.toString();
if (/^\d+$/.test(keyString)) {
throw new Error(
`Deleting array elements by index is not allowed: .${keyString}`
);
}
const { jsonRemove } = getStatementsForTable(tableName);
const newPath = [...pathSoFar, keyString];
const jsonPath = pathToJsonPathString(newPath);
jsonRemove.run(jsonPath, rowId);
// Update in-memory object
const rowData = cacheForTable.get(rowId);
let cursor: any = rowData;
for (let i = 0; i < newPath.length - 1; i++) {
const seg = newPath[i];
if (cursor[seg] == null || typeof cursor[seg] !== 'object') {
return true;
}
cursor = cursor[seg];
}
delete cursor[newPath[newPath.length - 1]];
return true;
},
has(_, propKey) {
if (typeof propKey === 'symbol') {
return Reflect.has(_, propKey);
}
loadRow(tableName, rowId);
const cacheForTable = memoryCache.get(tableName)!;
if (!cacheForTable.has(rowId)) {
return false;
}
const rowData = cacheForTable.get(rowId);
let current = rowData;
for (const p of pathSoFar) {
if (current == null || typeof current !== 'object') {
return false;
}
current = current[p];
}
if (current && typeof current === 'object') {
return Object.prototype.hasOwnProperty.call(current, propKey);
}
return false;
},
}
);
}
////////////////////////////////////////
// F) Create the "Reader" table object
////////////////////////////////////////
function createTableReader(tableName: string): TableReader<any> {
const { selectAllIds, checkExistence } = getStatementsForTable(tableName);
const cacheForTable = memoryCache.get(tableName)!;
const readerImplementation = {
forEach(callback: (id: string, data: any) => void) {
const rows = selectAllIds.all() as Array<{ id: string }>;
for (const r of rows) {
loadRow(tableName, r.id);
const cached = cacheForTable.get(r.id);
if (cached !== undefined) {
callback(r.id, cached);
}
}
},
keys(): string[] {
return selectAllIds.all().map((r: any) => r.id);
},
values(): any[] {
return selectAllIds.all().map((r: any) => cacheForTable.get(r.id));
},
dict(): Record<string, any> {
return selectAllIds.all().reduce((acc, r: any) => {
acc[r.id] = cacheForTable.get(r.id);
return acc;
}, {} as Record<string, any>);
},
entries(): Array<[string, any]> {
return selectAllIds.all().map((r: any) => {
loadRow(tableName, r.id);
return [r.id, cacheForTable.get(r.id)] as [string, any];
});
},
has(id: string) {
if (cacheForTable.has(id)) return true;
const row = checkExistence.get(id);
return !!row;
},
};
return new Proxy(readerImplementation, {
get(target, propKey, receiver) {
if (typeof propKey === 'symbol') {
return Reflect.get(target, propKey, receiver);
}
if (Reflect.has(target, propKey)) {
return Reflect.get(target, propKey, receiver);
}
// otherwise treat propKey as rowId
const rowId = propKey.toString();
loadRow(tableName, rowId);
return cacheForTable.get(rowId);
},
set() {
throw new Error(`Cannot write via Reader API`);
},
deleteProperty() {
throw new Error(`Cannot delete via Reader API`);
},
has(target, propKey) {
if (typeof propKey === 'symbol') {
return Reflect.has(target, propKey);
}
if (Reflect.has(target, propKey)) {
return true;
}
const rowId = propKey.toString();
if (cacheForTable.has(rowId)) {
return true;
}
const row = checkExistence.get(rowId);
return !!row;
},
}) as TableReader<any>;
}
////////////////////////////////////////
// G) Create the "Writer" table object
////////////////////////////////////////
function createTableWriter(tableName: string): TableWriter<any> {
const { checkExistence, selectAllIds, upsertWholeRow, deleteRow } =
getStatementsForTable(tableName);
const cacheForTable = memoryCache.get(tableName)!;
const writerImplementation = {
forEach(callback: (id: string, rowProxy: any) => void) {
const rows = selectAllIds.all() as Array<{ id: string }>;
for (const r of rows) {
loadRow(tableName, r.id);
callback(r.id, createRowProxy(tableName, r.id));
}
},
keys(): string[] {
return selectAllIds.all().map((r: any) => r.id);
},
entries(): Array<[string, any]> {
return selectAllIds.all().map((r: any) => {
loadRow(tableName, r.id);
return [r.id, createRowProxy(tableName, r.id)] as [string, any];
});
},
has(id: string) {
if (cacheForTable.has(id)) return true;
const row = checkExistence.get(id);
return !!row;
},
};
return new Proxy(writerImplementation, {
get(target, propKey, receiver) {
if (typeof propKey === 'symbol') {
return Reflect.get(target, propKey, receiver);
}
if (Reflect.has(target, propKey)) {
return Reflect.get(target, propKey, receiver);
}
const rowId = propKey.toString();
loadRow(tableName, rowId);
return createRowProxy(tableName, rowId);
},
set(_, rowId, value) {
// upsert entire row
const idString = rowId.toString();
cacheForTable.set(idString, value);
upsertWholeRow.run(idString, JSON.stringify(value));
return true;
},
deleteProperty(_, rowId) {
const idString = rowId.toString();
cacheForTable.delete(idString);
deleteRow.run(idString);
return true;
},
has(target, propKey) {
if (typeof propKey === 'symbol') {
return Reflect.has(target, propKey);
}
if (Reflect.has(target, propKey)) {
return true;
}
const rowId = propKey.toString();
if (cacheForTable.has(rowId)) {
return true;
}
const row = checkExistence.get(rowId);
return !!row;
},
}) as TableWriter<any>;
}
////////////////////////////////////////
// H) Build the overall "rdr" and "wtr" objects
////////////////////////////////////////
const rdrObj = {} as DBClient<TSchema>['rdr'];
const wtrObj = {} as DBClient<TSchema>['wtr'];
for (const tableName of Object.keys(schema)) {
Object.defineProperty(rdrObj, tableName, {
value: createTableReader(tableName),
enumerable: true,
configurable: false,
writable: false,
});
Object.defineProperty(wtrObj, tableName, {
value: createTableWriter(tableName),
enumerable: true,
configurable: false,
writable: false,
});
}
return {
rdr: rdrObj,
wtr: wtrObj,
};
}
@vedantroy
Copy link
Author

vedantroy commented Dec 22, 2024

Tests:

// node --import tsx --test **/*.test.ts
import { test } from 'node:test';
import * as assert from 'node:assert';
import Database from 'better-sqlite3';
import { createDatabaseClient } from './proxy'; 

// Sample types
interface User {
  name: string;
  age?: number;
  nested?: {
    foo?: {
      bar?: number;
    };
    array?: number[];
    items?: Array<{
      type: string;
      weight: number;
    }>;
    someString?: string;
    emptyObj?: Record<string, unknown>;
  };
}

interface Generation {
  createdBy: string;
  config?: {
    flags?: string[];
  };
}

interface MySchema {
  users: User;
  generations: Generation;
}

test('db-proxy-partial (with separate reader/writer): Basic CRUD + partial updates', async (t) => {
  // 1) Initialize an in-memory DB and create our tables
  const db = new Database(':memory:');
  db.exec(`
    CREATE TABLE users (
      id TEXT PRIMARY KEY,
      data JSON
    );
  `);
  db.exec(`
    CREATE TABLE generations (
      id TEXT PRIMARY KEY,
      data JSON
    );
  `);

  // 2) Create the parallel client with separate .rdr/.wtr
  const { rdr, wtr } = createDatabaseClient<MySchema>(db, {
    users: {} as User,
    generations: {} as Generation,
  });

  // ---------------------------------------------------------
  // 1. Basic insert
  // ---------------------------------------------------------
  await t.test('1. Basic insert', () => {
    wtr.users['alice'] = { name: 'Alice', age: 30 };
    const alice = rdr.users['alice'];
    assert.ok(alice, '#1.1 alice should exist');
    assert.strictEqual(alice?.name, 'Alice', '#1.2 name should be Alice');
    assert.strictEqual(alice?.age, 30, '#1.3 age should be 30');
    assert.deepStrictEqual(alice, { name: 'Alice', age: 30 }, '#1.4 full object should match');
  });

  // ---------------------------------------------------------
  // 2. Nested set (partial update)
  // ---------------------------------------------------------
  await t.test('2. Nested set (partial update)', () => {
    wtr.users['alice'].nested = { foo: { bar: 42 } };
    assert.deepStrictEqual(rdr.users['alice']?.nested, { foo: { bar: 42 } }, '#2.1 nested object should match');
  });

  // ---------------------------------------------------------
  // 3. Another nested set
  // ---------------------------------------------------------
  await t.test('3. Another nested set', () => {
    wtr.users['alice'].nested.foo.bar = 999;
    assert.strictEqual(rdr.users['alice']?.nested?.foo?.bar, 999, '#3.1 nested value should be 999');
  });

  // ---------------------------------------------------------
  // 4. Create new path that doesn't exist
  // ---------------------------------------------------------
  await t.test('4. Create new path that does not exist', () => {
    wtr.users['alice'].nested.array = [1, 2, 3];
    assert.deepStrictEqual(rdr.users['alice']?.nested?.array, [1, 2, 3], '#4.1 array should match');
  });

  // ---------------------------------------------------------
  // 5. Nested delete
  // ---------------------------------------------------------
  await t.test('5. Nested delete', () => {
    delete wtr.users['alice'].nested.foo.bar;
    assert.deepStrictEqual(rdr.users['alice']?.nested?.foo, {}, '#5.1 foo should be empty object');
  });

  // ---------------------------------------------------------
  // 6. Delete entire row
  // ---------------------------------------------------------
  await t.test('6. Delete entire row', () => {
    delete wtr.users['alice'];
    assert.strictEqual(rdr.users['alice'], undefined, '#6.1 alice should be undefined');
    assert.throws(() => {
      console.log(wtr.users['alice'].name);
    }, /not found in table 'users'/, '#6.2 should throw not found error');
  });

  // ---------------------------------------------------------
  // 7. Insert second table
  // ---------------------------------------------------------
  await t.test('7. Insert second table', () => {
    wtr.generations['gen1'] = { createdBy: 'system' };
    assert.strictEqual(rdr.generations['gen1']?.createdBy, 'system', '#7.1 createdBy should be system');
  });

  // ---------------------------------------------------------
  // 8. forEach, keys, entries
  // ---------------------------------------------------------
  await t.test('8. forEach, keys, entries', () => {
    wtr.users['bob'] = { name: 'Bob' };
    wtr.users['charlie'] = { name: 'Charlie' };

    const userKeys = rdr.users.keys().sort();
    assert.deepStrictEqual(userKeys, ['bob', 'charlie'], '#8.1 keys should match');

    const userEntries = rdr.users.entries();
    assert.strictEqual(userEntries.length, 2, '#8.2 should have 2 entries');

    let forEachCount = 0;
    rdr.users.forEach((id, rowProxy) => {
      forEachCount++;
      assert.ok(['bob', 'charlie'].includes(id), '#8.3 id should be bob or charlie');
      rowProxy.age = forEachCount * 10; 
    });
    assert.strictEqual(forEachCount, 2, '#8.4 should have iterated twice');

    assert.strictEqual(rdr.users['bob']?.age, 10, '#8.5 bob age should be 10');
    assert.strictEqual(rdr.users['charlie']?.age, 20, '#8.6 charlie age should be 20');
  });

  // ---------------------------------------------------------
  // 9. 'has' checks
  // ---------------------------------------------------------
  await t.test('9. "has" checks', () => {
    assert.ok(rdr.users.has('bob'), '#9.1 should have bob');
    assert.ok(!rdr.users.has('alice'), '#9.2 should not have alice'); 
    assert.ok('bob' in rdr.users, '#9.3 bob should be in users');
    assert.ok(!('alice' in rdr.users), '#9.4 alice should not be in users');
    assert.ok('nested' in rdr.users['bob'] === false, '#9.5 nested should not be in bob');
  });

  // ---------------------------------------------------------
  // 10. Setting deeply nested object in one shot
  // ---------------------------------------------------------
  await t.test('10. Setting deeply nested object in one shot', () => {
    wtr.users['bob'].nested = { foo: { bar: 123 } };
    assert.strictEqual(rdr.users['bob']?.nested?.foo?.bar, 123, '#10.1 nested value should be 123');
  });

  // ---------------------------------------------------------
  // 11. Array manipulations
  // ---------------------------------------------------------
  await t.test('11. Array manipulations', () => {
    wtr.users['charlie'].nested = { array: [10, 20, 30] };
    assert.deepStrictEqual(rdr.users['charlie']?.nested?.array, [10, 20, 30], '#11.1 array should match');

    wtr.users['charlie'].nested.array[1] = 999;
    assert.deepStrictEqual(rdr.users['charlie']?.nested?.array, [10, 999, 30], '#11.2 array with updated index should match');
  });

  // ---------------------------------------------------------
  // 12. Attempt out-of-range indexing
  // ---------------------------------------------------------
  await t.test('12. Attempt out-of-range indexing', () => {
    assert.throws(() => {
      delete wtr.users['charlie'].nested.array[2];
    }, /Deleting array elements by index is not allowed/, '#12.1 should throw on array element deletion');

    assert.throws(() => {
      const index = 2;
      delete wtr.users['charlie'].nested.array[index];
    }, /Deleting array elements by index is not allowed/, '#12.2 should throw on array element deletion with variable');
  });

  // ---------------------------------------------------------
  // 13. Check top-level upsert overwriting partial changes
  // ---------------------------------------------------------
  await t.test('13. Overwrite entire row with top-level upsert', () => {
    wtr.users['charlie'] = { name: 'CHAZ' };
    assert.strictEqual(rdr.users['charlie']?.name, 'CHAZ', '#13.1 name should be CHAZ');
    assert.strictEqual(rdr.users['charlie']?.nested, undefined, '#13.2 nested should be undefined');
  });

  // ---------------------------------------------------------
  // 14. ensure generation table is unaffected
  // ---------------------------------------------------------
  await t.test('14. Check generation table still good', () => {
    assert.strictEqual(rdr.generations['gen1']?.createdBy, 'system', '#14.1 generation should be unchanged');
  });

  // ---------------------------------------------------------
  // 15. Insert a new row with complex nested data + array of objects
  // ---------------------------------------------------------
  await t.test('15. Insert user with nested items array', () => {
    wtr.users['david'] = {
      name: 'David',
      nested: {
        items: [
          { type: 'apple', weight: 2 },
          { type: 'banana', weight: 4 },
          { type: 'cherry', weight: 1 },
        ]
      }
    };
    const user = rdr.users['david'];
    assert.ok(user, '#15.1 david user should exist');
    assert.strictEqual(user?.name, 'David', '#15.2 name should be David');
    assert.deepStrictEqual(user?.nested?.items, [
      { type: 'apple', weight: 2 },
      { type: 'banana', weight: 4 },
      { type: 'cherry', weight: 1 },
    ], '#15.3 items array should match');
  });

  // ---------------------------------------------------------
  // 16. Filter array items in place (simulate array transformation)
  // ---------------------------------------------------------
  await t.test('16. Filter items in nested array', () => {
    const currentItems = rdr.users['david']?.nested?.items ?? [];
    const filtered = currentItems.filter(item => item.weight < 3);
    wtr.users['david'].nested.items = filtered;
    assert.deepStrictEqual(rdr.users['david']?.nested?.items, [
      { type: 'apple', weight: 2 },
      { type: 'cherry', weight: 1 },
    ], '#16.1 after filter, only apple/cherry remain');
  });

  // ---------------------------------------------------------
  // 17. Add items to an existing items array
  // ---------------------------------------------------------
  await t.test('17. Push new item to existing array (by reassigning)', () => {
    const current = rdr.users['david']?.nested?.items ?? [];
    const newItems = [...current, { type: 'date', weight: 2 }];
    wtr.users['david'].nested.items = newItems;
    const updated = rdr.users['david']?.nested?.items ?? [];
    assert.deepStrictEqual(updated.map(i => i.type), ['apple', 'cherry', 'date'], '#17.1 new item is appended');
  });

  // ---------------------------------------------------------
  // 18. Overwrite entire nested object
  // ---------------------------------------------------------
  await t.test('18. Overwrite entire "nested" object with new structure', () => {
    wtr.users['david'].nested = {
      foo: { bar: 777 },
    };
    assert.strictEqual(rdr.users['david']?.nested?.foo?.bar, 777, '#18.1 bar should now be 777');
    assert.strictEqual(rdr.users['david']?.nested?.items, undefined, '#18.2 items should be gone');
  });

  // ---------------------------------------------------------
  // 19. Revert "nested" back to an array-based structure
  // ---------------------------------------------------------
  await t.test('19. Reassign nested with array of objects again', () => {
    wtr.users['david'].nested = {
      items: [
        { type: 'guava', weight: 3 },
      ]
    };
    assert.deepStrictEqual(rdr.users['david']?.nested?.items, [
      { type: 'guava', weight: 3 }
    ], '#19.1 items should have single guava');
    assert.strictEqual(rdr.users['david']?.nested?.foo, undefined, '#19.2 foo should be gone');
  });

  // ---------------------------------------------------------
  // 20. Attempt to delete a property that doesn't exist
  // ---------------------------------------------------------
  await t.test('20. Deleting non-existent property in nested object', () => {
    delete wtr.users['david'].nested.notARealProp;
    assert.ok(true, '#20.1 deleting a non-existent property should not throw');
  });

  // ---------------------------------------------------------
  // 21. Attempt to partial-update a property that does not exist yet
  // ---------------------------------------------------------
  await t.test('21. Creating a new nested object on the fly', () => {
    wtr.users['david'].nested.newProp = { hello: 'world' };
    assert.deepStrictEqual(rdr.users['david']?.nested?.newProp, { hello: 'world' }, '#21.1 newProp should match');
  });

  // ---------------------------------------------------------
  // 22. Check "in" operator on a deeply nested property
  // ---------------------------------------------------------
  await t.test('22. "in" operator on deeply nested property', () => {
    assert.ok('newProp' in rdr.users['david']?.nested!, '#22.1 newProp should exist');
    assert.ok(!('fakeKey' in rdr.users['david']?.nested!), '#22.2 fakeKey should not exist');
  });

  // ---------------------------------------------------------
  // 23. Attempt partial update with null
  // ---------------------------------------------------------
  await t.test('23. Partial update a field to null', () => {
    wtr.users['david'].nested.newProp = null;
    assert.strictEqual(rdr.users['david']?.nested?.newProp, null, '#23.1 newProp should now be null');
  });

  // ---------------------------------------------------------
  // 24. Insert a generation row with config
  // ---------------------------------------------------------
  await t.test('24. Insert generation with config flags', () => {
    wtr.generations['gen2'] = { 
      createdBy: 'tester',
      config: { flags: ['flag1', 'flag2'] }
    };
    assert.deepStrictEqual(rdr.generations['gen2']?.config?.flags, ['flag1', 'flag2'], '#24.1 flags should match');
  });

  // ---------------------------------------------------------
  // 25. Partial update on generation config
  // ---------------------------------------------------------
  await t.test('25. Append a new flag to generation config (by reassigning)', () => {
    const currentFlags = rdr.generations['gen2']?.config?.flags ?? [];
    const newFlags = [...currentFlags, 'flag3'];
    wtr.generations['gen2'].config.flags = newFlags;
    assert.deepStrictEqual(rdr.generations['gen2']?.config?.flags, ['flag1', 'flag2', 'flag3'], '#25.1 new flag appended');
  });

  // ---------------------------------------------------------
  // 26. Deleting a nested property in generation config
  // ---------------------------------------------------------
  await t.test('26. Delete nested property in generation config', () => {
    delete wtr.generations['gen2'].config.flags;
    assert.ok(!('flags' in rdr.generations['gen2']?.config!), '#26.1 flags should not exist anymore');
  });

  // ---------------------------------------------------------
  // 27. Checking presence of generation row
  // ---------------------------------------------------------
  await t.test('27. "has" checks on generation table', () => {
    assert.ok(rdr.generations.has('gen1'), '#27.1 should have gen1');
    assert.ok(rdr.generations.has('gen2'), '#27.2 should have gen2');
    assert.ok(!rdr.generations.has('genX'), '#27.3 should not have genX');
  });

  // ---------------------------------------------------------
  // 28. "in" operator checks for generation table
  // ---------------------------------------------------------
  await t.test('28. "in" operator for generation', () => {
    assert.ok('gen1' in rdr.generations, '#28.1 gen1 should exist in generations');
    assert.ok(!('genX' in rdr.generations), '#28.2 genX should not exist');
  });

  // ---------------------------------------------------------
  // 29. Overwrite generation row
  // ---------------------------------------------------------
  await t.test('29. Overwrite generation row', () => {
    wtr.generations['gen2'] = { createdBy: 'overwriter' };
    assert.strictEqual(rdr.generations['gen2']?.createdBy, 'overwriter', '#29.1 createdBy = overwriter');
    assert.strictEqual(rdr.generations['gen2']?.config, undefined, '#29.2 config should be gone');
  });

  // ---------------------------------------------------------
  // 30. Delete generation row
  // ---------------------------------------------------------
  await t.test('30. Delete generation row gen1', () => {
    delete wtr.generations['gen1'];
    assert.strictEqual(rdr.generations['gen1'], undefined, '#30.1 gen1 should be undefined now');
  });

  // ---------------------------------------------------------
  // 31. "in" check on missing nested property
  // ---------------------------------------------------------
  await t.test('31. "in" check for missing nested property', () => {
    assert.ok(!('nope' in rdr.users['bob']?.nested!), '#31.1 nope property not in bob.nested');
  });

  // ---------------------------------------------------------
  // 32. Reassign entire row with partial overlap
  // ---------------------------------------------------------
  await t.test('32. Reassign entire bob row with partial overlap', () => {
    wtr.users['bob'] = { name: 'Bobby', nested: { foo: { bar: 456 } } };
    const bob = rdr.users['bob'];
    assert.strictEqual(bob?.name, 'Bobby', '#32.1 name changed to Bobby');
    assert.strictEqual(bob?.age, undefined, '#32.2 age removed');
    assert.deepStrictEqual(bob?.nested, { foo: { bar: 456 } }, '#32.3 new nested structure');
  });

  // ---------------------------------------------------------
  // 33. Reassign entire row again
  // ---------------------------------------------------------
  await t.test('33. Another reassign bob row', () => {
    wtr.users['bob'] = { name: 'Bob 2.0' };
    const bob = rdr.users['bob'];
    assert.strictEqual(bob?.name, 'Bob 2.0', '#33.1 name changed');
    assert.strictEqual(bob?.nested, undefined, '#33.2 nested is gone');
  });

  // ---------------------------------------------------------
  // 34. Insert user ethan, then nest partial props
  // ---------------------------------------------------------
  await t.test('34. Insert user ethan', () => {
    wtr.users['ethan'] = { name: 'Ethan' };
    wtr.users['ethan'].nested = {};
    wtr.users['ethan'].nested.foo = {};
    wtr.users['ethan'].nested.foo.bar = 10;
    assert.strictEqual(rdr.users['ethan']?.nested?.foo?.bar, 10, '#34.1 bar is 10');
  });

  // ---------------------------------------------------------
  // 35. Assign nested array of objects in ethan
  // ---------------------------------------------------------
  await t.test('35. Assign nested array of objects in "ethan"', () => {
    wtr.users['ethan'].nested.array = [
      { type: 'A', val: 1 },
      { type: 'B', val: 2 },
    ];
    assert.deepStrictEqual(rdr.users['ethan']?.nested?.array, [
      { type: 'A', val: 1 },
      { type: 'B', val: 2 },
    ], '#35.1 array of objects match');
  });

  // ---------------------------------------------------------
  // 36. Modify an object inside ethan's array
  // ---------------------------------------------------------
  await t.test('36. Modify nested array item property', () => {
    wtr.users['ethan'].nested.array[1].val = 999;
    assert.deepStrictEqual(rdr.users['ethan']?.nested?.array, [
      { type: 'A', val: 1 },
      { type: 'B', val: 999 },
    ], '#36.1 second item val changed to 999');
  });

  // ---------------------------------------------------------
  // 37. Reassign array to simulate splice
  // ---------------------------------------------------------
  await t.test('37. Reassign array to simulate splice', () => {
    const currentArr = rdr.users['ethan']?.nested?.array ?? [];
    const newArr = currentArr.slice(1); // remove first element
    wtr.users['ethan'].nested.array = newArr;
    assert.deepStrictEqual(rdr.users['ethan']?.nested?.array, [
      { type: 'B', val: 999 },
    ], '#37.1 first item removed');
  });

  // ---------------------------------------------------------
  // 38. Nested property with string
  // ---------------------------------------------------------
  await t.test('38. Setting nested string property', () => {
    wtr.users['ethan'].nested.someString = 'Hello, world!';
    assert.strictEqual(rdr.users['ethan']?.nested?.someString, 'Hello, world!', '#38.1 someString match');
  });

  // ---------------------------------------------------------
  // 39. Setting a property to an empty object
  // ---------------------------------------------------------
  await t.test('39. Setting a property to empty object', () => {
    wtr.users['ethan'].nested.emptyObj = {};
    assert.deepStrictEqual(rdr.users['ethan']?.nested?.emptyObj, {}, '#39.1 emptyObj = {}');
  });

  // ---------------------------------------------------------
  // 40. Final check: multiple rows exist
  // ---------------------------------------------------------
  await t.test('40. Final row count checks', () => {
    // Currently, we have bob, charlie, david, ethan
    const userKeys = rdr.users.keys().sort();
    assert.deepStrictEqual(userKeys, ['bob', 'charlie', 'david', 'ethan'], '#40.1 four users remain');

    // generations: only gen2 remains
    const genKeys = rdr.generations.keys().sort();
    assert.deepStrictEqual(genKeys, ['gen2'], '#40.2 only gen2 remains');
    assert.strictEqual(rdr.generations['gen2']?.createdBy, 'overwriter', '#40.3 createdBy = overwriter');
  });

  // ---------------------------------------------------------
  // 41. Demonstrate ++ operator
  // ---------------------------------------------------------
  await t.test('41. ++ operator works', () => {
    wtr.users['bob'].age = 2;
    assert.strictEqual(rdr.users['bob']?.age, 2, '#41.1 age should be 2');
    wtr.users['bob'].age++;
    assert.strictEqual(rdr.users['bob']?.age, 3, '#41.2 age should be 3');
  });

  // ---------------------------------------------------------
  // 42. Demonstrate Object.keys on a row
  // ---------------------------------------------------------
  await t.test('42. Object.keys on a row from rdr', () => {
    // Let's check 'ethan' since it has multiple properties
    const ethanObj = rdr.users['ethan'];
    // We expect: name, nested
    const keys = Object.keys(ethanObj!).sort();
    assert.deepStrictEqual(keys, ['name', 'nested'], '#42.1 ethan should have name + nested');

    // Also, let's do the same for 'bob' now that he has an age
    const bobObj = rdr.users['bob'];
    const bobKeys = Object.keys(bobObj!).sort();
    // By test #41, bob has name: 'Bob 2.0', age: 3
    assert.deepStrictEqual(bobKeys, ['age', 'name'], '#42.2 bob has age + name');
  });

  await t.test('42. values', () => {
    delete wtr.users['ethan']
    delete wtr.users['david']
    const values = rdr.users.values();
    assert.deepStrictEqual(values, [
      { name: 'Bob 2.0', age: 3 },
      { name: 'CHAZ' },
    ]);
  });

  await t.test('43. dict', () => {
    const dict = rdr.users.dict();
    assert.deepStrictEqual(dict, {
      bob: { name: 'Bob 2.0', age: 3 },
      charlie: { name: 'CHAZ' },
    });
  });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment